Rewrote the Pagination handler.

This commit is contained in:
Pachira 2024-12-19 01:22:26 +00:00
parent 706fbe8283
commit 0f1a8886a6
38 changed files with 135 additions and 147 deletions

View file

@ -32,13 +32,13 @@ if($categoryInfo->isLink) {
return;
}
$forumPagination = new Pagination($msz->forumCtx->topics->countTopics(
$forumPagination = Pagination::fromInput($msz->forumCtx->topics->countTopics(
categoryInfo: $categoryInfo,
global: true,
deleted: $perms->check(Perm::F_POST_DELETE_ANY) ? null : false
), 20);
if(!$forumPagination->hasValidOffset())
if(!$forumPagination->validOffset)
Template::throwError(404);
$children = [];

View file

@ -101,18 +101,18 @@ $topicPosts = $topicInfo->postsCount;
if($canDeleteAny)
$topicPosts += $topicInfo->deletedPostsCount;
$topicPagination = new Pagination($topicPosts, 10, 'page');
if(isset($preceedingPostCount))
$topicPagination->setPage((int)floor($preceedingPostCount / $topicPagination->getRange()), true);
$pagination = Pagination::fromPage($topicPosts, (int)floor($preceedingPostCount / 10), 10, 0);
else
$pagination = Pagination::fromInput($topicPosts, 10, 'page');
if(!$topicPagination->hasValidOffset())
if(!$pagination->validOffset)
Template::throwError(404);
$postInfos = $msz->forumCtx->posts->getPosts(
topicInfo: $topicInfo,
deleted: $perms->check(Perm::F_POST_DELETE_ANY) ? null : false,
pagination: $topicPagination,
pagination: $pagination,
);
if(empty($postInfos))
@ -163,7 +163,7 @@ Template::render('forum.topic', [
'category_info' => $categoryInfo,
'topic_posts' => $posts,
'can_reply' => $canReply,
'topic_pagination' => $topicPagination,
'topic_pagination' => $pagination,
'topic_can_delete' => $canDelete,
'topic_can_delete_any' => $canDeleteAny,
'topic_can_nuke_or_restore' => $canNukeOrRestore,

View file

@ -9,8 +9,8 @@ if(!isset($msz) || !($msz instanceof \Misuzu\MisuzuContext))
if(!$msz->authInfo->getPerms('global')->check(Perm::G_CL_CHANGES_MANAGE))
Template::throwError(403);
$pagination = new Pagination($msz->changelog->countChanges(), 30);
if(!$pagination->hasValidOffset())
$pagination = Pagination::fromInput($msz->changelog->countChanges(), 30);
if(!$pagination->validOffset)
Template::throwError(404);
$changeInfos = $msz->changelog->getChanges(pagination: $pagination);

View file

@ -31,8 +31,8 @@ if(filter_input(INPUT_GET, 'm') === 'explode') {
return;
}
$pagination = new Pagination($msz->forumCtx->topicRedirects->countTopicRedirects(), 20);
if(!$pagination->hasValidOffset())
$pagination = Pagination::fromInput($msz->forumCtx->topicRedirects->countTopicRedirects(), 20);
if(!$pagination->validOffset)
Template::throwError(404);
$redirs = $msz->forumCtx->topicRedirects->getTopicRedirects(pagination: $pagination);

View file

@ -9,9 +9,8 @@ if(!isset($msz) || !($msz instanceof \Misuzu\MisuzuContext))
if(!$msz->authInfo->getPerms('global')->check(Perm::G_LOGS_VIEW))
Template::throwError(403);
$pagination = new Pagination($msz->auditLog->countLogs(), 50);
if(!$pagination->hasValidOffset())
$pagination = Pagination::fromInput($msz->auditLog->countLogs(), 50);
if(!$pagination->validOffset)
Template::throwError(404);
$logs = iterator_to_array($msz->auditLog->getLogs(pagination: $pagination));

View file

@ -7,9 +7,8 @@ if(!isset($msz) || !($msz instanceof \Misuzu\MisuzuContext))
if(!$msz->authInfo->getPerms('global')->check(Perm::G_NEWS_CATEGORIES_MANAGE))
Template::throwError(403);
$pagination = new Pagination($msz->news->countCategories(), 15);
if(!$pagination->hasValidOffset())
$pagination = Pagination::fromInput($msz->news->countCategories(), 15);
if(!$pagination->validOffset)
Template::throwError(404);
$categories = $msz->news->getCategories(pagination: $pagination);

View file

@ -7,12 +7,12 @@ if(!isset($msz) || !($msz instanceof \Misuzu\MisuzuContext))
if(!$msz->authInfo->getPerms('global')->check(Perm::G_NEWS_POSTS_MANAGE))
Template::throwError(403);
$pagination = new Pagination($msz->news->countPosts(
$pagination = Pagination::fromInput($msz->news->countPosts(
includeScheduled: true,
includeDeleted: true
), 15);
if(!$pagination->hasValidOffset())
if(!$pagination->validOffset)
Template::throwError(404);
$posts = $msz->news->getPosts(

View file

@ -19,9 +19,8 @@ if(filter_has_var(INPUT_GET, 'u')) {
}
}
$pagination = new Pagination($msz->usersCtx->bans->countBans(userInfo: $filterUser), 10);
if(!$pagination->hasValidOffset())
$pagination = Pagination::fromInput($msz->usersCtx->bans->countBans(userInfo: $filterUser), 10);
if(!$pagination->validOffset)
Template::throwError(404);
$banList = [];

View file

@ -9,9 +9,8 @@ if(!isset($msz) || !($msz instanceof \Misuzu\MisuzuContext))
if(!$msz->authInfo->getPerms('user')->check(Perm::U_USERS_MANAGE))
Template::throwError(403);
$pagination = new Pagination($msz->usersCtx->users->countUsers(), 30);
if(!$pagination->hasValidOffset())
$pagination = Pagination::fromInput($msz->usersCtx->users->countUsers(), 30);
if(!$pagination->validOffset)
Template::throwError(404);
$userList = [];

View file

@ -19,9 +19,8 @@ if(filter_has_var(INPUT_GET, 'u')) {
}
}
$pagination = new Pagination($msz->usersCtx->modNotes->countNotes(userInfo: $filterUser), 10);
if(!$pagination->hasValidOffset())
$pagination = Pagination::fromInput($msz->usersCtx->modNotes->countNotes(userInfo: $filterUser), 10);
if(!$pagination->validOffset)
Template::throwError(404);
$notes = [];

View file

@ -8,9 +8,8 @@ if(!$msz->authInfo->getPerms('user')->check(Perm::U_ROLES_MANAGE))
Template::throwError(403);
$roles = $msz->usersCtx->roles;
$pagination = new Pagination($roles->countRoles(), 10);
if(!$pagination->hasValidOffset())
$pagination = Pagination::fromInput($roles->countRoles(), 10);
if(!$pagination->validOffset)
Template::throwError(404);
$rolesAll = [];

View file

@ -19,9 +19,8 @@ if(filter_has_var(INPUT_GET, 'u')) {
}
}
$pagination = new Pagination($msz->usersCtx->warnings->countWarnings(userInfo: $filterUser), 10);
if(!$pagination->hasValidOffset())
$pagination = Pagination::fromInput($msz->usersCtx->warnings->countWarnings(userInfo: $filterUser), 10);
if(!$pagination->validOffset)
Template::throwError(404);
$warnList = [];

View file

@ -76,7 +76,7 @@ $canManageUsers = $msz->authInfo->getPerms('user')->check(Perm::U_USERS_MANAGE);
$deleted = $canManageUsers ? null : false;
$rolesAll = $msz->usersCtx->roles->getRoles(hidden: false);
$pagination = new Pagination($msz->usersCtx->users->countUsers(roleInfo: $roleInfo, deleted: $deleted), 15);
$pagination = Pagination::fromInput($msz->usersCtx->users->countUsers(roleInfo: $roleInfo, deleted: $deleted), 15);
$userList = [];
$userInfos = $msz->usersCtx->users->getUsers(

View file

@ -10,8 +10,8 @@ $currentUser = $msz->authInfo->userInfo;
if($currentUser === null)
Template::throwError(401);
$loginHistoryPagination = new Pagination($msz->authCtx->loginAttempts->countAttempts(userInfo: $currentUser), 5, 'hp');
$accountLogPagination = new Pagination($msz->auditLog->countLogs(userInfo: $currentUser), 10, 'ap');
$loginHistoryPagination = Pagination::fromInput($msz->authCtx->loginAttempts->countAttempts(userInfo: $currentUser), 5, 'hp');
$accountLogPagination = Pagination::fromInput($msz->auditLog->countLogs(userInfo: $currentUser), 10, 'ap');
$loginHistory = iterator_to_array($msz->authCtx->loginAttempts->getAttempts(userInfo: $currentUser, pagination: $loginHistoryPagination));
$auditLogs = iterator_to_array($msz->auditLog->getLogs(userInfo: $currentUser, pagination: $accountLogPagination));

View file

@ -42,7 +42,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
} else break;
}
$pagination = new Pagination($msz->authCtx->sessions->countSessions(userInfo: $currentUser), 10);
$pagination = Pagination::fromInput($msz->authCtx->sessions->countSessions(userInfo: $currentUser), 10);
$sessionList = [];
$sessionInfos = $msz->authCtx->sessions->getSessions(userInfo: $currentUser, pagination: $pagination);

View file

@ -87,8 +87,8 @@ class AuditLog {
if($hasRemoteAddr)
$stmt->addParameter(++$args, $remoteAddr);
if($hasPagination) {
$stmt->addParameter(++$args, $pagination->getRange());
$stmt->addParameter(++$args, $pagination->getOffset());
$stmt->addParameter(++$args, $pagination->range);
$stmt->addParameter(++$args, $pagination->offset);
}
$stmt->execute();

View file

@ -112,8 +112,8 @@ class LoginAttempts {
if($hasTimeRange)
$stmt->addParameter(++$args, $timeRange);
if($hasPagination) {
$stmt->addParameter(++$args, $pagination->getRange());
$stmt->addParameter(++$args, $pagination->getOffset());
$stmt->addParameter(++$args, $pagination->range);
$stmt->addParameter(++$args, $pagination->offset);
}
$stmt->execute();

View file

@ -78,8 +78,8 @@ class Sessions {
if($hasUserInfo)
$stmt->addParameter(++$args, $userInfo);
if($hasPagination) {
$stmt->addParameter(++$args, $pagination->getRange());
$stmt->addParameter(++$args, $pagination->getOffset());
$stmt->addParameter(++$args, $pagination->range);
$stmt->addParameter(++$args, $pagination->offset);
}
$stmt->execute();

View file

@ -166,8 +166,8 @@ class Changelog {
foreach($tags as $tag)
$stmt->addParameter(++$args, (string)$tag);
if($hasPagination) {
$stmt->addParameter(++$args, $pagination->getRange());
$stmt->addParameter(++$args, $pagination->getOffset());
$stmt->addParameter(++$args, $pagination->range);
$stmt->addParameter(++$args, $pagination->offset);
}
$stmt->execute();

View file

@ -64,8 +64,8 @@ final class ChangelogRoutes implements RouteHandler, UrlSource {
}
$count = $this->changelog->countChanges($filterUser, $filterDate, $filterTags);
$pagination = new Pagination($count, 30);
if(!$pagination->hasValidOffset())
$pagination = Pagination::fromRequest($request, $count, 30, 'p');
if(!$pagination->validOffset)
return 404;
$changes = [];

View file

@ -65,8 +65,8 @@ class Comments {
if($hasOwner)
$stmt->addParameter(++$args, $owner);
if($hasPagination) {
$stmt->addParameter(++$args, $pagination->getRange());
$stmt->addParameter(++$args, $pagination->getOffset());
$stmt->addParameter(++$args, $pagination->range);
$stmt->addParameter(++$args, $pagination->offset);
}
$stmt->execute();

View file

@ -41,8 +41,8 @@ class Counters {
$args = 0;
$stmt = $this->cache->get($query);
if($hasPagination) {
$stmt->addParameter(++$args, $pagination->getRange());
$stmt->addParameter(++$args, $pagination->getOffset());
$stmt->addParameter(++$args, $pagination->range);
$stmt->addParameter(++$args, $pagination->offset);
}
$stmt->execute();

View file

@ -154,8 +154,8 @@ class ForumCategories {
if($hasType)
$stmt->addParameter(++$args, $type);
if($hasPagination) {
$stmt->addParameter(++$args, $pagination->getRange());
$stmt->addParameter(++$args, $pagination->getOffset());
$stmt->addParameter(++$args, $pagination->range);
$stmt->addParameter(++$args, $pagination->offset);
}
$stmt->execute();

View file

@ -186,8 +186,8 @@ class ForumPosts {
if($hasSearchQuery)
$stmt->addParameter(++$args, $searchQuery);
if($hasPagination) {
$stmt->addParameter(++$args, $pagination->getRange());
$stmt->addParameter(++$args, $pagination->getOffset());
$stmt->addParameter(++$args, $pagination->range);
$stmt->addParameter(++$args, $pagination->offset);
}
$stmt->execute();

View file

@ -42,12 +42,12 @@ class ForumPostsRoutes implements RouteHandler, UrlSource {
upToPostInfo: $post,
deleted: $canDeleteAny ? null : false
);
$pageNumber = (int)ceil($postsCount / 10); // epic magic number
$pageNumber = ((int)floor($postsCount / 10)) + 1; // epic magic number
return $response->redirect($this->urls->format('forum-topic', [
'topic' => $post->topicId,
'page' => $pageNumber,
'post' => sprintf('p%s', $post->id),
'topic_fragment' => sprintf('p%s', $post->id),
]));
}

View file

@ -56,8 +56,8 @@ class ForumTopicRedirects {
if($hasUserInfo)
$stmt->addParameter(++$args, $userInfo);
if($hasPagination) {
$stmt->addParameter(++$args, $pagination->getRange());
$stmt->addParameter(++$args, $pagination->getOffset());
$stmt->addParameter(++$args, $pagination->range);
$stmt->addParameter(++$args, $pagination->offset);
}
$stmt->execute();

View file

@ -193,8 +193,8 @@ class ForumTopics {
if($hasSearchQuery)
$stmt->addParameter(++$args, $searchQuery);
if($hasPagination) {
$stmt->addParameter(++$args, $pagination->getRange());
$stmt->addParameter(++$args, $pagination->getOffset());
$stmt->addParameter(++$args, $pagination->range);
$stmt->addParameter(++$args, $pagination->offset);
}
$stmt->execute();

View file

@ -34,7 +34,7 @@ class LegacyRoutes implements RouteHandler, UrlSource {
$urls->register('forum-reply-new', '/forum/posting.php', ['t' => '<topic>']);
$urls->register('forum-category', '/forum/forum.php', ['f' => '<forum>', 'p' => '<page>']);
$urls->register('forum-category-root', '/forum/index.php', fragment: '<forum>');
$urls->register('forum-topic', '/forum/topic.php', ['t' => '<topic>', 'page' => '<page>'], '<post>');
$urls->register('forum-topic', '/forum/topic.php', ['t' => '<topic>', 'page' => '<page>'], '<topic_fragment>');
$urls->register('forum-topic-create', '/forum/posting.php', ['f' => '<forum>']);
$urls->register('forum-post', '/forum/topic.php', ['p' => '<post>'], 'p<post>');
$urls->register('forum-post-create', '/forum/posting.php', ['t' => '<topic>']);

View file

@ -148,8 +148,8 @@ class MessagesDatabase {
if($hasReplyTo)
$stmt->addParameter(++$args, $replyTo instanceof MessageInfo ? $replyTo->replyToId : $replyTo);
if($hasPagination) {
$stmt->addParameter(++$args, $pagination->getRange());
$stmt->addParameter(++$args, $pagination->getOffset());
$stmt->addParameter(++$args, $pagination->range);
$stmt->addParameter(++$args, $pagination->offset);
}
$stmt->execute();

View file

@ -116,13 +116,13 @@ class MessagesRoutes implements RouteHandler, UrlSource {
$sent = $folderTrash ? null : !$folderDrafts;
$deleted = $folderTrash;
$pagination = new Pagination($msgsDb->countMessages(
$pagination = Pagination::fromRequest($request, $msgsDb->countMessages(
ownerInfo: $selfInfo,
authorInfo: $authorInfo,
recipientInfo: $recipientInfo,
sent: $sent,
deleted: $deleted,
), 50, 'page');
), 50);
$messageInfos = $msgsDb->getMessages(
ownerInfo: $selfInfo,

View file

@ -54,8 +54,8 @@ class News {
$args = 0;
if($hasPagination) {
$stmt->addParameter(++$args, $pagination->getRange());
$stmt->addParameter(++$args, $pagination->getOffset());
$stmt->addParameter(++$args, $pagination->range);
$stmt->addParameter(++$args, $pagination->offset);
}
$stmt->execute();
@ -259,8 +259,8 @@ class News {
if($hasSearchQuery)
$stmt->addParameter(++$args, $searchQuery);
if($hasPagination) {
$stmt->addParameter(++$args, $pagination->getRange());
$stmt->addParameter(++$args, $pagination->getOffset());
$stmt->addParameter(++$args, $pagination->range);
$stmt->addParameter(++$args, $pagination->offset);
}
$stmt->execute();

View file

@ -101,11 +101,11 @@ class NewsRoutes implements RouteHandler, UrlSource {
#[HttpGet('/news')]
#[UrlFormat('news-index', '/news', ['p' => '<page>'])]
public function getIndex(): int|string {
public function getIndex(HttpResponseBuilder $response, HttpRequest $request): int|string {
$categories = $this->news->getCategories(hidden: false);
$pagination = new Pagination($this->news->countPosts(onlyFeatured: true), 5);
if(!$pagination->hasValidOffset())
$pagination = Pagination::fromRequest($request, $this->news->countPosts(onlyFeatured: true), 5, 'p');
if(!$pagination->validOffset)
return 404;
$posts = $this->getNewsPostsForView($pagination);
@ -129,8 +129,8 @@ class NewsRoutes implements RouteHandler, UrlSource {
if($type !== '')
return $this->getFeed($response, $request, $categoryInfo);
$pagination = new Pagination($this->news->countPosts(categoryInfo: $categoryInfo), 5);
if(!$pagination->hasValidOffset())
$pagination = Pagination::fromRequest($request, $this->news->countPosts(categoryInfo: $categoryInfo), 5, 'p');
if(!$pagination->validOffset)
return 404;
$posts = $this->getNewsPostsForView($pagination, $categoryInfo);

View file

@ -1,79 +1,74 @@
<?php
namespace Misuzu;
use Index\Http\HttpRequest;
final class Pagination {
private const INVALID_OFFSET = -1;
private const START_PAGE = 1;
public const DEFAULT_PARAM = 'p';
public private(set) bool $validOffset;
private int $count = 0;
private int $range = 0;
private int $offset = 0;
public function __construct(int $count, int $range = -1, ?string $readParam = self::DEFAULT_PARAM) {
public function __construct(
public private(set) int $count = 0,
public private(set) int $range = 0,
public private(set) int $offset = 0
) {
$this->count = max(0, $count);
$this->range = $range < 0 ? $count : $range;
if($range < 1)
$this->range = $count;
if(!empty($readParam))
$this->readPage($readParam);
$this->validOffset = $offset >= 0 && $offset < $count;
$this->offset = max(0, $offset);
}
public function getCount(): int {
return $this->count;
public int $pages {
get => (int)ceil($this->count / $this->range);
}
public function getRange(): int {
return $this->range;
public int $page {
get => (int)floor($this->offset / $this->range) + 1;
}
public function getPages(): int {
return (int)ceil($this->getCount() / $this->getRange());
public static function fromPage(
int $count,
int $page,
int $range = 0,
int $firstPage = 1
): self {
if($range < 1)
$range = $count;
$offset = $range * ($page - $firstPage);
if($offset < 0 || $offset >= $count)
$offset = -1;
return new Pagination($count, $range, $offset);
}
public function hasValidOffset(): bool {
return $this->offset !== self::INVALID_OFFSET;
public static function fromInput(
int $count,
int $range = 0,
string $pageParam = 'p',
int $firstPage = 1
): self {
return self::fromPage(
$count,
(int)(filter_input(INPUT_GET, $pageParam, FILTER_SANITIZE_NUMBER_INT) ?? $firstPage),
$range,
$firstPage
);
}
public function getOffset(): int {
return $this->hasValidOffset() ? $this->offset : 0;
}
public function setOffset(int $offset): self {
if($offset < 0)
$offset = self::INVALID_OFFSET;
$this->offset = $offset;
return $this;
}
public function getPage(): int {
if($this->getOffset() < 1)
return self::START_PAGE;
return (int)floor($this->getOffset() / $this->getRange()) + self::START_PAGE;
}
public function setPage(int $page, bool $zeroBased = false): self {
if(!$zeroBased)
$page -= self::START_PAGE;
$this->setOffset($this->getRange() * $page);
return $this;
}
/** @param ?array<string, mixed> $source */
public function readPage(string $name = self::DEFAULT_PARAM, int $default = self::START_PAGE, ?array $source = null): self {
$this->setPage(self::param($name, $default, $source));
return $this;
}
/** @param ?array<string, mixed> $source */
public static function param(string $name = self::DEFAULT_PARAM, int $default = self::START_PAGE, ?array $source = null): int {
$source ??= $_GET;
if(isset($source[$name]) && is_string($source[$name]) && ctype_digit($source[$name]))
return (int)$source[$name];
return $default;
public static function fromRequest(
HttpRequest $request,
int $count,
int $range = 0,
string $pageParam = 'page',
int $firstPage = 1
): Pagination {
return self::fromPage(
$count,
(int)($request->getParam($pageParam, FILTER_SANITIZE_NUMBER_INT) ?? $firstPage),
$range,
$firstPage
);
}
}

View file

@ -95,8 +95,8 @@ class Bans {
if($hasUserInfo)
$stmt->addParameter(++$args, $userInfo);
if($hasPagination) {
$stmt->addParameter(++$args, $pagination->getRange());
$stmt->addParameter(++$args, $pagination->getOffset());
$stmt->addParameter(++$args, $pagination->range);
$stmt->addParameter(++$args, $pagination->offset);
}
$stmt->execute();

View file

@ -87,8 +87,8 @@ class ModNotes {
if($hasAuthorInfo)
$stmt->addParameter(++$args, $authorInfo);
if($hasPagination) {
$stmt->addParameter(++$args, $pagination->getRange());
$stmt->addParameter(++$args, $pagination->getOffset());
$stmt->addParameter(++$args, $pagination->range);
$stmt->addParameter(++$args, $pagination->offset);
}
$stmt->execute();

View file

@ -86,8 +86,8 @@ class Roles {
if($hasUserInfo)
$stmt->nextParameter($userInfo);
if($hasPagination) {
$stmt->nextParameter($pagination->getRange());
$stmt->nextParameter($pagination->getOffset());
$stmt->nextParameter($pagination->range);
$stmt->nextParameter($pagination->offset);
}
$stmt->execute();

View file

@ -207,8 +207,8 @@ class Users {
if($hasSearchQuery)
$stmt->addParameter(++$args, $searchQuery);
if($hasPagination) {
$stmt->addParameter(++$args, $pagination->getRange());
$stmt->addParameter(++$args, $pagination->getOffset());
$stmt->addParameter(++$args, $pagination->range);
$stmt->addParameter(++$args, $pagination->offset);
}
$stmt->execute();

View file

@ -102,8 +102,8 @@ class Warnings {
if($hasBacklog)
$stmt->addParameter(++$args, $backlog);
if($hasPagination) {
$stmt->addParameter(++$args, $pagination->getRange());
$stmt->addParameter(++$args, $pagination->getOffset());
$stmt->addParameter(++$args, $pagination->range);
$stmt->addParameter(++$args, $pagination->offset);
}
$stmt->execute();