311 lines
12 KiB
PHP
311 lines
12 KiB
PHP
<?php
|
|
namespace Misuzu\News;
|
|
|
|
use RuntimeException;
|
|
use Index\DateTime;
|
|
use Index\Data\DbTools;
|
|
use Index\Data\IDbConnection;
|
|
use Index\Routing\IRouter;
|
|
use Index\Routing\IRouteHandler;
|
|
use Misuzu\Pagination;
|
|
use Misuzu\Template;
|
|
use Misuzu\Auth\AuthInfo;
|
|
use Misuzu\Comments\Comments;
|
|
use Misuzu\Comments\CommentsCategory;
|
|
use Misuzu\Comments\CommentsEx;
|
|
use Misuzu\Config\IConfig;
|
|
use Misuzu\Feeds\Feed;
|
|
use Misuzu\Feeds\FeedItem;
|
|
use Misuzu\Feeds\AtomFeedSerializer;
|
|
use Misuzu\Feeds\RssFeedSerializer;
|
|
use Misuzu\News\News;
|
|
use Misuzu\News\NewsCategoryInfo;
|
|
use Misuzu\Users\UsersContext;
|
|
use Misuzu\Parsers\Parser;
|
|
|
|
class NewsRoutes implements IRouteHandler {
|
|
public function __construct(
|
|
private IConfig $config,
|
|
private AuthInfo $authInfo,
|
|
private News $news,
|
|
private UsersContext $usersCtx,
|
|
private Comments $comments
|
|
) {}
|
|
|
|
public function registerRoutes(IRouter $router): void {
|
|
$router->get('/news', $this->getIndex(...));
|
|
$router->get('/news.rss', $this->getFeedRss(...));
|
|
$router->get('/news.atom', $this->getFeedAtom(...));
|
|
$router->get('/news/:category', $this->getCategory(...));
|
|
$router->get('/news/post/:id', $this->getPost(...));
|
|
|
|
$router->get('/news.php', function($response, $request) {
|
|
$postId = $request->getParam('n', FILTER_SANITIZE_NUMBER_INT)
|
|
?? $request->getParam('p', FILTER_SANITIZE_NUMBER_INT);
|
|
|
|
if($postId > 0)
|
|
$location = url('news-post', ['post' => $postId]);
|
|
else {
|
|
$catId = $request->getParam('c', FILTER_SANITIZE_NUMBER_INT);
|
|
$pageId = $request->getParam('page', FILTER_SANITIZE_NUMBER_INT);
|
|
$location = url($catId > 0 ? 'news-category' : 'news-index', ['category' => $catId, 'page' => $pageId]);
|
|
}
|
|
|
|
$response->redirect($location, true);
|
|
});
|
|
|
|
$router->get('/news.php/rss', function($response, $request) {
|
|
$catId = $request->getParam('c', FILTER_SANITIZE_NUMBER_INT);
|
|
$location = url($catId > 0 ? 'news-category-feed-rss' : 'news-feed-rss', ['category' => $catId]);
|
|
$response->redirect($location, true);
|
|
});
|
|
|
|
$router->get('/news.php/atom', function($response, $request) {
|
|
$catId = $request->getParam('c', FILTER_SANITIZE_NUMBER_INT);
|
|
$location = url($catId > 0 ? 'news-category-feed-atom' : 'news-feed-atom', ['category' => $catId]);
|
|
$response->redirect($location, true);
|
|
});
|
|
|
|
$router->get('/news/index.php', function($response, $request) {
|
|
$response->redirect(url('news-index', [
|
|
'page' => $request->getParam('page', FILTER_SANITIZE_NUMBER_INT),
|
|
]), true);
|
|
});
|
|
|
|
$router->get('/news/category.php', function($response, $request) {
|
|
$response->redirect(url('news-category', [
|
|
'category' => $request->getParam('c', FILTER_SANITIZE_NUMBER_INT),
|
|
'page' => $request->getParam('p', FILTER_SANITIZE_NUMBER_INT),
|
|
]), true);
|
|
});
|
|
|
|
$router->get('/news/post.php', function($response, $request) {
|
|
$response->redirect(url('news-post', [
|
|
'post' => $request->getParam('p', FILTER_SANITIZE_NUMBER_INT),
|
|
]), true);
|
|
});
|
|
|
|
$router->get('/news/feed.php/rss', function($response, $request) {
|
|
$catId = (int)$request->getParam('c', FILTER_SANITIZE_NUMBER_INT);
|
|
$response->redirect(url(
|
|
$catId > 0 ? 'news-category-feed-rss' : 'news-feed-rss',
|
|
['category' => $catId]
|
|
), true);
|
|
});
|
|
|
|
$router->get('/news/feed.php/atom', function($response, $request) {
|
|
$catId = (int)$request->getParam('c', FILTER_SANITIZE_NUMBER_INT);
|
|
$response->redirect(url(
|
|
$catId > 0 ? 'news-category-feed-atom' : 'news-feed-atom',
|
|
['category' => $catId]
|
|
), true);
|
|
});
|
|
}
|
|
|
|
private array $categoryInfos = [];
|
|
|
|
private function getNewsPostsForView(Pagination $pagination, ?NewsCategoryInfo $categoryInfo = null): array {
|
|
$posts = [];
|
|
$postInfos = $this->news->getPosts(
|
|
categoryInfo: $categoryInfo,
|
|
onlyFeatured: $categoryInfo === null,
|
|
pagination: $pagination
|
|
);
|
|
|
|
foreach($postInfos as $postInfo) {
|
|
$categoryId = $postInfo->getCategoryId();
|
|
$userInfo = $postInfo->hasUserId() ? $this->usersCtx->getUserInfo($postInfo->getUserId()) : null;
|
|
|
|
if(array_key_exists($categoryId, $this->categoryInfos))
|
|
$categoryInfo = $this->categoryInfos[$categoryId];
|
|
else
|
|
$this->categoryInfos[$categoryId] = $categoryInfo = $this->news->getCategory(postInfo: $postInfo);
|
|
|
|
$commentsCount = $postInfo->hasCommentsCategoryId()
|
|
? $this->comments->countPosts(categoryInfo: $postInfo->getCommentsCategoryId(), deleted: false)
|
|
: 0;
|
|
|
|
$posts[] = [
|
|
'post' => $postInfo,
|
|
'category' => $categoryInfo,
|
|
'user' => $userInfo,
|
|
'user_colour' => $this->usersCtx->getUserColour($userInfo),
|
|
'comments_count' => $commentsCount,
|
|
];
|
|
}
|
|
|
|
return $posts;
|
|
}
|
|
|
|
private function getNewsPostsForFeed(?NewsCategoryInfo $categoryInfo = null): array {
|
|
$posts = [];
|
|
$postInfos = $this->news->getPosts(
|
|
categoryInfo: $categoryInfo,
|
|
onlyFeatured: $categoryInfo === null,
|
|
pagination: new Pagination(10)
|
|
);
|
|
|
|
foreach($postInfos as $postInfo) {
|
|
$userId = $postInfo->getUserId();
|
|
$categoryId = $postInfo->getCategoryId();
|
|
$userInfo = $postInfo->hasUserId() ? $this->usersCtx->getUserInfo($postInfo->getUserId()) : null;
|
|
|
|
$posts[] = [
|
|
'post' => $postInfo,
|
|
'category' => $categoryInfo,
|
|
'user' => $userInfo,
|
|
];
|
|
}
|
|
|
|
return $posts;
|
|
}
|
|
|
|
public function getIndex() {
|
|
$categories = $this->news->getCategories(hidden: false);
|
|
|
|
$pagination = new Pagination($this->news->countPosts(onlyFeatured: true), 5);
|
|
if(!$pagination->hasValidOffset())
|
|
return 404;
|
|
|
|
$posts = $this->getNewsPostsForView($pagination);
|
|
|
|
return Template::renderRaw('news.index', [
|
|
'news_categories' => $categories,
|
|
'news_posts' => $posts,
|
|
'news_pagination' => $pagination,
|
|
]);
|
|
}
|
|
|
|
public function getFeedRss($response) {
|
|
return $this->getFeed($response, 'rss');
|
|
}
|
|
|
|
public function getFeedAtom($response) {
|
|
return $this->getFeed($response, 'atom');
|
|
}
|
|
|
|
public function getCategory($response, $request, string $fileName) {
|
|
$categoryId = pathinfo($fileName, PATHINFO_FILENAME);
|
|
$type = pathinfo($fileName, PATHINFO_EXTENSION);
|
|
|
|
try {
|
|
$categoryInfo = $this->news->getCategory(categoryId: $categoryId);
|
|
} catch(RuntimeException $ex) {
|
|
return 404;
|
|
}
|
|
|
|
if($type === 'rss')
|
|
return $this->getCategoryFeedRss($response, $request, $categoryInfo);
|
|
elseif($type === 'atom')
|
|
return $this->getCategoryFeedAtom($response, $request, $categoryInfo);
|
|
elseif($type !== '')
|
|
return 404;
|
|
|
|
$pagination = new Pagination($this->news->countPosts(categoryInfo: $categoryInfo), 5);
|
|
if(!$pagination->hasValidOffset())
|
|
return 404;
|
|
|
|
$posts = $this->getNewsPostsForView($pagination, $categoryInfo);
|
|
|
|
return Template::renderRaw('news.category', [
|
|
'news_category' => $categoryInfo,
|
|
'news_posts' => $posts,
|
|
'news_pagination' => $pagination,
|
|
]);
|
|
}
|
|
|
|
private function getCategoryFeedRss($response, $request, NewsCategoryInfo $categoryInfo) {
|
|
return $this->getFeed($response, 'rss', $categoryInfo);
|
|
}
|
|
|
|
private function getCategoryFeedAtom($response, $request, NewsCategoryInfo $categoryInfo) {
|
|
return $this->getFeed($response, 'atom', $categoryInfo);
|
|
}
|
|
|
|
public function getPost($response, $request, string $postId) {
|
|
try {
|
|
$postInfo = $this->news->getPost($postId);
|
|
} catch(RuntimeException $ex) {
|
|
return 404;
|
|
}
|
|
|
|
if(!$postInfo->isPublished() || $postInfo->isDeleted())
|
|
return 404;
|
|
|
|
$categoryInfo = $this->news->getCategory(postInfo: $postInfo);
|
|
|
|
if($postInfo->hasCommentsCategoryId())
|
|
try {
|
|
$commentsCategory = $this->comments->getCategory(categoryId: $postInfo->getCommentsCategoryId());
|
|
} catch(RuntimeException $ex) {}
|
|
|
|
if(!isset($commentsCategory)) {
|
|
$commentsCategory = $this->comments->ensureCategory($postInfo->getCommentsCategoryName());
|
|
$this->news->updatePostCommentCategory($postInfo, $commentsCategory);
|
|
}
|
|
|
|
$userInfo = $postInfo->hasUserId() ? $this->usersCtx->getUserInfo($postInfo->getUserId()) : null;
|
|
$comments = new CommentsEx($this->authInfo, $this->comments, $this->usersCtx);
|
|
|
|
return Template::renderRaw('news.post', [
|
|
'post_info' => $postInfo,
|
|
'post_category_info' => $categoryInfo,
|
|
'post_user_info' => $userInfo,
|
|
'post_user_colour' => $this->usersCtx->getUserColour($userInfo),
|
|
'comments_info' => $comments->getCommentsForLayout($commentsCategory),
|
|
]);
|
|
}
|
|
|
|
private function getFeed($response, string $feedType, ?NewsCategoryInfo $categoryInfo = null) {
|
|
$hasCategory = $categoryInfo !== null;
|
|
$siteName = $this->config->getString('site.name', 'Misuzu');
|
|
$posts = $this->getNewsPostsForFeed($categoryInfo);
|
|
|
|
$serialiser = match($feedType) {
|
|
'rss' => new RssFeedSerializer,
|
|
'atom' => new AtomFeedSerializer,
|
|
default => throw new RuntimeException('Invalid $feedType specified.'),
|
|
};
|
|
|
|
$response->setContentType(sprintf('application/%s+xml; charset=utf-8', $feedType));
|
|
$feed = (new Feed)
|
|
->setTitle($siteName . ' » ' . ($hasCategory ? $categoryInfo->getName() : 'Featured News'))
|
|
->setDescription($hasCategory ? $categoryInfo->getDescription() : 'A live featured news feed.')
|
|
->setContentUrl(url_prefix(false) . ($hasCategory ? url('news-category', ['category' => $categoryInfo->getId()]) : url('news-index')))
|
|
->setFeedUrl(url_prefix(false) . ($hasCategory ? url("news-category-feed-{$feedType}", ['category' => $categoryInfo->getId()]) : url("news-feed-{$feedType}")));
|
|
|
|
foreach($posts as $post) {
|
|
$postInfo = $post['post'];
|
|
$userInfo = $post['user'];
|
|
|
|
$userId = 0;
|
|
$userName = 'Author';
|
|
if($userInfo !== null) {
|
|
$userId = $userInfo->getId();
|
|
$userName = $userInfo->getName();
|
|
}
|
|
|
|
$postUrl = url_prefix(false) . url('news-post', ['post' => $postInfo->getId()]);
|
|
$commentsUrl = url_prefix(false) . url('news-post-comments', ['post' => $postInfo->getId()]);
|
|
$authorUrl = url_prefix(false) . url('user-profile', ['user' => $userId]);
|
|
|
|
$feedItem = (new FeedItem)
|
|
->setTitle($postInfo->getTitle())
|
|
->setSummary($postInfo->getFirstParagraph())
|
|
->setContent(Parser::instance(Parser::MARKDOWN)->parseText($postInfo->getBody()))
|
|
->setCreationDate($postInfo->getCreatedTime())
|
|
->setUniqueId($postUrl)
|
|
->setContentUrl($postUrl)
|
|
->setCommentsUrl($commentsUrl)
|
|
->setAuthorName($userName)
|
|
->setAuthorUrl($authorUrl);
|
|
|
|
if(!$feed->hasLastUpdate() || $feed->getLastUpdate() < $feedItem->getCreationDate())
|
|
$feed->setLastUpdate($feedItem->getCreationDate());
|
|
|
|
$feed->addItem($feedItem);
|
|
}
|
|
|
|
return $serialiser->serializeFeed($feed);
|
|
}
|
|
}
|