//further progress
This commit is contained in:
parent
7ea5e9414d
commit
ceb05fc3f7
15 changed files with 373 additions and 651 deletions
|
@ -77,10 +77,8 @@ require_once 'src/manage.php';
|
||||||
require_once 'src/url.php';
|
require_once 'src/url.php';
|
||||||
require_once 'src/Forum/perms.php';
|
require_once 'src/Forum/perms.php';
|
||||||
require_once 'src/Forum/forum.php';
|
require_once 'src/Forum/forum.php';
|
||||||
require_once 'src/Forum/leaderboard.php';
|
|
||||||
require_once 'src/Forum/post.php';
|
require_once 'src/Forum/post.php';
|
||||||
require_once 'src/Forum/topic.php';
|
require_once 'src/Forum/topic.php';
|
||||||
require_once 'src/Forum/validate.php';
|
|
||||||
|
|
||||||
$dbConfig = parse_ini_file(MSZ_CONFIG . '/config.ini', true, INI_SCANNER_TYPED);
|
$dbConfig = parse_ini_file(MSZ_CONFIG . '/config.ini', true, INI_SCANNER_TYPED);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Misuzu;
|
namespace Misuzu;
|
||||||
|
|
||||||
|
use Misuzu\Forum\ForumLeaderboard;
|
||||||
use Misuzu\Users\User;
|
use Misuzu\Users\User;
|
||||||
|
|
||||||
require_once '../../misuzu.php';
|
require_once '../../misuzu.php';
|
||||||
|
@ -14,7 +15,7 @@ $leaderboardMode = !empty($_GET['mode']) && is_string($_GET['mode']) && ctype_lo
|
||||||
$leaderboardId = !empty($_GET['id']) && is_string($_GET['id'])
|
$leaderboardId = !empty($_GET['id']) && is_string($_GET['id'])
|
||||||
&& ctype_digit($_GET['id'])
|
&& ctype_digit($_GET['id'])
|
||||||
? $_GET['id']
|
? $_GET['id']
|
||||||
: MSZ_FORUM_LEADERBOARD_CATEGORY_ALL;
|
: ForumLeaderboard::CATEGORY_ALL;
|
||||||
$leaderboardIdLength = strlen($leaderboardId);
|
$leaderboardIdLength = strlen($leaderboardId);
|
||||||
|
|
||||||
$leaderboardYear = $leaderboardIdLength === 4 || $leaderboardIdLength === 6 ? substr($leaderboardId, 0, 4) : null;
|
$leaderboardYear = $leaderboardIdLength === 4 || $leaderboardIdLength === 6 ? substr($leaderboardId, 0, 4) : null;
|
||||||
|
@ -22,8 +23,8 @@ $leaderboardMonth = $leaderboardIdLength === 6 ? substr($leaderboardId, 4, 2) :
|
||||||
|
|
||||||
$unrankedForums = !empty($_GET['allow_unranked']) ? [] : Config::get('forum_leader.unranked.forum', Config::TYPE_ARR);
|
$unrankedForums = !empty($_GET['allow_unranked']) ? [] : Config::get('forum_leader.unranked.forum', Config::TYPE_ARR);
|
||||||
$unrankedTopics = !empty($_GET['allow_unranked']) ? [] : Config::get('forum_leader.unranked.topic', Config::TYPE_ARR);
|
$unrankedTopics = !empty($_GET['allow_unranked']) ? [] : Config::get('forum_leader.unranked.topic', Config::TYPE_ARR);
|
||||||
$leaderboards = forum_leaderboard_categories();
|
$leaderboards = ForumLeaderboard::categories();
|
||||||
$leaderboard = forum_leaderboard_listing($leaderboardYear, $leaderboardMonth, $unrankedForums, $unrankedTopics);
|
$leaderboard = ForumLeaderboard::listing($leaderboardYear, $leaderboardMonth, $unrankedForums, $unrankedTopics);
|
||||||
|
|
||||||
$leaderboardName = 'All Time';
|
$leaderboardName = 'All Time';
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,10 @@ use Misuzu\Forum\ForumCategory;
|
||||||
use Misuzu\Forum\ForumCategoryNotFoundException;
|
use Misuzu\Forum\ForumCategoryNotFoundException;
|
||||||
use Misuzu\Forum\ForumTopic;
|
use Misuzu\Forum\ForumTopic;
|
||||||
use Misuzu\Forum\ForumTopicNotFoundException;
|
use Misuzu\Forum\ForumTopicNotFoundException;
|
||||||
|
use Misuzu\Forum\ForumTopicCreationFailedException;
|
||||||
|
use Misuzu\Forum\ForumTopicUpdateFailedException;
|
||||||
|
use Misuzu\Forum\ForumPost;
|
||||||
|
use Misuzu\Forum\ForumPostNotFoundException;
|
||||||
use Misuzu\Net\IPAddress;
|
use Misuzu\Net\IPAddress;
|
||||||
use Misuzu\Parsers\Parser;
|
use Misuzu\Parsers\Parser;
|
||||||
use Misuzu\Users\User;
|
use Misuzu\Users\User;
|
||||||
|
@ -130,6 +134,7 @@ if($mode === 'edit') {
|
||||||
}
|
}
|
||||||
|
|
||||||
$notices = [];
|
$notices = [];
|
||||||
|
$isNewTopic = false;
|
||||||
|
|
||||||
if(!empty($_POST)) {
|
if(!empty($_POST)) {
|
||||||
$topicTitle = $_POST['post']['title'] ?? '';
|
$topicTitle = $_POST['post']['title'] ?? '';
|
||||||
|
@ -141,7 +146,7 @@ if(!empty($_POST)) {
|
||||||
if(!CSRF::validateRequest()) {
|
if(!CSRF::validateRequest()) {
|
||||||
$notices[] = 'Could not verify request.';
|
$notices[] = 'Could not verify request.';
|
||||||
} else {
|
} else {
|
||||||
$isEditingTopic = empty($topicInfo) || ($mode === 'edit' && $post['is_opening_post']);
|
$isEditingTopic = $isNewTopic || ($mode === 'edit' && $post['is_opening_post']);
|
||||||
|
|
||||||
if($mode === 'create') {
|
if($mode === 'create') {
|
||||||
$timeoutCheck = max(1, forum_timeout($forumInfo->getId(), $currentUserId));
|
$timeoutCheck = max(1, forum_timeout($forumInfo->getId(), $currentUserId));
|
||||||
|
@ -153,20 +158,14 @@ if(!empty($_POST)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if($isEditingTopic) {
|
if($isEditingTopic) {
|
||||||
$originalTopicTitle = empty($topicInfo) ? null : $topicInfo->getTitle();
|
$originalTopicTitle = $isNewTopic ? null : $topicInfo->getTitle();
|
||||||
$topicTitleChanged = $topicTitle !== $originalTopicTitle;
|
$topicTitleChanged = $topicTitle !== $originalTopicTitle;
|
||||||
$originalTopicType = empty($topicInfo) ? ForumTopic::TYPE_DISCUSSION : $topicInfo->getType();
|
$originalTopicType = $isNewTopic ? ForumTopic::TYPE_DISCUSSION : $topicInfo->getType();
|
||||||
$topicTypeChanged = $topicType !== null && $topicType !== $originalTopicType;
|
$topicTypeChanged = $topicType !== null && $topicType !== $originalTopicType;
|
||||||
|
|
||||||
switch(forum_validate_title($topicTitle)) {
|
$validateTopicTitle = ForumTopic::validateTitle($topicTitle);
|
||||||
case 'too-short':
|
if(!empty($validateTopicTitle))
|
||||||
$notices[] = 'Topic title was too short.';
|
$notices[] = ForumTopic::titleValidationErrorString($validateTopicTitle);
|
||||||
break;
|
|
||||||
|
|
||||||
case 'too-long':
|
|
||||||
$notices[] = 'Topic title was too long.';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if($mode === 'create' && $topicType === null) {
|
if($mode === 'create' && $topicType === null) {
|
||||||
$topicType = array_key_first($topicTypes);
|
$topicType = array_key_first($topicTypes);
|
||||||
|
@ -175,19 +174,12 @@ if(!empty($_POST)) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!Parser::isValid($postParser)) {
|
if(!Parser::isValid($postParser))
|
||||||
$notices[] = 'Invalid parser selected.';
|
$notices[] = 'Invalid parser selected.';
|
||||||
}
|
|
||||||
|
|
||||||
switch(forum_validate_post($postText)) {
|
$postBodyValidation = ForumPost::validateBody($postText);
|
||||||
case 'too-short':
|
if(!empty($postBodyValidation))
|
||||||
$notices[] = 'Post content was too short.';
|
$notices[] = ForumPost::bodyValidationErrorString($postBodyValidation);
|
||||||
break;
|
|
||||||
|
|
||||||
case 'too-long':
|
|
||||||
$notices[] = 'Post content was too long.';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(empty($notices)) {
|
if(empty($notices)) {
|
||||||
switch($mode) {
|
switch($mode) {
|
||||||
|
@ -195,12 +187,9 @@ if(!empty($_POST)) {
|
||||||
if(!empty($topicInfo)) {
|
if(!empty($topicInfo)) {
|
||||||
$topicInfo->bumpTopic();
|
$topicInfo->bumpTopic();
|
||||||
} else {
|
} else {
|
||||||
$topicId = forum_topic_create(
|
$isNewTopic = true;
|
||||||
$forumInfo->getId(),
|
$topicInfo = ForumTopic::create($forumInfo, $currentUser, $topicTitle, $topicType);
|
||||||
$currentUserId,
|
$topicId = $topicInfo->getId();
|
||||||
$topicTitle,
|
|
||||||
$topicType
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$postId = forum_post_create(
|
$postId = forum_post_create(
|
||||||
|
@ -213,7 +202,7 @@ if(!empty($_POST)) {
|
||||||
$postSignature
|
$postSignature
|
||||||
);
|
);
|
||||||
forum_topic_mark_read($currentUserId, $topicId, $forumInfo->getId());
|
forum_topic_mark_read($currentUserId, $topicId, $forumInfo->getId());
|
||||||
$forumInfo->increaseTopicPostCount(empty($topicInfo));
|
$forumInfo->increaseTopicPostCount($isNewTopic);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'edit':
|
case 'edit':
|
||||||
|
@ -222,7 +211,11 @@ if(!empty($_POST)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if($isEditingTopic && ($topicTitleChanged || $topicTypeChanged)) {
|
if($isEditingTopic && ($topicTitleChanged || $topicTypeChanged)) {
|
||||||
if(!forum_topic_update($topicId, $topicTitle, $topicType)) {
|
$topicInfo->setTitle($topicTitle)->setType($topicType);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$topicInfo->update();
|
||||||
|
} catch(ForumTopicUpdateFailedException $ex) {
|
||||||
$notices[] = 'Topic update failed.';
|
$notices[] = 'Topic update failed.';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -230,7 +223,7 @@ if(!empty($_POST)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(empty($notices)) {
|
if(empty($notices)) {
|
||||||
$redirect = url(empty($topicInfo) ? 'forum-topic' : 'forum-post', [
|
$redirect = url($isNewTopic ? 'forum-topic' : 'forum-post', [
|
||||||
'topic' => $topicId ?? 0,
|
'topic' => $topicId ?? 0,
|
||||||
'post' => $postId ?? 0,
|
'post' => $postId ?? 0,
|
||||||
'post_fragment' => 'p' . ($postId ?? 0),
|
'post_fragment' => 'p' . ($postId ?? 0),
|
||||||
|
@ -242,7 +235,7 @@ if(!empty($_POST)) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!empty($topicInfo)) {
|
if(!$isNewTopic && !empty($topicInfo)) {
|
||||||
Template::set('posting_topic', $topicInfo);
|
Template::set('posting_topic', $topicInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,7 @@ $canNukeOrRestore = $canDeleteAny && $topicInfo->isDeleted();
|
||||||
$canDelete = !$topicInfo->isDeleted() && (
|
$canDelete = !$topicInfo->isDeleted() && (
|
||||||
$canDeleteAny || (
|
$canDeleteAny || (
|
||||||
$topicPostsTotal > 0
|
$topicPostsTotal > 0
|
||||||
&& $topicPostsTotal <= MSZ_FORUM_TOPIC_DELETE_POST_LIMIT
|
&& $topicPostsTotal <= ForumTopic::DELETE_POST_LIMIT
|
||||||
&& $canDeleteOwn
|
&& $canDeleteOwn
|
||||||
&& $topicInfo->getUserId() === $topicUserId
|
&& $topicInfo->getUserId() === $topicUserId
|
||||||
)
|
)
|
||||||
|
@ -104,47 +104,20 @@ if(in_array($moderationMode, $validModerationModes, true)) {
|
||||||
|
|
||||||
switch($moderationMode) {
|
switch($moderationMode) {
|
||||||
case 'delete':
|
case 'delete':
|
||||||
$canDeleteCode = forum_topic_can_delete($topicInfo, $topicUserId);
|
$canDeleteCodes = [
|
||||||
$canDeleteMsg = '';
|
'view' => 404,
|
||||||
$responseCode = 200;
|
'deleted' => 404,
|
||||||
|
'owner' => 403,
|
||||||
|
'age' => 403,
|
||||||
|
'permission' => 403,
|
||||||
|
'posts' => 403,
|
||||||
|
'' => 200,
|
||||||
|
];
|
||||||
|
$canDelete = $topicInfo->canDelete($topicUser);
|
||||||
|
$canDeleteMsg = ForumTopic::canDeleteErrorString($canDelete);
|
||||||
|
$responseCode = $canDeleteCodes[$canDelete] ?? 500;
|
||||||
|
|
||||||
switch($canDeleteCode) {
|
if($canDelete !== '') {
|
||||||
case MSZ_E_FORUM_TOPIC_DELETE_USER:
|
|
||||||
$responseCode = 401;
|
|
||||||
$canDeleteMsg = 'You must be logged in to delete topics.';
|
|
||||||
break;
|
|
||||||
case MSZ_E_FORUM_TOPIC_DELETE_TOPIC:
|
|
||||||
$responseCode = 404;
|
|
||||||
$canDeleteMsg = "This topic doesn't exist.";
|
|
||||||
break;
|
|
||||||
case MSZ_E_FORUM_TOPIC_DELETE_DELETED:
|
|
||||||
$responseCode = 404;
|
|
||||||
$canDeleteMsg = 'This topic has already been marked as deleted.';
|
|
||||||
break;
|
|
||||||
case MSZ_E_FORUM_TOPIC_DELETE_OWNER:
|
|
||||||
$responseCode = 403;
|
|
||||||
$canDeleteMsg = 'You can only delete your own topics.';
|
|
||||||
break;
|
|
||||||
case MSZ_E_FORUM_TOPIC_DELETE_OLD:
|
|
||||||
$responseCode = 401;
|
|
||||||
$canDeleteMsg = 'This topic has existed for too long. Ask a moderator to remove if it absolutely necessary.';
|
|
||||||
break;
|
|
||||||
case MSZ_E_FORUM_TOPIC_DELETE_PERM:
|
|
||||||
$responseCode = 401;
|
|
||||||
$canDeleteMsg = 'You are not allowed to delete topics.';
|
|
||||||
break;
|
|
||||||
case MSZ_E_FORUM_TOPIC_DELETE_POSTS:
|
|
||||||
$responseCode = 403;
|
|
||||||
$canDeleteMsg = 'This topic already has replies, you may no longer delete it. Ask a moderator to remove if it absolutely necessary.';
|
|
||||||
break;
|
|
||||||
case MSZ_E_FORUM_TOPIC_DELETE_OK:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
$responseCode = 500;
|
|
||||||
$canDeleteMsg = sprintf('Unknown error \'%d\'', $canDelete);
|
|
||||||
}
|
|
||||||
|
|
||||||
if($canDeleteCode !== MSZ_E_FORUM_TOPIC_DELETE_OK) {
|
|
||||||
if($isXHR) {
|
if($isXHR) {
|
||||||
http_response_code($responseCode);
|
http_response_code($responseCode);
|
||||||
echo json_encode([
|
echo json_encode([
|
||||||
|
@ -181,26 +154,18 @@ if(in_array($moderationMode, $validModerationModes, true)) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$deleteTopic = forum_topic_delete($topicInfo->getId());
|
$topicInfo->delete();
|
||||||
|
AuditLog::create(AuditLog::FORUM_TOPIC_DELETE, [$topicInfo->getId()]);
|
||||||
if($deleteTopic) {
|
|
||||||
AuditLog::create(AuditLog::FORUM_TOPIC_DELETE, [$topicInfo->getId()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if($isXHR) {
|
if($isXHR) {
|
||||||
echo json_encode([
|
echo json_encode([
|
||||||
'success' => $deleteTopic,
|
'success' => true,
|
||||||
'topic_id' => $topicInfo->getId(),
|
'topic_id' => $topicInfo->getId(),
|
||||||
'message' => $deleteTopic ? 'Topic deleted!' : 'Failed to delete topic.',
|
'message' => 'Topic deleted!',
|
||||||
]);
|
]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!$deleteTopic) {
|
|
||||||
echo render_error(500);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
url_redirect('forum-category', [
|
url_redirect('forum-category', [
|
||||||
'forum' => $topicInfo->getCategoryId(),
|
'forum' => $topicInfo->getCategoryId(),
|
||||||
]);
|
]);
|
||||||
|
@ -232,13 +197,7 @@ if(in_array($moderationMode, $validModerationModes, true)) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$restoreTopic = forum_topic_restore($topicInfo->getId());
|
$topicInfo->restore();
|
||||||
|
|
||||||
if(!$restoreTopic) {
|
|
||||||
echo render_error(500);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
AuditLog::create(AuditLog::FORUM_TOPIC_RESTORE, [$topicInfo->getId()]);
|
AuditLog::create(AuditLog::FORUM_TOPIC_RESTORE, [$topicInfo->getId()]);
|
||||||
http_response_code(204);
|
http_response_code(204);
|
||||||
|
|
||||||
|
@ -275,13 +234,7 @@ if(in_array($moderationMode, $validModerationModes, true)) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$nukeTopic = forum_topic_nuke($topicInfo->getId());
|
$topicInfo->nuke();
|
||||||
|
|
||||||
if(!$nukeTopic) {
|
|
||||||
echo render_error(500);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
AuditLog::create(AuditLog::FORUM_TOPIC_NUKE, [$topicInfo->getId()]);
|
AuditLog::create(AuditLog::FORUM_TOPIC_NUKE, [$topicInfo->getId()]);
|
||||||
http_response_code(204);
|
http_response_code(204);
|
||||||
|
|
||||||
|
|
|
@ -344,50 +344,19 @@ switch($profileMode) {
|
||||||
|
|
||||||
case 'forum-topics':
|
case 'forum-topics':
|
||||||
$template = 'profile.topics';
|
$template = 'profile.topics';
|
||||||
$topicsCount = forum_topic_count_user($profileUser->getId(), $currentUserId);
|
|
||||||
$topicsPagination = new Pagination($topicsCount, 20);
|
|
||||||
|
|
||||||
if(!$topicsPagination->hasValidOffset()) {
|
|
||||||
echo render_error(404);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$topics = forum_topic_listing_user(
|
|
||||||
$profileUser->getId(), $currentUserId,
|
|
||||||
$topicsPagination->getOffset(), $topicsPagination->getRange()
|
|
||||||
);
|
|
||||||
|
|
||||||
Template::set([
|
Template::set([
|
||||||
'title' => $profileUser->getUsername() . ' / topics',
|
'title' => $profileUser->getUsername() . ' / topics',
|
||||||
'canonical_url' => url('user-profile-forum-topics', ['user' => $profileUser->getId(), 'page' => Pagination::param()]),
|
'canonical_url' => url('user-profile-forum-topics', ['user' => $profileUser->getId(), 'page' => Pagination::param()]),
|
||||||
'profile_topics' => $topics,
|
|
||||||
'profile_topics_pagination' => $topicsPagination,
|
|
||||||
]);
|
]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'forum-posts':
|
case 'forum-posts':
|
||||||
$template = 'profile.posts';
|
$template = 'profile.posts';
|
||||||
$postsCount = forum_post_count_user($profileUser->getId());
|
|
||||||
$postsPagination = new Pagination($postsCount, 20);
|
|
||||||
|
|
||||||
if(!$postsPagination->hasValidOffset()) {
|
|
||||||
echo render_error(404);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$posts = forum_post_listing(
|
|
||||||
$profileUser->getId(),
|
|
||||||
$postsPagination->getOffset(),
|
|
||||||
$postsPagination->getRange(),
|
|
||||||
false,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
Template::set([
|
Template::set([
|
||||||
'title' => $profileUser->getUsername() . ' / posts',
|
'title' => $profileUser->getUsername() . ' / posts',
|
||||||
'canonical_url' => url('user-profile-forum-posts', ['user' => $profileUser->getId(), 'page' => Pagination::param()]),
|
'canonical_url' => url('user-profile-forum-posts', ['user' => $profileUser->getId(), 'page' => Pagination::param()]),
|
||||||
'profile_posts' => $posts,
|
|
||||||
'profile_posts_pagination' => $postsPagination,
|
|
||||||
]);
|
]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -414,7 +414,7 @@ class ForumCategory {
|
||||||
}
|
}
|
||||||
|
|
||||||
if($save && !$this->isRoot()) {
|
if($save && !$this->isRoot()) {
|
||||||
$setCounts = \Misuzu\DB::prepare(
|
$setCounts = DB::prepare(
|
||||||
'UPDATE `msz_forum_categories`'
|
'UPDATE `msz_forum_categories`'
|
||||||
. ' SET `forum_count_topics` = :topics, `forum_count_posts` = :posts'
|
. ' SET `forum_count_topics` = :topics, `forum_count_posts` = :posts'
|
||||||
. ' WHERE `forum_id` = :forum'
|
. ' WHERE `forum_id` = :forum'
|
||||||
|
|
103
src/Forum/ForumLeaderboard.php
Normal file
103
src/Forum/ForumLeaderboard.php
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
<?php
|
||||||
|
namespace Misuzu\Forum;
|
||||||
|
|
||||||
|
use Misuzu\DB;
|
||||||
|
|
||||||
|
final class ForumLeaderboard {
|
||||||
|
public const START_YEAR = 2018;
|
||||||
|
public const START_MONTH = 12;
|
||||||
|
public const CATEGORY_ALL = 0;
|
||||||
|
|
||||||
|
public static function isValidYear(?int $year): bool {
|
||||||
|
return !is_null($year) && $year >= self::START_YEAR && $year <= date('Y');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function isValidMonth(?int $year, ?int $month): bool {
|
||||||
|
if(is_null($month) || !self::isValidYear($year) || $month < 1 || $month > 12)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
$combo = sprintf('%04d%02d', $year, $month);
|
||||||
|
$start = sprintf('%04d%02d', self::START_YEAR, self::START_MONTH);
|
||||||
|
$current = date('Ym');
|
||||||
|
|
||||||
|
return $combo >= $start && $combo <= $current;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function categories(): array {
|
||||||
|
$categories = [
|
||||||
|
self::CATEGORY_ALL => 'All Time',
|
||||||
|
];
|
||||||
|
|
||||||
|
$currentYear = date('Y');
|
||||||
|
$currentMonth = date('m');
|
||||||
|
|
||||||
|
for($i = $currentYear; $i >= self::START_YEAR; $i--) {
|
||||||
|
$categories[$i] = sprintf('Leaderboard %d', $i);
|
||||||
|
}
|
||||||
|
|
||||||
|
for($i = $currentYear, $j = $currentMonth;;) {
|
||||||
|
$categories[sprintf('%d%02d', $i, $j)] = sprintf('Leaderboard %d-%02d', $i, $j);
|
||||||
|
|
||||||
|
if($j <= 1) {
|
||||||
|
$i--; $j = 12;
|
||||||
|
} else $j--;
|
||||||
|
|
||||||
|
if($i <= self::START_YEAR && $j < self::START_MONTH)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $categories;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function listing(
|
||||||
|
?int $year = null,
|
||||||
|
?int $month = null,
|
||||||
|
array $unrankedForums = [],
|
||||||
|
array $unrankedTopics = []
|
||||||
|
): array {
|
||||||
|
$hasYear = self::isValidYear($year);
|
||||||
|
$hasMonth = $hasYear && self::isValidMonth($year, $month);
|
||||||
|
$unrankedForums = implode(',', $unrankedForums);
|
||||||
|
$unrankedTopics = implode(',', $unrankedTopics);
|
||||||
|
|
||||||
|
$rawLeaderboard = DB::query(sprintf(
|
||||||
|
'
|
||||||
|
SELECT
|
||||||
|
u.`user_id`, u.`username`,
|
||||||
|
COUNT(fp.`post_id`) as `posts`
|
||||||
|
FROM `msz_users` AS u
|
||||||
|
INNER JOIN `msz_forum_posts` AS fp
|
||||||
|
ON fp.`user_id` = u.`user_id`
|
||||||
|
WHERE fp.`post_deleted` IS NULL
|
||||||
|
%s %s %s
|
||||||
|
GROUP BY u.`user_id`
|
||||||
|
HAVING `posts` > 0
|
||||||
|
ORDER BY `posts` DESC
|
||||||
|
',
|
||||||
|
$unrankedForums ? sprintf('AND fp.`forum_id` NOT IN (%s)', $unrankedForums) : '',
|
||||||
|
$unrankedTopics ? sprintf('AND fp.`topic_id` NOT IN (%s)', $unrankedTopics) : '',
|
||||||
|
!$hasYear ? '' : sprintf(
|
||||||
|
'AND DATE(fp.`post_created`) BETWEEN \'%1$04d-%2$02d-01\' AND \'%1$04d-%3$02d-31\'',
|
||||||
|
$year,
|
||||||
|
$hasMonth ? $month : 1,
|
||||||
|
$hasMonth ? $month : 12
|
||||||
|
)
|
||||||
|
))->fetchAll();
|
||||||
|
|
||||||
|
$leaderboard = [];
|
||||||
|
$ranking = 0;
|
||||||
|
$lastPosts = null;
|
||||||
|
|
||||||
|
foreach($rawLeaderboard as $entry) {
|
||||||
|
if(is_null($lastPosts) || $lastPosts > $entry['posts']) {
|
||||||
|
$ranking++;
|
||||||
|
$lastPosts = $entry['posts'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$entry['rank'] = $ranking;
|
||||||
|
$leaderboard[] = $entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $leaderboard;
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,6 +15,9 @@ class ForumPostCreationFailedException extends ForumPostException {}
|
||||||
class ForumPost {
|
class ForumPost {
|
||||||
public const PER_PAGE = 10;
|
public const PER_PAGE = 10;
|
||||||
|
|
||||||
|
public const BODY_MIN_LENGTH = 1;
|
||||||
|
public const BODY_MAX_LENGTH = 60000;
|
||||||
|
|
||||||
// Database fields
|
// Database fields
|
||||||
private $post_id = -1;
|
private $post_id = -1;
|
||||||
private $topic_id = -1;
|
private $topic_id = -1;
|
||||||
|
@ -176,11 +179,6 @@ class ForumPost {
|
||||||
public function isDeleted(): bool {
|
public function isDeleted(): bool {
|
||||||
return $this->getDeletedTime() >= 0;
|
return $this->getDeletedTime() >= 0;
|
||||||
}
|
}
|
||||||
public function setDeleted(bool $deleted): self {
|
|
||||||
if($this->isDeleted() !== $deleted)
|
|
||||||
$this->post_deleted = $deleted ? time() : null;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isOpeningPost(): bool {
|
public function isOpeningPost(): bool {
|
||||||
return $this->getTopic()->isOpeningPost($this);
|
return $this->getTopic()->isOpeningPost($this);
|
||||||
|
@ -211,6 +209,58 @@ class ForumPost {
|
||||||
return $this->getUser()->getId() === $user->getId();
|
return $this->getUser()->getId() === $user->getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function validateBody(string $body): string {
|
||||||
|
$length = mb_strlen(trim($body));
|
||||||
|
if($length < self::BODY_MIN_LENGTH)
|
||||||
|
return 'short';
|
||||||
|
if($length > self::BODY_MAX_LENGTH)
|
||||||
|
return 'long';
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
public static function bodyValidationErrorString(string $error): string {
|
||||||
|
switch($error) {
|
||||||
|
case 'short':
|
||||||
|
return sprintf('Post body was too short, it has to be at least %d characters!', self::BODY_MIN_LENGTH);
|
||||||
|
case 'long':
|
||||||
|
return sprintf("Post body was too long, it can't be longer than %d characters!", self::BODY_MAX_LENGTH);
|
||||||
|
case '':
|
||||||
|
return 'Post body is correctly formatted!';
|
||||||
|
default:
|
||||||
|
return 'Post body is incorrectly formatted.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function deleteTopic(ForumTopic $topic): void {
|
||||||
|
// Deleting posts should only be possible while the topic is already in a deleted state
|
||||||
|
if(!$topic->isDeleted())
|
||||||
|
return;
|
||||||
|
DB::prepare(
|
||||||
|
'UPDATE `' . DB::PREFIX . self::TABLE . '`'
|
||||||
|
. ' SET `post_deleted` = NOW()'
|
||||||
|
. ' WHERE `topic_id` = :topic'
|
||||||
|
. ' AND `post_deleted` IS NULL'
|
||||||
|
)->bind('topic', $topic->getId())->execute();
|
||||||
|
}
|
||||||
|
public static function restoreTopic(ForumTopic $topic): void {
|
||||||
|
// This looks like an error but it's not, run this before restoring the topic
|
||||||
|
if(!$topic->isDeleted())
|
||||||
|
return;
|
||||||
|
DB::prepare(
|
||||||
|
'UPDATE `' . DB::PREFIX . self::TABLE . '`'
|
||||||
|
. ' SET `post_deleted` = NULL'
|
||||||
|
. ' WHERE `topic_id` = :topic'
|
||||||
|
. ' AND `post_deleted` = FROM_UNIXTIME(:deleted)'
|
||||||
|
)->bind('topic', $topic->getId())->bind('deleted', $topic->getDeletedTime())->execute();
|
||||||
|
}
|
||||||
|
public static function nukeTopic(ForumTopic $topic): void { // Does this need to exist? Happens implicitly through foreign keys.
|
||||||
|
// Hard deleting should only be allowed if the topic is already soft deleted
|
||||||
|
if(!$topic->isDeleted())
|
||||||
|
return;
|
||||||
|
DB::prepare('DELETE FROM `' . DB::PREFIX . self::TABLE . '` WHERE `topic_id` = :topic')
|
||||||
|
->bind('topic', $topic->getId())
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
|
||||||
private static function countQueryBase(): string {
|
private static function countQueryBase(): string {
|
||||||
return sprintf(self::QUERY_SELECT, sprintf('COUNT(*)', self::TABLE));
|
return sprintf(self::QUERY_SELECT, sprintf('COUNT(*)', self::TABLE));
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@ use Misuzu\Users\User;
|
||||||
|
|
||||||
class ForumTopicException extends ForumException {}
|
class ForumTopicException extends ForumException {}
|
||||||
class ForumTopicNotFoundException extends ForumTopicException {}
|
class ForumTopicNotFoundException extends ForumTopicException {}
|
||||||
|
class ForumTopicCreationFailedException extends ForumTopicException {}
|
||||||
|
class ForumTopicUpdateFailedException extends ForumTopicException {}
|
||||||
|
|
||||||
class ForumTopic {
|
class ForumTopic {
|
||||||
public const TYPE_DISCUSSION = 0;
|
public const TYPE_DISCUSSION = 0;
|
||||||
|
@ -35,6 +37,12 @@ class ForumTopic {
|
||||||
self::TYPE_GLOBAL_ANNOUNCEMENT,
|
self::TYPE_GLOBAL_ANNOUNCEMENT,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public const TITLE_MIN_LENGTH = 3;
|
||||||
|
public const TITLE_MAX_LENGTH = 100;
|
||||||
|
|
||||||
|
public const DELETE_AGE_LIMIT = 60 * 60 * 24;
|
||||||
|
public const DELETE_POST_LIMIT = 2;
|
||||||
|
|
||||||
// Database fields
|
// Database fields
|
||||||
private $topic_id = -1;
|
private $topic_id = -1;
|
||||||
private $forum_id = -1;
|
private $forum_id = -1;
|
||||||
|
@ -236,11 +244,6 @@ class ForumTopic {
|
||||||
public function isDeleted(): bool {
|
public function isDeleted(): bool {
|
||||||
return $this->getDeletedTime() >= 0;
|
return $this->getDeletedTime() >= 0;
|
||||||
}
|
}
|
||||||
public function setDeleted(bool $deleted): self {
|
|
||||||
if($this->isDeleted() !== $deleted)
|
|
||||||
$this->topic_deleted = $deleted ? time() : null;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getLockedTime(): int {
|
public function getLockedTime(): int {
|
||||||
return $this->topic_locked === null ? -1 : $this->topic_locked;
|
return $this->topic_locked === null ? -1 : $this->topic_locked;
|
||||||
|
@ -274,7 +277,7 @@ class ForumTopic {
|
||||||
public function hasUnread(?User $user): bool {
|
public function hasUnread(?User $user): bool {
|
||||||
if($user === null)
|
if($user === null)
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return mt_rand(0, 10) >= 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hasParticipated(?User $user): bool {
|
public function hasParticipated(?User $user): bool {
|
||||||
|
@ -304,6 +307,112 @@ class ForumTopic {
|
||||||
return $this->getCategory()->canView($user);
|
return $this->getCategory()->canView($user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function canDelete(User $user): string {
|
||||||
|
if(false) // check if viewable
|
||||||
|
return 'view';
|
||||||
|
|
||||||
|
// check if user can view deleted posts/is mod
|
||||||
|
$canDeleteAny = false;
|
||||||
|
|
||||||
|
if($this->isDeleted())
|
||||||
|
return $canDeleteAny ? 'deleted' : 'view';
|
||||||
|
|
||||||
|
if(!$canDeleteAny) {
|
||||||
|
if(false) // check if user can delete posts
|
||||||
|
return 'permission';
|
||||||
|
if($user->getId() !== $this->getUserId())
|
||||||
|
return 'owner';
|
||||||
|
if($this->getCreatedTime() <= time() - self::DELETE_AGE_LIMIT)
|
||||||
|
return 'age';
|
||||||
|
if($this->getActualPostCount(true) >= self::DELETE_POST_LIMIT)
|
||||||
|
return 'posts';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
public static function canDeleteErrorString(string $error): string {
|
||||||
|
switch($error) {
|
||||||
|
case 'view':
|
||||||
|
return 'This topic doesn\'t exist.';
|
||||||
|
case 'deleted':
|
||||||
|
return 'This topic has already been marked as deleted.';
|
||||||
|
case 'permission':
|
||||||
|
return 'You aren\'t allowed to this topic.';
|
||||||
|
case 'owner':
|
||||||
|
return 'You can only delete your own topics.';
|
||||||
|
case 'age':
|
||||||
|
return 'This topic is too old to be deleted. Ask a moderator to remove it if you deem it absolutely necessary.';
|
||||||
|
case 'posts':
|
||||||
|
return 'This topic has too many replies to be deleted. Ask a moderator to remove it if you deem it absolutely necessary.';
|
||||||
|
case '':
|
||||||
|
return 'Topic can be deleted!';
|
||||||
|
default:
|
||||||
|
return 'Topic cannot be deleted.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete(): void {
|
||||||
|
if($this->isDeleted())
|
||||||
|
return;
|
||||||
|
$this->topic_deleted = time();
|
||||||
|
DB::prepare('UPDATE `' . DB::PREFIX . self::TABLE . '` SET `topic_deleted` = NOW() WHERE `topic_id` = :topic')
|
||||||
|
->bind('topic', $this->getId())
|
||||||
|
->execute();
|
||||||
|
ForumPost::deleteTopic($this);
|
||||||
|
}
|
||||||
|
public function restore(): void {
|
||||||
|
if(!$this->isDeleted())
|
||||||
|
return;
|
||||||
|
ForumPost::restoreTopic($this);
|
||||||
|
DB::prepare('UPDATE `' . DB::PREFIX . self::TABLE . '` SET `topic_deleted` = NULL WHERE `topic_id` = :topic')
|
||||||
|
->bind('topic', $this->getId())
|
||||||
|
->execute();
|
||||||
|
$this->topic_deleted = null;
|
||||||
|
}
|
||||||
|
public function nuke(): void {
|
||||||
|
if(!$this->isDeleted())
|
||||||
|
return;
|
||||||
|
DB::prepare('DELETE FROM `' . DB::PREFIX . self::TABLE . '` WHERE `topic_id` = :topic')
|
||||||
|
->bind('topic', $this->getId())
|
||||||
|
->execute();
|
||||||
|
//ForumPost::nukeTopic($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function create(ForumCategory $category, User $user, string $title, int $type = self::TYPE_DISCUSSION): ForumTopic {
|
||||||
|
$create = DB::prepare(
|
||||||
|
'INSERT INTO `msz_forum_topics` (`forum_id`, `user_id`, `topic_title`, `topic_type`) VALUES (:forum, :user, :title, :type)'
|
||||||
|
)->bind('forum', $category->getId())->bind('user', $user->getId())
|
||||||
|
->bind('title', $title)->bind('type', $type)
|
||||||
|
->execute();
|
||||||
|
if(!$create)
|
||||||
|
throw new ForumTopicCreationFailedException;
|
||||||
|
$topicId = DB::lastId();
|
||||||
|
if($topicId < 1)
|
||||||
|
throw new ForumTopicCreationFailedException;
|
||||||
|
|
||||||
|
try {
|
||||||
|
return self::byId($topicId);
|
||||||
|
} catch(ForumTopicNotFoundException $ex) {
|
||||||
|
throw new ForumTopicCreationFailedException;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(): void {
|
||||||
|
if($this->getId() < 1)
|
||||||
|
throw new ForumTopicUpdateFailedException;
|
||||||
|
|
||||||
|
if(!DB::prepare(
|
||||||
|
'UPDATE `msz_forum_topics`'
|
||||||
|
. ' SET `topic_title` = :title,'
|
||||||
|
. ' `topic_type` = :type'
|
||||||
|
. ' WHERE `topic_id` = :topic'
|
||||||
|
)->bind('topic', $this->getId())
|
||||||
|
->bind('title', $this->getTitle())
|
||||||
|
->bind('type', $this->getType())
|
||||||
|
->execute())
|
||||||
|
throw new ForumTopicUpdateFailedException;
|
||||||
|
}
|
||||||
|
|
||||||
public function synchronise(bool $save = true): array {
|
public function synchronise(bool $save = true): array {
|
||||||
$stats = DB::prepare(
|
$stats = DB::prepare(
|
||||||
'SELECT :topic AS `topic`, ('
|
'SELECT :topic AS `topic`, ('
|
||||||
|
@ -337,6 +446,27 @@ class ForumTopic {
|
||||||
return $stats;
|
return $stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function validateTitle(string $title): string {
|
||||||
|
$length = mb_strlen(trim($title));
|
||||||
|
if($length < self::TITLE_MIN_LENGTH)
|
||||||
|
return 'short';
|
||||||
|
if($length > self::TITLE_MAX_LENGTH)
|
||||||
|
return 'long';
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
public static function titleValidationErrorString(string $error): string {
|
||||||
|
switch($error) {
|
||||||
|
case 'short':
|
||||||
|
return sprintf('Topic title was too short, it has to be at least %d characters!', self::TITLE_MIN_LENGTH);
|
||||||
|
case 'long':
|
||||||
|
return sprintf("Topic title was too long, it can't be longer than %d characters!", self::TITLE_MAX_LENGTH);
|
||||||
|
case '':
|
||||||
|
return 'Topic title is correctly formatted!';
|
||||||
|
default:
|
||||||
|
return 'Topic title is incorrectly formatted.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static function countQueryBase(): string {
|
private static function countQueryBase(): string {
|
||||||
return sprintf(self::QUERY_SELECT, sprintf('COUNT(*)', self::TABLE));
|
return sprintf(self::QUERY_SELECT, sprintf('COUNT(*)', self::TABLE));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,98 +0,0 @@
|
||||||
<?php
|
|
||||||
define('MSZ_FORUM_LEADERBOARD_START_YEAR', 2018);
|
|
||||||
define('MSZ_FORUM_LEADERBOARD_START_MONTH', 12);
|
|
||||||
define('MSZ_FORUM_LEADERBOARD_CATEGORY_ALL', 0);
|
|
||||||
|
|
||||||
function forum_leaderboard_year_valid(?int $year): bool {
|
|
||||||
return !is_null($year) && $year >= MSZ_FORUM_LEADERBOARD_START_YEAR && $year <= date('Y');
|
|
||||||
}
|
|
||||||
|
|
||||||
function forum_leaderboard_month_valid(?int $year, ?int $month): bool {
|
|
||||||
if(is_null($month) || !forum_leaderboard_year_valid($year) || $month < 1 || $month > 12) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$combo = sprintf('%04d%02d', $year, $month);
|
|
||||||
$start = sprintf('%04d%02d', MSZ_FORUM_LEADERBOARD_START_YEAR, MSZ_FORUM_LEADERBOARD_START_MONTH);
|
|
||||||
$current = date('Ym');
|
|
||||||
|
|
||||||
return $combo >= $start && $combo <= $current;
|
|
||||||
}
|
|
||||||
|
|
||||||
function forum_leaderboard_categories(): array {
|
|
||||||
$categories = [
|
|
||||||
MSZ_FORUM_LEADERBOARD_CATEGORY_ALL => 'All Time',
|
|
||||||
];
|
|
||||||
|
|
||||||
$currentYear = date('Y');
|
|
||||||
$currentMonth = date('m');
|
|
||||||
|
|
||||||
for($i = $currentYear; $i >= MSZ_FORUM_LEADERBOARD_START_YEAR; $i--) {
|
|
||||||
$categories[$i] = sprintf('Leaderboard %d', $i);
|
|
||||||
}
|
|
||||||
|
|
||||||
for($i = $currentYear, $j = $currentMonth;;) {
|
|
||||||
$categories[sprintf('%d%02d', $i, $j)] = sprintf('Leaderboard %d-%02d', $i, $j);
|
|
||||||
|
|
||||||
if($j <= 1) {
|
|
||||||
$i--; $j = 12;
|
|
||||||
} else $j--;
|
|
||||||
|
|
||||||
if($i <= MSZ_FORUM_LEADERBOARD_START_YEAR && $j < MSZ_FORUM_LEADERBOARD_START_MONTH)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $categories;
|
|
||||||
}
|
|
||||||
|
|
||||||
function forum_leaderboard_listing(
|
|
||||||
?int $year = null,
|
|
||||||
?int $month = null,
|
|
||||||
array $unrankedForums = [],
|
|
||||||
array $unrankedTopics = []
|
|
||||||
): array {
|
|
||||||
$hasYear = forum_leaderboard_year_valid($year);
|
|
||||||
$hasMonth = $hasYear && forum_leaderboard_month_valid($year, $month);
|
|
||||||
$unrankedForums = implode(',', $unrankedForums);
|
|
||||||
$unrankedTopics = implode(',', $unrankedTopics);
|
|
||||||
|
|
||||||
$rawLeaderboard = \Misuzu\DB::query(sprintf(
|
|
||||||
'
|
|
||||||
SELECT
|
|
||||||
u.`user_id`, u.`username`,
|
|
||||||
COUNT(fp.`post_id`) as `posts`
|
|
||||||
FROM `msz_users` AS u
|
|
||||||
INNER JOIN `msz_forum_posts` AS fp
|
|
||||||
ON fp.`user_id` = u.`user_id`
|
|
||||||
WHERE fp.`post_deleted` IS NULL
|
|
||||||
%s %s %s
|
|
||||||
GROUP BY u.`user_id`
|
|
||||||
HAVING `posts` > 0
|
|
||||||
ORDER BY `posts` DESC
|
|
||||||
',
|
|
||||||
$unrankedForums ? sprintf('AND fp.`forum_id` NOT IN (%s)', $unrankedForums) : '',
|
|
||||||
$unrankedTopics ? sprintf('AND fp.`topic_id` NOT IN (%s)', $unrankedTopics) : '',
|
|
||||||
!$hasYear ? '' : sprintf(
|
|
||||||
'AND DATE(fp.`post_created`) BETWEEN \'%1$04d-%2$02d-01\' AND \'%1$04d-%3$02d-31\'',
|
|
||||||
$year,
|
|
||||||
$hasMonth ? $month : 1,
|
|
||||||
$hasMonth ? $month : 12
|
|
||||||
)
|
|
||||||
))->fetchAll();
|
|
||||||
|
|
||||||
$leaderboard = [];
|
|
||||||
$ranking = 0;
|
|
||||||
$lastPosts = null;
|
|
||||||
|
|
||||||
foreach($rawLeaderboard as $entry) {
|
|
||||||
if(is_null($lastPosts) || $lastPosts > $entry['posts']) {
|
|
||||||
$ranking++;
|
|
||||||
$lastPosts = $entry['posts'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$entry['rank'] = $ranking;
|
|
||||||
$leaderboard[] = $entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $leaderboard;
|
|
||||||
}
|
|
|
@ -1,55 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
function forum_topic_create(
|
|
||||||
int $forumId,
|
|
||||||
int $userId,
|
|
||||||
string $title,
|
|
||||||
int $type = \Misuzu\Forum\ForumTopic::TYPE_DISCUSSION
|
|
||||||
): int {
|
|
||||||
if(empty($title) || !in_array($type, \Misuzu\Forum\ForumTopic::TYPES)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
$createTopic = \Misuzu\DB::prepare('
|
|
||||||
INSERT INTO `msz_forum_topics`
|
|
||||||
(`forum_id`, `user_id`, `topic_title`, `topic_type`)
|
|
||||||
VALUES
|
|
||||||
(:forum_id, :user_id, :topic_title, :topic_type)
|
|
||||||
');
|
|
||||||
$createTopic->bind('forum_id', $forumId);
|
|
||||||
$createTopic->bind('user_id', $userId);
|
|
||||||
$createTopic->bind('topic_title', $title);
|
|
||||||
$createTopic->bind('topic_type', $type);
|
|
||||||
|
|
||||||
return $createTopic->execute() ? \Misuzu\DB::lastId() : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function forum_topic_update(int $topicId, ?string $title, ?int $type = null): bool {
|
|
||||||
if($topicId < 1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure it's null and not some other kinda empty
|
|
||||||
if(empty($title)) {
|
|
||||||
$title = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if($type !== null && !in_array($type, \Misuzu\Forum\ForumTopic::TYPES)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$updateTopic = \Misuzu\DB::prepare('
|
|
||||||
UPDATE `msz_forum_topics`
|
|
||||||
SET `topic_title` = COALESCE(:topic_title, `topic_title`),
|
|
||||||
`topic_type` = COALESCE(:topic_type, `topic_type`)
|
|
||||||
WHERE `topic_id` = :topic_id
|
|
||||||
');
|
|
||||||
$updateTopic->bind('topic_id', $topicId);
|
|
||||||
$updateTopic->bind('topic_title', $title);
|
|
||||||
$updateTopic->bind('topic_type', $type);
|
|
||||||
|
|
||||||
return $updateTopic->execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
function forum_topic_views_increment(int $topicId): void {
|
function forum_topic_views_increment(int $topicId): void {
|
||||||
if($topicId < 1) {
|
if($topicId < 1) {
|
||||||
return;
|
return;
|
||||||
|
@ -72,6 +21,7 @@ function forum_topic_mark_read(int $userId, int $topicId, int $forumId): void {
|
||||||
// previously a TRIGGER was used to achieve this behaviour,
|
// previously a TRIGGER was used to achieve this behaviour,
|
||||||
// but those explode when running on a lot of queries (like forum_mark_read() does)
|
// but those explode when running on a lot of queries (like forum_mark_read() does)
|
||||||
// so instead we get to live with this garbage now
|
// so instead we get to live with this garbage now
|
||||||
|
// JUST TO CLARIFY: "this behaviour" refers to forum_topic_views_increment only being executed when the topic is viewed for the first time
|
||||||
try {
|
try {
|
||||||
$markAsRead = \Misuzu\DB::prepare('
|
$markAsRead = \Misuzu\DB::prepare('
|
||||||
INSERT INTO `msz_forum_topics_track`
|
INSERT INTO `msz_forum_topics_track`
|
||||||
|
@ -104,283 +54,3 @@ function forum_topic_mark_read(int $userId, int $topicId, int $forumId): void {
|
||||||
$markAsRead->execute();
|
$markAsRead->execute();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function forum_topic_count_user(int $authorId, int $userId, bool $showDeleted = false): int {
|
|
||||||
$getTopics = \Misuzu\DB::prepare(sprintf(
|
|
||||||
'
|
|
||||||
SELECT COUNT(`topic_id`)
|
|
||||||
FROM `msz_forum_topics` AS t
|
|
||||||
WHERE t.`user_id` = :author_id
|
|
||||||
%1$s
|
|
||||||
',
|
|
||||||
$showDeleted ? '' : 'AND t.`topic_deleted` IS NULL'
|
|
||||||
));
|
|
||||||
$getTopics->bind('author_id', $authorId);
|
|
||||||
//$getTopics->bind('user_id', $userId);
|
|
||||||
|
|
||||||
return (int)$getTopics->fetchColumn();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove unneccesary stuff from the sql stmt
|
|
||||||
function forum_topic_listing_user(
|
|
||||||
int $authorId,
|
|
||||||
int $userId,
|
|
||||||
int $offset = 0,
|
|
||||||
int $take = 0,
|
|
||||||
bool $showDeleted = false
|
|
||||||
): array {
|
|
||||||
$hasPagination = $offset >= 0 && $take > 0;
|
|
||||||
$getTopics = \Misuzu\DB::prepare(sprintf(
|
|
||||||
'
|
|
||||||
SELECT
|
|
||||||
:user_id AS `target_user_id`,
|
|
||||||
t.`topic_id`, t.`topic_title`, t.`topic_locked`, t.`topic_type`, t.`topic_created`,
|
|
||||||
t.`topic_bumped`, t.`topic_deleted`, t.`topic_count_views`, f.`forum_type`,
|
|
||||||
au.`user_id` AS `author_id`, au.`username` AS `author_name`,
|
|
||||||
COALESCE(au.`user_colour`, ar.`role_colour`) AS `author_colour`,
|
|
||||||
lp.`post_id` AS `response_id`,
|
|
||||||
lp.`post_created` AS `response_created`,
|
|
||||||
lu.`user_id` AS `respondent_id`,
|
|
||||||
lu.`username` AS `respondent_name`,
|
|
||||||
COALESCE(lu.`user_colour`, lr.`role_colour`) AS `respondent_colour`,
|
|
||||||
(
|
|
||||||
SELECT COUNT(`post_id`)
|
|
||||||
FROM `msz_forum_posts`
|
|
||||||
WHERE `topic_id` = t.`topic_id`
|
|
||||||
%5$s
|
|
||||||
) AS `topic_count_posts`,
|
|
||||||
(
|
|
||||||
SELECT CEIL(COUNT(`post_id`) / %6$d)
|
|
||||||
FROM `msz_forum_posts`
|
|
||||||
WHERE `topic_id` = t.`topic_id`
|
|
||||||
%5$s
|
|
||||||
) AS `topic_pages`,
|
|
||||||
(
|
|
||||||
SELECT
|
|
||||||
`target_user_id` > 0
|
|
||||||
AND
|
|
||||||
t.`topic_bumped` > NOW() - INTERVAL 1 MONTH
|
|
||||||
AND (
|
|
||||||
SELECT COUNT(ti.`topic_id`) < 1
|
|
||||||
FROM `msz_forum_topics_track` AS tt
|
|
||||||
RIGHT JOIN `msz_forum_topics` AS ti
|
|
||||||
ON ti.`topic_id` = tt.`topic_id`
|
|
||||||
WHERE ti.`topic_id` = t.`topic_id`
|
|
||||||
AND tt.`user_id` = `target_user_id`
|
|
||||||
AND `track_last_read` >= `topic_bumped`
|
|
||||||
)
|
|
||||||
) AS `topic_unread`,
|
|
||||||
(
|
|
||||||
SELECT COUNT(`post_id`) > 0
|
|
||||||
FROM `msz_forum_posts`
|
|
||||||
WHERE `topic_id` = t.`topic_id`
|
|
||||||
AND `user_id` = `target_user_id`
|
|
||||||
LIMIT 1
|
|
||||||
) AS `topic_participated`
|
|
||||||
FROM `msz_forum_topics` AS t
|
|
||||||
LEFT JOIN `msz_forum_categories` AS f
|
|
||||||
ON f.`forum_id` = t.`forum_id`
|
|
||||||
LEFT JOIN `msz_users` AS au
|
|
||||||
ON t.`user_id` = au.`user_id`
|
|
||||||
LEFT JOIN `msz_roles` AS ar
|
|
||||||
ON ar.`role_id` = au.`display_role`
|
|
||||||
LEFT JOIN `msz_forum_posts` AS lp
|
|
||||||
ON lp.`post_id` = (
|
|
||||||
SELECT `post_id`
|
|
||||||
FROM `msz_forum_posts`
|
|
||||||
WHERE `topic_id` = t.`topic_id`
|
|
||||||
%5$s
|
|
||||||
ORDER BY `post_id` DESC
|
|
||||||
LIMIT 1
|
|
||||||
)
|
|
||||||
LEFT JOIN `msz_users` AS lu
|
|
||||||
ON lu.`user_id` = lp.`user_id`
|
|
||||||
LEFT JOIN `msz_roles` AS lr
|
|
||||||
ON lr.`role_id` = lu.`display_role`
|
|
||||||
WHERE au.`user_id` = :author_id
|
|
||||||
%1$s
|
|
||||||
ORDER BY FIELD(t.`topic_type`, %4$s), t.`topic_bumped` DESC
|
|
||||||
%2$s
|
|
||||||
',
|
|
||||||
$showDeleted ? '' : 'AND t.`topic_deleted` IS NULL',
|
|
||||||
$hasPagination ? 'LIMIT :offset, :take' : '',
|
|
||||||
\Misuzu\Forum\ForumTopic::TYPE_GLOBAL_ANNOUNCEMENT,
|
|
||||||
implode(',', \Misuzu\Forum\ForumTopic::TYPE_ORDER),
|
|
||||||
$showDeleted ? '' : 'AND `post_deleted` IS NULL',
|
|
||||||
\Misuzu\Forum\ForumPost::PER_PAGE
|
|
||||||
));
|
|
||||||
$getTopics->bind('author_id', $authorId);
|
|
||||||
$getTopics->bind('user_id', $userId);
|
|
||||||
|
|
||||||
if($hasPagination) {
|
|
||||||
$getTopics->bind('offset', $offset);
|
|
||||||
$getTopics->bind('take', $take);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $getTopics->fetchAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
define('MSZ_E_FORUM_TOPIC_DELETE_OK', 0); // deleting is fine
|
|
||||||
define('MSZ_E_FORUM_TOPIC_DELETE_USER', 1); // invalid user
|
|
||||||
define('MSZ_E_FORUM_TOPIC_DELETE_TOPIC', 2); // topic doesn't exist
|
|
||||||
define('MSZ_E_FORUM_TOPIC_DELETE_DELETED', 3); // topic is already marked as deleted
|
|
||||||
define('MSZ_E_FORUM_TOPIC_DELETE_OWNER', 4); // you may only delete your own topics
|
|
||||||
define('MSZ_E_FORUM_TOPIC_DELETE_OLD', 5); // topic has existed for too long to be deleted
|
|
||||||
define('MSZ_E_FORUM_TOPIC_DELETE_PERM', 6); // you aren't allowed to delete topics
|
|
||||||
define('MSZ_E_FORUM_TOPIC_DELETE_POSTS', 7); // the topic already has replies
|
|
||||||
|
|
||||||
// only allow topics made within a day of posting to be deleted by normal users
|
|
||||||
define('MSZ_FORUM_TOPIC_DELETE_TIME_LIMIT', 60 * 60 * 24);
|
|
||||||
|
|
||||||
// only allow topics with a single post to be deleted, includes soft deleted posts
|
|
||||||
define('MSZ_FORUM_TOPIC_DELETE_POST_LIMIT', 1);
|
|
||||||
|
|
||||||
// set $userId to null for system request, make sure this is NEVER EVER null on user request
|
|
||||||
// $topicId can also be a the return value of forum_topic_get if you already grabbed it once before
|
|
||||||
function forum_topic_can_delete($topicId, ?int $userId = null): int {
|
|
||||||
if($userId !== null && $userId < 1) {
|
|
||||||
return MSZ_E_FORUM_TOPIC_DELETE_USER;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(is_array($topicId)) {
|
|
||||||
$topic = $topicId;
|
|
||||||
} elseif(is_int($topicId)) {
|
|
||||||
try {
|
|
||||||
$topic = \Misuzu\Forum\ForumTopic::byId($topicId);
|
|
||||||
} catch(\Misuzu\Forum\ForumTopicNotFoundException $ex) {
|
|
||||||
return MSZ_E_FORUM_TOPIC_DELETE_TOPIC;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if($topicId instanceof \Misuzu\Forum\ForumTopic) {
|
|
||||||
$topic = [
|
|
||||||
'forum_id' => $topicId->getCategoryId(),
|
|
||||||
'topic_deleted' => $topicId->isDeleted(),
|
|
||||||
'author_user_id' => $topicId->getUserId(),
|
|
||||||
'topic_created' => date('c', $topicId->getCreatedTime()),
|
|
||||||
'topic_count_posts' => $topicId->getActualPostCount(true),
|
|
||||||
'topic_count_posts_deleted' => 0,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
if(empty($topic)) {
|
|
||||||
return MSZ_E_FORUM_TOPIC_DELETE_TOPIC;
|
|
||||||
}
|
|
||||||
|
|
||||||
$isSystemReq = $userId === null;
|
|
||||||
$perms = $isSystemReq ? 0 : forum_perms_get_user($topic['forum_id'], $userId)[MSZ_FORUM_PERMS_GENERAL];
|
|
||||||
$canDeleteAny = $isSystemReq ? true : perms_check($perms, MSZ_FORUM_PERM_DELETE_ANY_POST);
|
|
||||||
$canViewPost = $isSystemReq ? true : perms_check($perms, MSZ_FORUM_PERM_VIEW_FORUM);
|
|
||||||
$postIsDeleted = !empty($topic['topic_deleted']);
|
|
||||||
|
|
||||||
if(!$canViewPost) {
|
|
||||||
return MSZ_E_FORUM_TOPIC_DELETE_TOPIC;
|
|
||||||
}
|
|
||||||
|
|
||||||
if($postIsDeleted) {
|
|
||||||
return $canDeleteAny ? MSZ_E_FORUM_TOPIC_DELETE_DELETED : MSZ_E_FORUM_TOPIC_DELETE_TOPIC;
|
|
||||||
}
|
|
||||||
|
|
||||||
if($isSystemReq) {
|
|
||||||
return MSZ_E_FORUM_TOPIC_DELETE_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!$canDeleteAny) {
|
|
||||||
if(!perms_check($perms, MSZ_FORUM_PERM_DELETE_POST)) {
|
|
||||||
return MSZ_E_FORUM_TOPIC_DELETE_PERM;
|
|
||||||
}
|
|
||||||
|
|
||||||
if($topic['author_user_id'] !== $userId) {
|
|
||||||
return MSZ_E_FORUM_TOPIC_DELETE_OWNER;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(strtotime($topic['topic_created']) <= time() - MSZ_FORUM_TOPIC_DELETE_TIME_LIMIT) {
|
|
||||||
return MSZ_E_FORUM_TOPIC_DELETE_OLD;
|
|
||||||
}
|
|
||||||
|
|
||||||
$totalReplies = $topic['topic_count_posts'] + $topic['topic_count_posts_deleted'];
|
|
||||||
|
|
||||||
if($totalReplies > MSZ_E_FORUM_TOPIC_DELETE_POSTS) {
|
|
||||||
return MSZ_E_FORUM_TOPIC_DELETE_POSTS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return MSZ_E_FORUM_TOPIC_DELETE_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
function forum_topic_delete(int $topicId): bool {
|
|
||||||
if($topicId < 1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$markTopicDeleted = \Misuzu\DB::prepare('
|
|
||||||
UPDATE `msz_forum_topics`
|
|
||||||
SET `topic_deleted` = NOW()
|
|
||||||
WHERE `topic_id` = :topic
|
|
||||||
AND `topic_deleted` IS NULL
|
|
||||||
');
|
|
||||||
$markTopicDeleted->bind('topic', $topicId);
|
|
||||||
|
|
||||||
if(!$markTopicDeleted->execute()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$markPostsDeleted = \Misuzu\DB::prepare('
|
|
||||||
UPDATE `msz_forum_posts` as p
|
|
||||||
SET p.`post_deleted` = (
|
|
||||||
SELECT `topic_deleted`
|
|
||||||
FROM `msz_forum_topics`
|
|
||||||
WHERE `topic_id` = p.`topic_id`
|
|
||||||
)
|
|
||||||
WHERE p.`topic_id` = :topic
|
|
||||||
AND p.`post_deleted` IS NULL
|
|
||||||
');
|
|
||||||
$markPostsDeleted->bind('topic', $topicId);
|
|
||||||
|
|
||||||
return $markPostsDeleted->execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
function forum_topic_restore(int $topicId): bool {
|
|
||||||
if($topicId < 1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$markPostsRestored = \Misuzu\DB::prepare('
|
|
||||||
UPDATE `msz_forum_posts` as p
|
|
||||||
SET p.`post_deleted` = NULL
|
|
||||||
WHERE p.`topic_id` = :topic
|
|
||||||
AND p.`post_deleted` = (
|
|
||||||
SELECT `topic_deleted`
|
|
||||||
FROM `msz_forum_topics`
|
|
||||||
WHERE `topic_id` = p.`topic_id`
|
|
||||||
)
|
|
||||||
');
|
|
||||||
$markPostsRestored->bind('topic', $topicId);
|
|
||||||
|
|
||||||
if(!$markPostsRestored->execute()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$markTopicRestored = \Misuzu\DB::prepare('
|
|
||||||
UPDATE `msz_forum_topics`
|
|
||||||
SET `topic_deleted` = NULL
|
|
||||||
WHERE `topic_id` = :topic
|
|
||||||
AND `topic_deleted` IS NOT NULL
|
|
||||||
');
|
|
||||||
$markTopicRestored->bind('topic', $topicId);
|
|
||||||
|
|
||||||
return $markTopicRestored->execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
function forum_topic_nuke(int $topicId): bool {
|
|
||||||
if($topicId < 1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$nukeTopic = \Misuzu\DB::prepare('
|
|
||||||
DELETE FROM `msz_forum_topics`
|
|
||||||
WHERE `topic_id` = :topic
|
|
||||||
');
|
|
||||||
$nukeTopic->bind('topic', $topicId);
|
|
||||||
return $nukeTopic->execute();
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
<?php
|
|
||||||
define('MSZ_TOPIC_TITLE_LENGTH_MIN', 3);
|
|
||||||
define('MSZ_TOPIC_TITLE_LENGTH_MAX', 100);
|
|
||||||
define('MSZ_POST_TEXT_LENGTH_MIN', 1);
|
|
||||||
define('MSZ_POST_TEXT_LENGTH_MAX', 60000);
|
|
||||||
|
|
||||||
function forum_validate_title(string $title): string {
|
|
||||||
$length = mb_strlen(trim($title));
|
|
||||||
|
|
||||||
if($length < MSZ_TOPIC_TITLE_LENGTH_MIN) {
|
|
||||||
return 'too-short';
|
|
||||||
}
|
|
||||||
|
|
||||||
if($length > MSZ_TOPIC_TITLE_LENGTH_MAX) {
|
|
||||||
return 'too-long';
|
|
||||||
}
|
|
||||||
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
function forum_validate_post(string $text): string {
|
|
||||||
$length = mb_strlen(trim($text));
|
|
||||||
|
|
||||||
if($length < MSZ_POST_TEXT_LENGTH_MIN) {
|
|
||||||
return 'too-short';
|
|
||||||
}
|
|
||||||
|
|
||||||
if($length > MSZ_POST_TEXT_LENGTH_MAX) {
|
|
||||||
return 'too-long';
|
|
||||||
}
|
|
||||||
|
|
||||||
return '';
|
|
||||||
}
|
|
|
@ -1,6 +1,5 @@
|
||||||
{% extends 'forum/master.twig' %}
|
{% extends 'forum/master.twig' %}
|
||||||
{% from 'macros.twig' import avatar %}
|
{% from 'macros.twig' import avatar %}
|
||||||
{% from 'forum/macros.twig' import forum_header %}
|
|
||||||
|
|
||||||
{% set title = 'Forum Leaderboard » ' ~ leaderboard_name %}
|
{% set title = 'Forum Leaderboard » ' ~ leaderboard_name %}
|
||||||
{% set canonical_url = url('forum-leaderboard', {
|
{% set canonical_url = url('forum-leaderboard', {
|
||||||
|
@ -9,18 +8,23 @@
|
||||||
}) %}
|
}) %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{{ forum_header(title, [], false, canonical_url, [
|
<div class="container forum__header">
|
||||||
{
|
<a class="forum__header__title" href="{{ canonical_url }}">
|
||||||
'html': '<i class="fab fa-markdown fa-fw"></i> Markdown',
|
{{ title }}
|
||||||
'url': url('forum-leaderboard', {'id': leaderboard_id, 'mode': 'markdown'}),
|
</a>
|
||||||
'display': leaderboard_mode != 'markdown',
|
|
||||||
},
|
<div class="forum__header__actions">
|
||||||
{
|
{% if leaderboard_mode == 'markdown' %}
|
||||||
'html': '<i class="fas fa-table fa-fw"></i> Table',
|
<a class="forum__header__action" href="{{ url('forum-leaderboard', {'id': leaderboard_id}) }}">
|
||||||
'url': url('forum-leaderboard', {'id': leaderboard_id}),
|
<i class="fas fa-table fa-fw"></i> Table
|
||||||
'display': leaderboard_mode == 'markdown',
|
</a>
|
||||||
},
|
{% else %}
|
||||||
]) }}
|
<a class="forum__header__action" href="{{ url('forum-leaderboard', {'id': leaderboard_id, 'mode': 'markdown'}) }}">
|
||||||
|
<i class="fas fa-markdown fa-fw"></i> Markdown
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="container forum__leaderboard__categories">
|
<div class="container forum__leaderboard__categories">
|
||||||
{% for id, name in leaderboard_categories %}
|
{% for id, name in leaderboard_categories %}
|
||||||
|
|
|
@ -1,22 +1,13 @@
|
||||||
{% extends 'profile/master.twig' %}
|
{% extends 'profile/master.twig' %}
|
||||||
{% from 'forum/macros.twig' import forum_post_listing %}
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="profile">
|
<div class="profile">
|
||||||
{% include 'profile/_layout/header.twig' %}
|
{% include 'profile/_layout/header.twig' %}
|
||||||
|
|
||||||
{% set sp = profile_posts_pagination.pages > 1
|
<div class="warning">
|
||||||
? '<div class="container profile__pagination">' ~ profile_posts_pagination.render(canonical_url) ~ '</div>'
|
<div class="warning__content">
|
||||||
: '' %}
|
<p>User post listing is gone for a while, it will be back someday but with less bad.</p>
|
||||||
|
</div>
|
||||||
{% if sp is not empty %}
|
</div>
|
||||||
{{ sp|raw }}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{{ forum_post_listing(profile_posts) }}
|
|
||||||
|
|
||||||
{% if sp is not empty %}
|
|
||||||
{{ sp|raw }}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,22 +1,13 @@
|
||||||
{% extends 'profile/master.twig' %}
|
{% extends 'profile/master.twig' %}
|
||||||
{% from 'forum/macros.twig' import forum_topic_listing %}
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="profile">
|
<div class="profile">
|
||||||
{% include 'profile/_layout/header.twig' %}
|
{% include 'profile/_layout/header.twig' %}
|
||||||
|
|
||||||
{% set sp = profile_topics_pagination.pages > 1
|
<div class="warning">
|
||||||
? '<div class="container profile__pagination">' ~ profile_topics_pagination.render(canonical_url) ~ '</div>'
|
<div class="warning__content">
|
||||||
: '' %}
|
<p>User topic listing is gone for a while, it will be back someday but with less bad.</p>
|
||||||
|
</div>
|
||||||
{% if sp is not empty %}
|
</div>
|
||||||
{{ sp|raw }}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{{ forum_topic_listing(profile_topics) }}
|
|
||||||
|
|
||||||
{% if sp is not empty %}
|
|
||||||
{{ sp|raw }}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue