Added sanity to changelog code.
This commit is contained in:
parent
4cac71c23e
commit
c365e32d89
34 changed files with 1010 additions and 646 deletions
|
@ -6,6 +6,8 @@ use Misuzu\Database\Database;
|
|||
use Misuzu\Database\DatabaseMigrationManager;
|
||||
use Misuzu\Net\GeoIP;
|
||||
use Misuzu\Net\IPAddress;
|
||||
use Misuzu\Users\User;
|
||||
use Misuzu\Users\UserNotFoundException;
|
||||
|
||||
define('MSZ_STARTUP', microtime(true));
|
||||
define('MSZ_ROOT', __DIR__);
|
||||
|
@ -29,6 +31,7 @@ set_include_path(get_include_path() . PATH_SEPARATOR . MSZ_ROOT);
|
|||
|
||||
set_exception_handler(function(\Throwable $ex) {
|
||||
http_response_code(500);
|
||||
ob_clean();
|
||||
|
||||
if(MSZ_CLI || MSZ_DEBUG) {
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
|
@ -63,7 +66,6 @@ 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/manage.php';
|
||||
require_once 'src/url.php';
|
||||
require_once 'src/Forum/perms.php';
|
||||
|
@ -450,6 +452,10 @@ MIG;
|
|||
user_session_stop(true);
|
||||
$userDisplayInfo = [];
|
||||
} else {
|
||||
try {
|
||||
User::byId($cookieData['user_id'])->setCurrent();
|
||||
} catch(UserNotFoundException $ex) {}
|
||||
|
||||
user_bump_last_active($cookieData['user_id']);
|
||||
user_session_bump_active(user_session_current('session_id'));
|
||||
|
||||
|
|
|
@ -1,95 +1,2 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Comments\CommentsCategory;
|
||||
use Misuzu\Comments\CommentsCategoryNotFoundException;
|
||||
use Misuzu\Users\User;
|
||||
use Misuzu\Users\UserNotFoundException;
|
||||
|
||||
require_once '../misuzu.php';
|
||||
|
||||
$changelogChange = !empty($_GET['c']) && is_string($_GET['c']) ? (int)$_GET['c'] : 0;
|
||||
$changelogDate = !empty($_GET['d']) && is_string($_GET['d']) ? (string)$_GET['d'] : '';
|
||||
$changelogUser = !empty($_GET['u']) && is_string($_GET['u']) ? (int)$_GET['u'] : 0;
|
||||
$changelogTags = !empty($_GET['t']) && is_string($_GET['t']) ? (string)$_GET['t'] : '';
|
||||
|
||||
if($changelogChange > 0) {
|
||||
$change = changelog_change_get($changelogChange);
|
||||
|
||||
if(!$change) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
$commentsCategoryName = "changelog-date-{$change['change_date']}";
|
||||
try {
|
||||
$commentsCategory = CommentsCategory::byName($commentsCategoryName);
|
||||
} catch(CommentsCategoryNotFoundException $ex) {
|
||||
$commentsCategory = new CommentsCategory($commentsCategoryName);
|
||||
$commentsCategory->save();
|
||||
}
|
||||
|
||||
try {
|
||||
$commentsUser = User::byId(user_session_current('user_id', 0));
|
||||
} catch(UserNotFoundException $ex) {
|
||||
$commentsUser = null;
|
||||
}
|
||||
|
||||
Template::render('changelog.change', [
|
||||
'change' => $change,
|
||||
'tags' => changelog_change_tags_get($change['change_id']),
|
||||
'comments_category' => $commentsCategory,
|
||||
'comments_user' => $commentsUser,
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!empty($changelogDate)) {
|
||||
$dateParts = explode('-', $changelogDate, 3);
|
||||
|
||||
if(count($dateParts) !== 3
|
||||
|| !array_test($dateParts, 'ctype_digit')
|
||||
|| !checkdate($dateParts[1], $dateParts[2], $dateParts[0])) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$changesCount = !empty($changelogDate) ? -1 : changelog_count_changes($changelogDate, $changelogUser);
|
||||
$changesPagination = new Pagination($changesCount, 30);
|
||||
$changes = $changesCount === -1 || $changesPagination->hasValidOffset()
|
||||
? changelog_get_changes($changelogDate, $changelogUser, $changesPagination->getOffset(), $changesPagination->getRange())
|
||||
: [];
|
||||
|
||||
if(!$changes) {
|
||||
http_response_code(404);
|
||||
}
|
||||
|
||||
if(!empty($changelogDate) && count($changes) > 0) {
|
||||
$commentsCategoryName = "changelog-date-{$changelogDate}";
|
||||
try {
|
||||
$commentsCategory = CommentsCategory::byName($commentsCategoryName);
|
||||
} catch(CommentsCategoryNotFoundException $ex) {
|
||||
$commentsCategory = new CommentsCategory($commentsCategoryName);
|
||||
$commentsCategory->save();
|
||||
}
|
||||
|
||||
try {
|
||||
$commentsUser = User::byId(user_session_current('user_id', 0));
|
||||
} catch(UserNotFoundException $ex) {
|
||||
$commentsUser = null;
|
||||
}
|
||||
|
||||
Template::set([
|
||||
'comments_category' => $commentsCategory,
|
||||
'comments_user' => $commentsUser,
|
||||
]);
|
||||
}
|
||||
|
||||
Template::render('changelog.index', [
|
||||
'changes' => $changes,
|
||||
'changelog_count' => $changesCount,
|
||||
'changelog_date' => $changelogDate,
|
||||
'changelog_user' => $changelogUser,
|
||||
'changelog_pagination' => $changesPagination,
|
||||
]);
|
||||
require_once __DIR__ . '/index.php';
|
||||
|
|
|
@ -29,9 +29,8 @@ if(!CSRF::validateRequest()) {
|
|||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$currentUserInfo = User::byId(user_session_current('user_id', 0));
|
||||
} catch(UserNotFoundException $ex) {
|
||||
$currentUserInfo = User::getCurrent();
|
||||
if($currentUserInfo === null) {
|
||||
echo render_info_or_json($isXHR, 'You must be logged in to manage comments.', 401);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,13 @@ Router::addRoutes(
|
|||
Route::get('/info', 'index', 'Info'),
|
||||
Route::get('/info/([A-Za-z0-9_/]+)', 'page', 'Info'),
|
||||
|
||||
// Changelog
|
||||
Route::get('/changelog', 'index', 'Changelog')->addChildren(
|
||||
Route::get('.atom', 'feedAtom'),
|
||||
Route::get('.rss', 'feedRss'),
|
||||
Route::get('/change/([0-9]+)', 'change'),
|
||||
),
|
||||
|
||||
// News
|
||||
Route::get('/news', 'index', 'News')->addChildren(
|
||||
Route::get('.atom', 'feedIndexAtom'),
|
||||
|
@ -52,6 +59,7 @@ Router::addRoutes(
|
|||
Route::get('/index.php', url('index')),
|
||||
Route::get('/info.php', url('info')),
|
||||
Route::get('/settings.php', url('settings-index')),
|
||||
Route::get('/changelog.php', 'legacy', 'Changelog'),
|
||||
Route::get('/info.php/([A-Za-z0-9_/]+)', 'redir', 'Info'),
|
||||
Route::get('/auth.php', 'legacy', 'Auth'),
|
||||
Route::get('/news.php', 'legacy', 'News'),
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Changelog\ChangelogChange;
|
||||
use Misuzu\Changelog\ChangelogChangeNotFoundException;
|
||||
use Misuzu\Changelog\ChangelogTag;
|
||||
use Misuzu\Changelog\ChangelogTagNotFoundException;
|
||||
use Misuzu\Users\User;
|
||||
use Misuzu\Users\UserNotFoundException;
|
||||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
if(!perms_check_user(MSZ_PERMS_CHANGELOG, user_session_current('user_id'), MSZ_PERM_CHANGELOG_MANAGE_CHANGES)) {
|
||||
|
@ -8,122 +15,75 @@ if(!perms_check_user(MSZ_PERMS_CHANGELOG, user_session_current('user_id'), MSZ_P
|
|||
return;
|
||||
}
|
||||
|
||||
$changeId = (int)($_GET['c'] ?? 0);
|
||||
define('MANAGE_ACTIONS', [
|
||||
['action_id' => ChangelogChange::ACTION_ADD, 'action_name' => 'Added'],
|
||||
['action_id' => ChangelogChange::ACTION_REMOVE, 'action_name' => 'Removed'],
|
||||
['action_id' => ChangelogChange::ACTION_UPDATE, 'action_name' => 'Updated'],
|
||||
['action_id' => ChangelogChange::ACTION_FIX, 'action_name' => 'Fixed'],
|
||||
['action_id' => ChangelogChange::ACTION_IMPORT, 'action_name' => 'Imported'],
|
||||
['action_id' => ChangelogChange::ACTION_REVERT, 'action_name' => 'Reverted'],
|
||||
]);
|
||||
|
||||
$changeId = (int)filter_input(INPUT_GET, 'c', FILTER_SANITIZE_NUMBER_INT);
|
||||
$tags = ChangelogTag::all();
|
||||
|
||||
if($changeId > 0)
|
||||
try {
|
||||
$change = ChangelogChange::byId($changeId);
|
||||
} catch(ChangelogChangeNotFoundException $ex) {
|
||||
url_redirect('manage-changelog-changes');
|
||||
return;
|
||||
}
|
||||
|
||||
if($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
|
||||
if(!empty($_POST['change']) && is_array($_POST['change'])) {
|
||||
if($changeId > 0) {
|
||||
$postChange = DB::prepare('
|
||||
UPDATE `msz_changelog_changes`
|
||||
SET `change_log` = :log,
|
||||
`change_text` = :text,
|
||||
`change_action` = :action,
|
||||
`user_id` = :user,
|
||||
`change_created` = :created
|
||||
WHERE `change_id` = :change_id
|
||||
');
|
||||
$postChange->bind('change_id', $changeId);
|
||||
} else {
|
||||
$postChange = DB::prepare('
|
||||
INSERT INTO `msz_changelog_changes`
|
||||
(
|
||||
`change_log`, `change_text`, `change_action`,
|
||||
`user_id`, `change_created`
|
||||
)
|
||||
VALUES
|
||||
(:log, :text, :action, :user, :created)
|
||||
');
|
||||
if(!isset($change)) {
|
||||
$change = new ChangelogChange;
|
||||
$isNew = true;
|
||||
}
|
||||
|
||||
$postChange->bind('log', $_POST['change']['log']);
|
||||
$postChange->bind('action', $_POST['change']['action']);
|
||||
$postChange->bind('text', strlen($_POST['change']['text'])
|
||||
? $_POST['change']['text']
|
||||
: null);
|
||||
$postChange->bind('user', is_numeric($_POST['change']['user'])
|
||||
? $_POST['change']['user']
|
||||
: null);
|
||||
$postChange->bind('created', strlen($_POST['change']['created'])
|
||||
? $_POST['change']['created']
|
||||
: null);
|
||||
$postChange->execute();
|
||||
$changeUserId = filter_var($_POST['change']['user'], FILTER_SANITIZE_NUMBER_INT);
|
||||
if($changeUserId === 0)
|
||||
$changeUser = null;
|
||||
else
|
||||
try {
|
||||
$changeUser = User::byId($changeUserId);
|
||||
} catch(UserNotFoundException $ex) {
|
||||
$changeUser = User::getCurrent();
|
||||
}
|
||||
|
||||
if($changeId < 1) {
|
||||
$changeId = DB::lastId();
|
||||
audit_log(MSZ_AUDIT_CHANGELOG_ENTRY_CREATE, user_session_current('user_id', 0), [$changeId]);
|
||||
} else {
|
||||
audit_log(MSZ_AUDIT_CHANGELOG_ENTRY_EDIT, user_session_current('user_id', 0), [$changeId]);
|
||||
}
|
||||
$change->setHeader($_POST['change']['log'])
|
||||
->setBody($_POST['change']['text'])
|
||||
->setAction($_POST['change']['action'])
|
||||
->setUser($changeUser)
|
||||
->save();
|
||||
|
||||
audit_log(
|
||||
empty($isNew)
|
||||
? MSZ_AUDIT_CHANGELOG_ENTRY_EDIT
|
||||
: MSZ_AUDIT_CHANGELOG_ENTRY_CREATE,
|
||||
User::getCurrent()->getId(),
|
||||
[$change->getId()]
|
||||
);
|
||||
}
|
||||
|
||||
if(!empty($_POST['tags']) && is_array($_POST['tags']) && array_test($_POST['tags'], 'ctype_digit')) {
|
||||
$setTags = array_apply($_POST['tags'], 'intval');
|
||||
|
||||
$removeTags = DB::prepare(sprintf('
|
||||
DELETE FROM `msz_changelog_change_tags`
|
||||
WHERE `change_id` = :change_id
|
||||
AND `tag_id` NOT IN (%s)
|
||||
', implode(',', $setTags)));
|
||||
$removeTags->bind('change_id', $changeId);
|
||||
$removeTags->execute();
|
||||
|
||||
$addTag = DB::prepare('
|
||||
INSERT IGNORE INTO `msz_changelog_change_tags`
|
||||
(`change_id`, `tag_id`)
|
||||
VALUES
|
||||
(:change_id, :tag_id)
|
||||
');
|
||||
$addTag->bind('change_id', $changeId);
|
||||
|
||||
foreach($setTags as $role) {
|
||||
$addTag->bind('tag_id', $role);
|
||||
$addTag->execute();
|
||||
}
|
||||
if(isset($change) && !empty($_POST['tags']) && is_array($_POST['tags']) && array_test($_POST['tags'], 'ctype_digit')) {
|
||||
$applyTags = [];
|
||||
foreach($_POST['tags'] as $tagId)
|
||||
try {
|
||||
$applyTags[] = ChangelogTag::byId((int)filter_var($tagId, FILTER_SANITIZE_NUMBER_INT));
|
||||
} catch(ChangelogTagNotFoundException $ex) {}
|
||||
$change->setTags($applyTags);
|
||||
}
|
||||
}
|
||||
|
||||
$actions = [
|
||||
['action_id' => MSZ_CHANGELOG_ACTION_ADD, 'action_name' => 'Added'],
|
||||
['action_id' => MSZ_CHANGELOG_ACTION_REMOVE, 'action_name' => 'Removed'],
|
||||
['action_id' => MSZ_CHANGELOG_ACTION_UPDATE, 'action_name' => 'Updated'],
|
||||
['action_id' => MSZ_CHANGELOG_ACTION_FIX, 'action_name' => 'Fixed'],
|
||||
['action_id' => MSZ_CHANGELOG_ACTION_IMPORT, 'action_name' => 'Imported'],
|
||||
['action_id' => MSZ_CHANGELOG_ACTION_REVERT, 'action_name' => 'Reverted'],
|
||||
];
|
||||
|
||||
if($changeId > 0) {
|
||||
$getChange = DB::prepare('
|
||||
SELECT
|
||||
`change_id`, `change_log`, `change_text`, `user_id`,
|
||||
`change_action`, `change_created`
|
||||
FROM `msz_changelog_changes`
|
||||
WHERE `change_id` = :change_id
|
||||
');
|
||||
$getChange->bind('change_id', $changeId);
|
||||
$change = $getChange->fetch();
|
||||
|
||||
if(!$change) {
|
||||
url_redirect('manage-changelog-changes');
|
||||
if(!empty($isNew)) {
|
||||
url_redirect('manage-changelog-change', ['change' => $change->getId()]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$getChangeTags = DB::prepare('
|
||||
SELECT
|
||||
ct.`tag_id`, ct.`tag_name`,
|
||||
(
|
||||
SELECT COUNT(`change_id`) > 0
|
||||
FROM `msz_changelog_change_tags`
|
||||
WHERE `tag_id` = ct.`tag_id`
|
||||
AND `change_id` = :change_id
|
||||
) AS `has_tag`
|
||||
FROM `msz_changelog_tags` AS ct
|
||||
');
|
||||
$getChangeTags->bind('change_id', $change['change_id'] ?? 0);
|
||||
$changeTags = $getChangeTags->fetchAll();
|
||||
|
||||
Template::render('manage.changelog.change', [
|
||||
'change' => $change ?? null,
|
||||
'change_tags' => $changeTags,
|
||||
'change_actions' => $actions,
|
||||
'change_tags' => $tags,
|
||||
'change_actions' => MANAGE_ACTIONS,
|
||||
]);
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Changelog\ChangelogChange;
|
||||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
if(!perms_check_user(MSZ_PERMS_CHANGELOG, user_session_current('user_id'), MSZ_PERM_CHANGELOG_MANAGE_CHANGES)) {
|
||||
|
@ -8,54 +10,16 @@ if(!perms_check_user(MSZ_PERMS_CHANGELOG, user_session_current('user_id'), MSZ_P
|
|||
return;
|
||||
}
|
||||
|
||||
$changesCount = (int)DB::query('
|
||||
SELECT COUNT(`change_id`)
|
||||
FROM `msz_changelog_changes`
|
||||
')->fetchColumn();
|
||||
|
||||
$changelogPagination = new Pagination($changesCount, 30);
|
||||
$changelogPagination = new Pagination(ChangelogChange::countAll(), 30);
|
||||
|
||||
if(!$changelogPagination->hasValidOffset()) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
$getChanges = DB::prepare('
|
||||
SELECT
|
||||
c.`change_id`, c.`change_log`, c.`change_created`, c.`change_action`,
|
||||
u.`user_id`, u.`username`,
|
||||
COALESCE(u.`user_colour`, r.`role_colour`) AS `user_colour`,
|
||||
DATE(`change_created`) AS `change_date`,
|
||||
!ISNULL(c.`change_text`) AS `change_has_text`
|
||||
FROM `msz_changelog_changes` AS c
|
||||
LEFT JOIN `msz_users` AS u
|
||||
ON u.`user_id` = c.`user_id`
|
||||
LEFT JOIN `msz_roles` AS r
|
||||
ON r.`role_id` = u.`display_role`
|
||||
ORDER BY c.`change_id` DESC
|
||||
LIMIT :offset, :take
|
||||
');
|
||||
$getChanges->bind('take', $changelogPagination->getRange());
|
||||
$getChanges->bind('offset', $changelogPagination->getOffset());
|
||||
$changes = $getChanges->fetchAll();
|
||||
|
||||
$getTags = DB::prepare('
|
||||
SELECT
|
||||
t.`tag_id`, t.`tag_name`, t.`tag_description`
|
||||
FROM `msz_changelog_change_tags` as ct
|
||||
LEFT JOIN `msz_changelog_tags` as t
|
||||
ON t.`tag_id` = ct.`tag_id`
|
||||
WHERE ct.`change_id` = :change_id
|
||||
');
|
||||
|
||||
// grab tags
|
||||
for($i = 0; $i < count($changes); $i++) {
|
||||
$getTags->bind('change_id', $changes[$i]['change_id']);
|
||||
$changes[$i]['tags'] = $getTags->fetchAll();
|
||||
}
|
||||
$changes = ChangelogChange::all($changelogPagination);
|
||||
|
||||
Template::render('manage.changelog.changes', [
|
||||
'changelog_changes' => $changes,
|
||||
'changelog_changes_count' => $changesCount,
|
||||
'changelog_pagination' => $changelogPagination,
|
||||
]);
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Changelog\ChangelogTag;
|
||||
use Misuzu\Changelog\ChangelogTagNotFoundException;
|
||||
use Misuzu\Users\User;
|
||||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
if(!perms_check_user(MSZ_PERMS_CHANGELOG, user_session_current('user_id'), MSZ_PERM_CHANGELOG_MANAGE_TAGS)) {
|
||||
|
@ -8,57 +12,41 @@ if(!perms_check_user(MSZ_PERMS_CHANGELOG, user_session_current('user_id'), MSZ_P
|
|||
return;
|
||||
}
|
||||
|
||||
$tagId = (int)($_GET['t'] ?? 0);
|
||||
$tagId = (int)filter_input(INPUT_GET, 't', FILTER_SANITIZE_NUMBER_INT);
|
||||
|
||||
if(!empty($_POST['tag']) && is_array($_POST['tag']) && CSRF::validateRequest()) {
|
||||
if($tagId > 0) {
|
||||
$updateTag = DB::prepare('
|
||||
UPDATE `msz_changelog_tags`
|
||||
SET `tag_name` = :name,
|
||||
`tag_description` = :description,
|
||||
`tag_archived` = :archived
|
||||
WHERE `tag_id` = :id
|
||||
');
|
||||
$updateTag->bind('id', $tagId);
|
||||
} else {
|
||||
$updateTag = DB::prepare('
|
||||
INSERT INTO `msz_changelog_tags`
|
||||
(`tag_name`, `tag_description`, `tag_archived`)
|
||||
VALUES
|
||||
(:name, :description, :archived)
|
||||
');
|
||||
}
|
||||
|
||||
$updateTag->bind('name', $_POST['tag']['name']);
|
||||
$updateTag->bind('description', $_POST['tag']['description']);
|
||||
$updateTag->bind('archived', empty($_POST['tag']['archived']) ? null : date('Y-m-d H:i:s'));
|
||||
$updateTag->execute();
|
||||
|
||||
if($tagId < 1) {
|
||||
$tagId = DB::lastId();
|
||||
audit_log(MSZ_AUDIT_CHANGELOG_TAG_EDIT, user_session_current('user_id', 0), [$tagId]);
|
||||
url_redirect('manage-changelog-tag', ['tag' => $tagId]);
|
||||
return;
|
||||
} else {
|
||||
audit_log(MSZ_AUDIT_CHANGELOG_TAG_CREATE, user_session_current('user_id', 0), [$tagId]);
|
||||
}
|
||||
}
|
||||
|
||||
if($tagId > 0) {
|
||||
$getTag = DB::prepare('
|
||||
SELECT `tag_id`, `tag_name`, `tag_description`, `tag_archived`, `tag_created`
|
||||
FROM `msz_changelog_tags`
|
||||
WHERE `tag_id` = :tag_id
|
||||
');
|
||||
$getTag->bind('tag_id', $tagId);
|
||||
$tag = $getTag->fetch();
|
||||
|
||||
if($tag) {
|
||||
Template::set('edit_tag', $tag);
|
||||
} else {
|
||||
if($tagId > 0)
|
||||
try {
|
||||
$tagInfo = ChangelogTag::byId($tagId);
|
||||
} catch(ChangelogTagNotFoundException $ex) {
|
||||
url_redirect('manage-changelog-tags');
|
||||
return;
|
||||
}
|
||||
|
||||
if(!empty($_POST['tag']) && is_array($_POST['tag']) && CSRF::validateRequest()) {
|
||||
if(!isset($tagInfo)) {
|
||||
$tagInfo = new ChangelogTag;
|
||||
$isNew = true;
|
||||
}
|
||||
|
||||
$tagInfo->setName($_POST['tag']['name'])
|
||||
->setDescription($_POST['tag']['description'])
|
||||
->setArchived(!empty($_POST['tag']['archived']))
|
||||
->save();
|
||||
|
||||
audit_log(
|
||||
empty($isNew)
|
||||
? MSZ_AUDIT_CHANGELOG_TAG_EDIT
|
||||
: MSZ_AUDIT_CHANGELOG_TAG_CREATE,
|
||||
User::getCurrent()->getId(),
|
||||
[$tagInfo->getId()]
|
||||
);
|
||||
|
||||
if(!empty($isNew)) {
|
||||
url_redirect('manage-changelog-tag', ['tag' => $tagInfo->getId()]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Template::render('manage.changelog.tag');
|
||||
Template::render('manage.changelog.tag', [
|
||||
'edit_tag' => $tagInfo ?? null,
|
||||
]);
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Changelog\ChangelogTag;
|
||||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
if(!perms_check_user(MSZ_PERMS_CHANGELOG, user_session_current('user_id'), MSZ_PERM_CHANGELOG_MANAGE_TAGS)) {
|
||||
|
@ -8,18 +10,6 @@ if(!perms_check_user(MSZ_PERMS_CHANGELOG, user_session_current('user_id'), MSZ_P
|
|||
return;
|
||||
}
|
||||
|
||||
$getTags = DB::prepare('
|
||||
SELECT
|
||||
t.`tag_id`, t.`tag_name`, t.`tag_description`, t.`tag_created`,
|
||||
(
|
||||
SELECT COUNT(ct.`change_id`)
|
||||
FROM `msz_changelog_change_tags` as ct
|
||||
WHERE ct.`tag_id` = t.`tag_id`
|
||||
) as `tag_count`
|
||||
FROM `msz_changelog_tags` as t
|
||||
ORDER BY t.`tag_id` ASC
|
||||
');
|
||||
|
||||
Template::render('manage.changelog.tags', [
|
||||
'changelog_tags' => $getTags->fetchAll(),
|
||||
'changelog_tags' => ChangelogTag::all(),
|
||||
]);
|
||||
|
|
|
@ -13,8 +13,8 @@ if(!user_session_active()) {
|
|||
}
|
||||
|
||||
$errors = [];
|
||||
$currentUserId = user_session_current('user_id');
|
||||
$currentUser = User::byId($currentUserId);
|
||||
$currentUser = User::getCurrent();
|
||||
$currentUserId = $currentUser->getId();
|
||||
$currentEmail = user_email_get($currentUserId);
|
||||
$isRestricted = user_warning_check_restriction($currentUserId);
|
||||
$twoFactorInfo = user_totp_info($currentUserId);
|
||||
|
|
|
@ -26,8 +26,8 @@ function db_to_zip(ZipArchive $archive, int $userId, string $filename, string $q
|
|||
}
|
||||
|
||||
$errors = [];
|
||||
$currentUserId = user_session_current('user_id');
|
||||
$currentUser = User::byId($currentUserId);
|
||||
$currentUser = User::getCurrent();
|
||||
$currentUserId = $currentUser->getId();
|
||||
|
||||
if(isset($_POST['action']) && is_string($_POST['action'])) {
|
||||
if(isset($_POST['password']) && is_string($_POST['password'])
|
||||
|
|
281
src/Changelog/ChangelogChange.php
Normal file
281
src/Changelog/ChangelogChange.php
Normal file
|
@ -0,0 +1,281 @@
|
|||
<?php
|
||||
namespace Misuzu\Changelog;
|
||||
|
||||
use JsonSerializable;
|
||||
use UnexpectedValueException;
|
||||
use Misuzu\DB;
|
||||
use Misuzu\Memoizer;
|
||||
use Misuzu\Pagination;
|
||||
use Misuzu\Comments\CommentsCategory;
|
||||
use Misuzu\Comments\CommentsCategoryNotFoundException;
|
||||
use Misuzu\Parsers\Parser;
|
||||
use Misuzu\Users\User;
|
||||
use Misuzu\Users\UserNotFoundException;
|
||||
|
||||
class ChangelogChangeException extends ChangelogException {}
|
||||
class ChangelogChangeNotFoundException extends ChangelogChangeException {}
|
||||
|
||||
class ChangelogChange implements JsonSerializable {
|
||||
public const ACTION_UNKNOWN = 1;
|
||||
public const ACTION_ADD = 1;
|
||||
public const ACTION_REMOVE = 2;
|
||||
public const ACTION_UPDATE = 3;
|
||||
public const ACTION_FIX = 4;
|
||||
public const ACTION_IMPORT = 5;
|
||||
public const ACTION_REVERT = 6;
|
||||
|
||||
private const ACTION_STRINGS = [
|
||||
self::ACTION_UNKNOWN => ['unknown', 'Changed'],
|
||||
self::ACTION_ADD => ['add', 'Added'],
|
||||
self::ACTION_REMOVE => ['remove', 'Removed'],
|
||||
self::ACTION_UPDATE => ['update', 'Updated'],
|
||||
self::ACTION_FIX => ['fix', 'Fixed'],
|
||||
self::ACTION_IMPORT => ['import', 'Imported'],
|
||||
self::ACTION_REVERT => ['revert', 'Reverted'],
|
||||
];
|
||||
|
||||
public const DEFAULT_DATE = '0000-00-00';
|
||||
|
||||
// Database fields
|
||||
private $change_id = -1;
|
||||
private $user_id = null;
|
||||
private $change_action = null; // defaults null apparently, probably a previous oversight
|
||||
private $change_created = null;
|
||||
private $change_log = '';
|
||||
private $change_text = '';
|
||||
|
||||
private $user = null;
|
||||
private $userLookedUp = false;
|
||||
private $comments = null;
|
||||
private $tags = null;
|
||||
private $tagRelations = null;
|
||||
|
||||
public const TABLE = 'changelog_changes';
|
||||
private const QUERY_SELECT = 'SELECT %1$s FROM `' . DB::PREFIX . self::TABLE . '` AS '. self::TABLE;
|
||||
private const SELECT = '%1$s.`change_id`, %1$s.`user_id`, %1$s.`change_action`, %1$s.`change_log`, %1$s.`change_text`'
|
||||
. ', UNIX_TIMESTAMP(%1$s.`change_created`) AS `change_created`';
|
||||
|
||||
public function getId(): int {
|
||||
return $this->change_id < 1 ? -1 : $this->change_id;
|
||||
}
|
||||
|
||||
public function getUserId(): int {
|
||||
return $this->user_id < 1 ? -1 : $this->user_id;
|
||||
}
|
||||
public function setUserId(?int $userId): self {
|
||||
$this->user_id = $userId;
|
||||
$this->userLookedUp = false;
|
||||
$this->user = null;
|
||||
return $this;
|
||||
}
|
||||
public function getUser(): ?User {
|
||||
if(!$this->userLookedUp && ($userId = $this->getUserId()) > 0) {
|
||||
$this->userLookedUp = true;
|
||||
try {
|
||||
$this->user = User::byId($userId);
|
||||
} catch(UserNotFoundException $ex) {}
|
||||
}
|
||||
return $this->user;
|
||||
}
|
||||
public function setUser(?User $user): self {
|
||||
$this->user_id = $user === null ? null : $user->getId();
|
||||
$this->userLookedUp = true;
|
||||
$this->user = $user;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAction(): int {
|
||||
return $this->change_action ?? self::ACTION_UNKNOWN;
|
||||
}
|
||||
public function setAction(int $actionId): self {
|
||||
$this->change_action = $actionId;
|
||||
return $this;
|
||||
}
|
||||
private function getActionInfo(): array {
|
||||
return self::ACTION_STRINGS[$this->getAction()] ?? self::ACTION_STRINGS[self::ACTION_UNKNOWN];
|
||||
}
|
||||
public function getActionClass(): string {
|
||||
return $this->getActionInfo()[0];
|
||||
}
|
||||
public function getActionString(): string {
|
||||
return $this->getActionInfo()[1];
|
||||
}
|
||||
|
||||
public function getCreatedTime(): int {
|
||||
return $this->change_created ?? -1;
|
||||
}
|
||||
public function getDate(): string {
|
||||
return ($time = $this->getCreatedTime()) < 0 ? self::DEFAULT_DATE : gmdate('Y-m-d', $time);
|
||||
}
|
||||
|
||||
public function getHeader(): string {
|
||||
return $this->change_log;
|
||||
}
|
||||
public function setHeader(string $header): self {
|
||||
$this->change_log = $header;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBody(): string {
|
||||
return $this->change_text ?? '';
|
||||
}
|
||||
public function setBody(string $body): self {
|
||||
$this->change_text = $body;
|
||||
return $this;
|
||||
}
|
||||
public function hasBody(): bool {
|
||||
return !empty($this->change_text);
|
||||
}
|
||||
public function getParsedBody(): string {
|
||||
return Parser::instance(Parser::MARKDOWN)->parseText($this->getBody());
|
||||
}
|
||||
|
||||
public function getCommentsCategoryName(): ?string {
|
||||
return ($date = $this->getDate()) === self::DEFAULT_DATE ? null : sprintf('changelog-date-%s', $this->getDate());
|
||||
}
|
||||
public function hasCommentsCategory(): bool {
|
||||
return $this->getCreatedTime() >= 0;
|
||||
}
|
||||
public function getCommentsCategory(): CommentsCategory {
|
||||
if($this->comments === null) {
|
||||
$categoryName = $this->getCommentsCategoryName();
|
||||
|
||||
if(empty($categoryName))
|
||||
throw new UnexpectedValueException('Change comments category name is empty.');
|
||||
|
||||
try {
|
||||
$this->comments = CommentsCategory::byName($categoryName);
|
||||
} catch(CommentsCategoryNotFoundException $ex) {
|
||||
$this->comments = new CommentsCategory($categoryName);
|
||||
$this->comments->save();
|
||||
}
|
||||
}
|
||||
return $this->comments;
|
||||
}
|
||||
|
||||
public function getTags(): array {
|
||||
if($this->tags === null)
|
||||
$this->tags = ChangelogTag::byChange($this);
|
||||
return $this->tags;
|
||||
}
|
||||
public function getTagRelations(): array {
|
||||
if($this->tagRelations === null)
|
||||
$this->tagRelations = ChangelogChangeTag::byChange($this);
|
||||
return $this->tagRelations;
|
||||
}
|
||||
public function setTags(array $tags): self {
|
||||
ChangelogChangeTag::purgeChange($this);
|
||||
foreach($tags as $tag)
|
||||
if($tag instanceof ChangelogTag)
|
||||
ChangelogChangeTag::create($this, $tag);
|
||||
$this->tags = $tags;
|
||||
$this->tagRelations = null;
|
||||
return $this;
|
||||
}
|
||||
public function hasTag(ChangelogTag $other): bool {
|
||||
foreach($this->getTags() as $tag)
|
||||
if($tag->compare($other))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public function jsonSerialize() {
|
||||
return [
|
||||
'id' => $this->getId(),
|
||||
'user' => $this->getUserId(),
|
||||
'action' => $this->getActionId(),
|
||||
'header' => $this->getHeader(),
|
||||
'body' => $this->getBody(),
|
||||
'comments' => $this->getCommentsCategoryName(),
|
||||
'created' => ($time = $this->getCreatedTime()) < 0 ? null : date('c', $time),
|
||||
];
|
||||
}
|
||||
|
||||
public function save(): void {
|
||||
$isInsert = $this->getId() < 1;
|
||||
if($isInsert) {
|
||||
$query = 'INSERT INTO `%1$s%2$s` (`user_id`, `change_action`, `change_log`, `change_text`)'
|
||||
. ' VALUES (:user, :action, :header, :body)';
|
||||
} else {
|
||||
$query = 'UPDATE `%1$s%2$s` SET `user_id` = :user, `change_action` = :action, `change_log` = :header, `change_text` = :body'
|
||||
. ' WHERE `change_id` = :change';
|
||||
}
|
||||
|
||||
$saveChange = DB::prepare(sprintf($query, DB::PREFIX, self::TABLE))
|
||||
->bind('user', $this->user_id)
|
||||
->bind('action', $this->change_action)
|
||||
->bind('header', $this->change_log)
|
||||
->bind('body', $this->change_text);
|
||||
|
||||
if($isInsert) {
|
||||
$this->change_id = $saveChange->executeGetId();
|
||||
$this->change_created = time();
|
||||
} else {
|
||||
$saveChange->bind('change', $this->getId())
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
|
||||
private static function countQueryBase(): string {
|
||||
return sprintf(self::QUERY_SELECT, sprintf('COUNT(%s.`change_id`)', self::TABLE));
|
||||
}
|
||||
public static function countAll(?int $date = null, ?User $user = null): int {
|
||||
$countChanges = DB::prepare(
|
||||
self::countQueryBase()
|
||||
. ' WHERE 1' // this is still disgusting
|
||||
. ($date === null ? '' : ' AND DATE(`change_created`) = :date')
|
||||
. ($user === null ? '' : ' AND `user_id` = :user')
|
||||
);
|
||||
if($date !== null)
|
||||
$countChanges->bind('date', gmdate('Y-m-d', $date));
|
||||
if($user !== null)
|
||||
$countChanges->bind('user', $user->getId());
|
||||
return (int)$countChanges->fetchColumn();
|
||||
}
|
||||
|
||||
private static function memoizer(): Memoizer {
|
||||
static $memoizer = null;
|
||||
if($memoizer === null)
|
||||
$memoizer = new Memoizer;
|
||||
return $memoizer;
|
||||
}
|
||||
|
||||
private static function byQueryBase(): string {
|
||||
return sprintf(self::QUERY_SELECT, sprintf(self::SELECT, self::TABLE));
|
||||
}
|
||||
public static function byId(int $changeId): self {
|
||||
return self::memoizer()->find($changeId, function() use ($changeId) {
|
||||
$change = DB::prepare(self::byQueryBase() . ' WHERE `change_id` = :change')
|
||||
->bind('change', $changeId)
|
||||
->fetchObject(self::class);
|
||||
if(!$change)
|
||||
throw new ChangelogChangeNotFoundException;
|
||||
return $change;
|
||||
});
|
||||
}
|
||||
public static function all(?Pagination $pagination = null, ?int $date = null, ?User $user = null): array {
|
||||
$changeQuery = self::byQueryBase()
|
||||
. ' WHERE 1' // this is still disgusting
|
||||
. ($date === null ? '' : ' AND DATE(`change_created`) = :date')
|
||||
. ($user === null ? '' : ' AND `user_id` = :user')
|
||||
. ' GROUP BY `change_created`, `change_id`'
|
||||
. ' ORDER BY `change_created` DESC, `change_id` DESC';
|
||||
|
||||
if($pagination !== null)
|
||||
$changeQuery .= ' LIMIT :range OFFSET :offset';
|
||||
|
||||
$getChanges = DB::prepare($changeQuery);
|
||||
|
||||
if($date !== null)
|
||||
$getChanges->bind('date', gmdate('Y-m-d', $date));
|
||||
|
||||
if($user !== null)
|
||||
$getChanges->bind('user', $user->getId());
|
||||
|
||||
if($pagination !== null)
|
||||
$getChanges->bind('range', $pagination->getRange())
|
||||
->bind('offset', $pagination->getOffset());
|
||||
|
||||
return $getChanges->fetchObjects(self::class);
|
||||
}
|
||||
}
|
96
src/Changelog/ChangelogChangeTag.php
Normal file
96
src/Changelog/ChangelogChangeTag.php
Normal file
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
namespace Misuzu\Changelog;
|
||||
|
||||
use JsonSerializable;
|
||||
use Misuzu\DB;
|
||||
|
||||
class ChangelogChangeTagException extends ChangelogException {}
|
||||
class ChangelogChangeTagNotFoundException extends ChangelogChangeTagException {}
|
||||
class ChangelogChangeCreationFailedException extends ChangelogChangeTagException {}
|
||||
|
||||
class ChangelogChangeTag implements JsonSerializable {
|
||||
// Database fields
|
||||
private $change_id = -1;
|
||||
private $tag_id = -1;
|
||||
|
||||
private $change = null;
|
||||
private $tag = null;
|
||||
|
||||
public const TABLE = 'changelog_change_tags';
|
||||
private const QUERY_SELECT = 'SELECT %1$s FROM `' . DB::PREFIX . self::TABLE . '` AS '. self::TABLE;
|
||||
private const SELECT = '%1$s.`change_id`, %1$s.`tag_id`';
|
||||
|
||||
public function getChangeId(): int {
|
||||
return $this->change_id < 1 ? -1 : $this->change_id;
|
||||
}
|
||||
public function getChange(): ChangelogChange {
|
||||
if($this->change === null)
|
||||
$this->change = ChangelogChange::byId($this->getChangeId());
|
||||
return $this->change;
|
||||
}
|
||||
|
||||
public function getTagId(): int {
|
||||
return $this->tag_id < 1 ? -1 : $this->tag_id;
|
||||
}
|
||||
public function getTag(): ChangelogTag {
|
||||
if($this->tag === null)
|
||||
$this->tag = ChangelogTag::byId($this->getTagId());
|
||||
return $this->tag;
|
||||
}
|
||||
|
||||
public function jsonSerialize() {
|
||||
return [
|
||||
'change' => $this->getChangeId(),
|
||||
'tag' => $this->getTagId(),
|
||||
];
|
||||
}
|
||||
|
||||
public static function create(ChangelogChange $change, ChangelogTag $tag, bool $return = false): ?self {
|
||||
$createRelation = DB::prepare(
|
||||
'REPLACE INTO `' . DB::PREFIX . self::TABLE . '` (`change_id`, `tag_id`)'
|
||||
. ' VALUES (:change, :tag)'
|
||||
)->bind('change', $change->getId())->bind('tag', $tag->getId());
|
||||
|
||||
if(!$createRelation->execute())
|
||||
throw new ChangelogChangeCreationFailedException;
|
||||
if(!$return)
|
||||
return null;
|
||||
|
||||
return self::byExact($change, $tag);
|
||||
}
|
||||
|
||||
public static function purgeChange(ChangelogChange $change): void {
|
||||
DB::prepare(
|
||||
'DELETE FROM `' . DB::PREFIX . self::TABLE . '` WHERE `change_id` = :change'
|
||||
)->bind('change', $change->getId())->execute();
|
||||
}
|
||||
|
||||
private static function countQueryBase(string $column): string {
|
||||
return sprintf(self::QUERY_SELECT, sprintf('COUNT(%s.`%s`)', self::TABLE, $column));
|
||||
}
|
||||
public static function countByTag(ChangelogTag $tag): int {
|
||||
return (int)DB::prepare(
|
||||
self::countQueryBase('change_id')
|
||||
. ' WHERE `tag_id` = :tag'
|
||||
)->bind('tag', $tag->getId())->fetchColumn();
|
||||
}
|
||||
|
||||
private static function byQueryBase(): string {
|
||||
return sprintf(self::QUERY_SELECT, sprintf(self::SELECT, self::TABLE));
|
||||
}
|
||||
public static function byExact(ChangelogChange $change, ChangelogTag $tag): array {
|
||||
$tag = DB::prepare(self::byQueryBase() . ' WHERE `tag_id` = :tag')
|
||||
->bind('change', $change->getId())
|
||||
->bind('tag', $tag->getId())
|
||||
->fetchObject(self::class);
|
||||
if(!$tag)
|
||||
throw new ChangelogChangeTagNotFoundException;
|
||||
return $tag;
|
||||
}
|
||||
public static function byChange(ChangelogChange $change): array {
|
||||
return DB::prepare(
|
||||
self::byQueryBase()
|
||||
. ' WHERE `change_id` = :change'
|
||||
)->bind('change', $change->getId())->fetchObjects(self::class);
|
||||
}
|
||||
}
|
6
src/Changelog/ChangelogException.php
Normal file
6
src/Changelog/ChangelogException.php
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?php
|
||||
namespace Misuzu\Changelog;
|
||||
|
||||
use Exception;
|
||||
|
||||
class ChangelogException extends Exception {}
|
140
src/Changelog/ChangelogTag.php
Normal file
140
src/Changelog/ChangelogTag.php
Normal file
|
@ -0,0 +1,140 @@
|
|||
<?php
|
||||
namespace Misuzu\Changelog;
|
||||
|
||||
use JsonSerializable;
|
||||
use Misuzu\DB;
|
||||
use Misuzu\Memoizer;
|
||||
|
||||
class ChangelogTagException extends ChangelogException {}
|
||||
class ChangelogTagNotFoundException extends ChangelogTagException {}
|
||||
|
||||
class ChangelogTag implements JsonSerializable {
|
||||
// Database fields
|
||||
private $tag_id = -1;
|
||||
private $tag_name = '';
|
||||
private $tag_description = '';
|
||||
private $tag_created = null;
|
||||
private $tag_archived = null;
|
||||
|
||||
private $changeCount = -1;
|
||||
|
||||
public const TABLE = 'changelog_tags';
|
||||
private const QUERY_SELECT = 'SELECT %1$s FROM `' . DB::PREFIX . self::TABLE . '` AS '. self::TABLE;
|
||||
private const SELECT = '%1$s.`tag_id`, %1$s.`tag_name`, %1$s.`tag_description`'
|
||||
. ', UNIX_TIMESTAMP(%1$s.`tag_created`) AS `tag_created`'
|
||||
. ', UNIX_TIMESTAMP(%1$s.`tag_archived`) AS `tag_archived`';
|
||||
|
||||
public function getId(): int {
|
||||
return $this->tag_id < 1 ? -1 : $this->tag_id;
|
||||
}
|
||||
|
||||
public function getName(): string {
|
||||
return $this->tag_name;
|
||||
}
|
||||
public function setName(string $name): self {
|
||||
$this->tag_name = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDescription(): string {
|
||||
return $this->tag_description;
|
||||
}
|
||||
public function hasDescription(): bool {
|
||||
return !empty($this->tag_description);
|
||||
}
|
||||
public function setDescription(string $description): self {
|
||||
$this->tag_description = $description;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCreatedTime(): int {
|
||||
return $this->tag_created ?? -1;
|
||||
}
|
||||
|
||||
public function getArchivedTime(): int {
|
||||
return $this->tag_archived ?? -1;
|
||||
}
|
||||
public function isArchived(): bool {
|
||||
return $this->getArchivedTime() >= 0;
|
||||
}
|
||||
public function setArchived(bool $archived): self {
|
||||
if($this->isArchived() !== $archived)
|
||||
$this->tag_archived = $archived ? time() : null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getChangeCount(): int {
|
||||
if($this->changeCount < 0)
|
||||
$this->changeCount = ChangelogChangeTag::countByTag($this);
|
||||
return $this->changeCount;
|
||||
}
|
||||
|
||||
public function jsonSerialize() {
|
||||
return [
|
||||
'id' => $this->getId(),
|
||||
'name' => $this->getName(),
|
||||
'description' => $this->getDescription(),
|
||||
'created' => ($time = $this->getCreatedTime()) < 0 ? null : date('c', $time),
|
||||
'archived' => ($time = $this->getArchivedTime()) < 0 ? null : date('c', $time),
|
||||
];
|
||||
}
|
||||
|
||||
public function compare(ChangelogTag $other): bool {
|
||||
return $other === $this || $other->getId() === $this->getId();
|
||||
}
|
||||
|
||||
public function save(): void {
|
||||
$isInsert = $this->getId() < 1;
|
||||
if($isInsert) {
|
||||
$query = 'INSERT INTO `%1$s%2$s` (`tag_name`, `tag_description`, `tag_archived`)'
|
||||
. ' VALUES (:name, :description, FROM_UNIXTIME(:archived))';
|
||||
} else {
|
||||
$query = 'UPDATE `%1$s%2$s` SET `tag_name` = :name, `tag_description` = :description, `tag_archived` = FROM_UNIXTIME(:archived)'
|
||||
. ' WHERE `tag_id` = :tag';
|
||||
}
|
||||
|
||||
$saveTag = DB::prepare(sprintf($query, DB::PREFIX, self::TABLE))
|
||||
->bind('name', $this->tag_name)
|
||||
->bind('description', $this->tag_description)
|
||||
->bind('archived', $this->tag_archived);
|
||||
|
||||
if($isInsert) {
|
||||
$this->tag_id = $saveTag->executeGetId();
|
||||
$this->tag_created = time();
|
||||
} else {
|
||||
$saveTag->bind('tag', $this->getId())
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
|
||||
private static function memoizer(): Memoizer {
|
||||
static $memoizer = null;
|
||||
if($memoizer === null)
|
||||
$memoizer = new Memoizer;
|
||||
return $memoizer;
|
||||
}
|
||||
|
||||
private static function byQueryBase(): string {
|
||||
return sprintf(self::QUERY_SELECT, sprintf(self::SELECT, self::TABLE));
|
||||
}
|
||||
public static function byId(int $tagId): self {
|
||||
return self::memoizer()->find($tagId, function() use ($tagId) {
|
||||
$tag = DB::prepare(self::byQueryBase() . ' WHERE `tag_id` = :tag')
|
||||
->bind('tag', $tagId)
|
||||
->fetchObject(self::class);
|
||||
if(!$tag)
|
||||
throw new ChangelogTagNotFoundException;
|
||||
return $tag;
|
||||
});
|
||||
}
|
||||
public static function byChange(ChangelogChange $change): array {
|
||||
return DB::prepare(
|
||||
self::byQueryBase()
|
||||
. ' WHERE `tag_id` IN (SELECT `tag_id` FROM `' . DB::PREFIX . ChangelogChangeTag::TABLE . '` WHERE `change_id` = :change)'
|
||||
)->bind('change', $change->getId())->fetchObjects(self::class);
|
||||
}
|
||||
public static function all(): array {
|
||||
return DB::prepare(self::byQueryBase())
|
||||
->fetchObjects(self::class);
|
||||
}
|
||||
}
|
|
@ -110,7 +110,7 @@ class CommentsCategory implements JsonSerializable {
|
|||
return CommentsVote::byCategory($this, $user, $rootOnly, $pagination);
|
||||
}
|
||||
|
||||
private static function getMemoizer() {
|
||||
private static function memoizer() {
|
||||
static $memoizer = null;
|
||||
if($memoizer === null)
|
||||
$memoizer = new Memoizer;
|
||||
|
@ -121,7 +121,7 @@ class CommentsCategory implements JsonSerializable {
|
|||
return sprintf(self::QUERY_SELECT, sprintf(self::SELECT, self::TABLE));
|
||||
}
|
||||
public static function byId(int $categoryId): self {
|
||||
return self::getMemoizer()->find($categoryId, function() use ($categoryId) {
|
||||
return self::memoizer()->find($categoryId, function() use ($categoryId) {
|
||||
$cat = DB::prepare(self::byQueryBase() . ' WHERE `category_id` = :cat_id')
|
||||
->bind('cat_id', $categoryId)
|
||||
->fetchObject(self::class);
|
||||
|
@ -131,7 +131,7 @@ class CommentsCategory implements JsonSerializable {
|
|||
});
|
||||
}
|
||||
public static function byName(string $categoryName): self {
|
||||
return self::getMemoizer()->find(function($category) use ($categoryName) {
|
||||
return self::memoizer()->find(function($category) use ($categoryName) {
|
||||
return $category->getName() === $categoryName;
|
||||
}, function() use ($categoryName) {
|
||||
$cat = DB::prepare(self::byQueryBase() . ' WHERE `category_name` = :name')
|
||||
|
|
|
@ -73,6 +73,7 @@ class CommentsPost implements JsonSerializable {
|
|||
}
|
||||
public function setUserId(int $userId): self {
|
||||
$this->user_id = $userId < 1 ? null : $userId;
|
||||
$this->userLookedUp = false;
|
||||
$this->user = null;
|
||||
return $this;
|
||||
}
|
||||
|
@ -87,6 +88,7 @@ class CommentsPost implements JsonSerializable {
|
|||
}
|
||||
public function setUser(?User $user): self {
|
||||
$this->user_id = $user === null ? null : $user->getId();
|
||||
$this->userLookedUp = true;
|
||||
$this->user = $user;
|
||||
return $this;
|
||||
}
|
||||
|
|
|
@ -106,7 +106,7 @@ class CommentsVote implements JsonSerializable {
|
|||
if(!$return)
|
||||
return null;
|
||||
|
||||
return CommentsVote::byExact($post, $user);
|
||||
return self::byExact($post, $user);
|
||||
}
|
||||
|
||||
public static function delete(CommentsPost $post, User $user): void {
|
||||
|
|
131
src/Http/Handlers/ChangelogHandler.php
Normal file
131
src/Http/Handlers/ChangelogHandler.php
Normal file
|
@ -0,0 +1,131 @@
|
|||
<?php
|
||||
namespace Misuzu\Http\Handlers;
|
||||
|
||||
use ErrorException;
|
||||
use HttpResponse;
|
||||
use HttpRequest;
|
||||
use Misuzu\Config;
|
||||
use Misuzu\Pagination;
|
||||
use Misuzu\Changelog\ChangelogChange;
|
||||
use Misuzu\Changelog\ChangelogChangeNotFoundException;
|
||||
use Misuzu\Changelog\ChangelogTag;
|
||||
use Misuzu\Changelog\ChangelogTagNotFoundException;
|
||||
use Misuzu\Feeds\Feed;
|
||||
use Misuzu\Feeds\FeedItem;
|
||||
use Misuzu\Feeds\AtomFeedSerializer;
|
||||
use Misuzu\Feeds\RssFeedSerializer;
|
||||
use Misuzu\Users\User;
|
||||
use Misuzu\Users\UserNotFoundException;
|
||||
|
||||
class ChangelogHandler extends Handler {
|
||||
public function index(HttpResponse $response, HttpRequest $request) {
|
||||
$filterDate = $request->getQueryParam('date', FILTER_SANITIZE_STRING);
|
||||
$filterUser = $request->getQueryParam('user', FILTER_SANITIZE_NUMBER_INT);
|
||||
//$filterTags = $request->getQueryParam('tags');
|
||||
|
||||
if($filterDate !== null)
|
||||
try {
|
||||
$dateParts = explode('-', $filterDate, 3);
|
||||
$filterDate = gmmktime(12, 0, 0, $dateParts[1], $dateParts[2], $dateParts[0]);
|
||||
} catch(ErrorException $ex) {
|
||||
return 404;
|
||||
}
|
||||
|
||||
if($filterUser !== null)
|
||||
try {
|
||||
$filterUser = User::byId($filterUser);
|
||||
} catch(UserNotFoundException $ex) {
|
||||
return 404;
|
||||
}
|
||||
|
||||
/*if($filterTags !== null) {
|
||||
$splitTags = explode(',', $filterTags);
|
||||
$filterTags = [];
|
||||
for($i = 0; $i < min(10, count($splitTags)); ++$i)
|
||||
try {
|
||||
$filterTags[] = ChangelogTag::byId($splitTags[$i]);
|
||||
} catch(ChangelogTagNotFoundException $ex) {
|
||||
return 404;
|
||||
}
|
||||
}*/
|
||||
|
||||
$count = $filterDate !== null ? -1 : ChangelogChange::countAll($filterDate, $filterUser);
|
||||
$pagination = new Pagination($count, 30);
|
||||
if(!$pagination->hasValidOffset())
|
||||
return 404;
|
||||
|
||||
$changes = ChangelogChange::all($pagination, $filterDate, $filterUser);
|
||||
if(empty($changes))
|
||||
return 404;
|
||||
|
||||
$response->setTemplate('changelog.index', [
|
||||
'changelog_infos' => $changes,
|
||||
'changelog_date' => $filterDate,
|
||||
'changelog_user' => $filterUser,
|
||||
'changelog_pagination' => $pagination,
|
||||
'comments_user' => User::getCurrent(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function change(HttpResponse $response, HttpRequest $request, int $changeId) {
|
||||
try {
|
||||
$changeInfo = ChangelogChange::byId($changeId);
|
||||
} catch(ChangelogChangeNotFoundException $ex) {
|
||||
return 404;
|
||||
}
|
||||
|
||||
$response->setTemplate('changelog.change', [
|
||||
'change_info' => $changeInfo,
|
||||
'comments_user' => User::getCurrent(),
|
||||
]);
|
||||
}
|
||||
|
||||
private function createFeed(string $feedMode): Feed {
|
||||
$changes = ChangelogChange::all(new Pagination(10));
|
||||
|
||||
$feed = (new Feed)
|
||||
->setTitle(Config::get('site.name', Config::TYPE_STR, 'Misuzu') . ' » Changelog')
|
||||
->setDescription('Live feed of changes to ' . Config::get('site.name', Config::TYPE_STR, 'Misuzu') . '.')
|
||||
->setContentUrl(url_prefix(false) . url('changelog-index'))
|
||||
->setFeedUrl(url_prefix(false) . url("changelog-feed-{$feedMode}"));
|
||||
|
||||
foreach($changes as $change) {
|
||||
$changeUrl = url_prefix(false) . url('changelog-change', ['change' => $change->getId()]);
|
||||
$commentsUrl = url_prefix(false) . url('changelog-change-comments', ['change' => $change->getId()]);
|
||||
|
||||
$feedItem = (new FeedItem)
|
||||
->setTitle($change->getActionString() . ': ' . $change->getHeader())
|
||||
->setCreationDate($change->getCreatedTime())
|
||||
->setUniqueId($changeUrl)
|
||||
->setContentUrl($changeUrl)
|
||||
->setCommentsUrl($commentsUrl);
|
||||
|
||||
$feed->addItem($feedItem);
|
||||
}
|
||||
|
||||
return $feed;
|
||||
}
|
||||
|
||||
public function feedAtom(HttpResponse $response, HttpRequest $request) {
|
||||
$response->setContentType('application/atom+xml; charset=utf-8');
|
||||
return (new AtomFeedSerializer)->serializeFeed(self::createFeed('atom'));
|
||||
}
|
||||
|
||||
public function feedRss(HttpResponse $response, HttpRequest $request) {
|
||||
$response->setContentType('application/rss+xml; charset=utf-8');
|
||||
return (new RssFeedSerializer)->serializeFeed(self::createFeed('rss'));
|
||||
}
|
||||
|
||||
public function legacy(HttpResponse $response, HttpRequest $request) {
|
||||
$changeId = $request->getQueryParam('c', FILTER_SANITIZE_NUMBER_INT);
|
||||
if($changeId) {
|
||||
$response->redirect(url('changelog-change', ['change' => $changeId]), true);
|
||||
return;
|
||||
}
|
||||
|
||||
$response->redirect(url('changelog-index', [
|
||||
'date' => $request->getQueryParam('d', FILTER_SANITIZE_STRING),
|
||||
'user' => $request->getQueryParam('u', FILTER_SANITIZE_NUMBER_INT),
|
||||
]), true);
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ use HttpRequest;
|
|||
use Misuzu\Config;
|
||||
use Misuzu\DB;
|
||||
use Misuzu\Pagination;
|
||||
use Misuzu\Changelog\ChangelogChange;
|
||||
use Misuzu\News\NewsPost;
|
||||
|
||||
final class HomeHandler extends Handler {
|
||||
|
@ -55,15 +56,7 @@ final class HomeHandler extends Handler {
|
|||
) AS `count_forum_posts`
|
||||
')->fetch();
|
||||
|
||||
$changelog = DB::query('
|
||||
SELECT
|
||||
`change_id`, `change_log`, `change_action`,
|
||||
DATE(`change_created`) AS `change_date`,
|
||||
!ISNULL(`change_text`) AS `change_has_text`
|
||||
FROM `msz_changelog_changes`
|
||||
ORDER BY `change_created` DESC
|
||||
LIMIT 10
|
||||
')->fetchAll();
|
||||
$changelog = ChangelogChange::all(new Pagination(10));
|
||||
|
||||
$birthdays = user_session_active() ? user_get_birthdays() : [];
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@ use Misuzu\News\NewsCategoryNotFoundException;
|
|||
use Misuzu\News\NewsPostNotException;
|
||||
use Misuzu\Parsers\Parser;
|
||||
use Misuzu\Users\User;
|
||||
use Misuzu\Users\UserNotFoundException;
|
||||
|
||||
final class NewsHandler extends Handler {
|
||||
public function index(HttpResponse $response, HttpRequest $request) {
|
||||
|
@ -61,18 +60,13 @@ final class NewsHandler extends Handler {
|
|||
if(!$postInfo->isPublished() || $postInfo->isDeleted())
|
||||
return 404;
|
||||
|
||||
$postInfo->ensureCommentsSection();
|
||||
$commentsInfo = $postInfo->getCommentSection();
|
||||
try {
|
||||
$commentsUser = User::byId(user_session_current('user_id', 0));
|
||||
} catch(UserNotFoundException $ex) {
|
||||
$commentsUser = null;
|
||||
}
|
||||
$postInfo->ensureCommentsCategory();
|
||||
$commentsInfo = $postInfo->getCommentsCategory();
|
||||
|
||||
$response->setTemplate('news.post', [
|
||||
'post_info' => $postInfo,
|
||||
'comments_info' => $commentsInfo,
|
||||
'comments_user' => $commentsUser,
|
||||
'comments_user' => User::getCurrent(),
|
||||
]);
|
||||
|
||||
}
|
||||
|
@ -95,9 +89,9 @@ final class NewsHandler extends Handler {
|
|||
|
||||
$feedItem = (new FeedItem)
|
||||
->setTitle($post->getTitle())
|
||||
->setSummary(first_paragraph($post->getText()))
|
||||
->setSummary($post->getFirstParagraph())
|
||||
->setContent(Parser::instance(Parser::MARKDOWN)->parseText($post->getText()))
|
||||
->setCreationDate(strtotime($post->getCreatedTime()))
|
||||
->setCreationDate($post->getCreatedTime())
|
||||
->setUniqueId($postUrl)
|
||||
->setContentUrl($postUrl)
|
||||
->setCommentsUrl($commentsUrl)
|
||||
|
|
|
@ -2,13 +2,14 @@
|
|||
namespace Misuzu\News;
|
||||
|
||||
use ArrayAccess;
|
||||
use JsonSerializable;
|
||||
use Misuzu\DB;
|
||||
use Misuzu\Pagination;
|
||||
|
||||
class NewsCategoryException extends NewsException {};
|
||||
class NewsCategoryNotFoundException extends NewsCategoryException {};
|
||||
|
||||
class NewsCategory implements ArrayAccess {
|
||||
class NewsCategory implements ArrayAccess, JsonSerializable {
|
||||
// Database fields
|
||||
private $category_id = -1;
|
||||
private $category_name = '';
|
||||
|
@ -57,6 +58,16 @@ class NewsCategory implements ArrayAccess {
|
|||
return $this->category_created === null ? -1 : $this->category_created;
|
||||
}
|
||||
|
||||
public function jsonSerialize() {
|
||||
return [
|
||||
'id' => $this->getId(),
|
||||
'name' => $this->getName(),
|
||||
'description' => $this->getDescription(),
|
||||
'is_hidden' => $this->isHidden(),
|
||||
'created' => ($time = $this->getCreatedTime()) < 0 ? null : date('c', $time),
|
||||
];
|
||||
}
|
||||
|
||||
// Purely cosmetic, use ::countAll for pagination
|
||||
public function getPostCount(): int {
|
||||
if($this->postCount < 0)
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
<?php
|
||||
namespace Misuzu\News;
|
||||
|
||||
use JsonSerializable;
|
||||
use Misuzu\DB;
|
||||
use Misuzu\Pagination;
|
||||
use Misuzu\Comments\CommentsCategory;
|
||||
use Misuzu\Comments\CommentsCategoryNotFoundException;
|
||||
use Misuzu\Parsers\Parser;
|
||||
use Misuzu\Users\User;
|
||||
use Misuzu\Users\UserNotFoundException;
|
||||
|
||||
class NewsPostException extends NewsException {};
|
||||
class NewsPostNotFoundException extends NewsPostException {};
|
||||
|
||||
class NewsPost {
|
||||
class NewsPost implements JsonSerializable {
|
||||
// Database fields
|
||||
private $post_id = -1;
|
||||
private $category_id = -1;
|
||||
|
@ -69,6 +71,7 @@ class NewsPost {
|
|||
}
|
||||
public function setUserId(int $userId): self {
|
||||
$this->user_id = $userId < 1 ? null : $userId;
|
||||
$this->userLookedUp = false;
|
||||
$this->user = null;
|
||||
return $this;
|
||||
}
|
||||
|
@ -83,19 +86,20 @@ class NewsPost {
|
|||
}
|
||||
public function setUser(?User $user): self {
|
||||
$this->user_id = $user === null ? null : $user->getId();
|
||||
$this->userLookedUp = true;
|
||||
$this->user = $user;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCommentSectionId(): int {
|
||||
public function getCommentsCategoryId(): int {
|
||||
return $this->comment_section_id < 1 ? -1 : $this->comment_section_id;
|
||||
}
|
||||
public function hasCommentSection(): bool {
|
||||
return $this->getCommentSectionId() > 0;
|
||||
public function hasCommentsCategory(): bool {
|
||||
return $this->getCommentsCategoryId() > 0;
|
||||
}
|
||||
public function getCommentSection(): CommentsCategory {
|
||||
public function getCommentsCategory(): CommentsCategory {
|
||||
if($this->comments === null)
|
||||
$this->comments = CommentsCategory::byId($this->getCommentSectionId());
|
||||
$this->comments = CommentsCategory::byId($this->getCommentsCategoryId());
|
||||
return $this->comments;
|
||||
}
|
||||
|
||||
|
@ -122,6 +126,15 @@ class NewsPost {
|
|||
$this->post_text = $text;
|
||||
return $this;
|
||||
}
|
||||
public function getParsedText(): string {
|
||||
return Parser::instance(Parser::MARKDOWN)->parseText($this->getText());
|
||||
}
|
||||
public function getFirstParagraph(): string {
|
||||
return first_paragraph($this->getText());
|
||||
}
|
||||
public function getParsedFirstParagraph(): string {
|
||||
return Parser::instance(Parser::MARKDOWN)->parseText($this->getFirstParagraph());
|
||||
}
|
||||
|
||||
public function getScheduledTime(): int {
|
||||
return $this->post_scheduled === null ? -1 : $this->post_scheduled;
|
||||
|
@ -158,18 +171,33 @@ class NewsPost {
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function ensureCommentsSection(): void {
|
||||
if($this->hasCommentSection())
|
||||
public function jsonSerialize() {
|
||||
return [
|
||||
'id' => $this->getId(),
|
||||
'category' => $this->getCategoryId(),
|
||||
'user' => $this->getUserId(),
|
||||
'comments' => $this->getCommentsCategoryId(),
|
||||
'is_featured' => $this->isFeatured(),
|
||||
'title' => $this->getTitle(),
|
||||
'text' => $this->getText(),
|
||||
'scheduled' => ($time = $this->getScheduledTime()) < 0 ? null : date('c', $time),
|
||||
'created' => ($time = $this->getCreatedTime()) < 0 ? null : date('c', $time),
|
||||
'updated' => ($time = $this->getUpdatedTime()) < 0 ? null : date('c', $time),
|
||||
'deleted' => ($time = $this->getDeletedTime()) < 0 ? null : date('c', $time),
|
||||
];
|
||||
}
|
||||
|
||||
public function ensureCommentsCategory(): void {
|
||||
if($this->hasCommentsCategory())
|
||||
return;
|
||||
|
||||
$this->comments = (new CommentsCategory)
|
||||
->setName("news-{$this->getId()}");
|
||||
$this->comments = new CommentsCategory("news-{$this->getId()}");
|
||||
$this->comments->save();
|
||||
|
||||
$this->comment_section_id = $this->comments->getId();
|
||||
DB::prepare('UPDATE `msz_news_posts` SET `comment_section_id` = :comment_section_id WHERE `post_id` = :post_id')
|
||||
->execute([
|
||||
'comment_section_id' => $this->getCommentSectionId(),
|
||||
'comment_section_id' => $this->getCommentsCategoryId(),
|
||||
'post_id' => $this->getId(),
|
||||
]);
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ final class TwigMisuzu extends Twig_Extension {
|
|||
return [
|
||||
new Twig_Filter('html_colour', 'html_colour'),
|
||||
new Twig_Filter('country_name', 'get_country_name'),
|
||||
new Twig_Filter('first_paragraph', 'first_paragraph'),
|
||||
new Twig_Filter('byte_symbol', 'byte_symbol'),
|
||||
new Twig_Filter('html_link', 'html_link'),
|
||||
// deprecate this call, convert to html in php
|
||||
|
@ -32,7 +31,6 @@ final class TwigMisuzu extends Twig_Extension {
|
|||
new Twig_Function('url_list', 'url_list'),
|
||||
new Twig_Function('html_tag', 'html_tag'),
|
||||
new Twig_Function('html_avatar', 'html_avatar'),
|
||||
new Twig_Function('changelog_action_name', 'changelog_action_name'),
|
||||
new Twig_Function('forum_may_have_children', 'forum_may_have_children'),
|
||||
new Twig_Function('forum_may_have_topics', 'forum_may_have_topics'),
|
||||
new Twig_Function('forum_has_priority_voting', 'forum_has_priority_voting'),
|
||||
|
|
|
@ -34,6 +34,8 @@ class User {
|
|||
public $user_background_settings = 0;
|
||||
public $user_title = null;
|
||||
|
||||
private static $localUser = null;
|
||||
|
||||
private const USER_SELECT = '
|
||||
SELECT u.`user_id`, u.`username`, u.`password`, u.`email`, u.`user_super`, u.`user_title`,
|
||||
u.`user_country`, u.`user_colour`, u.`display_role`, u.`user_totp_key`,
|
||||
|
@ -55,32 +57,6 @@ class User {
|
|||
//
|
||||
}
|
||||
|
||||
public static function create(
|
||||
string $username,
|
||||
string $password,
|
||||
string $email,
|
||||
string $ipAddress
|
||||
): ?User {
|
||||
$createUser = DB::prepare('
|
||||
INSERT INTO `msz_users` (
|
||||
`username`, `password`, `email`, `register_ip`,
|
||||
`last_ip`, `user_country`, `display_role`
|
||||
) VALUES (
|
||||
:username, :password, LOWER(:email), INET6_ATON(:register_ip),
|
||||
INET6_ATON(:last_ip), :user_country, 1
|
||||
)
|
||||
') ->bind('username', $username)->bind('email', $email)
|
||||
->bind('register_ip', $ipAddress)->bind('last_ip', $ipAddress)
|
||||
->bind('password', user_password_hash($password))
|
||||
->bind('user_country', IPAddress::country($ipAddress))
|
||||
->executeGetId();
|
||||
|
||||
if($createUser < 1)
|
||||
return null;
|
||||
|
||||
return static::byId($createUser);
|
||||
}
|
||||
|
||||
public function getId(): int {
|
||||
return $this->user_id < 1 ? -1 : $this->user_id;
|
||||
}
|
||||
|
@ -127,6 +103,10 @@ class User {
|
|||
return !empty($this->user_deleted);
|
||||
}
|
||||
|
||||
public function getDisplayRoleId(): int {
|
||||
return $this->display_role < 1 ? -1 : $this->display_role;
|
||||
}
|
||||
|
||||
public function hasTOTP(): bool {
|
||||
return !empty($this->user_totp_key);
|
||||
}
|
||||
|
@ -144,6 +124,10 @@ class User {
|
|||
return ($this->user_background_settings & MSZ_USER_BACKGROUND_ATTRIBUTE_SLIDE) > 0;
|
||||
}
|
||||
|
||||
public function getTitle(): string {
|
||||
return $this->user_title;
|
||||
}
|
||||
|
||||
public function profileFields(bool $filterEmpty = true): array {
|
||||
if(($userId = $this->getId()) < 1)
|
||||
return [];
|
||||
|
@ -165,6 +149,42 @@ class User {
|
|||
return $this->commentPermsArray;
|
||||
}
|
||||
|
||||
public function setCurrent(): void {
|
||||
self::$localUser = $this;
|
||||
}
|
||||
public static function unsetCurrent(): void {
|
||||
self::$localUser = null;
|
||||
}
|
||||
public static function getCurrent(): ?User {
|
||||
return self::$localUser;
|
||||
}
|
||||
|
||||
public static function create(
|
||||
string $username,
|
||||
string $password,
|
||||
string $email,
|
||||
string $ipAddress
|
||||
): ?User {
|
||||
$createUser = DB::prepare('
|
||||
INSERT INTO `msz_users` (
|
||||
`username`, `password`, `email`, `register_ip`,
|
||||
`last_ip`, `user_country`, `display_role`
|
||||
) VALUES (
|
||||
:username, :password, LOWER(:email), INET6_ATON(:register_ip),
|
||||
INET6_ATON(:last_ip), :user_country, 1
|
||||
)
|
||||
') ->bind('username', $username)->bind('email', $email)
|
||||
->bind('register_ip', $ipAddress)->bind('last_ip', $ipAddress)
|
||||
->bind('password', user_password_hash($password))
|
||||
->bind('user_country', IPAddress::country($ipAddress))
|
||||
->executeGetId();
|
||||
|
||||
if($createUser < 1)
|
||||
return null;
|
||||
|
||||
return static::byId($createUser);
|
||||
}
|
||||
|
||||
private static function getMemoizer() {
|
||||
static $memoizer = null;
|
||||
if($memoizer === null)
|
||||
|
@ -184,7 +204,7 @@ class User {
|
|||
}
|
||||
public static function findForLogin(string $usernameOrEmail): ?User {
|
||||
$usernameOrEmailLower = mb_strtolower($usernameOrEmail);
|
||||
return self::getMemoizer()->find(function() use ($usernameOrEmailLower) {
|
||||
return self::getMemoizer()->find(function($user) use ($usernameOrEmailLower) {
|
||||
return mb_strtolower($user->getUsername()) === $usernameOrEmailLower
|
||||
|| mb_strtolower($user->getEmailAddress()) === $usernameOrEmailLower;
|
||||
}, function() use ($usernameOrEmail) {
|
||||
|
@ -199,7 +219,7 @@ class User {
|
|||
}
|
||||
public static function findForProfile($userIdOrName): ?User {
|
||||
$userIdOrNameLower = mb_strtolower($userIdOrName);
|
||||
return self::getMemoizer()->find(function() use ($userIdOrNameLower) {
|
||||
return self::getMemoizer()->find(function($user) use ($userIdOrNameLower) {
|
||||
return $user->getId() == $userIdOrNameLower || mb_strtolower($user->getUsername()) === $userIdOrNameLower;
|
||||
}, function() use ($userIdOrName) {
|
||||
$user = DB::prepare(self::USER_SELECT . 'WHERE `user_id` = :user_id OR LOWER(`username`) = LOWER(:username)')
|
||||
|
|
|
@ -1,131 +0,0 @@
|
|||
<?php
|
||||
define('MSZ_CHANGELOG_ACTION_ADD', 1);
|
||||
define('MSZ_CHANGELOG_ACTION_REMOVE', 2);
|
||||
define('MSZ_CHANGELOG_ACTION_UPDATE', 3);
|
||||
define('MSZ_CHANGELOG_ACTION_FIX', 4);
|
||||
define('MSZ_CHANGELOG_ACTION_IMPORT', 5);
|
||||
define('MSZ_CHANGELOG_ACTION_REVERT', 6);
|
||||
define('MSZ_CHANGELOG_ACTIONS', [
|
||||
MSZ_CHANGELOG_ACTION_ADD => 'add',
|
||||
MSZ_CHANGELOG_ACTION_REMOVE => 'remove',
|
||||
MSZ_CHANGELOG_ACTION_UPDATE => 'update',
|
||||
MSZ_CHANGELOG_ACTION_FIX => 'fix',
|
||||
MSZ_CHANGELOG_ACTION_IMPORT => 'import',
|
||||
MSZ_CHANGELOG_ACTION_REVERT => 'revert',
|
||||
]);
|
||||
|
||||
function changelog_action_name(int $action): string {
|
||||
return changelog_action_is_valid($action) ? MSZ_CHANGELOG_ACTIONS[$action] : '';
|
||||
}
|
||||
|
||||
function changelog_action_is_valid(int $action): bool {
|
||||
return array_key_exists($action, MSZ_CHANGELOG_ACTIONS);
|
||||
}
|
||||
|
||||
define('MSZ_CHANGELOG_GET_QUERY', '
|
||||
SELECT
|
||||
c.`change_id`, c.`change_log`, c.`change_action`,
|
||||
u.`user_id`, u.`username`,
|
||||
DATE(`change_created`) AS `change_date`,
|
||||
!ISNULL(c.`change_text`) AS `change_has_text`,
|
||||
COALESCE(u.`user_colour`, r.`role_colour`) AS `user_colour`
|
||||
FROM `msz_changelog_changes` AS c
|
||||
LEFT JOIN `msz_users` AS u
|
||||
ON u.`user_id` = c.`user_id`
|
||||
LEFT JOIN `msz_roles` AS r
|
||||
ON r.`role_id` = u.`display_role`
|
||||
WHERE %s
|
||||
AND %s
|
||||
GROUP BY `change_created`, `change_id`
|
||||
ORDER BY `change_created` DESC, `change_id` DESC
|
||||
%s
|
||||
');
|
||||
|
||||
function changelog_get_changes(string $date, int $user, int $offset, int $take): array {
|
||||
$hasDate = mb_strlen($date) > 0;
|
||||
$hasUser = $user > 0;
|
||||
|
||||
$query = sprintf(
|
||||
MSZ_CHANGELOG_GET_QUERY,
|
||||
$hasDate ? 'DATE(c.`change_created`) = :date' : '1',
|
||||
$hasUser ? 'c.`user_id` = :user' : '1',
|
||||
!$hasDate ? 'LIMIT :offset, :take' : ''
|
||||
);
|
||||
|
||||
$prep = \Misuzu\DB::prepare($query);
|
||||
|
||||
if(!$hasDate) {
|
||||
$prep->bind('offset', $offset);
|
||||
$prep->bind('take', $take);
|
||||
} else {
|
||||
$prep->bind('date', $date);
|
||||
}
|
||||
|
||||
if($hasUser) {
|
||||
$prep->bind('user', $user);
|
||||
}
|
||||
|
||||
return $prep->fetchAll();
|
||||
}
|
||||
|
||||
define('MSZ_CHANGELOG_COUNT_QUERY', '
|
||||
SELECT COUNT(`change_id`)
|
||||
FROM `msz_changelog_changes`
|
||||
WHERE %s
|
||||
AND %s
|
||||
');
|
||||
|
||||
function changelog_count_changes(string $date, int $user): int {
|
||||
$hasDate = mb_strlen($date) > 0;
|
||||
$hasUser = $user > 0;
|
||||
|
||||
$query = sprintf(
|
||||
MSZ_CHANGELOG_COUNT_QUERY,
|
||||
$hasDate ? 'DATE(`change_created`) = :date' : '1',
|
||||
$hasUser ? '`user_id` = :user' : '1'
|
||||
);
|
||||
|
||||
$prep = \Misuzu\DB::prepare($query);
|
||||
|
||||
if($hasDate) {
|
||||
$prep->bind('date', $date);
|
||||
}
|
||||
|
||||
if($hasUser) {
|
||||
$prep->bind('user', $user);
|
||||
}
|
||||
|
||||
return (int)$prep->fetchColumn();
|
||||
}
|
||||
|
||||
function changelog_change_get(int $changeId): array {
|
||||
$getChange = \Misuzu\DB::prepare('
|
||||
SELECT
|
||||
c.`change_id`, c.`change_created`, c.`change_log`, c.`change_text`, c.`change_action`,
|
||||
u.`user_id`, u.`username`, u.`display_role` AS `user_role`,
|
||||
DATE(`change_created`) AS `change_date`,
|
||||
COALESCE(u.`user_title`, r.`role_title`) AS `user_title`,
|
||||
COALESCE(u.`user_colour`, r.`role_colour`) AS `user_colour`
|
||||
FROM `msz_changelog_changes` AS c
|
||||
LEFT JOIN `msz_users` AS u
|
||||
ON u.`user_id` = c.`user_id`
|
||||
LEFT JOIN `msz_roles` AS r
|
||||
ON r.`role_id` = u.`display_role`
|
||||
WHERE `change_id` = :change_id
|
||||
');
|
||||
$getChange->bind('change_id', $changeId);
|
||||
return $getChange->fetch();
|
||||
}
|
||||
|
||||
function changelog_change_tags_get(int $changeId): array {
|
||||
$getTags = \Misuzu\DB::prepare('
|
||||
SELECT
|
||||
t.`tag_id`, t.`tag_name`, t.`tag_description`
|
||||
FROM `msz_changelog_tags` as t
|
||||
LEFT JOIN `msz_changelog_change_tags` as ct
|
||||
ON ct.`tag_id` = t.`tag_id`
|
||||
WHERE ct.`change_id` = :change_id
|
||||
');
|
||||
$getTags->bind('change_id', $changeId);
|
||||
return $getTags->fetchAll();
|
||||
}
|
|
@ -24,10 +24,11 @@ define('MSZ_URLS', [
|
|||
'auth-resolve-user' => ['/auth/login.php', ['resolve_user' => '<username>']],
|
||||
'auth-two-factor' => ['/auth/twofactor.php', ['token' => '<token>']],
|
||||
|
||||
'changelog-index' => ['/changelog.php'],
|
||||
'changelog-change' => ['/changelog.php', ['c' => '<change>']],
|
||||
'changelog-date' => ['/changelog.php', ['d' => '<date>']],
|
||||
'changelog-tag' => ['/changelog.php', ['t' => '<tag>']],
|
||||
'changelog-index' => ['/changelog', ['date' => '<date>', 'user' => '<user>', 'tags' => '<tags>']],
|
||||
'changelog-feed-rss' => ['/changelog.rss'],
|
||||
'changelog-feed-atom' => ['/changelog.atom'],
|
||||
'changelog-change' => ['/changelog/change/<change>'],
|
||||
'changelog-change-comments' => ['/changelog/change/<change>', [], 'comments'],
|
||||
|
||||
'news-index' => ['/news', ['p' => '<page>']],
|
||||
'news-category' => ['/news/<category>', ['p' => '<page>']],
|
||||
|
|
|
@ -2,68 +2,52 @@
|
|||
{% from 'macros.twig' import container_title, avatar %}
|
||||
{% from '_layout/comments.twig' import comments_section %}
|
||||
|
||||
{% set title = 'Changelog » Change #' ~ change.change_id %}
|
||||
{% set canonical_url = url('changelog-change', {'change': change.change_id}) %}
|
||||
{% set manage_link = url('manage-changelog-change', {'change': change.change_id}) %}
|
||||
{% set description = change.change_log %}
|
||||
|
||||
{% if change.change_action == constant('MSZ_CHANGELOG_ACTION_ADD') %}
|
||||
{% set action_name = 'Added' %}
|
||||
{% elseif change.change_action == constant('MSZ_CHANGELOG_ACTION_REMOVE') %}
|
||||
{% set action_name = 'Removed' %}
|
||||
{% elseif change.change_action == constant('MSZ_CHANGELOG_ACTION_UPDATE') %}
|
||||
{% set action_name = 'Updated' %}
|
||||
{% elseif change.change_action == constant('MSZ_CHANGELOG_ACTION_FIX') %}
|
||||
{% set action_name = 'Fixed' %}
|
||||
{% elseif change.change_action == constant('MSZ_CHANGELOG_ACTION_IMPORT') %}
|
||||
{% set action_name = 'Imported' %}
|
||||
{% elseif change.change_action == constant('MSZ_CHANGELOG_ACTION_REVERT') %}
|
||||
{% set action_name = 'Reverted' %}
|
||||
{% else %}
|
||||
{% set action_name = 'Unknown' %}
|
||||
{% endif %}
|
||||
{% set title = 'Changelog » Change #' ~ change_info.id %}
|
||||
{% set canonical_url = url('changelog-change', {'change': change_info.id}) %}
|
||||
{% set manage_link = url('manage-changelog-change', {'change': change_info.id}) %}
|
||||
{% set description = change_info.header %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container changelog__log changelog__action--{{ changelog_action_name(change.change_action) }}">
|
||||
<div class="container changelog__log changelog__action--{{ change_info.actionClass }}">
|
||||
<div class="changelog__log__action">
|
||||
{{ action_name }}
|
||||
{{ change_info.actionString }}
|
||||
</div>
|
||||
|
||||
<div class="changelog__log__text">
|
||||
{{ change.change_log }}
|
||||
{{ change_info.header }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container changelog__change" style="{% if change.user_colour is not null %}{{ change.user_colour|html_colour('--accent-colour') }}{% endif %}">
|
||||
<div class="container changelog__change"{% if change_info.user is not null %} style="--accent-colour: {{ change_info.user.colour }}"{% endif %}>
|
||||
<div class="changelog__change__info">
|
||||
<div class="changelog__change__info__background"></div>
|
||||
<div class="changelog__change__info__content">
|
||||
{% if change.user_id is not null %}
|
||||
{% if change_info.user.id is not null %}
|
||||
<div class="changelog__change__user">
|
||||
<a class="changelog__change__avatar" href="{{ url('user-profile', {'user': change.user_id}) }}">
|
||||
{{ avatar(change.user_id, 60, change.username) }}
|
||||
<a class="changelog__change__avatar" href="{{ url('user-profile', {'user': change_info.user.id}) }}">
|
||||
{{ avatar(change_info.user.id, 60, change_info.user.username) }}
|
||||
</a>
|
||||
|
||||
<div class="changelog__change__user__details">
|
||||
<a class="changelog__change__username" href="{{ url('user-profile', {'user': change.user_id}) }}">{{ change.username }}</a>
|
||||
<a class="changelog__change__userrole" href="{{ url('user-list', {'role': change.user_role}) }}">{{ change.user_title }}</a>
|
||||
<a class="changelog__change__username" href="{{ url('user-profile', {'user': change_info.user.id}) }}">{{ change_info.user.username }}</a>
|
||||
<a class="changelog__change__userrole" href="{{ url('user-list', {'role': change_info.user.displayRoleId}) }}">{{ change_info.user.title }}</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<a class="changelog__change__date" href="{{ url('changelog-date', {'date': change.change_date}) }}">
|
||||
<a class="changelog__change__date" href="{{ url('changelog-index', {'date': change_info.date}) }}">
|
||||
Created
|
||||
<time datetime="{{ change.change_created|date('c') }}" title="{{ change.change_created|date('r') }}">
|
||||
{{ change.change_created|time_diff }}
|
||||
<time datetime="{{ change_info.createdTime|date('c') }}" title="{{ change_info.createdTime|date('r') }}">
|
||||
{{ change_info.createdTime|time_diff }}
|
||||
</time>
|
||||
</a>
|
||||
|
||||
{% if tags|length > 0 %}
|
||||
{% if change_info.tags|length > 0 %}
|
||||
<ul class="changelog__change__tags">
|
||||
{% for tag in tags %}
|
||||
<li class="changelog__change__tag" title="{{ tag.tag_description }}">
|
||||
<a href="{{ url('changelog-tag', {'tag': tag.tag_id}) }}" class="changelog__change__tag__link">
|
||||
{{ tag.tag_name }}
|
||||
{% for tag in change_info.tags %}
|
||||
<li class="changelog__change__tag" title="{{ tag.description }}">
|
||||
<a href="{{ url('changelog-tag', {'tag': tag.id}) }}" class="changelog__change__tag__link">
|
||||
{{ tag.name }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
@ -75,18 +59,18 @@
|
|||
<div class="changelog__change__text markdown">
|
||||
<h1>{{ title }}</h1>
|
||||
|
||||
{% if change.change_text|length >= 1 %}
|
||||
{{ change.change_text|parse_text(constant('\\Misuzu\\Parsers\\Parser::MARKDOWN'))|raw }}
|
||||
{% if change_info.hasBody %}
|
||||
{{ change_info.parsedBody|raw }}
|
||||
{% else %}
|
||||
<p>This change has no additional notes.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if comments_category is defined %}
|
||||
{% if change_info.hasCommentsCategory %}
|
||||
<div class="container">
|
||||
{{ container_title('<i class="fas fa-comments fa-fw"></i> Comments for ' ~ change.change_date) }}
|
||||
{{ comments_section(comments_category, comments_user) }}
|
||||
{{ container_title('<i class="fas fa-comments fa-fw"></i> Comments for ' ~ change_info.date) }}
|
||||
{{ comments_section(change_info.commentsCategory, comments_user) }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -3,41 +3,51 @@
|
|||
{% from 'changelog/macros.twig' import changelog_listing %}
|
||||
{% from '_layout/comments.twig' import comments_section %}
|
||||
|
||||
{% set is_valid = changes|length > 0 %}
|
||||
{% set is_date = changelog_date|length > 0 %}
|
||||
{% set is_date = changelog_date > 0 %}
|
||||
{% set is_user = changelog_user is not null %}
|
||||
{% set title = 'Changelog' %}
|
||||
{% set changelog_date_fmt = changelog_date|default(false) ? changelog_date|date('Y-m-d') : '' %}
|
||||
|
||||
{% if is_valid %}
|
||||
{%
|
||||
set canonical_url = url_construct(url('changelog-index'), {
|
||||
'd': changelog_date,
|
||||
'u': changelog_user|default(0),
|
||||
'p': changelog_pagination.page,
|
||||
})
|
||||
%}
|
||||
{% set canonical_url = url_construct(url('changelog-index'), {
|
||||
'd': changelog_date_fmt,
|
||||
'u': changelog_user.id|default(0),
|
||||
'p': changelog_pagination.page,
|
||||
}) %}
|
||||
|
||||
{% if is_date or changelog_user %}
|
||||
{% set title = title ~ ' »' ~ (changelog_date ? ' ' ~ changes[0].change_date : '') ~ (changelog_user ? ' by ' ~ changes[0].username : '') %}
|
||||
{% endif %}
|
||||
{% if is_date or is_user %}
|
||||
{% set title = title ~ ' »' ~ (is_date ? ' ' ~ changelog_infos[0].date : '') ~ (is_user ? ' by ' ~ changelog_infos[0].user.username : '') %}
|
||||
{% else %}
|
||||
{% set feeds = [
|
||||
{
|
||||
'type': 'rss',
|
||||
'title': '',
|
||||
'url': url('changelog-feed-rss'),
|
||||
},
|
||||
{
|
||||
'type': 'atom',
|
||||
'title': '',
|
||||
'url': url('changelog-feed-atom'),
|
||||
},
|
||||
] %}
|
||||
{% endif %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container changelog__container">
|
||||
{{ container_title('<i class="fas fa-wrench fa-fw"></i> ' ~ title) }}
|
||||
|
||||
{{ changelog_listing(changes, is_date) }}
|
||||
{{ changelog_listing(changelog_infos, is_date) }}
|
||||
|
||||
{% if not is_date %}
|
||||
<div class="changelog__pagination">
|
||||
{{ pagination(changelog_pagination, url('changelog-index'), null, {'d':changelog_date, 'u':changelog_user|default(0)})}}
|
||||
{{ pagination(changelog_pagination, url('changelog-index'), null, {'date':changelog_date_fmt, 'user':changelog_user.id|default(0)})}}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if comments_category is defined %}
|
||||
{% if is_date %}
|
||||
<div class="container">
|
||||
{{ container_title('<i class="fas fa-comments fa-fw"></i> Comments') }}
|
||||
{{ comments_section(comments_category, comments_user) }}
|
||||
{{ comments_section(changelog_infos[0].commentsCategory, comments_user) }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
<div class="changelog__listing">
|
||||
{% if changes|length > 0 %}
|
||||
{% for change in changes %}
|
||||
{% if not hide_dates and (last_date is not defined or last_date != change.change_date) %}
|
||||
{% set last_date = change.change_date %}
|
||||
{% if not hide_dates and (last_date is not defined or last_date != change.date) %}
|
||||
{% set last_date = change.date %}
|
||||
|
||||
<a href="{{ is_manage ? '#cd' ~ last_date : url('changelog-date', {'date': last_date}) }}" class="changelog__listing__date" id="cd{{ last_date }}">
|
||||
<a href="{{ is_manage ? '#cd' ~ last_date : url('changelog-index', {'date': last_date}) }}" class="changelog__listing__date" id="cd{{ last_date }}">
|
||||
{{ last_date }}
|
||||
</a>
|
||||
{% endif %}
|
||||
|
@ -23,72 +23,52 @@
|
|||
{% endmacro %}
|
||||
|
||||
{% macro changelog_entry(change, is_small, is_manage) %}
|
||||
{% set change_url = url(is_manage ? 'manage-changelog-change' : 'changelog-change', {'change': change.change_id}) %}
|
||||
{% set has_text = change.change_has_text|default(false)
|
||||
or (change.change_text is defined and change.change_text|length > 0)
|
||||
%}
|
||||
{% set change_url = url(is_manage ? 'manage-changelog-change' : 'changelog-change', {'change': change.id}) %}
|
||||
|
||||
<div class="changelog__entry" id="cl{{ change.change_id }}">
|
||||
<div class="changelog__entry" id="cl{{ change.id }}">
|
||||
<div class="changelog__entry__info">
|
||||
{% if is_manage %}
|
||||
<a href="{{ change_url|format(change.change_id) }}" class="changelog__entry__datetime">
|
||||
<a href="{{ change_url }}" class="changelog__entry__datetime">
|
||||
<time class="changelog__datetime__text"
|
||||
datetime="{{ change.change_created|date('c') }}"
|
||||
title="{{ change.change_created|date('r') }}">
|
||||
{{ change.change_created|time_diff }}
|
||||
datetime="{{ change.createdTime|date('c') }}"
|
||||
title="{{ change.createdTime|date('r') }}">
|
||||
{{ change.createdTime|time_diff }}
|
||||
</time>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
{% if change.change_action == constant('MSZ_CHANGELOG_ACTION_ADD') %}
|
||||
{% set action_name = 'Added' %}
|
||||
{% elseif change.change_action == constant('MSZ_CHANGELOG_ACTION_REMOVE') %}
|
||||
{% set action_name = 'Removed' %}
|
||||
{% elseif change.change_action == constant('MSZ_CHANGELOG_ACTION_UPDATE') %}
|
||||
{% set action_name = 'Updated' %}
|
||||
{% elseif change.change_action == constant('MSZ_CHANGELOG_ACTION_FIX') %}
|
||||
{% set action_name = 'Fixed' %}
|
||||
{% elseif change.change_action == constant('MSZ_CHANGELOG_ACTION_IMPORT') %}
|
||||
{% set action_name = 'Imported' %}
|
||||
{% elseif change.change_action == constant('MSZ_CHANGELOG_ACTION_REVERT') %}
|
||||
{% set action_name = 'Reverted' %}
|
||||
{% else %}
|
||||
{% set action_name = 'Unknown' %}
|
||||
{% endif %}
|
||||
|
||||
<a class="changelog__entry__action changelog__action--{{ changelog_action_name(change.change_action) }}"
|
||||
href="{{ change_url|format(change.change_id) }}"
|
||||
{% if change.action_colour is defined %}style="{{ change.action_colour|html_colour('--action-colour') }}"{% endif %}
|
||||
{% if is_small %}title="{{ action_name }}"{% endif %}>
|
||||
<a class="changelog__entry__action changelog__action--{{ change.actionClass }}"
|
||||
href="{{ change_url }}"
|
||||
{% if is_small %}title="{{ change.actionString }}"{% endif %}>
|
||||
{% if not is_small %}
|
||||
<div class="changelog__entry__action__text">
|
||||
{{ action_name }}
|
||||
{{ change.actionString }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</a>
|
||||
|
||||
{% if change.user_id is defined %}
|
||||
{% if not is_small %}
|
||||
<a class="changelog__entry__user"
|
||||
href="{{ url(is_manage ? 'manage-user' : 'user-profile', {'user': change.user_id}) }}"
|
||||
style="{{ change.user_colour|html_colour }}">
|
||||
href="{{ url(is_manage ? 'manage-user' : 'user-profile', {'user': change.user.id}) }}"
|
||||
style="--user-colour: {{ change.user.colour }}">
|
||||
<div class="changelog__entry__user__text">
|
||||
{{ change.username }}
|
||||
{{ change.user.username }}
|
||||
</div>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="changelog__entry__text">
|
||||
<a class="changelog__entry__log{% if has_text %} changelog__entry__log--link{% endif %}"
|
||||
{% if has_text %}href="{{ change_url|format(change.change_id) }}"{% endif %}>
|
||||
{{ change.change_log }}
|
||||
<a class="changelog__entry__log{% if change.hasBody %} changelog__entry__log--link{% endif %}"
|
||||
{% if change.hasBody %}href="{{ change_url }}"{% endif %}>
|
||||
{{ change.header }}
|
||||
</a>
|
||||
|
||||
{% if is_manage %}
|
||||
<div class="changelog__entry__tags">
|
||||
{% for tag in change.tags %}
|
||||
<a href="{{ url(is_manage ? 'manage-changelog-tag' : 'changelog-tag', {'tag': tag.tag_id}) }}" class="changelog__entry__tag">
|
||||
{{ tag.tag_name }}
|
||||
<a href="{{ url(is_manage ? 'manage-changelog-tag' : 'changelog-tag', {'tag': tag.id}) }}" class="changelog__entry__tag">
|
||||
{{ tag.name }}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
|
|
@ -3,39 +3,39 @@
|
|||
{% from '_layout/input.twig' import input_csrf, input_text, input_select, input_checkbox %}
|
||||
|
||||
{% if change is not null %}
|
||||
{% set site_link = url('changelog-change', {'change': change.change_id}) %}
|
||||
{% set site_link = url('changelog-change', {'change': change.id}) %}
|
||||
{% endif %}
|
||||
|
||||
{% block manage_content %}
|
||||
<div class="container">
|
||||
<form action="{{ url('manage-changelog-change', {'change': change.change_id|default(0)}) }}" method="post">
|
||||
<form action="{{ url('manage-changelog-change', {'change': change.id|default(0)}) }}" method="post">
|
||||
{{ input_csrf() }}
|
||||
|
||||
{{ container_title(change is not null ? 'Editing #' ~ change.change_id : 'Adding a new change') }}
|
||||
{{ container_title(change is not null ? 'Editing #' ~ change.id : 'Adding a new change') }}
|
||||
|
||||
<div style="display: flex; margin: 2px 5px;">
|
||||
{{ input_select('change[action]', change_actions, change.change_action|default(0), 'action_name', 'action_id') }}
|
||||
{{ input_text('change[log]', '', change is not null ? change.change_log : '', 'text', '', true, {'maxlength':255,'style':'flex-grow:1'}) }}
|
||||
{{ input_select('change[action]', change_actions, change.action|default(0), 'action_name', 'action_id') }}
|
||||
{{ input_text('change[log]', '', change.header|default(''), 'text', '', true, {'maxlength':255,'style':'flex-grow:1'}) }}
|
||||
</div>
|
||||
|
||||
<label class="form__label">
|
||||
<div class="form__label__text">Text</div>
|
||||
<div class="form__label__input">
|
||||
<textarea class="input__textarea" name="change[text]" maxlength="65535">{{ change is not null ? change.change_text : '' }}</textarea>
|
||||
<textarea class="input__textarea" name="change[text]" maxlength="65535">{{ change.body|default('') }}</textarea>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="form__label">
|
||||
<div class="form__label__text">Contributor Id</div>
|
||||
<div class="form__label__input">
|
||||
{{ input_text('change[user]', '', change.user_id|default(current_user.user_id), 'number', '', false, {'min':1}) }}
|
||||
{{ input_text('change[user]', '', change.userId|default(current_user.user_id), 'number', '', false, {'min':1}) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="form__label">
|
||||
<div class="form__label__text">Created</div>
|
||||
<div class="form__label__input">
|
||||
{{ input_text('change[created]', '', (change is not null ? change.change_created : ''|date('Y-m-d H:i:s')), 'text', '', true) }}
|
||||
{{ input_text('change[created]', '', change.createdTime|default(null)|date('Y-m-d H:i:s'), 'text', '', true) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
|
@ -44,9 +44,9 @@
|
|||
<label class="manage__tag">
|
||||
<div class="manage__tag__background"></div>
|
||||
<div class="manage__tag__content">
|
||||
{{ input_checkbox('tags[]', '', tag.has_tag, 'manage__tag__checkbox', tag.tag_id) }}
|
||||
{{ input_checkbox('tags[]', '', change.hasTag(tag)|default(false), 'manage__tag__checkbox', tag.id) }}
|
||||
<div class="manage__tag__title">
|
||||
{{ tag.tag_name }}
|
||||
{{ tag.name }}
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
|
|
|
@ -4,29 +4,29 @@
|
|||
|
||||
{% block manage_content %}
|
||||
<div class="container">
|
||||
<form action="{{ url('manage-changelog-tag', {'tag': edit_tag.tag_id|default(0)}) }}" method="post">
|
||||
<form action="{{ url('manage-changelog-tag', {'tag': edit_tag.id|default(0)}) }}" method="post">
|
||||
{{ input_csrf() }}
|
||||
|
||||
{{ container_title(edit_tag is defined ? 'Editing ' ~ edit_tag.tag_name ~ ' (' ~ edit_tag.tag_id ~ ')' : 'Adding a new tag') }}
|
||||
{{ container_title(edit_tag is defined ? 'Editing ' ~ edit_tag.name ~ ' (' ~ edit_tag.id ~ ')' : 'Adding a new tag') }}
|
||||
|
||||
<label class="form__label" style="width:100%">
|
||||
<div class="form__label__text">Name</div>
|
||||
<div class="form__label__input">
|
||||
{{ input_text('tag[name]', '', edit_tag is defined ? edit_tag.tag_name : '', 'text', '', true, {'maxlength':255}) }}
|
||||
{{ input_text('tag[name]', '', edit_tag is defined ? edit_tag.name : '', 'text', '', true, {'maxlength':255}) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="form__label" style="width:100%">
|
||||
<div class="form__label__text">Description</div>
|
||||
<div class="form__label__input">
|
||||
<textarea class="input__textarea" name="tag[description]" maxlength="65535">{{ edit_tag is defined ? edit_tag.tag_description : '' }}</textarea>
|
||||
<textarea class="input__textarea" name="tag[description]" maxlength="65535">{{ edit_tag.description|default('') }}</textarea>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="form__label">
|
||||
<div class="form__label__text">Archived</div>
|
||||
<div class="form__label__input">
|
||||
{{ input_checkbox('tag[archived]', '', edit_tag is defined and edit_tag.tag_archived is not null) }}
|
||||
{{ input_checkbox('tag[archived]', '', edit_tag.archived|default(false)) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
|
@ -34,7 +34,7 @@
|
|||
<label class="form__label">
|
||||
<div class="form__label__text">Created</div>
|
||||
<div class="form__label__input">
|
||||
{{ input_text('', '', edit_tag.tag_created) }}
|
||||
{{ input_text('', '', edit_tag.createdTime|date('r')) }}
|
||||
</div>
|
||||
</label>
|
||||
{% endif %}
|
||||
|
|
|
@ -3,28 +3,26 @@
|
|||
|
||||
{% block manage_content %}
|
||||
<div class="changelog-actions-tags">
|
||||
{% if changelog_tags is defined %}
|
||||
<div class="container changelog-actions-tags__panel changelog-actions-tags__panel--tags">
|
||||
{{ container_title('Tags') }}
|
||||
<div class="container changelog-actions-tags__panel changelog-actions-tags__panel--tags">
|
||||
{{ container_title('Tags') }}
|
||||
|
||||
<a href="{{ url('manage-changelog-tag') }}" class="input__button">Create new tag</a>
|
||||
<a href="{{ url('manage-changelog-tag') }}" class="input__button">Create new tag</a>
|
||||
|
||||
<div class="changelog-actions-tags__list">
|
||||
{% for tag in changelog_tags %}
|
||||
<a href="{{ url('manage-changelog-tag', {'tag': tag.tag_id}) }}" class="changelog-actions-tags__entry">
|
||||
<div class="listing__entry__content changelog-tags__content">
|
||||
<div class="changelog-tags__text">
|
||||
{{ tag.tag_name }} ({{ tag.tag_count }})
|
||||
</div>
|
||||
|
||||
<div class="changelog-tags__description">
|
||||
{{ tag.tag_description }}
|
||||
</div>
|
||||
<div class="changelog-actions-tags__list">
|
||||
{% for tag in changelog_tags %}
|
||||
<a href="{{ url('manage-changelog-tag', {'tag': tag.id}) }}" class="changelog-actions-tags__entry">
|
||||
<div class="listing__entry__content changelog-tags__content">
|
||||
<div class="changelog-tags__text">
|
||||
{{ tag.name }} ({{ tag.changeCount }})
|
||||
</div>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="changelog-tags__description">
|
||||
{{ tag.description }}
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
{{ post.createdTime|date('r') }},
|
||||
{{ post.updatedTime|date('r') }},
|
||||
{{ post.deletedTime|date('r') }},
|
||||
{{ post.commentSectionId }}
|
||||
{{ post.commentsCategoryId }}
|
||||
</p>
|
||||
{% endfor %}
|
||||
|
||||
|
|
|
@ -33,12 +33,12 @@
|
|||
<div class="news__preview__content markdown">
|
||||
<h1>{{ post.title }}</h1>
|
||||
<div class="news__preview__text">
|
||||
{{ post.text|first_paragraph|parse_text(constant('\\Misuzu\\Parsers\\Parser::MARKDOWN'))|raw }}
|
||||
{{ post.parsedFirstParagraph|raw }}
|
||||
</div>
|
||||
<div class="news__preview__links">
|
||||
<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">
|
||||
{{ not post.hasCommentSection or post.commentSection.postCount < 1 ? 'No' : post.commentSection.postCount|number_format }} comment{{ not post.hasCommentSection or post.commentSection.postCount != 1 ? 's' : '' }}
|
||||
{{ not post.hasCommentsCategory or post.commentsCategory.postCount < 1 ? 'No' : post.commentsCategory.postCount|number_format }} comment{{ not post.hasCommentsCategory or post.commentsCategory.postCount != 1 ? 's' : '' }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -88,7 +88,7 @@
|
|||
|
||||
<div class="news__post__text markdown">
|
||||
<h1>{{ post.title }}</h1>
|
||||
{{ post.text|parse_text(constant('\\Misuzu\\Parsers\\Parser::MARKDOWN'))|raw }}
|
||||
{{ post.parsedText|raw }}
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
|
Loading…
Add table
Reference in a new issue