Moved news pages into the router and made news object oriented.
This commit is contained in:
parent
4871df92f9
commit
b66eb8ba76
47 changed files with 980 additions and 780 deletions
|
@ -13,6 +13,17 @@ Misuzu.Urls.handleVariable = function(value, vars) {
|
|||
return ''; // not sure if there's a proper substitute for this, should probably resolve these in url_list
|
||||
if(value[0] === '{' && value.slice(-1) === '}')
|
||||
return Misuzu.CSRF.getToken();
|
||||
|
||||
// Allow file extensions
|
||||
var split = value.split('.'),
|
||||
extension = split[split.length - 1],
|
||||
fileName = split.slice(0, -1).join('.');
|
||||
if(value !== fileName) {
|
||||
var fallback = Misuzu.Urls.handleVariable(fileName, vars);
|
||||
if(fallback !== fileName)
|
||||
return fallback + '.' + extension;
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
Misuzu.Urls.v = function(name, value) {
|
||||
|
|
|
@ -61,16 +61,15 @@ class_alias(\Misuzu\Http\HttpResponseMessage::class, '\HttpResponse');
|
|||
class_alias(\Misuzu\Http\HttpRequestMessage::class, '\HttpRequest');
|
||||
|
||||
require_once 'utility.php';
|
||||
require_once 'src/perms.php';
|
||||
require_once 'src/audit_log.php';
|
||||
require_once 'src/changelog.php';
|
||||
require_once 'src/comments.php';
|
||||
require_once 'src/manage.php';
|
||||
require_once 'src/news.php';
|
||||
require_once 'src/perms.php';
|
||||
require_once 'src/url.php';
|
||||
require_once 'src/Forum/perms.php';
|
||||
require_once 'src/Forum/forum.php';
|
||||
require_once 'src/Forum/leaderboard.php';
|
||||
require_once 'src/Forum/perms.php';
|
||||
require_once 'src/Forum/poll.php';
|
||||
require_once 'src/Forum/post.php';
|
||||
require_once 'src/Forum/topic.php';
|
||||
|
@ -500,7 +499,7 @@ MIG;
|
|||
$inManageMode = starts_with($_SERVER['REQUEST_URI'], '/manage');
|
||||
$hasManageAccess = !empty($userDisplayInfo['user_id'])
|
||||
&& !user_warning_check_restriction($userDisplayInfo['user_id'])
|
||||
&& perms_check_user(MSZ_PERMS_GENERAL, $userDisplayInfo['user_id'], General::PERM_CAN_MANAGE);
|
||||
&& perms_check_user(MSZ_PERMS_GENERAL, $userDisplayInfo['user_id'], MSZ_PERM_GENERAL_CAN_MANAGE);
|
||||
Template::set('has_manage_access', $hasManageAccess);
|
||||
|
||||
if($inManageMode) {
|
||||
|
|
|
@ -5,7 +5,7 @@ use Misuzu\Http\HttpRequestMessage;
|
|||
use Misuzu\Http\Routing\Router;
|
||||
use Misuzu\Http\Routing\Route;
|
||||
|
||||
require_once '../misuzu.php';
|
||||
require_once __DIR__ . '/../misuzu.php';
|
||||
|
||||
$request = HttpRequestMessage::fromGlobals();
|
||||
|
||||
|
@ -22,6 +22,16 @@ Router::addRoutes(
|
|||
Route::get('/info', 'index', 'Info'),
|
||||
Route::get('/info/([A-Za-z0-9_/]+)', 'page', 'Info'),
|
||||
|
||||
// News
|
||||
Route::get('/news', 'index', 'News')->addChildren(
|
||||
Route::get('.atom', 'feedIndexAtom'),
|
||||
Route::get('.rss', 'feedIndexRss'),
|
||||
Route::get('/([0-9]+)', 'viewCategory'),
|
||||
Route::get('/([0-9]+).atom', 'feedCategoryAtom'),
|
||||
Route::get('/([0-9]+).rss', 'feedCategoryRss'),
|
||||
Route::get('/post/([0-9]+)', 'viewPost')
|
||||
),
|
||||
|
||||
// Forum
|
||||
Route::group('/forum', 'Forum')->addChildren(
|
||||
Route::get('/mark-as-read', 'markAsReadGET')->addFilters('EnforceLogIn'),
|
||||
|
@ -43,6 +53,15 @@ Router::addRoutes(
|
|||
Route::get('/info.php', url('info')),
|
||||
Route::get('/info.php/([A-Za-z0-9_/]+)', 'redir', 'Info'),
|
||||
Route::get('/auth.php', 'legacy', 'Auth'),
|
||||
Route::get('/news.php', 'legacy', 'News'),
|
||||
Route::get('/news.php/rss', 'legacy', 'News'),
|
||||
Route::get('/news.php/atom', 'legacy', 'News'),
|
||||
Route::get('/news/index.php', 'legacy', 'News'),
|
||||
Route::get('/news/category.php', 'legacy', 'News'),
|
||||
Route::get('/news/post.php', 'legacy', 'News'),
|
||||
Route::get('/news/feed.php', 'legacy', 'News'),
|
||||
Route::get('/news/feed.php/rss', 'legacy', 'News'),
|
||||
Route::get('/news/feed.php/atom', 'legacy', 'News'),
|
||||
);
|
||||
|
||||
$response = Router::handle($request);
|
||||
|
|
|
@ -5,7 +5,7 @@ use Misuzu\Net\IPAddressBlacklist;
|
|||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
if(!perms_check_user(MSZ_PERMS_GENERAL, user_session_current('user_id'), General::PERM_MANAGE_BLACKLIST)) {
|
||||
if(!perms_check_user(MSZ_PERMS_GENERAL, user_session_current('user_id'), MSZ_PERM_GENERAL_MANAGE_BLACKLIST)) {
|
||||
echo render_error(403);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ namespace Misuzu;
|
|||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
if(!perms_check_user(MSZ_PERMS_GENERAL, user_session_current('user_id'), General::PERM_MANAGE_EMOTES)) {
|
||||
if(!perms_check_user(MSZ_PERMS_GENERAL, user_session_current('user_id'), MSZ_PERM_GENERAL_MANAGE_EMOTES)) {
|
||||
echo render_error(403);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ namespace Misuzu;
|
|||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
if(!perms_check_user(MSZ_PERMS_GENERAL, user_session_current('user_id'), General::PERM_MANAGE_EMOTES)) {
|
||||
if(!perms_check_user(MSZ_PERMS_GENERAL, user_session_current('user_id'), MSZ_PERM_GENERAL_MANAGE_EMOTES)) {
|
||||
echo render_error(403);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ namespace Misuzu;
|
|||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
if(!perms_check_user(MSZ_PERMS_GENERAL, user_session_current('user_id'), General::PERM_VIEW_LOGS)) {
|
||||
if(!perms_check_user(MSZ_PERMS_GENERAL, user_session_current('user_id'), MSZ_PERM_GENERAL_VIEW_LOGS)) {
|
||||
echo render_error(403);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ namespace Misuzu;
|
|||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
if(!perms_check_user(MSZ_PERMS_GENERAL, user_session_current('user_id'), General::PERM_MANAGE_CONFIG)) {
|
||||
if(!perms_check_user(MSZ_PERMS_GENERAL, user_session_current('user_id'), MSZ_PERM_GENERAL_MANAGE_CONFIG)) {
|
||||
echo render_error(403);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\News\NewsCategory;
|
||||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
if(!perms_check_user(MSZ_PERMS_NEWS, user_session_current('user_id'), MSZ_PERM_NEWS_MANAGE_CATEGORIES)) {
|
||||
|
@ -8,14 +10,14 @@ if(!perms_check_user(MSZ_PERMS_NEWS, user_session_current('user_id'), MSZ_PERM_N
|
|||
return;
|
||||
}
|
||||
|
||||
$categoriesPagination = new Pagination(news_categories_count(true), 15);
|
||||
$categoriesPagination = new Pagination(NewsCategory::countAll(true), 15);
|
||||
|
||||
if(!$categoriesPagination->hasValidOffset()) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
$categories = news_categories_get($categoriesPagination->getOffset(), $categoriesPagination->getRange(), true, false, true);
|
||||
$categories = NewsCategory::all($categoriesPagination, true);
|
||||
|
||||
Template::render('manage.news.categories', [
|
||||
'news_categories' => $categories,
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\News\NewsCategory;
|
||||
use Misuzu\News\NewsCategoryNotFoundException;
|
||||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
if(!perms_check_user(MSZ_PERMS_NEWS, user_session_current('user_id'), MSZ_PERM_NEWS_MANAGE_CATEGORIES)) {
|
||||
|
@ -8,29 +11,40 @@ if(!perms_check_user(MSZ_PERMS_NEWS, user_session_current('user_id'), MSZ_PERM_N
|
|||
return;
|
||||
}
|
||||
|
||||
$category = [];
|
||||
$categoryId = (int)($_GET['c'] ?? null);
|
||||
$categoryId = (int)filter_input(INPUT_GET, 'c', FILTER_SANITIZE_NUMBER_INT);
|
||||
|
||||
if($categoryId > 0)
|
||||
try {
|
||||
$categoryInfo = NewsCategory::byId($categoryId);
|
||||
Template::set('category_info', $categoryInfo);
|
||||
} catch(NewsCategoryNotFoundException $ex) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!empty($_POST['category']) && CSRF::validateRequest()) {
|
||||
$originalCategoryId = (int)($_POST['category']['id'] ?? null);
|
||||
$categoryId = news_category_create(
|
||||
$_POST['category']['name'] ?? null,
|
||||
$_POST['category']['description'] ?? null,
|
||||
!empty($_POST['category']['hidden']),
|
||||
$originalCategoryId
|
||||
);
|
||||
if(!isset($categoryInfo)) {
|
||||
$categoryInfo = new NewsCategory;
|
||||
$isNew = true;
|
||||
}
|
||||
|
||||
$categoryInfo->setName($_POST['category']['name'])
|
||||
->setDescription($_POST['category']['description'])
|
||||
->setHidden(!empty($_POST['category']['hidden']))
|
||||
->save();
|
||||
|
||||
audit_log(
|
||||
$originalCategoryId === $categoryId
|
||||
empty($isNew)
|
||||
? MSZ_AUDIT_NEWS_CATEGORY_EDIT
|
||||
: MSZ_AUDIT_NEWS_CATEGORY_CREATE,
|
||||
user_session_current('user_id'),
|
||||
[$categoryId]
|
||||
[$categoryInfo->getId()]
|
||||
);
|
||||
|
||||
if(!empty($isNew)) {
|
||||
header('Location: ' . url('manage-news-category', ['category' => $categoryInfo->getId()]));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if($categoryId > 0) {
|
||||
$category = news_category_get($categoryId);
|
||||
}
|
||||
|
||||
Template::render('manage.news.category', compact('category'));
|
||||
Template::render('manage.news.category');
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\News\NewsCategory;
|
||||
use Misuzu\News\NewsPost;
|
||||
use Misuzu\News\NewsPostNotFoundException;
|
||||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
if(!perms_check_user(MSZ_PERMS_NEWS, user_session_current('user_id'), MSZ_PERM_NEWS_MANAGE_POSTS)) {
|
||||
|
@ -8,50 +12,63 @@ if(!perms_check_user(MSZ_PERMS_NEWS, user_session_current('user_id'), MSZ_PERM_N
|
|||
return;
|
||||
}
|
||||
|
||||
$post = [];
|
||||
$postId = (int)($_GET['p'] ?? null);
|
||||
$categories = news_categories_get(0, 0, false, false, true);
|
||||
$postId = (int)filter_input(INPUT_GET, 'p', FILTER_SANITIZE_NUMBER_INT);
|
||||
if($postId > 0)
|
||||
try {
|
||||
$postInfo = NewsPost::byId($postId);
|
||||
Template::set('post_info', $postInfo);
|
||||
} catch(NewsPostNotFoundException $ex) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
$categories = NewsCategory::all(null, true);
|
||||
|
||||
if(!empty($_POST['post']) && CSRF::validateRequest()) {
|
||||
$originalPostId = (int)($_POST['post']['id'] ?? null);
|
||||
if(!isset($postInfo)) {
|
||||
$postInfo = new NewsPost;
|
||||
$isNew = true;
|
||||
}
|
||||
|
||||
$currentUserId = user_session_current('user_id');
|
||||
$title = $_POST['post']['title'] ?? null;
|
||||
$isFeatured = !empty($_POST['post']['featured']);
|
||||
$postId = news_post_create(
|
||||
$title,
|
||||
$_POST['post']['text'] ?? null,
|
||||
(int)($_POST['post']['category'] ?? null),
|
||||
user_session_current('user_id'),
|
||||
$isFeatured,
|
||||
null,
|
||||
$originalPostId
|
||||
);
|
||||
$postInfo->setTitle( $_POST['post']['title'])
|
||||
->setText($_POST['post']['text'])
|
||||
->setCategoryId($_POST['post']['category'])
|
||||
->setFeatured(!empty($_POST['post']['featured']));
|
||||
|
||||
if(!empty($isNew))
|
||||
$postInfo->setUserId($currentUserId);
|
||||
|
||||
$postInfo->save();
|
||||
|
||||
audit_log(
|
||||
$originalPostId === $postId
|
||||
empty($isNew)
|
||||
? MSZ_AUDIT_NEWS_POST_EDIT
|
||||
: MSZ_AUDIT_NEWS_POST_CREATE,
|
||||
$currentUserId,
|
||||
[$postId]
|
||||
[$postInfo->getId()]
|
||||
);
|
||||
|
||||
if(!$originalPostId && $isFeatured) {
|
||||
$twitterApiKey = Config::get('twitter.api.key', Config::TYPE_STR);
|
||||
$twitterApiSecret = Config::get('twitter.api.secret', Config::TYPE_STR);
|
||||
$twitterToken = Config::get('twitter.token.key', Config::TYPE_STR);
|
||||
$twitterTokenSecret = Config::get('twitter.token.secret', Config::TYPE_STR);
|
||||
if(!empty($isNew)) {
|
||||
if($postInfo->isFeatured()) {
|
||||
$twitterApiKey = Config::get('twitter.api.key', Config::TYPE_STR);
|
||||
$twitterApiSecret = Config::get('twitter.api.secret', Config::TYPE_STR);
|
||||
$twitterToken = Config::get('twitter.token.key', Config::TYPE_STR);
|
||||
$twitterTokenSecret = Config::get('twitter.token.secret', Config::TYPE_STR);
|
||||
|
||||
if(!empty($twitterApiKey) && !empty($twitterApiSecret)
|
||||
&& !empty($twitterToken) && !empty($twitterTokenSecret)) {
|
||||
Twitter::init($twitterApiKey, $twitterApiSecret, $twitterToken, $twitterTokenSecret);
|
||||
$url = url('news-post', ['post' => $postId]);
|
||||
Twitter::sendTweet("News :: {$title}\nhttps://{$_SERVER['HTTP_HOST']}{$url}");
|
||||
if(!empty($twitterApiKey) && !empty($twitterApiSecret)
|
||||
&& !empty($twitterToken) && !empty($twitterTokenSecret)) {
|
||||
Twitter::init($twitterApiKey, $twitterApiSecret, $twitterToken, $twitterTokenSecret);
|
||||
$url = url('news-post', ['post' => $postInfo->getId()]);
|
||||
Twitter::sendTweet("News :: {$postInfo->getTitle()}\nhttps://{$_SERVER['HTTP_HOST']}{$url}");
|
||||
}
|
||||
}
|
||||
|
||||
header('Location: ' . url('manage-news-post', ['post' => $postInfo->getId()]));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if($postId > 0) {
|
||||
$post = news_post_get($postId);
|
||||
}
|
||||
|
||||
Template::render('manage.news.post', compact('post', 'categories'));
|
||||
Template::render('manage.news.post', [
|
||||
'categories' => $categories,
|
||||
]);
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\News\NewsPost;
|
||||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
if(!perms_check_user(MSZ_PERMS_NEWS, user_session_current('user_id'), MSZ_PERM_NEWS_MANAGE_POSTS)) {
|
||||
|
@ -8,18 +10,14 @@ if(!perms_check_user(MSZ_PERMS_NEWS, user_session_current('user_id'), MSZ_PERM_N
|
|||
return;
|
||||
}
|
||||
|
||||
$postsPagination = new Pagination(news_posts_count(null, false, true, false), 15);
|
||||
$postsPagination = new Pagination(NewsPost::countAll(false, true, true), 15);
|
||||
|
||||
if(!$postsPagination->hasValidOffset()) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
$posts = news_posts_get(
|
||||
$postsPagination->getOffset(),
|
||||
$postsPagination->getRange(),
|
||||
null, false, true, false
|
||||
);
|
||||
$posts = NewsPost::all($postsPagination, false, true, true);
|
||||
|
||||
Template::render('manage.news.posts', [
|
||||
'news_posts' => $posts,
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
require_once __DIR__ . '/index.php';
|
||||
return;
|
||||
//namespace Misuzu;
|
||||
|
||||
require_once '../misuzu.php';
|
||||
|
||||
|
@ -20,12 +22,9 @@ if(!empty($feedMode) && in_array($feedMode, ['rss', 'atom'])) {
|
|||
$location = empty($categoryId) ? url("news-feed-{$feedMode}") : url("news-category-feed-{$feedMode}", ['category' => $categoryId]);
|
||||
}
|
||||
|
||||
if($postId > 0) {
|
||||
if($postId > 0)
|
||||
$location = url('news-post', ['post' => $postId]);
|
||||
}
|
||||
|
||||
if($categoryId > 0) {
|
||||
if($categoryId > 0)
|
||||
$location = url('news-category', ['category' => $categoryId, 'page' => Pagination::param('page')]);
|
||||
}
|
||||
|
||||
redirect($location);
|
||||
|
|
|
@ -1,34 +1,2 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
require_once '../../misuzu.php';
|
||||
|
||||
$categoryId = !empty($_GET['c']) && is_string($_GET['c']) ? (int)$_GET['c'] : 0;
|
||||
$category = news_category_get($categoryId, true);
|
||||
|
||||
if(empty($category)) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
$categoryPagination = new Pagination($category['posts_count'], 5);
|
||||
|
||||
if(!$categoryPagination->hasValidOffset()) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
$posts = news_posts_get(
|
||||
$categoryPagination->getOffset(),
|
||||
$categoryPagination->getRange(),
|
||||
$category['category_id']
|
||||
);
|
||||
|
||||
$featured = news_posts_get(0, 10, $category['category_id'], true);
|
||||
|
||||
Template::render('news.category', [
|
||||
'category' => $category,
|
||||
'posts' => $posts,
|
||||
'featured' => $featured,
|
||||
'news_pagination' => $categoryPagination,
|
||||
]);
|
||||
require_once __DIR__ . '/../index.php';
|
||||
|
|
|
@ -1,76 +1,2 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Feeds\Feed;
|
||||
use Misuzu\Feeds\FeedItem;
|
||||
use Misuzu\Feeds\AtomFeedSerializer;
|
||||
use Misuzu\Feeds\RssFeedSerializer;
|
||||
use Misuzu\Parsers\Parser;
|
||||
|
||||
require_once '../../misuzu.php';
|
||||
|
||||
$feedMode = trim($_SERVER['PATH_INFO'] ?? '', '/');
|
||||
|
||||
switch($feedMode) {
|
||||
case 'rss':
|
||||
$feedSerializer = new RssFeedSerializer;
|
||||
break;
|
||||
case 'atom':
|
||||
$feedSerializer = new AtomFeedSerializer;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!isset($feedSerializer)) {
|
||||
echo render_error(400);
|
||||
return;
|
||||
}
|
||||
|
||||
$categoryId = !empty($_GET['c']) && is_string($_GET['c']) ? (int)$_GET['c'] : 0;
|
||||
|
||||
if(!empty($categoryId)) {
|
||||
$category = news_category_get($categoryId);
|
||||
|
||||
if(empty($category)) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$posts = news_posts_get(0, 10, $category['category_id'] ?? null, empty($category));
|
||||
|
||||
if(!$posts) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
$feed = (new Feed)
|
||||
->setTitle(Config::get('site.name', Config::TYPE_STR, 'Misuzu') . ' » ' . ($category['category_name'] ?? 'Featured News'))
|
||||
->setDescription($category['category_description'] ?? 'A live featured news feed.')
|
||||
->setContentUrl(url_prefix(false) . (empty($category) ? url('news-index') : url('news-category', ['category' => $category['category_id']])))
|
||||
->setFeedUrl(url_prefix(false) . (empty($category) ? url("news-feed-{$feedMode}") : url("news-category-feed-{$feedMode}", ['category' => $category['category_id']])));
|
||||
|
||||
foreach($posts as $post) {
|
||||
$postUrl = url_prefix(false) . url('news-post', ['post' => $post['post_id']]);
|
||||
$commentsUrl = url_prefix(false) . url('news-post-comments', ['post' => $post['post_id']]);
|
||||
$authorUrl = url_prefix(false) . url('user-profile', ['user' => $post['user_id']]);
|
||||
|
||||
$feedItem = (new FeedItem)
|
||||
->setTitle($post['post_title'])
|
||||
->setSummary(first_paragraph($post['post_text']))
|
||||
->setContent(Parser::instance(Parser::MARKDOWN)->parseText($post['post_text']))
|
||||
->setCreationDate(strtotime($post['post_created']))
|
||||
->setUniqueId($postUrl)
|
||||
->setContentUrl($postUrl)
|
||||
->setCommentsUrl($commentsUrl)
|
||||
->setAuthorName($post['username'])
|
||||
->setAuthorUrl($authorUrl);
|
||||
|
||||
if(!$feed->hasLastUpdate() || $feed->getLastUpdate() < $feedItem->getCreationDate())
|
||||
$feed->setLastUpdate($feedItem->getCreationDate());
|
||||
|
||||
$feed->addItem($feedItem);
|
||||
}
|
||||
|
||||
header("Content-Type: application/{$feedMode}+xml; charset=utf-8");
|
||||
|
||||
echo $feedSerializer->serializeFeed($feed);
|
||||
require_once __DIR__ . '/../index.php';
|
||||
|
|
|
@ -1,31 +1,2 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
require_once '../../misuzu.php';
|
||||
|
||||
$categories = news_categories_get(0, 0, true);
|
||||
|
||||
$newsPagination = new Pagination(news_posts_count(null, true), 5, 'page');
|
||||
|
||||
if(!$newsPagination->hasValidOffset()) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
$posts = news_posts_get(
|
||||
$newsPagination->getOffset(),
|
||||
$newsPagination->getRange(),
|
||||
null,
|
||||
true
|
||||
);
|
||||
|
||||
if(!$posts) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
Template::render('news.index', [
|
||||
'categories' => $categories,
|
||||
'posts' => $posts,
|
||||
'news_pagination' => $newsPagination,
|
||||
]);
|
||||
require_once __DIR__ . '/../index.php';
|
||||
|
|
|
@ -1,33 +1,2 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
require_once '../../misuzu.php';
|
||||
|
||||
$postId = !empty($_GET['p']) && is_string($_GET['p']) ? (int)$_GET['p'] : 0;
|
||||
$post = news_post_get($postId);
|
||||
|
||||
if(!$post) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
if($post['comment_section_id'] === null) {
|
||||
$commentsInfo = comments_category_create("news-{$post['post_id']}");
|
||||
|
||||
if($commentsInfo) {
|
||||
$post['comment_section_id'] = $commentsInfo['category_id'];
|
||||
news_post_comments_set(
|
||||
$post['post_id'],
|
||||
$post['comment_section_id'] = $commentsInfo['category_id']
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$commentsInfo = comments_category_info($post['comment_section_id']);
|
||||
}
|
||||
|
||||
Template::render('news.post', [
|
||||
'post' => $post,
|
||||
'comments_perms' => comments_get_perms(user_session_current('user_id', 0)),
|
||||
'comments_category' => $commentsInfo,
|
||||
'comments' => comments_category_get($commentsInfo['category_id'], user_session_current('user_id', 0)),
|
||||
]);
|
||||
require_once __DIR__ . '/../index.php';
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\News\NewsPost;
|
||||
|
||||
require_once '../misuzu.php';
|
||||
|
||||
$searchQuery = !empty($_GET['q']) && is_string($_GET['q']) ? $_GET['q'] : '';
|
||||
|
@ -8,7 +10,7 @@ $searchQuery = !empty($_GET['q']) && is_string($_GET['q']) ? $_GET['q'] : '';
|
|||
if(!empty($searchQuery)) {
|
||||
$forumTopics = forum_topic_listing_search($searchQuery, user_session_current('user_id', 0));
|
||||
$forumPosts = forum_post_search($searchQuery);
|
||||
$newsPosts = news_posts_search($searchQuery);
|
||||
$newsPosts = NewsPost::bySearchQuery($searchQuery);
|
||||
|
||||
$findUsers = DB::prepare(sprintf(
|
||||
'
|
||||
|
|
|
@ -129,7 +129,6 @@ class Colour {
|
|||
public function getCSS(): string {
|
||||
if($this->getInherit())
|
||||
return 'inherit';
|
||||
|
||||
return '#' . $this->getHex();
|
||||
}
|
||||
|
||||
|
@ -141,4 +140,8 @@ class Colour {
|
|||
|
||||
return $this->getLuminance() > self::READABILITY_THRESHOLD ? $dark : $light;
|
||||
}
|
||||
|
||||
public function __toString() {
|
||||
return $this->getCSS();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,9 @@ use Misuzu\Database\Database;
|
|||
final class DB {
|
||||
private static $instance;
|
||||
|
||||
public const PREFIX = 'msz_';
|
||||
public const QUERY_SELECT = 'SELECT %2$s FROM `' . self::PREFIX . '%1$s` AS %1$s';
|
||||
|
||||
public const ATTRS = [
|
||||
PDO::ATTR_CASE => PDO::CASE_NATURAL,
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
|
|
|
@ -1,51 +1,4 @@
|
|||
<?php
|
||||
/**********************
|
||||
* GLOBAL PERMISSIONS *
|
||||
**********************/
|
||||
define('MSZ_PERM_FORUM_MANAGE_FORUMS', 1);
|
||||
define('MSZ_PERM_FORUM_VIEW_LEADERBOARD', 2);
|
||||
|
||||
/*************************
|
||||
* PER-FORUM PERMISSIONS *
|
||||
*************************/
|
||||
define('MSZ_FORUM_PERM_LIST_FORUM', 1); // can see stats, but will get error when trying to view
|
||||
define('MSZ_FORUM_PERM_VIEW_FORUM', 1 << 1);
|
||||
|
||||
define('MSZ_FORUM_PERM_CREATE_TOPIC', 1 << 10);
|
||||
//define('MSZ_FORUM_PERM_DELETE_TOPIC', 1 << 11); // use MSZ_FORUM_PERM_DELETE_ANY_POST instead
|
||||
define('MSZ_FORUM_PERM_MOVE_TOPIC', 1 << 12);
|
||||
define('MSZ_FORUM_PERM_LOCK_TOPIC', 1 << 13);
|
||||
define('MSZ_FORUM_PERM_STICKY_TOPIC', 1 << 14);
|
||||
define('MSZ_FORUM_PERM_ANNOUNCE_TOPIC', 1 << 15);
|
||||
define('MSZ_FORUM_PERM_GLOBAL_ANNOUNCE_TOPIC', 1 << 16);
|
||||
define('MSZ_FORUM_PERM_BUMP_TOPIC', 1 << 17);
|
||||
define('MSZ_FORUM_PERM_PRIORITY_VOTE', 1 << 18);
|
||||
|
||||
define('MSZ_FORUM_PERM_CREATE_POST', 1 << 20);
|
||||
define('MSZ_FORUM_PERM_EDIT_POST', 1 << 21);
|
||||
define('MSZ_FORUM_PERM_EDIT_ANY_POST', 1 << 22);
|
||||
define('MSZ_FORUM_PERM_DELETE_POST', 1 << 23);
|
||||
define('MSZ_FORUM_PERM_DELETE_ANY_POST', 1 << 24);
|
||||
|
||||
// shorthands, never use these to SET!!!!!!!
|
||||
define('MSZ_FORUM_PERM_SET_READ', MSZ_FORUM_PERM_LIST_FORUM | MSZ_FORUM_PERM_VIEW_FORUM);
|
||||
define(
|
||||
'MSZ_FORUM_PERM_SET_WRITE',
|
||||
MSZ_FORUM_PERM_CREATE_TOPIC
|
||||
| MSZ_FORUM_PERM_MOVE_TOPIC
|
||||
| MSZ_FORUM_PERM_LOCK_TOPIC
|
||||
| MSZ_FORUM_PERM_STICKY_TOPIC
|
||||
| MSZ_FORUM_PERM_ANNOUNCE_TOPIC
|
||||
| MSZ_FORUM_PERM_GLOBAL_ANNOUNCE_TOPIC
|
||||
| MSZ_FORUM_PERM_CREATE_POST
|
||||
| MSZ_FORUM_PERM_EDIT_POST
|
||||
| MSZ_FORUM_PERM_EDIT_ANY_POST
|
||||
| MSZ_FORUM_PERM_DELETE_POST
|
||||
| MSZ_FORUM_PERM_DELETE_ANY_POST
|
||||
| MSZ_FORUM_PERM_BUMP_TOPIC
|
||||
| MSZ_FORUM_PERM_PRIORITY_VOTE
|
||||
);
|
||||
|
||||
define('MSZ_FORUM_TYPE_DISCUSSION', 0);
|
||||
define('MSZ_FORUM_TYPE_CATEGORY', 1);
|
||||
define('MSZ_FORUM_TYPE_LINK', 2);
|
||||
|
|
|
@ -1,6 +1,44 @@
|
|||
<?php
|
||||
define('MSZ_FORUM_PERMS_GENERAL', 'forum');
|
||||
|
||||
define('MSZ_FORUM_PERM_LIST_FORUM', 1); // can see stats, but will get error when trying to view
|
||||
define('MSZ_FORUM_PERM_VIEW_FORUM', 1 << 1);
|
||||
|
||||
define('MSZ_FORUM_PERM_CREATE_TOPIC', 1 << 10);
|
||||
//define('MSZ_FORUM_PERM_DELETE_TOPIC', 1 << 11); // use MSZ_FORUM_PERM_DELETE_ANY_POST instead
|
||||
define('MSZ_FORUM_PERM_MOVE_TOPIC', 1 << 12);
|
||||
define('MSZ_FORUM_PERM_LOCK_TOPIC', 1 << 13);
|
||||
define('MSZ_FORUM_PERM_STICKY_TOPIC', 1 << 14);
|
||||
define('MSZ_FORUM_PERM_ANNOUNCE_TOPIC', 1 << 15);
|
||||
define('MSZ_FORUM_PERM_GLOBAL_ANNOUNCE_TOPIC', 1 << 16);
|
||||
define('MSZ_FORUM_PERM_BUMP_TOPIC', 1 << 17);
|
||||
define('MSZ_FORUM_PERM_PRIORITY_VOTE', 1 << 18);
|
||||
|
||||
define('MSZ_FORUM_PERM_CREATE_POST', 1 << 20);
|
||||
define('MSZ_FORUM_PERM_EDIT_POST', 1 << 21);
|
||||
define('MSZ_FORUM_PERM_EDIT_ANY_POST', 1 << 22);
|
||||
define('MSZ_FORUM_PERM_DELETE_POST', 1 << 23);
|
||||
define('MSZ_FORUM_PERM_DELETE_ANY_POST', 1 << 24);
|
||||
|
||||
// shorthands, never use these to SET!!!!!!!
|
||||
define('MSZ_FORUM_PERM_SET_READ', MSZ_FORUM_PERM_LIST_FORUM | MSZ_FORUM_PERM_VIEW_FORUM);
|
||||
define(
|
||||
'MSZ_FORUM_PERM_SET_WRITE',
|
||||
MSZ_FORUM_PERM_CREATE_TOPIC
|
||||
| MSZ_FORUM_PERM_MOVE_TOPIC
|
||||
| MSZ_FORUM_PERM_LOCK_TOPIC
|
||||
| MSZ_FORUM_PERM_STICKY_TOPIC
|
||||
| MSZ_FORUM_PERM_ANNOUNCE_TOPIC
|
||||
| MSZ_FORUM_PERM_GLOBAL_ANNOUNCE_TOPIC
|
||||
| MSZ_FORUM_PERM_CREATE_POST
|
||||
| MSZ_FORUM_PERM_EDIT_POST
|
||||
| MSZ_FORUM_PERM_EDIT_ANY_POST
|
||||
| MSZ_FORUM_PERM_DELETE_POST
|
||||
| MSZ_FORUM_PERM_DELETE_ANY_POST
|
||||
| MSZ_FORUM_PERM_BUMP_TOPIC
|
||||
| MSZ_FORUM_PERM_PRIORITY_VOTE
|
||||
);
|
||||
|
||||
define('MSZ_FORUM_PERM_MODES', [
|
||||
MSZ_FORUM_PERMS_GENERAL,
|
||||
]);
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
final class General {
|
||||
public const PERM_CAN_MANAGE = 0x00000001;
|
||||
public const PERM_VIEW_LOGS = 0x00000002;
|
||||
public const PERM_MANAGE_EMOTES = 0x00000004;
|
||||
public const PERM_MANAGE_CONFIG = 0x00000008;
|
||||
public const PERM_IS_TESTER = 0x00000010;
|
||||
public const PERM_MANAGE_BLACKLIST = 0x00000020;
|
||||
}
|
|
@ -5,6 +5,8 @@ use HttpResponse;
|
|||
use HttpRequest;
|
||||
use Misuzu\Config;
|
||||
use Misuzu\DB;
|
||||
use Misuzu\Pagination;
|
||||
use Misuzu\News\NewsPost;
|
||||
|
||||
final class HomeHandler extends Handler {
|
||||
public function index(HttpResponse $response, HttpRequest $request): void {
|
||||
|
@ -17,7 +19,7 @@ final class HomeHandler extends Handler {
|
|||
];
|
||||
}
|
||||
|
||||
$news = news_posts_get(0, 5, null, true);
|
||||
$featuredNews = NewsPost::all(new Pagination(5), true);
|
||||
|
||||
$stats = DB::query('
|
||||
SELECT
|
||||
|
@ -95,7 +97,7 @@ final class HomeHandler extends Handler {
|
|||
'online_users' => $onlineUsers,
|
||||
'birthdays' => $birthdays,
|
||||
'featured_changelog' => $changelog,
|
||||
'featured_news' => $news,
|
||||
'featured_news' => $featuredNews,
|
||||
'linked_data' => $linkedData ?? null,
|
||||
]);
|
||||
}
|
||||
|
|
202
src/Http/Handlers/NewsHandler.php
Normal file
202
src/Http/Handlers/NewsHandler.php
Normal file
|
@ -0,0 +1,202 @@
|
|||
<?php
|
||||
namespace Misuzu\Http\Handlers;
|
||||
|
||||
use HttpResponse;
|
||||
use HttpRequest;
|
||||
use Misuzu\Config;
|
||||
use Misuzu\DB;
|
||||
use Misuzu\Pagination;
|
||||
use Misuzu\Feeds\Feed;
|
||||
use Misuzu\Feeds\FeedItem;
|
||||
use Misuzu\Feeds\AtomFeedSerializer;
|
||||
use Misuzu\Feeds\RssFeedSerializer;
|
||||
use Misuzu\News\NewsCategory;
|
||||
use Misuzu\News\NewsPost;
|
||||
use Misuzu\News\NewsCategoryNotFoundException;
|
||||
use Misuzu\News\NewsPostNotException;
|
||||
use Misuzu\Parsers\Parser;
|
||||
|
||||
final class NewsHandler extends Handler {
|
||||
public function index(HttpResponse $response, HttpRequest $request) {
|
||||
$categories = NewsCategory::all();
|
||||
$newsPagination = new Pagination(NewsPost::countAll(true), 5);
|
||||
|
||||
if(!$newsPagination->hasValidOffset())
|
||||
return 404;
|
||||
|
||||
$response->setTemplate('news.index', [
|
||||
'categories' => $categories,
|
||||
'posts' => NewsPost::all($newsPagination, true),
|
||||
'news_pagination' => $newsPagination,
|
||||
]);
|
||||
}
|
||||
|
||||
public function viewCategory(HttpResponse $response, HttpRequest $request, int $categoryId) {
|
||||
try {
|
||||
$categoryInfo = NewsCategory::byId($categoryId);
|
||||
} catch(NewsCategoryNotFoundException $ex) {
|
||||
return 404;
|
||||
}
|
||||
|
||||
$categoryPagination = new Pagination(NewsPost::countByCategory($categoryInfo), 5);
|
||||
if(!$categoryPagination->hasValidOffset())
|
||||
return 404;
|
||||
|
||||
$posts = NewsPost::byCategory($categoryInfo, $categoryPagination);
|
||||
|
||||
$response->setTemplate('news.category', [
|
||||
'category_info' => $categoryInfo,
|
||||
'posts' => $posts,
|
||||
'news_pagination' => $categoryPagination,
|
||||
]);
|
||||
}
|
||||
|
||||
public function viewPost(HttpResponse $response, HttpRequest $request, int $postId) {
|
||||
try {
|
||||
$postInfo = NewsPost::byId($postId);
|
||||
} catch(NewsPostNotFoundException $ex) {
|
||||
return 404;
|
||||
}
|
||||
|
||||
if(!$postInfo->isPublished() || $postInfo->isDeleted())
|
||||
return 404;
|
||||
|
||||
$postInfo->ensureCommentsSection();
|
||||
$commentsInfo = $postInfo->getCommentSection();
|
||||
|
||||
$response->setTemplate('news.post', [
|
||||
'post_info' => $postInfo,
|
||||
'comments_perms' => comments_get_perms(user_session_current('user_id', 0)),
|
||||
'comments_category' => $commentsInfo,
|
||||
'comments' => comments_category_get($commentsInfo['category_id'], user_session_current('user_id', 0)),
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
private function createFeed(string $feedMode, ?NewsCategory $categoryInfo, array $posts): Feed {
|
||||
$hasCategory = !empty($categoryInfo);
|
||||
$pagination = new Pagination(10);
|
||||
$posts = $hasCategory ? NewsPost::byCategory($categoryInfo, $pagination) : NewsPost::all($pagination, true);
|
||||
|
||||
$feed = (new Feed)
|
||||
->setTitle(Config::get('site.name', Config::TYPE_STR, 'Misuzu') . ' » ' . ($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-{$feedMode}", ['category' => $categoryInfo->getId()]) : url("news-feed-{$feedMode}")));
|
||||
|
||||
foreach($posts as $post) {
|
||||
$postUrl = url_prefix(false) . url('news-post', ['post' => $post->getId()]);
|
||||
$commentsUrl = url_prefix(false) . url('news-post-comments', ['post' => $post->getId()]);
|
||||
$authorUrl = url_prefix(false) . url('user-profile', ['user' => $post->getUser()->getId()]);
|
||||
|
||||
$feedItem = (new FeedItem)
|
||||
->setTitle($post->getTitle())
|
||||
->setSummary(first_paragraph($post->getText()))
|
||||
->setContent(Parser::instance(Parser::MARKDOWN)->parseText($post->getText()))
|
||||
->setCreationDate(strtotime($post->getCreatedTime()))
|
||||
->setUniqueId($postUrl)
|
||||
->setContentUrl($postUrl)
|
||||
->setCommentsUrl($commentsUrl)
|
||||
->setAuthorName($post->getUser()->getUsername())
|
||||
->setAuthorUrl($authorUrl);
|
||||
|
||||
if(!$feed->hasLastUpdate() || $feed->getLastUpdate() < $feedItem->getCreationDate())
|
||||
$feed->setLastUpdate($feedItem->getCreationDate());
|
||||
|
||||
$feed->addItem($feedItem);
|
||||
}
|
||||
|
||||
return $feed;
|
||||
}
|
||||
|
||||
public function feedIndexAtom(HttpResponse $response, HttpRequest $request) {
|
||||
$response->setContentType('application/atom+xml; charset=utf-8');
|
||||
return (new AtomFeedSerializer)->serializeFeed(
|
||||
self::createFeed('atom', null, NewsPost::all(new Pagination(10), true))
|
||||
);
|
||||
}
|
||||
|
||||
public function feedIndexRss(HttpResponse $response, HttpRequest $request) {
|
||||
$response->setContentType('application/rss+xml; charset=utf-8');
|
||||
return (new RssFeedSerializer)->serializeFeed(
|
||||
self::createFeed('rss', null, NewsPost::all(new Pagination(10), true))
|
||||
);
|
||||
}
|
||||
|
||||
public function feedCategoryAtom(HttpResponse $response, HttpRequest $request, int $categoryId) {
|
||||
try {
|
||||
$categoryInfo = NewsCategory::byId($categoryId);
|
||||
} catch(NewsCategoryNotFoundException $ex) {
|
||||
return 404;
|
||||
}
|
||||
|
||||
$response->setContentType('application/atom+xml; charset=utf-8');
|
||||
return (new AtomFeedSerializer)->serializeFeed(
|
||||
self::createFeed('atom', $categoryInfo, NewsPost::byCategory($categoryInfo, new Pagination(10)))
|
||||
);
|
||||
}
|
||||
|
||||
public function feedCategoryRss(HttpResponse $response, HttpRequest $request, int $categoryId) {
|
||||
try {
|
||||
$categoryInfo = NewsCategory::byId($categoryId);
|
||||
} catch(NewsCategoryNotFoundException $ex) {
|
||||
return 404;
|
||||
}
|
||||
|
||||
$response->setContentType('application/rss+xml; charset=utf-8');
|
||||
return (new RssFeedSerializer)->serializeFeed(
|
||||
self::createFeed('rss', $categoryInfo, NewsPost::byCategory($categoryInfo, new Pagination(10)))
|
||||
);
|
||||
}
|
||||
|
||||
public function legacy(HttpResponse $response, HttpRequest $request) {
|
||||
$location = url('news-index');
|
||||
|
||||
switch('/' . trim($request->getUri()->getPath(), '/')) {
|
||||
case '/news/index.php':
|
||||
$location = url('news-index', [
|
||||
'page' => $request->getQueryParam('page', FILTER_SANITIZE_NUMBER_INT),
|
||||
]);
|
||||
break;
|
||||
|
||||
case '/news/category.php':
|
||||
$location = url('news-category', [
|
||||
'category' => $request->getQueryParam('c', FILTER_SANITIZE_NUMBER_INT),
|
||||
'page' => $request->getQueryParam('p', FILTER_SANITIZE_NUMBER_INT),
|
||||
]);
|
||||
break;
|
||||
|
||||
case '/news/post.php':
|
||||
$location = url('news-post', [
|
||||
'post' => $request->getQueryParam('p', FILTER_SANITIZE_NUMBER_INT),
|
||||
]);
|
||||
break;
|
||||
|
||||
case '/news/feed.php':
|
||||
return 400;
|
||||
|
||||
case '/news/feed.php/rss':
|
||||
case '/news/feed.php/atom':
|
||||
$feedType = basename($request->getUri()->getPath());
|
||||
$catId = $request->getQueryParam('c', FILTER_SANITIZE_NUMBER_INT);
|
||||
$location = url($catId > 0 ? "news-category-feed-{$feedType}" : "news-feed-{$feedType}", ['category' => $catId]);
|
||||
break;
|
||||
|
||||
case '/news.php/rss':
|
||||
case '/news.php/atom':
|
||||
$feedType = basename($request->getUri()->getPath());
|
||||
case '/news.php':
|
||||
$postId = $request->getQueryParam('n', FILTER_SANITIZE_NUMBER_INT) ?? $request->getQueryParam('p', FILTER_SANITIZE_NUMBER_INT);
|
||||
if($postId > 0)
|
||||
$location = url('news-post', ['post' => $postId]);
|
||||
else {
|
||||
$catId = $request->getQueryParam('c', FILTER_SANITIZE_NUMBER_INT);
|
||||
$pageId = $request->getQueryParam('page', FILTER_SANITIZE_NUMBER_INT);
|
||||
$location = url($catId > 0 ? (isset($feedType) ? "news-category-feed-{$feedType}" : 'news-category') : (isset($feedType) ? "news-feed-{$feedType}" : 'news-index'), ['category' => $catId, 'page' => $pageId]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
$response->redirect($location, true);
|
||||
}
|
||||
}
|
|
@ -59,7 +59,7 @@ class Route implements Serializable {
|
|||
public function getPath(): string {
|
||||
$path = $this->path;
|
||||
if($this->parentRoute !== null)
|
||||
$path = $this->parentRoute->getPath() . '/' . trim($path, '/');
|
||||
$path = $this->parentRoute->getPath() . ($path[0] !== '.' ? '/' : '') . trim($path, '/');
|
||||
return $path;
|
||||
}
|
||||
public function setPath(string $path): self {
|
||||
|
@ -91,7 +91,7 @@ class Route implements Serializable {
|
|||
$matches = [];
|
||||
if(!in_array($request->getMethod(), $this->methods))
|
||||
return false;
|
||||
return preg_match('#^' . $this->getPath() . '$#', $request->getUri()->getPath(), $matches) === 1;
|
||||
return preg_match('#^' . $this->getPath() . '$#', '/' . trim($request->getUri()->getPath(), '/'), $matches) === 1;
|
||||
}
|
||||
|
||||
public function serialize() {
|
||||
|
|
143
src/News/NewsCategory.php
Normal file
143
src/News/NewsCategory.php
Normal file
|
@ -0,0 +1,143 @@
|
|||
<?php
|
||||
namespace Misuzu\News;
|
||||
|
||||
use ArrayAccess;
|
||||
use Misuzu\DB;
|
||||
use Misuzu\Pagination;
|
||||
|
||||
class NewsCategoryException extends NewsException {};
|
||||
class NewsCategoryNotFoundException extends NewsCategoryException {};
|
||||
|
||||
class NewsCategory implements ArrayAccess {
|
||||
// Database fields
|
||||
private $category_id = -1;
|
||||
private $category_name = '';
|
||||
private $category_description = '';
|
||||
private $category_is_hidden = false;
|
||||
private $category_created = null;
|
||||
|
||||
private $postCount = -1;
|
||||
|
||||
private const TABLE = 'news_categories';
|
||||
private const SELECT = '%1$s.`category_id`, %1$s.`category_name`, %1$s.`category_description`, %1$s.`category_is_hidden`'
|
||||
. ', UNIX_TIMESTAMP(%1$s.`category_created`) AS `category_created`';
|
||||
|
||||
public function __construct() {}
|
||||
|
||||
public function getId(): int {
|
||||
return $this->category_id < 1 ? -1 : $this->category_id;
|
||||
}
|
||||
|
||||
public function getName(): string {
|
||||
return $this->category_name ?? '';
|
||||
}
|
||||
public function setName(string $name): self {
|
||||
$this->category_name = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDescription(): string {
|
||||
return $this->category_description ?? '';
|
||||
}
|
||||
public function setDescription(string $description): self {
|
||||
$this->category_description = $description;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isHidden(): bool {
|
||||
return $this->category_is_hidden !== 0;
|
||||
}
|
||||
public function setHidden(bool $hide): self {
|
||||
$this->category_is_hidden = $hide ? 1 : 0;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCreatedTime(): int {
|
||||
return $this->category_created === null ? -1 : $this->category_created;
|
||||
}
|
||||
|
||||
// Purely cosmetic, use ::countAll for pagination
|
||||
public function getPostCount(): int {
|
||||
if($this->postCount < 0)
|
||||
$this->postCount = (int)DB::prepare('
|
||||
SELECT COUNT(`post_id`)
|
||||
FROM `msz_news_posts`
|
||||
WHERE `category_id` = :cat_id
|
||||
AND `post_scheduled` <= NOW()
|
||||
AND `post_deleted` IS NULL
|
||||
')->bind('cat_id', $this->getId())->fetchColumn();
|
||||
|
||||
return $this->postCount;
|
||||
}
|
||||
|
||||
public function save(): void {
|
||||
$isInsert = $this->getId() < 1;
|
||||
if($isInsert) {
|
||||
$query = 'INSERT INTO `%1$s%2$s` (`category_name`, `category_description`, `category_is_hidden`) VALUES'
|
||||
. ' (:name, :description, :hidden)';
|
||||
} else {
|
||||
$query = 'UPDATE `%1$s%2$s` SET `category_name` = :name, `category_description` = :description, `category_is_hidden` = :hidden'
|
||||
. ' WHERE `category_id` = :category';
|
||||
}
|
||||
|
||||
$savePost = DB::prepare(sprintf($query, DB::PREFIX, self::TABLE))
|
||||
->bind('name', $this->category_name)
|
||||
->bind('description', $this->category_description)
|
||||
->bind('hidden', $this->category_is_hidden);
|
||||
|
||||
if($isInsert) {
|
||||
$this->category_id = $savePost->executeGetId();
|
||||
$this->category_created = time();
|
||||
} else {
|
||||
$savePost->bind('category', $this->getId())
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
|
||||
private static function countQueryBase(): string {
|
||||
return sprintf(DB::QUERY_SELECT, self::TABLE, sprintf('COUNT(%s.`category_id`)', self::TABLE));
|
||||
}
|
||||
public static function countAll(bool $showHidden = false): int {
|
||||
return (int)DB::prepare(self::countQueryBase()
|
||||
. ($showHidden ? '' : ' WHERE `category_is_hidden` = 0'))
|
||||
->fetchColumn();
|
||||
}
|
||||
|
||||
private static function byQueryBase(): string {
|
||||
return sprintf(DB::QUERY_SELECT, self::TABLE, sprintf(self::SELECT, self::TABLE));
|
||||
}
|
||||
public static function byId(int $categoryId): self {
|
||||
$getCat = DB::prepare(self::byQueryBase() . ' WHERE `category_id` = :cat_id');
|
||||
$getCat->bind('cat_id', $categoryId);
|
||||
$cat = $getCat->fetchObject(self::class);
|
||||
if(!$cat)
|
||||
throw new NewsCategoryNotFoundException;
|
||||
return $cat;
|
||||
}
|
||||
public static function all(?Pagination $pagination = null, bool $showHidden = false): array {
|
||||
$catsQuery = self::byQueryBase()
|
||||
. ($showHidden ? '' : ' WHERE `category_is_hidden` = 0')
|
||||
. ' ORDER BY `category_id` ASC';
|
||||
|
||||
if($pagination !== null)
|
||||
$catsQuery .= ' LIMIT :range OFFSET :offset';
|
||||
|
||||
$getCats = DB::prepare($catsQuery);
|
||||
|
||||
if($pagination !== null)
|
||||
$getCats->bind('range', $pagination->getRange())
|
||||
->bind('offset', $pagination->getOffset());
|
||||
|
||||
return $getCats->fetchObjects(self::class);
|
||||
}
|
||||
|
||||
// Twig shim for the news category list in manage, don't use this class as an array normally.
|
||||
public function offsetExists($offset): bool {
|
||||
return $offset === 'name' || $offset === 'id';
|
||||
}
|
||||
public function offsetGet($offset) {
|
||||
return $this->{'get' . ucfirst($offset)}();
|
||||
}
|
||||
public function offsetSet($offset, $value) {}
|
||||
public function offsetUnset($offset) {}
|
||||
}
|
6
src/News/NewsException.php
Normal file
6
src/News/NewsException.php
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?php
|
||||
namespace Misuzu\News;
|
||||
|
||||
use Exception;
|
||||
|
||||
class NewsException extends Exception {}
|
287
src/News/NewsPost.php
Normal file
287
src/News/NewsPost.php
Normal file
|
@ -0,0 +1,287 @@
|
|||
<?php
|
||||
namespace Misuzu\News;
|
||||
|
||||
use Misuzu\DB;
|
||||
use Misuzu\Pagination;
|
||||
use Misuzu\Users\User;
|
||||
|
||||
class NewsPostException extends NewsException {};
|
||||
class NewsPostNotFoundException extends NewsPostException {};
|
||||
|
||||
class NewsPost {
|
||||
// Database fields
|
||||
private $post_id = -1;
|
||||
private $category_id = -1;
|
||||
private $user_id = null;
|
||||
private $comment_section_id = null;
|
||||
private $post_is_featured = false;
|
||||
private $post_title = '';
|
||||
private $post_text = '';
|
||||
private $post_scheduled = null;
|
||||
private $post_created = null;
|
||||
private $post_updated = null;
|
||||
private $post_deleted = null;
|
||||
|
||||
private $category = null;
|
||||
private $user = null;
|
||||
private $comments = null;
|
||||
private $commentCount = -1;
|
||||
|
||||
private const TABLE = 'news_posts';
|
||||
private const SELECT = '%1$s.`post_id`, %1$s.`category_id`, %1$s.`user_id`, %1$s.`comment_section_id`'
|
||||
. ', %1$s.`post_is_featured`, %1$s.`post_title`, %1$s.`post_text`'
|
||||
. ', UNIX_TIMESTAMP(%1$s.`post_scheduled`) AS `post_scheduled`'
|
||||
. ', UNIX_TIMESTAMP(%1$s.`post_created`) AS `post_created`'
|
||||
. ', UNIX_TIMESTAMP(%1$s.`post_updated`) AS `post_updated`'
|
||||
. ', UNIX_TIMESTAMP(%1$s.`post_deleted`) AS `post_deleted`';
|
||||
|
||||
public function __construct() {}
|
||||
|
||||
public function getId(): int {
|
||||
return $this->post_id < 1 ? -1 : $this->post_id;
|
||||
}
|
||||
|
||||
public function getCategoryId(): int {
|
||||
return $this->category_id < 1 ? -1 : $this->category_id;
|
||||
}
|
||||
public function setCategoryId(int $categoryId): self {
|
||||
$this->category_id = max(1, $categoryId);
|
||||
return $this;
|
||||
}
|
||||
public function getCategory(): NewsCategory {
|
||||
if($this->category === null && ($catId = $this->getCategoryId()) > 0)
|
||||
$this->category = NewsCategory::byId($catId);
|
||||
return $this->category;
|
||||
}
|
||||
public function setCategory(NewsCategory $category): self {
|
||||
$this->category_id = $category->getId();
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUserId(): int {
|
||||
return $this->user_id < 1 ? -1 : $this->user_id;
|
||||
}
|
||||
public function setUserId(int $userId): self {
|
||||
$this->user_id = $userId < 1 ? null : $userId;
|
||||
return $this;
|
||||
}
|
||||
public function getUser(): ?User {
|
||||
if($this->user === null && ($userId = $this->getUserId()) > 0)
|
||||
$this->user = User::byId($userId);
|
||||
return $this->user;
|
||||
}
|
||||
public function setUser(?User $user): self {
|
||||
$this->user_id = $user === null ? null : $user->getId();
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCommentSectionId(): int {
|
||||
return $this->comment_section_id < 1 ? -1 : $this->comment_section_id;
|
||||
}
|
||||
public function hasCommentsSection(): bool {
|
||||
return $this->getCommentSectionId() > 0;
|
||||
}
|
||||
public function getCommentSection() {
|
||||
if($this->comments === null && ($sectionId = $this->getCommentSectionId()) > 0)
|
||||
$this->comments = comments_category_info($sectionId);
|
||||
return $this->comments;
|
||||
}
|
||||
// Temporary solution, should be a method of whatever getCommentSection returns
|
||||
public function getCommentCount(): int {
|
||||
if($this->commentCount < 0)
|
||||
$this->commentCount = (int)DB::prepare('
|
||||
SELECT COUNT(`comment_id`)
|
||||
FROM `msz_comments_posts`
|
||||
WHERE `category_id` = :cat_id
|
||||
AND `comment_deleted` IS NULL
|
||||
')->bind('cat_id', $this->getCommentSectionId())->fetchColumn();
|
||||
|
||||
return $this->commentCount;
|
||||
}
|
||||
|
||||
public function isFeatured(): bool {
|
||||
return $this->post_is_featured !== 0;
|
||||
}
|
||||
public function setFeatured(bool $featured): self {
|
||||
$this->post_is_featured = $featured ? 1 : 0;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTitle(): string {
|
||||
return $this->post_title;
|
||||
}
|
||||
public function setTitle(string $title): self {
|
||||
$this->post_title = $title;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getText(): string {
|
||||
return $this->post_text;
|
||||
}
|
||||
public function setText(string $text): self {
|
||||
$this->post_text = $text;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getScheduledTime(): int {
|
||||
return $this->post_scheduled === null ? -1 : $this->post_scheduled;
|
||||
}
|
||||
public function setScheduledTime(int $scheduled): self {
|
||||
$time = ($time = $this->getCreatedTime()) < 0 ? time() : $time;
|
||||
$this->post_scheduled = $scheduled < $time ? $time : $scheduled;
|
||||
return $this;
|
||||
}
|
||||
public function isPublished(): bool {
|
||||
return $this->getScheduledTime() < time();
|
||||
}
|
||||
|
||||
public function getCreatedTime(): int {
|
||||
return $this->post_created === null ? -1 : $this->post_created;
|
||||
}
|
||||
|
||||
public function getUpdatedTime(): int {
|
||||
return $this->post_updated === null ? -1 : $this->post_updated;
|
||||
}
|
||||
public function isEdited(): bool {
|
||||
return $this->getUpdatedTime() >= 0;
|
||||
}
|
||||
|
||||
public function getDeletedTime(): int {
|
||||
return $this->post_deleted === null ? -1 : $this->post_deleted;
|
||||
}
|
||||
public function isDeleted(): bool {
|
||||
return $this->getDeletedTime() >= 0;
|
||||
}
|
||||
public function setDeleted(bool $isDeleted): self {
|
||||
$this->post_deleted = $isDeleted ? time() : null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function ensureCommentsSection(): void {
|
||||
if($this->hasCommentsSection())
|
||||
return;
|
||||
|
||||
$this->comments = comments_category_create("news-{$this->getId()}");
|
||||
|
||||
if($this->comments !== null) {
|
||||
$this->comment_section_id = (int)$this->comments['category_id'];
|
||||
DB::prepare('UPDATE `msz_news_posts` SET `comment_section_id` = :comment_section_id WHERE `post_id` = :post_id')
|
||||
->execute([
|
||||
'comment_section_id' => $this->getCommentSectionId(),
|
||||
'post_id' => $this->getId(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function save(): void {
|
||||
$isInsert = $this->getId() < 1;
|
||||
if($isInsert) {
|
||||
$query = 'INSERT INTO `%1$s%2$s` (`category_id`, `user_id`, `post_is_featured`, `post_title`'
|
||||
. ', `post_text`, `post_scheduled`, `post_deleted`) VALUES'
|
||||
. ' (:category, :user, :featured, :title, :text, FROM_UNIXTIME(:scheduled), FROM_UNIXTIME(:deleted))';
|
||||
} else {
|
||||
$query = 'UPDATE `%1$s%2$s` SET `category_id` = :category, `user_id` = :user, `post_is_featured` = :featured'
|
||||
. ', `post_title` = :title, `post_text` = :text, `post_scheduled` = FROM_UNIXTIME(:scheduled)'
|
||||
. ', `post_deleted` = FROM_UNIXTIME(:deleted)'
|
||||
. ' WHERE `post_id` = :post';
|
||||
}
|
||||
|
||||
$savePost = DB::prepare(sprintf($query, DB::PREFIX, self::TABLE))
|
||||
->bind('category', $this->category_id)
|
||||
->bind('user', $this->user_id)
|
||||
->bind('featured', $this->post_is_featured)
|
||||
->bind('title', $this->post_title)
|
||||
->bind('text', $this->post_text)
|
||||
->bind('scheduled', $this->post_scheduled)
|
||||
->bind('deleted', $this->post_deleted);
|
||||
|
||||
if($isInsert) {
|
||||
$this->post_id = $savePost->executeGetId();
|
||||
$this->post_created = time();
|
||||
} else {
|
||||
$this->post_updated = time();
|
||||
$savePost->bind('post', $this->getId())
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
|
||||
private static function countQueryBase(): string {
|
||||
return sprintf(DB::QUERY_SELECT, self::TABLE, sprintf('COUNT(%s.`post_id`)', self::TABLE));
|
||||
}
|
||||
public static function countAll(bool $onlyFeatured = false, bool $includeScheduled = false, bool $includeDeleted = false): int {
|
||||
return (int)DB::prepare(self::countQueryBase()
|
||||
. ' WHERE IF(:only_featured, `post_is_featured` <> 0, 1)'
|
||||
. ($includeScheduled ? '' : ' AND `post_scheduled` < NOW()')
|
||||
. ($includeDeleted ? '' : ' AND `post_deleted` IS NULL'))
|
||||
->bind('only_featured', $onlyFeatured ? 1 : 0)
|
||||
->fetchColumn();
|
||||
}
|
||||
public static function countByCategory(NewsCategory $category, bool $includeScheduled = false, bool $includeDeleted = false): int {
|
||||
return (int)DB::prepare(self::countQueryBase()
|
||||
. ' WHERE `category_id` = :cat_id'
|
||||
. ($includeScheduled ? '' : ' AND `post_scheduled` < NOW()')
|
||||
. ($includeDeleted ? '' : ' AND `post_deleted` IS NULL'))
|
||||
->bind('cat_id', $category->getId())
|
||||
->fetchColumn();
|
||||
}
|
||||
|
||||
private static function byQueryBase(): string {
|
||||
return sprintf(DB::QUERY_SELECT, self::TABLE, sprintf(self::SELECT, self::TABLE));
|
||||
}
|
||||
public static function byId(int $postId): self {
|
||||
$post = DB::prepare(self::byQueryBase() . ' WHERE `post_id` = :post_id')
|
||||
->bind('post_id', $postId)
|
||||
->fetchObject(self::class);
|
||||
if(!$post)
|
||||
throw new NewsPostNotFoundException;
|
||||
return $post;
|
||||
}
|
||||
public static function bySearchQuery(string $query, bool $includeScheduled = false, bool $includeDeleted = false): array {
|
||||
return DB::prepare(
|
||||
self::byQueryBase()
|
||||
. ' WHERE MATCH(`post_title`, `post_text`) AGAINST (:query IN NATURAL LANGUAGE MODE)'
|
||||
. ($includeScheduled ? '' : ' AND `post_scheduled` < NOW()')
|
||||
. ($includeDeleted ? '' : ' AND `post_deleted` IS NULL')
|
||||
. ' ORDER BY `post_id` DESC'
|
||||
) ->bind('query', $query)
|
||||
->fetchObjects(self::class);
|
||||
}
|
||||
public static function byCategory(NewsCategory $category, ?Pagination $pagination = null, bool $includeScheduled = false, bool $includeDeleted = false): array {
|
||||
$postsQuery = self::byQueryBase()
|
||||
. ' WHERE `category_id` = :cat_id'
|
||||
. ($includeScheduled ? '' : ' AND `post_scheduled` < NOW()')
|
||||
. ($includeDeleted ? '' : ' AND `post_deleted` IS NULL')
|
||||
. ' ORDER BY `post_id` DESC';
|
||||
|
||||
if($pagination !== null)
|
||||
$postsQuery .= ' LIMIT :range OFFSET :offset';
|
||||
|
||||
$getPosts = DB::prepare($postsQuery)
|
||||
->bind('cat_id', $category->getId());
|
||||
|
||||
if($pagination !== null)
|
||||
$getPosts->bind('range', $pagination->getRange())
|
||||
->bind('offset', $pagination->getOffset());
|
||||
|
||||
return $getPosts->fetchObjects(self::class);
|
||||
}
|
||||
public static function all(?Pagination $pagination = null, bool $onlyFeatured = false, bool $includeScheduled = false, bool $includeDeleted = false): array {
|
||||
$postsQuery = self::byQueryBase()
|
||||
. ' WHERE IF(:only_featured, `post_is_featured` <> 0, 1)'
|
||||
. ($includeScheduled ? '' : ' AND `post_scheduled` < NOW()')
|
||||
. ($includeDeleted ? '' : ' AND `post_deleted` IS NULL')
|
||||
. ' ORDER BY `post_id` DESC';
|
||||
|
||||
if($pagination !== null)
|
||||
$postsQuery .= ' LIMIT :range OFFSET :offset';
|
||||
|
||||
$getPosts = DB::prepare($postsQuery)
|
||||
->bind('only_featured', $onlyFeatured ? 1 : 0);
|
||||
|
||||
if($pagination !== null)
|
||||
$getPosts->bind('range', $pagination->getRange())
|
||||
->bind('offset', $pagination->getOffset());
|
||||
|
||||
return $getPosts->fetchObjects(self::class);
|
||||
}
|
||||
}
|
|
@ -10,9 +10,9 @@ final class Pagination {
|
|||
private int $range = 0;
|
||||
private int $offset = 0;
|
||||
|
||||
public function __construct(int $count, int $range, ?string $readParam = self::DEFAULT_PARAM) {
|
||||
$this->count = $count;
|
||||
$this->range = $range;
|
||||
public function __construct(int $count, int $range = -1, ?string $readParam = self::DEFAULT_PARAM) {
|
||||
$this->count = max(0, $count);
|
||||
$this->range = $range < 0 ? $count : $range;
|
||||
|
||||
if(!empty($readParam))
|
||||
$this->readPage($readParam);
|
||||
|
|
|
@ -53,7 +53,8 @@ class User {
|
|||
return static::get($createUser);
|
||||
}
|
||||
|
||||
public static function get(int $userId): ?User {
|
||||
public static function get(int $userId): ?User { return self::byId($userId); }
|
||||
public static function byId(int $userId): ?User {
|
||||
return DB::prepare(self::USER_SELECT . 'WHERE `user_id` = :user_id')
|
||||
->bind('user_id', $userId)
|
||||
->fetchObject(User::class);
|
||||
|
@ -72,10 +73,12 @@ class User {
|
|||
->fetchObject(User::class);
|
||||
}
|
||||
|
||||
public function hasUserId(): bool {
|
||||
public function hasUserId(): bool { return $this->hasId(); }
|
||||
public function getUserId(): int { return $this->getId(); }
|
||||
public function hasId(): bool {
|
||||
return isset($this->user_id) && $this->user_id > 0;
|
||||
}
|
||||
public function getUserId(): int {
|
||||
public function getId(): int {
|
||||
return $this->user_id ?? 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,20 +3,6 @@
|
|||
// Never ever EVER use it for ANYTHING other than determining display colours, there's a small chance that it might not be accurate.
|
||||
// And even if it were, roles properties are aggregated and thus must all be accounted for.
|
||||
|
||||
define('MSZ_PERM_USER_EDIT_PROFILE', 1);
|
||||
define('MSZ_PERM_USER_CHANGE_AVATAR', 1 << 1);
|
||||
define('MSZ_PERM_USER_CHANGE_BACKGROUND', 1 << 2);
|
||||
define('MSZ_PERM_USER_EDIT_ABOUT', 1 << 3);
|
||||
define('MSZ_PERM_USER_EDIT_BIRTHDATE', 1 << 4);
|
||||
define('MSZ_PERM_USER_EDIT_SIGNATURE', 1 << 5);
|
||||
|
||||
define('MSZ_PERM_USER_MANAGE_USERS', 1 << 20);
|
||||
define('MSZ_PERM_USER_MANAGE_ROLES', 1 << 21);
|
||||
define('MSZ_PERM_USER_MANAGE_PERMS', 1 << 22);
|
||||
define('MSZ_PERM_USER_MANAGE_REPORTS', 1 << 23);
|
||||
define('MSZ_PERM_USER_MANAGE_WARNINGS', 1 << 24);
|
||||
//define('MSZ_PERM_USER_MANAGE_BLACKLISTS', 1 << 25); // Replaced with General::PERM_MANAGE_BLACKLIST
|
||||
|
||||
define(
|
||||
'MSZ_USERS_PASSWORD_HASH_ALGO',
|
||||
defined('PASSWORD_ARGON2ID')
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
<?php
|
||||
define('MSZ_PERM_CHANGELOG_MANAGE_CHANGES', 1);
|
||||
define('MSZ_PERM_CHANGELOG_MANAGE_TAGS', 1 << 1);
|
||||
//define('MSZ_PERM_CHANGELOG_MANAGE_ACTIONS', 1 << 2); Deprecated, actions are hardcoded now
|
||||
|
||||
define('MSZ_CHANGELOG_ACTION_ADD', 1);
|
||||
define('MSZ_CHANGELOG_ACTION_REMOVE', 2);
|
||||
define('MSZ_CHANGELOG_ACTION_UPDATE', 3);
|
||||
|
|
|
@ -1,15 +1,6 @@
|
|||
<?php
|
||||
require_once 'Users/validation.php';
|
||||
|
||||
define('MSZ_PERM_COMMENTS_CREATE', 1);
|
||||
//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);
|
||||
|
||||
define('MSZ_COMMENTS_VOTE_INDIFFERENT', 0);
|
||||
define('MSZ_COMMENTS_VOTE_LIKE', 1);
|
||||
define('MSZ_COMMENTS_VOTE_DISLIKE', -1);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
function manage_get_menu(int $userId): array {
|
||||
if(!perms_check_user(MSZ_PERMS_GENERAL, $userId, \Misuzu\General::PERM_CAN_MANAGE)) {
|
||||
if(!perms_check_user(MSZ_PERMS_GENERAL, $userId, MSZ_PERM_GENERAL_CAN_MANAGE)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
|
@ -10,19 +10,19 @@ function manage_get_menu(int $userId): array {
|
|||
],
|
||||
];
|
||||
|
||||
if(perms_check_user(MSZ_PERMS_GENERAL, $userId, \Misuzu\General::PERM_VIEW_LOGS)) {
|
||||
if(perms_check_user(MSZ_PERMS_GENERAL, $userId, MSZ_PERM_GENERAL_VIEW_LOGS)) {
|
||||
$menu['General']['Logs'] = url('manage-general-logs');
|
||||
}
|
||||
|
||||
if(perms_check_user(MSZ_PERMS_GENERAL, $userId, \Misuzu\General::PERM_MANAGE_EMOTES)) {
|
||||
if(perms_check_user(MSZ_PERMS_GENERAL, $userId, MSZ_PERM_GENERAL_MANAGE_EMOTES)) {
|
||||
$menu['General']['Emoticons'] = url('manage-general-emoticons');
|
||||
}
|
||||
|
||||
if(perms_check_user(MSZ_PERMS_GENERAL, $userId, \Misuzu\General::PERM_MANAGE_CONFIG)) {
|
||||
if(perms_check_user(MSZ_PERMS_GENERAL, $userId, MSZ_PERM_GENERAL_MANAGE_CONFIG)) {
|
||||
$menu['General']['Settings'] = url('manage-general-settings');
|
||||
}
|
||||
|
||||
if(perms_check_user(MSZ_PERMS_GENERAL, $userId, \Misuzu\General::PERM_MANAGE_BLACKLIST)) {
|
||||
if(perms_check_user(MSZ_PERMS_GENERAL, $userId, MSZ_PERM_GENERAL_MANAGE_BLACKLIST)) {
|
||||
$menu['General']['IP Blacklist'] = url('manage-general-blacklist');
|
||||
}
|
||||
|
||||
|
@ -155,32 +155,32 @@ function manage_perms_list(array $rawPerms): array {
|
|||
[
|
||||
'section' => 'can-manage',
|
||||
'title' => 'Can access the management panel.',
|
||||
'perm' => \Misuzu\General::PERM_CAN_MANAGE,
|
||||
'perm' => MSZ_PERM_GENERAL_CAN_MANAGE,
|
||||
],
|
||||
[
|
||||
'section' => 'view-logs',
|
||||
'title' => 'Can view audit logs.',
|
||||
'perm' => \Misuzu\General::PERM_VIEW_LOGS,
|
||||
'perm' => MSZ_PERM_GENERAL_VIEW_LOGS,
|
||||
],
|
||||
[
|
||||
'section' => 'manage-emotes',
|
||||
'title' => 'Can manage emoticons.',
|
||||
'perm' => \Misuzu\General::PERM_MANAGE_EMOTES,
|
||||
'perm' => MSZ_PERM_GENERAL_MANAGE_EMOTES,
|
||||
],
|
||||
[
|
||||
'section' => 'manage-settings',
|
||||
'title' => 'Can manage general Misuzu settings.',
|
||||
'perm' => \Misuzu\General::PERM_MANAGE_CONFIG,
|
||||
'perm' => MSZ_PERM_GENERAL_MANAGE_CONFIG,
|
||||
],
|
||||
[
|
||||
'section' => 'tester',
|
||||
'title' => 'Can use experimental features.',
|
||||
'perm' => \Misuzu\General::PERM_IS_TESTER,
|
||||
'perm' => MSZ_PERM_GENERAL_IS_TESTER,
|
||||
],
|
||||
[
|
||||
'section' => 'manage-blacklist',
|
||||
'title' => 'Can manage blacklistings.',
|
||||
'perm' => \Misuzu\General::PERM_MANAGE_BLACKLIST,
|
||||
'perm' => MSZ_PERM_GENERAL_MANAGE_BLACKLIST,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
|
327
src/news.php
327
src/news.php
|
@ -1,327 +0,0 @@
|
|||
<?php
|
||||
define('MSZ_PERM_NEWS_MANAGE_POSTS', 1);
|
||||
define('MSZ_PERM_NEWS_MANAGE_CATEGORIES', 1 << 1);
|
||||
|
||||
function news_post_create(
|
||||
string $title,
|
||||
string $text,
|
||||
int $category,
|
||||
int $user,
|
||||
bool $featured = false,
|
||||
?int $scheduled = null,
|
||||
?int $postId = null
|
||||
): int {
|
||||
if($postId < 1) {
|
||||
$post = \Misuzu\DB::prepare('
|
||||
INSERT INTO `msz_news_posts`
|
||||
(`category_id`, `user_id`, `post_is_featured`, `post_title`, `post_text`, `post_scheduled`)
|
||||
VALUES
|
||||
(:category, :user, :featured, :title, :text, COALESCE(:scheduled, CURRENT_TIMESTAMP))
|
||||
');
|
||||
} else {
|
||||
$post = \Misuzu\DB::prepare('
|
||||
UPDATE `msz_news_posts`
|
||||
SET `category_id` = :category,
|
||||
`user_id` = :user,
|
||||
`post_is_featured` = :featured,
|
||||
`post_title` = :title,
|
||||
`post_text` = :text,
|
||||
`post_scheduled` = COALESCE(:scheduled, `post_scheduled`)
|
||||
WHERE `post_id` = :id
|
||||
');
|
||||
$post->bind('id', $postId);
|
||||
}
|
||||
|
||||
$post->bind('title', $title);
|
||||
$post->bind('text', $text);
|
||||
$post->bind('category', $category);
|
||||
$post->bind('user', $user);
|
||||
$post->bind('featured', $featured ? 1 : 0);
|
||||
$post->bind('scheduled', empty($scheduled) ? null : date('Y-m-d H:i:s', $scheduled));
|
||||
|
||||
return $post->execute() ? ($postId < 1 ? \Misuzu\DB::lastId() : $postId) : 0;
|
||||
}
|
||||
|
||||
function news_category_create(string $name, string $description, bool $isHidden, ?int $categoryId = null): int {
|
||||
if($categoryId < 1) {
|
||||
$category = \Misuzu\DB::prepare('
|
||||
INSERT INTO `msz_news_categories`
|
||||
(`category_name`, `category_description`, `category_is_hidden`)
|
||||
VALUES
|
||||
(:name, :description, :hidden)
|
||||
');
|
||||
} else {
|
||||
$category = \Misuzu\DB::prepare('
|
||||
UPDATE `msz_news_categories`
|
||||
SET `category_name` = :name,
|
||||
`category_description` = :description,
|
||||
`category_is_hidden` = :hidden
|
||||
WHERE `category_id` = :id
|
||||
');
|
||||
$category->bind('id', $categoryId);
|
||||
}
|
||||
|
||||
$category->bind('name', $name);
|
||||
$category->bind('description', $description);
|
||||
$category->bind('hidden', $isHidden ? 1 : 0);
|
||||
|
||||
return $category->execute() ? ($categoryId < 1 ? \Misuzu\DB::lastId() : $categoryId) : 0;
|
||||
}
|
||||
|
||||
function news_categories_get(
|
||||
int $offset,
|
||||
int $take,
|
||||
bool $includePostCount = false,
|
||||
bool $featuredOnly = false,
|
||||
bool $includeHidden = false,
|
||||
bool $exposeScheduled = false,
|
||||
bool $excludeDeleted = true
|
||||
): array {
|
||||
$getAll = $offset < 0 || $take < 1;
|
||||
|
||||
if($includePostCount) {
|
||||
$query = sprintf(
|
||||
'
|
||||
SELECT
|
||||
c.`category_id`, c.`category_name`, c.`category_is_hidden`,
|
||||
c.`category_created`,
|
||||
(
|
||||
SELECT COUNT(p.`post_id`)
|
||||
FROM `msz_news_posts` as p
|
||||
WHERE p.`category_id` = c.`category_id` %2$s %3$s %4$s
|
||||
) as `posts_count`
|
||||
FROM `msz_news_categories` as c
|
||||
%5$s
|
||||
GROUP BY c.`category_id`
|
||||
ORDER BY c.`category_id` DESC
|
||||
%1$s
|
||||
',
|
||||
$getAll ? '' : 'LIMIT :offset, :take',
|
||||
$featuredOnly ? 'AND p.`post_is_featured` != 0' : '',
|
||||
$exposeScheduled ? '' : 'AND p.`post_scheduled` < NOW()',
|
||||
$excludeDeleted ? 'AND p.`post_deleted` IS NULL' : '',
|
||||
$includeHidden ? '' : 'WHERE c.`category_is_hidden` = 0'
|
||||
);
|
||||
} else {
|
||||
$query = sprintf(
|
||||
'
|
||||
SELECT
|
||||
`category_id`, `category_name`, `category_is_hidden`,
|
||||
`category_created`
|
||||
FROM `msz_news_categories`
|
||||
%2$s
|
||||
ORDER BY `category_id` DESC
|
||||
%1$s
|
||||
',
|
||||
$getAll ? '' : 'LIMIT :offset, :take',
|
||||
$includeHidden ? '' : 'WHERE c.`category_is_hidden` != 0'
|
||||
);
|
||||
}
|
||||
|
||||
$getCats = \Misuzu\DB::prepare($query);
|
||||
|
||||
if(!$getAll) {
|
||||
$getCats->bind('offset', $offset);
|
||||
$getCats->bind('take', $take);
|
||||
}
|
||||
|
||||
return $getCats->fetchAll();
|
||||
}
|
||||
|
||||
function news_categories_count(bool $includeHidden = false): int {
|
||||
$countCats = \Misuzu\DB::prepare(sprintf('
|
||||
SELECT COUNT(`category_id`)
|
||||
FROM `msz_news_categories`
|
||||
%s
|
||||
', $includeHidden ? '' : 'WHERE `category_is_hidden` = 0'));
|
||||
|
||||
return (int)$countCats->fetchColumn();
|
||||
}
|
||||
|
||||
function news_category_get(
|
||||
int $category,
|
||||
bool $includePostCount = false,
|
||||
bool $featuredOnly = false,
|
||||
bool $exposeScheduled = false,
|
||||
bool $excludeDeleted = true
|
||||
): array {
|
||||
if($includePostCount) {
|
||||
$query = sprintf(
|
||||
'
|
||||
SELECT
|
||||
c.`category_id`, c.`category_name`, c.`category_description`,
|
||||
c.`category_is_hidden`, c.`category_created`,
|
||||
(
|
||||
SELECT COUNT(p.`post_id`)
|
||||
FROM `msz_news_posts` as p
|
||||
WHERE p.`category_id` = c.`category_id` %1$s %2$s %3$s
|
||||
) as `posts_count`
|
||||
FROM `msz_news_categories` as c
|
||||
WHERE c.`category_id` = :category
|
||||
GROUP BY c.`category_id`
|
||||
',
|
||||
$featuredOnly ? 'AND p.`post_is_featured` != 0' : '',
|
||||
$exposeScheduled ? '' : 'AND p.`post_scheduled` < NOW()',
|
||||
$excludeDeleted ? 'AND p.`post_deleted` IS NULL' : ''
|
||||
);
|
||||
} else {
|
||||
$query = '
|
||||
SELECT
|
||||
`category_id`, `category_name`, `category_description`,
|
||||
`category_is_hidden`, `category_created`
|
||||
FROM `msz_news_categories`
|
||||
WHERE `category_id` = :category
|
||||
GROUP BY `category_id`
|
||||
';
|
||||
}
|
||||
|
||||
$getCategory = \Misuzu\DB::prepare($query);
|
||||
$getCategory->bind('category', $category);
|
||||
return $getCategory->fetch();
|
||||
}
|
||||
|
||||
function news_posts_count(
|
||||
?int $category = null,
|
||||
bool $featuredOnly = false,
|
||||
bool $exposeScheduled = false,
|
||||
bool $excludeDeleted = true
|
||||
): int {
|
||||
$hasCategory= $category !== null;
|
||||
|
||||
$countPosts = \Misuzu\DB::prepare(sprintf(
|
||||
'
|
||||
SELECT COUNT(`post_id`)
|
||||
FROM `msz_news_posts`
|
||||
WHERE %1$s %2$s %3$s %4$s
|
||||
',
|
||||
$hasCategory ? '`category_id` = :category' : '1',
|
||||
$featuredOnly ? 'AND `post_is_featured` != 0' : '',
|
||||
$exposeScheduled ? '' : 'AND `post_scheduled` < NOW()',
|
||||
$excludeDeleted ? 'AND `post_deleted` IS NULL' : ''
|
||||
));
|
||||
|
||||
if($hasCategory) {
|
||||
$countPosts->bind('category', $category);
|
||||
}
|
||||
|
||||
return (int)$countPosts->fetchColumn();
|
||||
}
|
||||
|
||||
function news_posts_get(
|
||||
int $offset,
|
||||
int $take,
|
||||
?int $category = null,
|
||||
bool $featuredOnly = false,
|
||||
bool $exposeScheduled = false,
|
||||
bool $excludeDeleted = true
|
||||
): array {
|
||||
$getAll = $offset < 0 || $take < 1;
|
||||
$hasCategory = $category !== null;
|
||||
|
||||
$getPosts = \Misuzu\DB::prepare(sprintf(
|
||||
'
|
||||
SELECT
|
||||
p.`post_id`, p.`post_is_featured`, p.`post_title`, p.`post_text`, p.`comment_section_id`,
|
||||
p.`post_created`, p.`post_updated`, p.`post_deleted`, p.`post_scheduled`,
|
||||
c.`category_id`, c.`category_name`,
|
||||
u.`user_id`, u.`username`,
|
||||
COALESCE(u.`user_colour`, r.`role_colour`) as `user_colour`,
|
||||
(
|
||||
SELECT COUNT(`comment_id`)
|
||||
FROM `msz_comments_posts`
|
||||
WHERE `category_id` = `comment_section_id`
|
||||
AND `comment_deleted` IS NULL
|
||||
) as `post_comments`
|
||||
FROM `msz_news_posts` as p
|
||||
LEFT JOIN `msz_news_categories` as c
|
||||
ON p.`category_id` = c.`category_id`
|
||||
LEFT JOIN `msz_users` as u
|
||||
ON p.`user_id` = u.`user_id`
|
||||
LEFT JOIN `msz_roles` as r
|
||||
ON u.`display_role` = r.`role_id`
|
||||
WHERE %5$s %2$s %3$s %4$s
|
||||
ORDER BY p.`post_created` DESC
|
||||
%1$s
|
||||
',
|
||||
$getAll ? '' : 'LIMIT :offset, :take',
|
||||
$featuredOnly ? 'AND p.`post_is_featured` != 0' : '',
|
||||
$exposeScheduled ? '' : 'AND p.`post_scheduled` < NOW()',
|
||||
$excludeDeleted ? 'AND p.`post_deleted` IS NULL' : '',
|
||||
$hasCategory ? 'p.`category_id` = :category' : '1'
|
||||
));
|
||||
|
||||
if($hasCategory) {
|
||||
$getPosts->bind('category', $category);
|
||||
}
|
||||
|
||||
if(!$getAll) {
|
||||
$getPosts->bind('take', $take);
|
||||
$getPosts->bind('offset', $offset);
|
||||
}
|
||||
|
||||
return $getPosts->fetchAll();
|
||||
}
|
||||
|
||||
function news_posts_search(string $query): array {
|
||||
$searchPosts = \Misuzu\DB::prepare('
|
||||
SELECT
|
||||
p.`post_id`, p.`post_is_featured`, p.`post_title`, p.`post_text`, p.`comment_section_id`,
|
||||
p.`post_created`, p.`post_updated`, p.`post_deleted`, p.`post_scheduled`,
|
||||
c.`category_id`, c.`category_name`,
|
||||
u.`user_id`, u.`username`,
|
||||
COALESCE(u.`user_colour`, r.`role_colour`) as `user_colour`,
|
||||
(
|
||||
SELECT COUNT(`comment_id`)
|
||||
FROM `msz_comments_posts`
|
||||
WHERE `category_id` = `comment_section_id`
|
||||
AND `comment_deleted` IS NULL
|
||||
) as `post_comments`
|
||||
FROM `msz_news_posts` as p
|
||||
LEFT JOIN `msz_news_categories` as c
|
||||
ON p.`category_id` = c.`category_id`
|
||||
LEFT JOIN `msz_users` as u
|
||||
ON p.`user_id` = u.`user_id`
|
||||
LEFT JOIN `msz_roles` as r
|
||||
ON u.`display_role` = r.`role_id`
|
||||
WHERE MATCH(`post_title`, `post_text`)
|
||||
AGAINST (:query IN NATURAL LANGUAGE MODE)
|
||||
AND p.`post_deleted` IS NULL
|
||||
AND p.`post_scheduled` < NOW()
|
||||
ORDER BY p.`post_created` DESC
|
||||
');
|
||||
$searchPosts->bind('query', $query);
|
||||
|
||||
return $searchPosts->fetchAll();
|
||||
}
|
||||
|
||||
function news_post_comments_set(int $postId, int $sectionId): void {
|
||||
\Misuzu\DB::prepare('
|
||||
UPDATE `msz_news_posts`
|
||||
SET `comment_section_id` = :comment_section_id
|
||||
WHERE `post_id` = :post_id
|
||||
')->execute([
|
||||
'comment_section_id' => $sectionId,
|
||||
'post_id' => $postId,
|
||||
]);
|
||||
}
|
||||
|
||||
function news_post_get(int $postId): array {
|
||||
$getPost = \Misuzu\DB::prepare('
|
||||
SELECT
|
||||
p.`post_id`, p.`post_title`, p.`post_text`, p.`post_is_featured`, p.`post_scheduled`,
|
||||
p.`post_created`, p.`post_updated`, p.`post_deleted`, p.`comment_section_id`,
|
||||
c.`category_id`, c.`category_name`,
|
||||
u.`user_id`, u.`username`,
|
||||
COALESCE(u.`user_colour`, r.`role_colour`) as `user_colour`
|
||||
FROM `msz_news_posts` as p
|
||||
LEFT JOIN `msz_news_categories` as c
|
||||
ON p.`category_id` = c.`category_id`
|
||||
LEFT JOIN `msz_users` as u
|
||||
ON p.`user_id` = u.`user_id`
|
||||
LEFT JOIN `msz_roles` as r
|
||||
ON u.`display_role` = r.`role_id`
|
||||
WHERE `post_id` = :post_id
|
||||
');
|
||||
$getPost->bind(':post_id', $postId);
|
||||
return $getPost->fetch();
|
||||
}
|
|
@ -1,10 +1,48 @@
|
|||
<?php
|
||||
define('MSZ_PERMS_GENERAL', 'general');
|
||||
define('MSZ_PERM_GENERAL_CAN_MANAGE', 0x00000001);
|
||||
define('MSZ_PERM_GENERAL_VIEW_LOGS', 0x00000002);
|
||||
define('MSZ_PERM_GENERAL_MANAGE_EMOTES', 0x00000004);
|
||||
define('MSZ_PERM_GENERAL_MANAGE_CONFIG', 0x00000008);
|
||||
define('MSZ_PERM_GENERAL_IS_TESTER', 0x00000010);
|
||||
define('MSZ_PERM_GENERAL_MANAGE_BLACKLIST', 0x00000020);
|
||||
|
||||
define('MSZ_PERMS_USER', 'user');
|
||||
define('MSZ_PERM_USER_EDIT_PROFILE', 0x00000001);
|
||||
define('MSZ_PERM_USER_CHANGE_AVATAR', 0x00000002);
|
||||
define('MSZ_PERM_USER_CHANGE_BACKGROUND', 0x00000004);
|
||||
define('MSZ_PERM_USER_EDIT_ABOUT', 0x00000008);
|
||||
define('MSZ_PERM_USER_EDIT_BIRTHDATE', 0x00000010);
|
||||
define('MSZ_PERM_USER_EDIT_SIGNATURE', 0x00000020);
|
||||
define('MSZ_PERM_USER_MANAGE_USERS', 0x00100000);
|
||||
define('MSZ_PERM_USER_MANAGE_ROLES', 0x00200000);
|
||||
define('MSZ_PERM_USER_MANAGE_PERMS', 0x00400000);
|
||||
define('MSZ_PERM_USER_MANAGE_REPORTS', 0x00800000);
|
||||
define('MSZ_PERM_USER_MANAGE_WARNINGS', 0x01000000);
|
||||
//define('MSZ_PERM_USER_MANAGE_BLACKLISTS', 0x02000000); // Replaced with MSZ_PERM_MANAGE_BLACKLIST
|
||||
|
||||
define('MSZ_PERMS_CHANGELOG', 'changelog');
|
||||
define('MSZ_PERM_CHANGELOG_MANAGE_CHANGES', 0x00000001);
|
||||
define('MSZ_PERM_CHANGELOG_MANAGE_TAGS', 0x00000002);
|
||||
//define('MSZ_PERM_CHANGELOG_MANAGE_ACTIONS', 0x00000004); Deprecated, actions are hardcoded now
|
||||
|
||||
define('MSZ_PERMS_NEWS', 'news');
|
||||
define('MSZ_PERM_NEWS_MANAGE_POSTS', 0x00000001);
|
||||
define('MSZ_PERM_NEWS_MANAGE_CATEGORIES', 0x00000002);
|
||||
|
||||
define('MSZ_PERMS_FORUM', 'forum');
|
||||
define('MSZ_PERM_FORUM_MANAGE_FORUMS', 0x00000001);
|
||||
define('MSZ_PERM_FORUM_VIEW_LEADERBOARD', 0x00000002);
|
||||
|
||||
define('MSZ_PERMS_COMMENTS', 'comments');
|
||||
define('MSZ_PERM_COMMENTS_CREATE', 0x00000001);
|
||||
//define('MSZ_PERM_COMMENTS_EDIT_OWN', 0x00000002);
|
||||
//define('MSZ_PERM_COMMENTS_EDIT_ANY', 0x00000004);
|
||||
define('MSZ_PERM_COMMENTS_DELETE_OWN', 0x00000008);
|
||||
define('MSZ_PERM_COMMENTS_DELETE_ANY', 0x00000010);
|
||||
define('MSZ_PERM_COMMENTS_PIN', 0x00000020);
|
||||
define('MSZ_PERM_COMMENTS_LOCK', 0x00000040);
|
||||
define('MSZ_PERM_COMMENTS_VOTE', 0x00000080);
|
||||
|
||||
define('MSZ_PERM_MODES', [
|
||||
MSZ_PERMS_GENERAL, MSZ_PERMS_USER, MSZ_PERMS_CHANGELOG,
|
||||
|
|
34
src/url.php
34
src/url.php
|
@ -29,14 +29,14 @@ define('MSZ_URLS', [
|
|||
'changelog-date' => ['/changelog.php', ['d' => '<date>']],
|
||||
'changelog-tag' => ['/changelog.php', ['t' => '<tag>']],
|
||||
|
||||
'news-index' => ['/news', ['page' => '<page>']],
|
||||
'news-post' => ['/news/post.php', ['p' => '<post>']],
|
||||
'news-post-comments' => ['/news/post.php', ['p' => '<post>'], 'comments'],
|
||||
'news-category' => ['/news/category.php', ['c' => '<category>', 'p' => '<page>']],
|
||||
'news-feed-rss' => ['/news/feed.php/rss'],
|
||||
'news-category-feed-rss' => ['/news/feed.php/rss', ['c' => '<category>']],
|
||||
'news-feed-atom' => ['/news/feed.php/atom'],
|
||||
'news-category-feed-atom' => ['/news/feed.php/atom', ['c' => '<category>']],
|
||||
'news-index' => ['/news', ['p' => '<page>']],
|
||||
'news-category' => ['/news/<category>', ['p' => '<page>']],
|
||||
'news-post' => ['/news/post/<post>'],
|
||||
'news-post-comments' => ['/news/post/<post>', [], 'comments'],
|
||||
'news-feed-rss' => ['/news.rss'],
|
||||
'news-category-feed-rss' => ['/news/<category>.rss'],
|
||||
'news-feed-atom' => ['/news.atom'],
|
||||
'news-category-feed-atom' => ['/news/<category>.atom'],
|
||||
|
||||
'forum-index' => ['/forum'],
|
||||
'forum-leaderboard' => ['/forum/leaderboard.php', ['id' => '<id>', 'mode' => '<mode>']],
|
||||
|
@ -155,9 +155,8 @@ function url(string $name, array $variables = []): string {
|
|||
foreach($info[1] as $key => $value) {
|
||||
$value = url_variable($value, $variables);
|
||||
|
||||
if(empty($value) || ($key === 'page' && $value < 2)) {
|
||||
if(empty($value) || ($key === 'page' && $value < 2))
|
||||
continue;
|
||||
}
|
||||
|
||||
$url .= sprintf('%s=%s&', $key, $value);
|
||||
}
|
||||
|
@ -181,16 +180,21 @@ function url_redirect(string $name, array $variables = []): void {
|
|||
}
|
||||
|
||||
function url_variable(string $value, array $variables): string {
|
||||
if(starts_with($value, '<') && ends_with($value, '>')) {
|
||||
if(starts_with($value, '<') && ends_with($value, '>'))
|
||||
return $variables[trim($value, '<>')] ?? '';
|
||||
}
|
||||
|
||||
if(starts_with($value, '[') && ends_with($value, ']')) {
|
||||
if(starts_with($value, '[') && ends_with($value, ']'))
|
||||
return constant(trim($value, '[]'));
|
||||
}
|
||||
|
||||
if(starts_with($value, '{') && ends_with($value, '}')) {
|
||||
if(starts_with($value, '{') && ends_with($value, '}'))
|
||||
return \Misuzu\CSRF::token();
|
||||
|
||||
// Hack that allows variables with file extensions
|
||||
$pathInfo = pathinfo($value);
|
||||
if($value !== $pathInfo['filename']) {
|
||||
$fallback = url_variable($pathInfo['filename'], $variables);
|
||||
if($fallback !== $pathInfo['filename'])
|
||||
return $fallback . '.' . $pathInfo['extension'];
|
||||
}
|
||||
|
||||
return $value;
|
||||
|
|
|
@ -152,7 +152,7 @@
|
|||
{% endmacro %}
|
||||
|
||||
{% macro comments_section(comments, category, user, perms) %}
|
||||
<div class="comments">
|
||||
<div class="comments" id="comments">
|
||||
<div class="comments__input">
|
||||
{% if user|default(null) is null %}
|
||||
<div class="comments__notice">
|
||||
|
|
|
@ -9,10 +9,10 @@
|
|||
|
||||
{% for cat in news_categories %}
|
||||
<p>
|
||||
<a href="{{ url('manage-news-category', {'category': cat.category_id}) }}" class="input__button">{{ cat.category_id }}</a>
|
||||
{{ cat.category_name }},
|
||||
{{ cat.category_is_hidden }},
|
||||
{{ cat.category_created }}
|
||||
<a href="{{ url('manage-news-category', {'category': cat.id}) }}" class="input__button">{{ cat.id }}</a>
|
||||
{{ cat.name }},
|
||||
{{ cat.isHidden }},
|
||||
{{ cat.createdTime|date('r') }}
|
||||
</p>
|
||||
{% endfor %}
|
||||
|
||||
|
|
|
@ -2,29 +2,29 @@
|
|||
{% from 'macros.twig' import container_title %}
|
||||
{% from '_layout/input.twig' import input_hidden, input_csrf, input_text, input_checkbox %}
|
||||
|
||||
{% set is_new = category|length < 1 %}
|
||||
{% set is_new = category is not defined %}
|
||||
|
||||
{% block manage_content %}
|
||||
<form method="post" action="{{ url('manage-news-category', {'category': category.category_id|default(0)}) }}" class="container">
|
||||
{{ container_title(is_new ? 'New Category' : 'Editing ' ~ category.category_name) }}
|
||||
<form method="post" action="{{ url('manage-news-category', {'category': category_info.id|default(0)}) }}" class="container">
|
||||
{{ container_title(is_new ? 'New Category' : 'Editing ' ~ category_info.name) }}
|
||||
|
||||
{{ input_csrf() }}
|
||||
{{ input_hidden('category[id]', category.category_id|default(0)) }}
|
||||
{{ input_hidden('category[id]', category_info.id|default(0)) }}
|
||||
|
||||
<table style="color:inherit">
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td>{{ input_text('category[name]', '', category.category_name|default(), 'text', '', true) }}</td>
|
||||
<td>{{ input_text('category[name]', '', category_info.name|default(), 'text', '', true) }}</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td><textarea name="category[description]" required class="input__textarea">{{ category.category_description|default() }}</textarea></td>
|
||||
<td><textarea name="category[description]" required class="input__textarea">{{ category_info.description|default() }}</textarea></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Is Hidden</td>
|
||||
<td>{{ input_checkbox('category[hidden]', '', category.category_is_hidden|default(false)) }}</td>
|
||||
<td>{{ input_checkbox('category[hidden]', '', category_info.isHidden|default(false)) }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
|
|
@ -2,33 +2,33 @@
|
|||
{% from 'macros.twig' import container_title %}
|
||||
{% from '_layout/input.twig' import input_hidden, input_csrf, input_text, input_checkbox, input_select %}
|
||||
|
||||
{% set is_new = post|length < 1 %}
|
||||
{% set is_new = post_info is not defined %}
|
||||
|
||||
{% block manage_content %}
|
||||
<form method="post" action="{{ url('manage-news-post', {'post': post.post_id|default(0)}) }}" class="container">
|
||||
{{ container_title(is_new ? 'New Post' : 'Editing ' ~ post.post_title) }}
|
||||
<form method="post" action="{{ url('manage-news-post', {'post': post_info.id|default(0)}) }}" class="container">
|
||||
{{ container_title(is_new ? 'New Post' : 'Editing ' ~ post_info.title) }}
|
||||
|
||||
{{ input_csrf() }}
|
||||
{{ input_hidden('post[id]', post.post_id|default(0)) }}
|
||||
{{ input_hidden('post[id]', post_info.id|default(0)) }}
|
||||
|
||||
<table style="color:inherit">
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td>{{ input_text('post[title]', '', post.post_title|default(), 'text', '', true) }}</td>
|
||||
<td>{{ input_text('post[title]', '', post_info.title|default(), 'text', '', true) }}</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Category</td>
|
||||
<td>{{ input_select('post[category]', categories, post.category_id|default(0), 'category_name', 'category_id') }}</td>
|
||||
<td>{{ input_select('post[category]', categories, post_info.categoryId|default(0), 'name', 'id') }}</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Is Featured</td>
|
||||
<td>{{ input_checkbox('post[featured]', '', post.post_is_featured|default(false)) }}</td>
|
||||
<td>{{ input_checkbox('post[featured]', '', post_info.isFeatured|default(false)) }}</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="2"><textarea name="post[text]" required class="input__textarea">{{ post.post_text|default() }}</textarea></td>
|
||||
<td colspan="2"><textarea name="post[text]" required class="input__textarea">{{ post_info.text|default() }}</textarea></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
|
|
@ -9,16 +9,16 @@
|
|||
|
||||
{% for post in news_posts %}
|
||||
<p>
|
||||
<a href="{{ url('manage-news-post', {'post': post.post_id}) }}" class="input__button">{{ post.post_id }}</a>
|
||||
<a href="{{ url('manage-news-category', {'category': post.category_id}) }}" class="input__button">Cat: {{ post.category_id }}</a>
|
||||
{{ post.post_is_featured }},
|
||||
{{ post.user_id }},
|
||||
{{ post.post_title }},
|
||||
{{ post.post_scheduled }},
|
||||
{{ post.post_created }},
|
||||
{{ post.post_updated }},
|
||||
{{ post.post_deleted }},
|
||||
{{ post.comment_section_id }}
|
||||
<a href="{{ url('manage-news-post', {'post': post.id}) }}" class="input__button">{{ post.id }}</a>
|
||||
<a href="{{ url('manage-news-category', {'category': post.categoryId}) }}" class="input__button">Cat: {{ post.categoryId }}</a>
|
||||
{{ post.isFeatured }},
|
||||
{{ post.user.id }},
|
||||
{{ post.title }},
|
||||
{{ post.scheduledTime|date('r') }},
|
||||
{{ post.createdTime|date('r') }},
|
||||
{{ post.updatedTime|date('r') }},
|
||||
{{ post.deletedTime|date('r') }},
|
||||
{{ post.commentSectionId }}
|
||||
</p>
|
||||
{% endfor %}
|
||||
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
{% from 'macros.twig' import pagination, container_title %}
|
||||
{% from 'news/macros.twig' import news_preview %}
|
||||
|
||||
{% set title = category.category_name ~ ' :: News' %}
|
||||
{% set manage_link = url('manage-news-category', {'category': category.category_id}) %}
|
||||
{% set title = category_info.name ~ ' :: News' %}
|
||||
{% set manage_link = url('manage-news-category', {'category': category_info.id}) %}
|
||||
{% set canonical_url = url('news-category', {
|
||||
'category': category.category_id,
|
||||
'category': category_info.id,
|
||||
'page': news_pagination.page > 2 ? news_pagination.page : 0,
|
||||
}) %}
|
||||
|
||||
|
@ -13,12 +13,12 @@
|
|||
{
|
||||
'type': 'rss',
|
||||
'title': '',
|
||||
'url': url('news-category-feed-rss', {'category': category.category_id}),
|
||||
'url': url('news-category-feed-rss', {'category': category_info.id}),
|
||||
},
|
||||
{
|
||||
'type': 'atom',
|
||||
'title': '',
|
||||
'url': url('news-category-feed-atom', {'category': category.category_id}),
|
||||
'url': url('news-category-feed-atom', {'category': category_info.id}),
|
||||
},
|
||||
] %}
|
||||
|
||||
|
@ -30,36 +30,24 @@
|
|||
{% endfor %}
|
||||
|
||||
<div class="container" style="padding: 4px; display: {{ news_pagination.pages > 1 ? 'block' : 'none' }}">
|
||||
{{ pagination(news_pagination, url('news-category'), null, {'c':category.category_id}) }}
|
||||
{{ pagination(news_pagination, url('news-category', {'category':category_info.id})) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="news__sidebar">
|
||||
<div class="container news__list">
|
||||
{{ container_title('News » ' ~ category.category_name) }}
|
||||
{{ container_title('News » ' ~ category_info.name) }}
|
||||
|
||||
<div class="container__content">
|
||||
{{ category.category_description|length > 0 ? category.category_description : '' }}
|
||||
{{ category_info.description }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if featured|length > 0 %}
|
||||
<div class="container news__list">
|
||||
{{ container_title('Featured Posts') }}
|
||||
|
||||
<div class="container__content">
|
||||
{% for featured_post in featured %}
|
||||
<a class="news__list__item" href="{{ url('news-post', {'post': featured_post.post_id}) }}">{{ featured_post.post_title }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="container">
|
||||
{{ container_title('Feeds') }}
|
||||
|
||||
<div class="news__feeds">
|
||||
<a href="{{ url('news-category-feed-atom', {'category': category.category_id}) }}" class="news__feed">
|
||||
<a href="{{ url('news-category-feed-atom', {'category': category_info.id}) }}" class="news__feed">
|
||||
<div class="news__feed__icon">
|
||||
<i class="fas fa-rss"></i>
|
||||
</div>
|
||||
|
@ -67,7 +55,7 @@
|
|||
Atom
|
||||
</div>
|
||||
</a>
|
||||
<a href="{{ url('news-category-feed-rss', {'category': category.category_id}) }}" class="news__feed">
|
||||
<a href="{{ url('news-category-feed-rss', {'category': category_info.id}) }}" class="news__feed">
|
||||
<div class="news__feed__icon">
|
||||
<i class="fas fa-rss"></i>
|
||||
</div>
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
{% endfor %}
|
||||
|
||||
<div class="container" style="padding: 4px; display: {{ news_pagination.pages > 1 ? 'block' : 'none' }}">
|
||||
{{ pagination(news_pagination, url('news-index'), null, null, 'page') }}
|
||||
{{ pagination(news_pagination, url('news-index')) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -39,12 +39,12 @@
|
|||
|
||||
<div class="container__content">
|
||||
{% for category in categories %}
|
||||
<a class="news__list__item news__list__item--kvp" href="{{ url('news-category', {'category': category.category_id}) }}">
|
||||
<a class="news__list__item news__list__item--kvp" href="{{ url('news-category', {'category': category.id}) }}">
|
||||
<div class="news__list__name">
|
||||
{{ category.category_name }}
|
||||
{{ category.name }}
|
||||
</div>
|
||||
<div class="news__list__value">
|
||||
{{ category.posts_count }} post{{ category.posts_count == 1 ? '' : 's' }}
|
||||
{{ category.postCount }} post{{ category.postCount == 1 ? '' : 's' }}
|
||||
</div>
|
||||
</a>
|
||||
{% endfor %}
|
||||
|
|
|
@ -1,44 +1,44 @@
|
|||
{% macro news_preview(post) %}
|
||||
{% from 'macros.twig' import container_title, avatar %}
|
||||
|
||||
<div class="container news__preview" style="{% if post.user_colour is not null %}{{ post.user_colour|html_colour }}{% endif %}">
|
||||
<div class="container news__preview" style="{% if post.user is not null %}--user-colour: {{ post.user.colour }}{% endif %}">
|
||||
<div class="news__preview__info">
|
||||
<div class="news__preview__info__background"></div>
|
||||
<div class="news__preview__info__content">
|
||||
{% if post.user_id is not null %}
|
||||
{% if post.user.id is not null %}
|
||||
<div class="news__preview__user">
|
||||
<a class="news__preview__avatar" href="{{ url('user-profile', {'user': post.user_id}) }}">
|
||||
{{ avatar(post.user_id, 60, post.username) }}
|
||||
<a class="news__preview__avatar" href="{{ url('user-profile', {'user': post.user.id}) }}">
|
||||
{{ avatar(post.user.id, 60, post.user.username) }}
|
||||
</a>
|
||||
|
||||
<div class="news__preview__user__details">
|
||||
<a class="news__preview__username" href="{{ url('user-profile', {'user': post.user_id}) }}">{{ post.username }}</a>
|
||||
<a class="news__preview__username" href="{{ url('user-profile', {'user': post.user.id}) }}">{{ post.user.username }}</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<a class="news__preview__category" href="{{ url('news-category', {'category': post.category_id}) }}">
|
||||
{{ post.category_name }}
|
||||
<a class="news__preview__category" href="{{ url('news-category', {'category': post.category.id}) }}">
|
||||
{{ post.category.name }}
|
||||
</a>
|
||||
|
||||
<div class="news__preview__date">
|
||||
Posted
|
||||
<time datetime="{{ post.post_created|date('c') }}" title="{{ post.post_created|date('r') }}">
|
||||
{{ post.post_created|time_diff }}
|
||||
<time datetime="{{ post.createdTime|date('c') }}" title="{{ post.createdTime|date('r') }}">
|
||||
{{ post.createdTime|time_diff }}
|
||||
</time>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="news__preview__content markdown">
|
||||
<h1>{{ post.post_title }}</h1>
|
||||
<h1>{{ post.title }}</h1>
|
||||
<div class="news__preview__text">
|
||||
{{ post.post_text|first_paragraph|parse_text(constant('\\Misuzu\\Parsers\\Parser::MARKDOWN'))|raw }}
|
||||
{{ post.text|first_paragraph|parse_text(constant('\\Misuzu\\Parsers\\Parser::MARKDOWN'))|raw }}
|
||||
</div>
|
||||
<div class="news__preview__links">
|
||||
<a href="{{ url('news-post', {'post': post.post_id}) }}" class="news__preview__link">Continue reading</a>
|
||||
<a href="{{ url('news-post-comments', {'post': post.post_id}) }}" class="news__preview__link">
|
||||
{{ post.post_comments < 1 ? 'No' : post.post_comments|number_format }} comment{{ post.post_comments != 1 ? 's' : '' }}
|
||||
<a href="{{ url('news-post', {'post': post.id}) }}" class="news__preview__link">Continue reading</a>
|
||||
<a href="{{ url('news-post-comments', {'post': post.id}) }}" class="news__preview__link">
|
||||
{{ post.commentCount < 1 ? 'No' : post.commentCount|number_format }} comment{{ post.commentCount != 1 ? 's' : '' }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -48,38 +48,38 @@
|
|||
{% macro news_post(post) %}
|
||||
{% from 'macros.twig' import avatar %}
|
||||
|
||||
<div class="container news__post" style="{% if post.user_colour is not null %}{{ post.user_colour|html_colour('--accent-colour') }}{% endif %}">
|
||||
<div class="container news__post" style="{% if post.user is not null %}--accent-colour: {{ post.user.colour }}{% endif %}">
|
||||
<div class="news__post__info">
|
||||
<div class="news__post__info__background"></div>
|
||||
<div class="news__post__info__content">
|
||||
{% if post.user_id is not null %}
|
||||
{% if post.user is not null %}
|
||||
<div class="news__post__user">
|
||||
<a class="news__post__avatar" href="{{ url('user-profile', {'user': post.user_id}) }}">
|
||||
{{ avatar(post.user_id, 60, post.username) }}
|
||||
<a class="news__post__avatar" href="{{ url('user-profile', {'user': post.user.id}) }}">
|
||||
{{ avatar(post.user.id, 60, post.user.username) }}
|
||||
</a>
|
||||
|
||||
<div class="news__post__user__details">
|
||||
<a class="news__post__username" href="{{ url('user-profile', {'user': post.user_id}) }}">{{ post.username }}</a>
|
||||
<a class="news__post__username" href="{{ url('user-profile', {'user': post.user.id}) }}">{{ post.user.username }}</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<a class="news__post__category" href="{{ url('news-category', {'category': post.category_id}) }}">
|
||||
{{ post.category_name }}
|
||||
<a class="news__post__category" href="{{ url('news-category', {'category': post.category.id}) }}">
|
||||
{{ post.category.name }}
|
||||
</a>
|
||||
|
||||
<div class="news__post__date">
|
||||
Posted
|
||||
<time datetime="{{ post.post_created|date('c') }}" title="{{ post.post_created|date('r') }}">
|
||||
{{ post.post_created|time_diff }}
|
||||
<time datetime="{{ post.createdTime|date('c') }}" title="{{ post.createdTime|date('r') }}">
|
||||
{{ post.createdTime|time_diff }}
|
||||
</time>
|
||||
</div>
|
||||
|
||||
{% if post.post_updated|date('U') > post.post_created|date('U') %}
|
||||
{% if post.isEdited %}
|
||||
<div class="news__post__date">
|
||||
Updated
|
||||
<time datetime="{{ post.post_updated|date('c') }}" title="{{ post.post_updated|date('r') }}">
|
||||
{{ post.post_updated|time_diff }}
|
||||
<time datetime="{{ post.updatedTime|date('c') }}" title="{{ post.updatedTime|date('r') }}">
|
||||
{{ post.updatedTime|time_diff }}
|
||||
</time>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
@ -87,8 +87,8 @@
|
|||
</div>
|
||||
|
||||
<div class="news__post__text markdown">
|
||||
<h1>{{ post.post_title }}</h1>
|
||||
{{ post.post_text|parse_text(constant('\\Misuzu\\Parsers\\Parser::MARKDOWN'))|raw }}
|
||||
<h1>{{ post.title }}</h1>
|
||||
{{ post.text|parse_text(constant('\\Misuzu\\Parsers\\Parser::MARKDOWN'))|raw }}
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
|
|
@ -3,12 +3,12 @@
|
|||
{% from '_layout/comments.twig' import comments_section %}
|
||||
{% from 'news/macros.twig' import news_post %}
|
||||
|
||||
{% set title = post.post_title ~ ' :: News' %}
|
||||
{% set canonical_url = url('news-post', {'post': post.post_id}) %}
|
||||
{% set manage_link = url('manage-news-post', {'post': post.post_id}) %}
|
||||
{% set title = post_info.title ~ ' :: News' %}
|
||||
{% set canonical_url = url('news-post', {'post': post_info.id}) %}
|
||||
{% set manage_link = url('manage-news-post', {'post': post_info.id}) %}
|
||||
|
||||
{% block content %}
|
||||
{{ news_post(post) }}
|
||||
{{ news_post(post_info) }}
|
||||
|
||||
{% if comments is defined %}
|
||||
<div class="container">
|
||||
|
|
Loading…
Add table
Reference in a new issue