Restructured public folder and initialisation process.
This commit is contained in:
parent
67b41379e5
commit
a09c8b7ef6
62 changed files with 210 additions and 331 deletions
public
_github-callback.php_sockchat.phpauth.php
auth
changelog.phpcomments.phpforum
index.phpinfo.phpmanage
members.phpnews.phpnews
profile.phpsearch.phpsettings.phpsettings
user-assets.php
|
@ -1,134 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
$misuzuBypassLockdown = true;
|
||||
require_once '../misuzu.php';
|
||||
|
||||
function ghcb_strip_prefix(string $line): string {
|
||||
$findColon = mb_strpos($line, ':');
|
||||
return trim($findColon === false || $findColon >= 10 ? $line : mb_substr($line, $findColon + 1));
|
||||
}
|
||||
|
||||
function ghcb_changelog_action(string &$line): string {
|
||||
$original = trim($line);
|
||||
$line = ghcb_strip_prefix($line);
|
||||
|
||||
$firstSix = mb_strtolower(mb_substr($original, 0, 6));
|
||||
|
||||
if($firstSix === 'revert'
|
||||
|| $firstSix === 'restor')
|
||||
return 'revert';
|
||||
if($firstSix === 'import')
|
||||
return 'import';
|
||||
|
||||
$firstThree = mb_strtolower(mb_substr($original, 0, 3));
|
||||
|
||||
if($firstThree === 'add'
|
||||
|| $firstSix === 'create')
|
||||
return 'add';
|
||||
if($firstThree === 'fix')
|
||||
return 'fix';
|
||||
|
||||
$firstFour = mb_strtolower(mb_substr($original, 0, 4));
|
||||
$firstEight = mb_strtolower(mb_substr($original, 0, 8));
|
||||
|
||||
if($firstSix === 'delete'
|
||||
|| $firstSix === 'remove'
|
||||
|| $firstFour === 'nuke'
|
||||
|| $firstEight === 'dropkick')
|
||||
return 'remove';
|
||||
|
||||
return 'update';
|
||||
}
|
||||
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
|
||||
if($_SERVER['REQUEST_METHOD'] !== 'POST')
|
||||
die('no');
|
||||
|
||||
$config = MSZ_ROOT . '/config/github.ini';
|
||||
if(!is_file($config))
|
||||
die('config missing');
|
||||
|
||||
$config = parse_ini_file(MSZ_ROOT . '/config/github.ini', true);
|
||||
if(empty($config['tokens']['token']))
|
||||
die('config invalid');
|
||||
|
||||
$isGitea = isset($_SERVER['HTTP_X_GITEA_DELIVERY']) && isset($_SERVER['HTTP_X_GITEA_EVENT']);
|
||||
|
||||
$rawData = file_get_contents('php://input');
|
||||
$sigParts = $isGitea
|
||||
? ['sha256', $_SERVER['HTTP_X_GITEA_SIGNATURE']]
|
||||
: explode('=', $_SERVER['HTTP_X_HUB_SIGNATURE'] ?? '', 2);
|
||||
|
||||
if(empty($sigParts[1]))
|
||||
die('invalid signature');
|
||||
|
||||
$repoAuthenticated = false;
|
||||
foreach($config['tokens']['token'] as $repoName => $repoToken) {
|
||||
if(hash_equals(hash_hmac($sigParts[0], $rawData, $repoToken), $sigParts[1])) {
|
||||
$repoAuthenticated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!$repoAuthenticated)
|
||||
die('signature check failed');
|
||||
|
||||
$data = json_decode($_SERVER['CONTENT_TYPE'] === 'application/x-www-form-urlencoded'
|
||||
? $_POST['payload']
|
||||
: $rawData);
|
||||
|
||||
if(empty($data))
|
||||
die('body is corrupt');
|
||||
|
||||
if(empty($data->repository->full_name))
|
||||
die('body is corrupt');
|
||||
|
||||
if($data->repository->full_name !== $repoName)
|
||||
die('invalid repository token');
|
||||
|
||||
if($_SERVER['HTTP_X_GITHUB_EVENT'] !== 'push')
|
||||
die('only push event is supported');
|
||||
|
||||
$commitCount = count($data->commits);
|
||||
if($commitCount < 1)
|
||||
die('no commits received');
|
||||
|
||||
$repoInfo = $config['repo:' . $repoName] ?? [];
|
||||
|
||||
$repoMaster = 'refs/heads/master';
|
||||
if(!empty($repoInfo['master']))
|
||||
$repoMaster = $repoInfo['master'];
|
||||
|
||||
if($data->ref !== $repoMaster)
|
||||
die('only the master branch is tracked');
|
||||
|
||||
$changelog = $msz->getChangelog();
|
||||
|
||||
$tags = $repoInfo['tags'] ?? [];
|
||||
$addresses = $config['addresses'] ?? [];
|
||||
|
||||
foreach($data->commits as $commit) {
|
||||
$message = trim($commit->message);
|
||||
|
||||
if(mb_strpos($message, $repoName) !== false
|
||||
|| mb_substr($message, 0, 2) === '//'
|
||||
|| mb_strpos(mb_strtolower($message), 'merge pull request') !== false)
|
||||
continue;
|
||||
|
||||
$index = mb_strpos($message, "\n");
|
||||
$line = $index === false ? $message : mb_substr($message, 0, $index);
|
||||
$body = trim($index === false ? '' : mb_substr($message, $index + 1));
|
||||
|
||||
$changeInfo = $changelog->createChange(
|
||||
ghcb_changelog_action($line),
|
||||
$line, $body,
|
||||
$addresses[$commit->author->email] ?? null,
|
||||
max(1, strtotime($commit->timestamp))
|
||||
);
|
||||
|
||||
if(!empty($tags))
|
||||
foreach($tags as $tag)
|
||||
$changelog->addTagToChange($changeInfo, $tag);
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
<?php
|
||||
require_once __DIR__ . '/index.php';
|
|
@ -1,2 +0,0 @@
|
|||
<?php
|
||||
require_once __DIR__ . '/index.php';
|
|
@ -1,6 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
require_once '../../misuzu.php';
|
||||
|
||||
url_redirect('auth-login');
|
|
@ -1,167 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\AuthToken;
|
||||
use Misuzu\Users\User;
|
||||
use Misuzu\Users\UserNotFoundException;
|
||||
use Misuzu\Users\UserAuthSession;
|
||||
use Misuzu\Users\UserLoginAttempt;
|
||||
use Misuzu\Users\UserSession;
|
||||
use Misuzu\Users\UserSessionCreationFailedException;
|
||||
|
||||
require_once '../../misuzu.php';
|
||||
|
||||
if(UserSession::hasCurrent()) {
|
||||
url_redirect('index');
|
||||
return;
|
||||
}
|
||||
|
||||
if(!empty($_GET['resolve'])) {
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
try {
|
||||
// Only works for usernames, this is by design
|
||||
$userInfo = User::byUsername((string)filter_input(INPUT_GET, 'name'));
|
||||
} catch(UserNotFoundException $ex) {
|
||||
echo json_encode([
|
||||
'id' => 0,
|
||||
'name' => '',
|
||||
'avatar' => url('user-avatar', ['res' => 200, 'user' => 0]),
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'id' => $userInfo->getId(),
|
||||
'name' => $userInfo->getUsername(),
|
||||
'avatar' => url('user-avatar', ['user' => $userInfo->getId(), 'res' => 200]),
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
$notices = [];
|
||||
$ipAddress = $_SERVER['REMOTE_ADDR'];
|
||||
$countryCode = $_SERVER['COUNTRY_CODE'] ?? 'XX';
|
||||
$remainingAttempts = UserLoginAttempt::remaining($ipAddress);
|
||||
|
||||
$siteIsPrivate = $cfg->getBoolean('private.enable');
|
||||
if($siteIsPrivate) {
|
||||
[
|
||||
'private.perm.cat' => $loginPermCat,
|
||||
'private.perm.val' => $loginPermVal,
|
||||
'private.msg' => $sitePrivateMessage,
|
||||
'private.allow_password_reset' => $canResetPassword,
|
||||
] = $cfg->getValues([
|
||||
'private.perm.cat:s',
|
||||
'private.perm.val:i',
|
||||
'private.msg:s',
|
||||
['private.allow_password_reset:b', true],
|
||||
]);
|
||||
} else {
|
||||
$loginPermCat = '';
|
||||
$loginPermVal = 0;
|
||||
$sitePrivateMessage = '';
|
||||
$canResetPassword = true;
|
||||
}
|
||||
|
||||
while(!empty($_POST['login']) && is_array($_POST['login'])) {
|
||||
if(!CSRF::validateRequest()) {
|
||||
$notices[] = 'Was unable to verify the request, please try again!';
|
||||
break;
|
||||
}
|
||||
|
||||
$loginRedirect = empty($_POST['login']['redirect']) || !is_string($_POST['login']['redirect']) ? '' : $_POST['login']['redirect'];
|
||||
|
||||
if(empty($_POST['login']['username']) || empty($_POST['login']['password'])
|
||||
|| !is_string($_POST['login']['username']) || !is_string($_POST['login']['password'])) {
|
||||
$notices[] = "You didn't fill in a username and/or password.";
|
||||
break;
|
||||
}
|
||||
|
||||
if($remainingAttempts < 1) {
|
||||
$notices[] = "There are too many failed login attempts from your IP address, please try again later.";
|
||||
break;
|
||||
}
|
||||
|
||||
$attemptsRemainingError = sprintf(
|
||||
"%d attempt%s remaining",
|
||||
$remainingAttempts - 1,
|
||||
$remainingAttempts === 2 ? '' : 's'
|
||||
);
|
||||
$loginFailedError = "Invalid username or password, {$attemptsRemainingError}.";
|
||||
|
||||
try {
|
||||
$userInfo = User::byUsernameOrEMailAddress($_POST['login']['username']);
|
||||
} catch(UserNotFoundException $ex) {
|
||||
UserLoginAttempt::create($ipAddress, $countryCode, false);
|
||||
$notices[] = $loginFailedError;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!$userInfo->hasPassword()) {
|
||||
$notices[] = 'Your password has been invalidated, please reset it.';
|
||||
break;
|
||||
}
|
||||
|
||||
if($userInfo->isDeleted() || !$userInfo->checkPassword($_POST['login']['password'])) {
|
||||
UserLoginAttempt::create($ipAddress, $countryCode, false, $userInfo);
|
||||
$notices[] = $loginFailedError;
|
||||
break;
|
||||
}
|
||||
|
||||
if($userInfo->passwordNeedsRehash())
|
||||
$userInfo->setPassword($_POST['login']['password'])->save();
|
||||
|
||||
if(!empty($loginPermCat) && $loginPermVal > 0 && !perms_check_user($loginPermCat, $userInfo->getId(), $loginPermVal)) {
|
||||
$notices[] = "Login succeeded, but you're not allowed to browse the site right now.";
|
||||
UserLoginAttempt::create($ipAddress, $countryCode, true, $userInfo);
|
||||
break;
|
||||
}
|
||||
|
||||
if($userInfo->hasTOTP()) {
|
||||
url_redirect('auth-two-factor', [
|
||||
'token' => UserAuthSession::create($userInfo)->getToken(),
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
UserLoginAttempt::create($ipAddress, $countryCode, true, $userInfo);
|
||||
|
||||
try {
|
||||
$sessionInfo = UserSession::create($userInfo, $ipAddress, $countryCode);
|
||||
$sessionInfo->setCurrent();
|
||||
} catch(UserSessionCreationFailedException $ex) {
|
||||
$notices[] = "Something broke while creating a session for you, please tell an administrator or developer about this!";
|
||||
break;
|
||||
}
|
||||
|
||||
$authToken = AuthToken::create($userInfo, $sessionInfo);
|
||||
$authToken->applyCookie($sessionInfo->getExpiresTime());
|
||||
|
||||
if(!is_local_url($loginRedirect))
|
||||
$loginRedirect = url('index');
|
||||
|
||||
redirect($loginRedirect);
|
||||
return;
|
||||
}
|
||||
|
||||
$welcomeMode = !empty($_GET['welcome']);
|
||||
$loginUsername = !empty($_POST['login']['username']) && is_string($_POST['login']['username']) ? $_POST['login']['username'] : (
|
||||
!empty($_GET['username']) && is_string($_GET['username']) ? $_GET['username'] : ''
|
||||
);
|
||||
$loginRedirect = $welcomeMode ? url('index') : (!empty($_GET['redirect']) && is_string($_GET['redirect']) ? $_GET['redirect'] : null) ?? $_SERVER['HTTP_REFERER'] ?? url('index');
|
||||
$canRegisterAccount = !$siteIsPrivate;
|
||||
|
||||
Template::render('auth.login', [
|
||||
'login_notices' => $notices,
|
||||
'login_username' => $loginUsername,
|
||||
'login_redirect' => $loginRedirect,
|
||||
'login_can_reset_password' => $canResetPassword,
|
||||
'login_can_register' => $canRegisterAccount,
|
||||
'login_attempts_remaining' => $remainingAttempts,
|
||||
'login_welcome' => $welcomeMode,
|
||||
'login_private' => [
|
||||
'enabled' => $siteIsPrivate,
|
||||
'message' => $sitePrivateMessage,
|
||||
],
|
||||
]);
|
|
@ -1,23 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Users\User;
|
||||
use Misuzu\Users\UserSession;
|
||||
|
||||
require_once '../../misuzu.php';
|
||||
|
||||
if(!UserSession::hasCurrent()) {
|
||||
url_redirect('index');
|
||||
return;
|
||||
}
|
||||
|
||||
if(CSRF::validateRequest()) {
|
||||
AuthToken::nukeCookie();
|
||||
UserSession::getCurrent()->delete();
|
||||
UserSession::unsetCurrent();
|
||||
User::unsetCurrent();
|
||||
url_redirect('index');
|
||||
return;
|
||||
}
|
||||
|
||||
Template::render('auth.logout');
|
|
@ -1,150 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Users\User;
|
||||
use Misuzu\Users\UserNotFoundException;
|
||||
use Misuzu\Users\UserLoginAttempt;
|
||||
use Misuzu\Users\UserRecoveryToken;
|
||||
use Misuzu\Users\UserRecoveryTokenNotFoundException;
|
||||
use Misuzu\Users\UserRecoveryTokenCreationFailedException;
|
||||
use Misuzu\Users\UserSession;
|
||||
|
||||
require_once '../../misuzu.php';
|
||||
|
||||
if(UserSession::hasCurrent()) {
|
||||
url_redirect('settings-account');
|
||||
return;
|
||||
}
|
||||
|
||||
$reset = !empty($_POST['reset']) && is_array($_POST['reset']) ? $_POST['reset'] : [];
|
||||
$forgot = !empty($_POST['forgot']) && is_array($_POST['forgot']) ? $_POST['forgot'] : [];
|
||||
$userId = !empty($reset['user']) ? (int)$reset['user'] : (
|
||||
!empty($_GET['user']) ? (int)$_GET['user'] : 0
|
||||
);
|
||||
|
||||
if($userId > 0)
|
||||
try {
|
||||
$userInfo = User::byId($userId);
|
||||
} catch(UserNotFoundException $ex) {
|
||||
url_redirect('auth-forgot');
|
||||
return;
|
||||
}
|
||||
|
||||
$notices = [];
|
||||
$ipAddress = $_SERVER['REMOTE_ADDR'];
|
||||
$siteIsPrivate = $cfg->getBoolean('private.enable');
|
||||
$canResetPassword = $siteIsPrivate ? $cfg->getBoolean('private.allow_password_reset', true) : true;
|
||||
$remainingAttempts = UserLoginAttempt::remaining($ipAddress);
|
||||
|
||||
while($canResetPassword) {
|
||||
if(!empty($reset) && $userId > 0) {
|
||||
if(!CSRF::validateRequest()) {
|
||||
$notices[] = 'Was unable to verify the request, please try again!';
|
||||
break;
|
||||
}
|
||||
|
||||
$verificationCode = !empty($reset['verification']) && is_string($reset['verification']) ? $reset['verification'] : '';
|
||||
|
||||
try {
|
||||
$tokenInfo = UserRecoveryToken::byToken($verificationCode);
|
||||
} catch(UserRecoveryTokenNotFoundException $ex) {
|
||||
unset($tokenInfo);
|
||||
}
|
||||
|
||||
if(empty($tokenInfo) || !$tokenInfo->isValid() || $tokenInfo->getUserId() !== $userInfo->getId()) {
|
||||
$notices[] = 'Invalid verification code!';
|
||||
break;
|
||||
}
|
||||
|
||||
$password = !empty($reset['password']) && is_array($reset['password']) ? $reset['password'] : [];
|
||||
$passwordNew = !empty($password['new']) && is_string($password['new']) ? $password['new'] : '';
|
||||
$passwordConfirm = !empty($password['confirm']) && is_string($password['confirm']) ? $password['confirm'] : '';
|
||||
|
||||
if(empty($passwordNew) || empty($passwordConfirm)
|
||||
|| $passwordNew !== $passwordConfirm) {
|
||||
$notices[] = "Password confirmation failed!";
|
||||
break;
|
||||
}
|
||||
|
||||
if(User::validatePassword($passwordNew) !== '') {
|
||||
$notices[] = 'Your password is too weak!';
|
||||
break;
|
||||
}
|
||||
|
||||
// also disables two factor auth to prevent getting locked out of account entirely
|
||||
// this behaviour should really be replaced with recovery keys...
|
||||
$userInfo->setPassword($passwordNew)
|
||||
->removeTOTPKey()
|
||||
->save();
|
||||
|
||||
$msz->createAuditLog('PASSWORD_RESET', [], $userInfo);
|
||||
|
||||
$tokenInfo->invalidate();
|
||||
|
||||
url_redirect('auth-login', ['redirect' => '/']);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!empty($forgot)) {
|
||||
if(!CSRF::validateRequest()) {
|
||||
$notices[] = 'Was unable to verify the request, please try again!';
|
||||
break;
|
||||
}
|
||||
|
||||
if(empty($forgot['email']) || !is_string($forgot['email'])) {
|
||||
$notices[] = "You didn't supply an e-mail address.";
|
||||
break;
|
||||
}
|
||||
|
||||
if($remainingAttempts < 1) {
|
||||
$notices[] = "There are too many failed login attempts from your IP address, please try again later.";
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
$forgotUser = User::byEMailAddress($forgot['email']);
|
||||
} catch(UserNotFoundException $ex) {
|
||||
unset($forgotUser);
|
||||
}
|
||||
|
||||
if(empty($forgotUser) || $forgotUser->isDeleted()) {
|
||||
$notices[] = "This e-mail address is not registered with us.";
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
$tokenInfo = UserRecoveryToken::byUserAndRemoteAddress($forgotUser, $ipAddress);
|
||||
} catch(UserRecoveryTokenNotFoundException $ex) {
|
||||
$tokenInfo = UserRecoveryToken::create($forgotUser, $ipAddress);
|
||||
|
||||
$recoveryMessage = Mailer::template('password-recovery', [
|
||||
'username' => $forgotUser->getUsername(),
|
||||
'token' => $tokenInfo->getToken(),
|
||||
]);
|
||||
|
||||
$recoveryMail = Mailer::sendMessage(
|
||||
[$forgotUser->getEMailAddress() => $forgotUser->getUsername()],
|
||||
$recoveryMessage['subject'], $recoveryMessage['message']
|
||||
);
|
||||
|
||||
if(!$recoveryMail) {
|
||||
$notices[] = "Failed to send reset email, please contact the administrator.";
|
||||
$tokenInfo->invalidate();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
url_redirect('auth-reset', ['user' => $forgotUser->getId()]);
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
Template::render(isset($userInfo) ? 'auth.password_reset' : 'auth.password_forgot', [
|
||||
'password_notices' => $notices,
|
||||
'password_email' => !empty($forget['email']) && is_string($forget['email']) ? $forget['email'] : '',
|
||||
'password_attempts_remaining' => $remainingAttempts,
|
||||
'password_user' => $userInfo ?? null,
|
||||
'password_verification' => $verificationCode ?? '',
|
||||
]);
|
|
@ -1,101 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Users\User;
|
||||
use Misuzu\Users\UserCreationFailedException;
|
||||
use Misuzu\Users\UserLoginAttempt;
|
||||
use Misuzu\Users\UserRole;
|
||||
use Misuzu\Users\UserSession;
|
||||
use Misuzu\Users\UserWarning;
|
||||
|
||||
require_once '../../misuzu.php';
|
||||
|
||||
if(UserSession::hasCurrent()) {
|
||||
url_redirect('index');
|
||||
return;
|
||||
}
|
||||
|
||||
$register = !empty($_POST['register']) && is_array($_POST['register']) ? $_POST['register'] : [];
|
||||
$notices = [];
|
||||
$ipAddress = $_SERVER['REMOTE_ADDR'];
|
||||
$countryCode = $_SERVER['COUNTRY_CODE'] ?? 'XX';
|
||||
$remainingAttempts = UserLoginAttempt::remaining($_SERVER['REMOTE_ADDR']);
|
||||
$restricted = UserWarning::countByRemoteAddress($ipAddress) > 0 ? 'ban' : '';
|
||||
|
||||
while(!$restricted && !empty($register)) {
|
||||
if(!CSRF::validateRequest()) {
|
||||
$notices[] = 'Was unable to verify the request, please try again!';
|
||||
break;
|
||||
}
|
||||
|
||||
if($remainingAttempts < 1) {
|
||||
$notices[] = "There are too many failed login attempts from your IP address, you may not create an account right now.";
|
||||
break;
|
||||
}
|
||||
|
||||
if(empty($register['username']) || empty($register['password']) || empty($register['email']) || empty($register['question'])
|
||||
|| !is_string($register['username']) || !is_string($register['password']) || !is_string($register['email']) || !is_string($register['question'])) {
|
||||
$notices[] = "You haven't filled in all fields.";
|
||||
break;
|
||||
}
|
||||
|
||||
$checkSpamBot = mb_strtolower($register['question']);
|
||||
$spamBotValid = [
|
||||
'21', 'twentyone', 'twenty-one', 'twenty one',
|
||||
];
|
||||
$spamBotHint = [
|
||||
'19', 'nineteen', 'nine-teen', 'nine teen',
|
||||
];
|
||||
|
||||
if(!in_array($checkSpamBot, $spamBotValid)) {
|
||||
if(in_array($checkSpamBot, $spamBotHint))
|
||||
$notices[] = '_play_hint';
|
||||
|
||||
$notices[] = 'Human only cool club, robots begone.';
|
||||
break;
|
||||
}
|
||||
|
||||
$usernameValidation = User::validateUsername($register['username']);
|
||||
if($usernameValidation !== '')
|
||||
$notices[] = User::usernameValidationErrorString($usernameValidation);
|
||||
|
||||
$emailValidation = User::validateEMailAddress($register['email']);
|
||||
if($emailValidation !== '')
|
||||
$notices[] = $emailValidation === 'in-use'
|
||||
? 'This e-mail address has already been used!'
|
||||
: 'The e-mail address you entered is invalid!';
|
||||
|
||||
if($register['password_confirm'] !== $register['password'])
|
||||
$notices[] = 'The given passwords don\'t match.';
|
||||
|
||||
if(User::validatePassword($register['password']) !== '')
|
||||
$notices[] = 'Your password is too weak!';
|
||||
|
||||
if(!empty($notices))
|
||||
break;
|
||||
|
||||
try {
|
||||
$createUser = User::create(
|
||||
$register['username'],
|
||||
$register['password'],
|
||||
$register['email'],
|
||||
$ipAddress,
|
||||
$countryCode
|
||||
);
|
||||
} catch(UserCreationFailedException $ex) {
|
||||
$notices[] = 'Something went wrong while creating your account, please alert an administrator or a developer about this!';
|
||||
break;
|
||||
}
|
||||
|
||||
$createUser->addRole(UserRole::byDefault());
|
||||
|
||||
url_redirect('auth-login-welcome', ['username' => $createUser->getUsername()]);
|
||||
return;
|
||||
}
|
||||
|
||||
Template::render('auth.register', [
|
||||
'register_notices' => $notices,
|
||||
'register_username' => !empty($register['username']) && is_string($register['username']) ? $register['username'] : '',
|
||||
'register_email' => !empty($register['email']) && is_string($register['email']) ? $register['email'] : '',
|
||||
'register_restricted' => $restricted,
|
||||
]);
|
|
@ -1,21 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Users\User;
|
||||
|
||||
require_once '../../misuzu.php';
|
||||
|
||||
if(!isset($userInfoReal) || !$authToken->hasImpersonatedUserId() || !CSRF::validateRequest()) {
|
||||
url_redirect('index');
|
||||
return;
|
||||
}
|
||||
|
||||
$authToken->removeImpersonatedUserId();
|
||||
$authToken->applyCookie();
|
||||
|
||||
$impUserId = User::hasCurrent() ? User::getCurrent()->getId() : 0;
|
||||
|
||||
url_redirect(
|
||||
$impUserId > 0 ? 'manage-user' : 'index',
|
||||
['user' => $impUserId]
|
||||
);
|
|
@ -1,102 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Users\User;
|
||||
use Misuzu\Users\UserLoginAttempt;
|
||||
use Misuzu\Users\UserSession;
|
||||
use Misuzu\Users\UserSessionCreationFailedException;
|
||||
use Misuzu\Users\UserAuthSession;
|
||||
use Misuzu\Users\UserAuthSessionNotFoundException;
|
||||
|
||||
require_once '../../misuzu.php';
|
||||
|
||||
if(UserSession::hasCurrent()) {
|
||||
url_redirect('index');
|
||||
return;
|
||||
}
|
||||
|
||||
$ipAddress = $_SERVER['REMOTE_ADDR'];
|
||||
$countryCode = $_SERVER['COUNTRY_CODE'] ?? 'XX';
|
||||
$twofactor = !empty($_POST['twofactor']) && is_array($_POST['twofactor']) ? $_POST['twofactor'] : [];
|
||||
$notices = [];
|
||||
$remainingAttempts = UserLoginAttempt::remaining($ipAddress);
|
||||
|
||||
try {
|
||||
$tokenInfo = UserAuthSession::byToken(
|
||||
!empty($_GET['token']) && is_string($_GET['token']) ? $_GET['token'] : (
|
||||
!empty($twofactor['token']) && is_string($twofactor['token']) ? $twofactor['token'] : ''
|
||||
)
|
||||
);
|
||||
} catch(UserAuthSessionNotFoundException $ex) {}
|
||||
|
||||
if(empty($tokenInfo) || $tokenInfo->hasExpired()) {
|
||||
url_redirect('auth-login');
|
||||
return;
|
||||
}
|
||||
|
||||
$userInfo = $tokenInfo->getUser();
|
||||
|
||||
// checking user_totp_key specifically because there's a fringe chance that
|
||||
// there's a token present, but totp is actually disabled
|
||||
if(!$userInfo->hasTOTP()) {
|
||||
url_redirect('auth-login');
|
||||
return;
|
||||
}
|
||||
|
||||
while(!empty($twofactor)) {
|
||||
if(!CSRF::validateRequest()) {
|
||||
$notices[] = 'Was unable to verify the request, please try again!';
|
||||
break;
|
||||
}
|
||||
|
||||
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
|
||||
$redirect = !empty($twofactor['redirect']) && is_string($twofactor['redirect']) ? $twofactor['redirect'] : '';
|
||||
|
||||
if(empty($twofactor['code']) || !is_string($twofactor['code'])) {
|
||||
$notices[] = 'Code field was empty.';
|
||||
break;
|
||||
}
|
||||
|
||||
if($remainingAttempts < 1) {
|
||||
$notices[] = 'There are too many failed login attempts from your IP address, please try again later.';
|
||||
break;
|
||||
}
|
||||
|
||||
if(!in_array($twofactor['code'], $userInfo->getValidTOTPTokens())) {
|
||||
$notices[] = sprintf(
|
||||
"Invalid two factor code, %d attempt%s remaining",
|
||||
$remainingAttempts - 1,
|
||||
$remainingAttempts === 2 ? '' : 's'
|
||||
);
|
||||
UserLoginAttempt::create($ipAddress, $countryCode, false, $userInfo);
|
||||
break;
|
||||
}
|
||||
|
||||
UserLoginAttempt::create($ipAddress, $countryCode, true, $userInfo);
|
||||
$tokenInfo->delete();
|
||||
|
||||
try {
|
||||
$sessionInfo = UserSession::create($userInfo, $ipAddress, $countryCode);
|
||||
$sessionInfo->setCurrent();
|
||||
} catch(UserSessionCreationFailedException $ex) {
|
||||
$notices[] = "Something broke while creating a session for you, please tell an administrator or developer about this!";
|
||||
break;
|
||||
}
|
||||
|
||||
$authToken = AuthToken::create($userInfo, $sessionInfo);
|
||||
$authToken->applyCookie($sessionInfo->getExpiresTime());
|
||||
|
||||
if(!is_local_url($redirect)) {
|
||||
$redirect = url('index');
|
||||
}
|
||||
|
||||
redirect($redirect);
|
||||
return;
|
||||
}
|
||||
|
||||
Template::render('auth.twofactor', [
|
||||
'twofactor_notices' => $notices,
|
||||
'twofactor_redirect' => !empty($_GET['redirect']) && is_string($_GET['redirect']) ? $_GET['redirect'] : url('index'),
|
||||
'twofactor_attempts_remaining' => $remainingAttempts,
|
||||
'twofactor_token' => $tokenInfo->getToken(),
|
||||
]);
|
|
@ -1,2 +0,0 @@
|
|||
<?php
|
||||
require_once __DIR__ . '/index.php';
|
|
@ -1,259 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use RuntimeException;
|
||||
use Misuzu\Users\User;
|
||||
|
||||
require_once '../misuzu.php';
|
||||
|
||||
// basing whether or not this is an xhr request on whether a referrer header is present
|
||||
// this page is never directy accessed, under normal circumstances
|
||||
$redirect = !empty($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : url('index');
|
||||
|
||||
if(!is_local_url($redirect)) {
|
||||
echo render_info('Possible request forgery detected.', 403);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!CSRF::validateRequest()) {
|
||||
echo render_info("Couldn't verify this request, please refresh the page and try again.", 403);
|
||||
return;
|
||||
}
|
||||
|
||||
$currentUserInfo = User::getCurrent();
|
||||
if($currentUserInfo === null) {
|
||||
echo render_info('You must be logged in to manage comments.', 401);
|
||||
return;
|
||||
}
|
||||
|
||||
if($currentUserInfo->isBanned()) {
|
||||
echo render_info('You have been banned, check your profile for more information.', 403);
|
||||
return;
|
||||
}
|
||||
if($currentUserInfo->isSilenced()) {
|
||||
echo render_info('You have been silenced, check your profile for more information.', 403);
|
||||
return;
|
||||
}
|
||||
|
||||
$comments = $msz->getComments();
|
||||
|
||||
$commentPerms = $currentUserInfo->commentPerms();
|
||||
|
||||
$commentId = (string)filter_input(INPUT_GET, 'c', FILTER_SANITIZE_NUMBER_INT);
|
||||
$commentMode = (string)filter_input(INPUT_GET, 'm');
|
||||
$commentVote = (int)filter_input(INPUT_GET, 'v', FILTER_SANITIZE_NUMBER_INT);
|
||||
|
||||
if(!empty($commentId)) {
|
||||
try {
|
||||
$commentInfo = $comments->getPostById($commentId);
|
||||
} catch(RuntimeException $ex) {
|
||||
echo render_info('Post not found.', 404);
|
||||
return;
|
||||
}
|
||||
|
||||
$categoryInfo = $comments->getCategoryByPost($commentInfo);
|
||||
}
|
||||
|
||||
if($commentMode !== 'create' && empty($commentInfo)) {
|
||||
echo render_error(400);
|
||||
return;
|
||||
}
|
||||
|
||||
switch($commentMode) {
|
||||
case 'pin':
|
||||
case 'unpin':
|
||||
if(!$commentPerms['can_pin'] && !$categoryInfo->isOwner($currentUserInfo)) {
|
||||
echo render_info("You're not allowed to pin comments.", 403);
|
||||
break;
|
||||
}
|
||||
|
||||
if($commentInfo->isDeleted()) {
|
||||
echo render_info("This comment doesn't exist!", 400);
|
||||
break;
|
||||
}
|
||||
|
||||
if($commentInfo->isReply()) {
|
||||
echo render_info("You can't pin replies!", 400);
|
||||
break;
|
||||
}
|
||||
|
||||
$isPinning = $commentMode === 'pin';
|
||||
|
||||
if($isPinning) {
|
||||
if($commentInfo->isPinned()) {
|
||||
echo render_info('This comment is already pinned.', 400);
|
||||
break;
|
||||
}
|
||||
|
||||
$comments->pinPost($commentInfo);
|
||||
} else {
|
||||
if(!$commentInfo->isPinned()) {
|
||||
echo render_info("This comment isn't pinned yet.", 400);
|
||||
break;
|
||||
}
|
||||
|
||||
$comments->unpinPost($commentInfo);
|
||||
}
|
||||
|
||||
redirect($redirect . '#comment-' . $commentInfo->getId());
|
||||
break;
|
||||
|
||||
case 'vote':
|
||||
if(!$commentPerms['can_vote'] && !$categoryInfo->isOwner($currentUserInfo)) {
|
||||
echo render_info("You're not allowed to vote on comments.", 403);
|
||||
break;
|
||||
}
|
||||
|
||||
if($commentInfo->isDeleted()) {
|
||||
echo render_info("This comment doesn't exist!", 400);
|
||||
break;
|
||||
}
|
||||
|
||||
if($commentVote > 0)
|
||||
$comments->addPostPositiveVote($commentInfo, $currentUserInfo);
|
||||
elseif($commentVote < 0)
|
||||
$comments->addPostNegativeVote($commentInfo, $currentUserInfo);
|
||||
else
|
||||
$comments->removePostVote($commentInfo, $currentUserInfo);
|
||||
|
||||
redirect($redirect . '#comment-' . $commentInfo->getId());
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
if(!$commentPerms['can_delete'] && !$categoryInfo->isOwner($currentUserInfo)) {
|
||||
echo render_info("You're not allowed to delete comments.", 403);
|
||||
break;
|
||||
}
|
||||
|
||||
if($commentInfo->isDeleted()) {
|
||||
echo render_info(
|
||||
$commentPerms['can_delete_any'] ? 'This comment is already marked for deletion.' : "This comment doesn't exist.",
|
||||
400
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
$isOwnComment = $commentInfo->getUserId() === (string)$currentUserInfo->getId();
|
||||
$isModAction = $commentPerms['can_delete_any'] && !$isOwnComment;
|
||||
|
||||
if(!$isModAction && !$isOwnComment) {
|
||||
echo render_info("You're not allowed to delete comments made by others.", 403);
|
||||
break;
|
||||
}
|
||||
|
||||
$comments->deletePost($commentInfo);
|
||||
|
||||
if($isModAction) {
|
||||
$msz->createAuditLog('COMMENT_ENTRY_DELETE_MOD', [
|
||||
$commentInfo->getId(),
|
||||
$commentUserId = $commentInfo->getUserId(),
|
||||
'<username>',
|
||||
]);
|
||||
} else {
|
||||
$msz->createAuditLog('COMMENT_ENTRY_DELETE', [$commentInfo->getId()]);
|
||||
}
|
||||
|
||||
redirect($redirect);
|
||||
break;
|
||||
|
||||
case 'restore':
|
||||
if(!$commentPerms['can_delete_any']) {
|
||||
echo render_info("You're not allowed to restore deleted comments.", 403);
|
||||
break;
|
||||
}
|
||||
|
||||
if(!$commentInfo->isDeleted()) {
|
||||
echo render_info("This comment isn't in a deleted state.", 400);
|
||||
break;
|
||||
}
|
||||
|
||||
$comments->restorePost($commentInfo);
|
||||
|
||||
$msz->createAuditLog('COMMENT_ENTRY_RESTORE', [
|
||||
$commentInfo->getId(),
|
||||
$commentUserId = $commentInfo->getUserId(),
|
||||
'<username>',
|
||||
]);
|
||||
|
||||
redirect($redirect . '#comment-' . $commentInfo->getId());
|
||||
break;
|
||||
|
||||
case 'create':
|
||||
if(!$commentPerms['can_comment'] && !$categoryInfo->isOwner($currentUserInfo)) {
|
||||
echo render_info("You're not allowed to post comments.", 403);
|
||||
break;
|
||||
}
|
||||
|
||||
if(empty($_POST['comment']) || !is_array($_POST['comment'])) {
|
||||
echo render_info('Missing data.', 400);
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
$categoryId = isset($_POST['comment']['category']) && is_string($_POST['comment']['category'])
|
||||
? (int)$_POST['comment']['category']
|
||||
: 0;
|
||||
$categoryInfo = $comments->getCategoryById($categoryId);
|
||||
} catch(RuntimeException $ex) {
|
||||
echo render_info('This comment category doesn\'t exist.', 404);
|
||||
break;
|
||||
}
|
||||
|
||||
if($categoryInfo->isLocked() && !$commentPerms['can_lock']) {
|
||||
echo render_info('This comment category has been locked.', 403);
|
||||
break;
|
||||
}
|
||||
|
||||
$commentText = !empty($_POST['comment']['text']) && is_string($_POST['comment']['text']) ? $_POST['comment']['text'] : '';
|
||||
$commentReply = (string)(!empty($_POST['comment']['reply']) && is_string($_POST['comment']['reply']) ? (int)$_POST['comment']['reply'] : 0);
|
||||
$commentLock = !empty($_POST['comment']['lock']) && $commentPerms['can_lock'];
|
||||
$commentPin = !empty($_POST['comment']['pin']) && $commentPerms['can_pin'];
|
||||
|
||||
if($commentLock) {
|
||||
if($categoryInfo->isLocked())
|
||||
$comments->unlockCategory($categoryInfo);
|
||||
else
|
||||
$comments->lockCategory($categoryInfo);
|
||||
}
|
||||
|
||||
if(strlen($commentText) > 0) {
|
||||
$commentText = preg_replace("/[\r\n]{2,}/", "\n", $commentText);
|
||||
} else {
|
||||
if($commentPerms['can_lock']) {
|
||||
echo render_info('The action has been processed.', 400);
|
||||
} else {
|
||||
echo render_info('Your comment is too short.', 400);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(mb_strlen($commentText) > 5000) {
|
||||
echo render_info('Your comment is too long.', 400);
|
||||
break;
|
||||
}
|
||||
|
||||
if($commentReply > 0) {
|
||||
try {
|
||||
$parentInfo = $comments->getPostById($commentReply);
|
||||
} catch(RuntimeException $ex) {}
|
||||
|
||||
if(!isset($parentInfo) || $parentInfo->isDeleted()) {
|
||||
echo render_info('The comment you tried to reply to does not exist.', 404);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$commentInfo = $comments->createPost(
|
||||
$categoryInfo,
|
||||
$parentInfo ?? null,
|
||||
$currentUserInfo,
|
||||
$commentText,
|
||||
$commentPin
|
||||
);
|
||||
|
||||
redirect($redirect . '#comment-' . $commentInfo->getId());
|
||||
break;
|
||||
|
||||
default:
|
||||
echo render_info('Not found.', 404);
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Users\User;
|
||||
|
||||
require_once '../../misuzu.php';
|
||||
|
||||
$forumId = !empty($_GET['f']) && is_string($_GET['f']) ? (int)$_GET['f'] : 0;
|
||||
$forumId = max($forumId, 0);
|
||||
|
||||
if($forumId === 0) {
|
||||
url_redirect('forum-index');
|
||||
exit;
|
||||
}
|
||||
|
||||
$forum = forum_get($forumId);
|
||||
$forumUser = User::getCurrent();
|
||||
$forumUserId = $forumUser === null ? 0 : $forumUser->getId();
|
||||
|
||||
if(empty($forum) || ($forum['forum_type'] == MSZ_FORUM_TYPE_LINK && empty($forum['forum_link']))) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
$perms = forum_perms_get_user($forum['forum_id'], $forumUserId)[MSZ_FORUM_PERMS_GENERAL];
|
||||
|
||||
if(!perms_check($perms, MSZ_FORUM_PERM_VIEW_FORUM)) {
|
||||
echo render_error(403);
|
||||
return;
|
||||
}
|
||||
|
||||
if(isset($forumUser) && $forumUser->hasActiveWarning())
|
||||
$perms &= ~MSZ_FORUM_PERM_SET_WRITE;
|
||||
|
||||
Template::set('forum_perms', $perms);
|
||||
|
||||
if($forum['forum_type'] == MSZ_FORUM_TYPE_LINK) {
|
||||
forum_increment_clicks($forum['forum_id']);
|
||||
redirect($forum['forum_link']);
|
||||
return;
|
||||
}
|
||||
|
||||
$forumPagination = new Pagination($forum['forum_topic_count'], 20);
|
||||
|
||||
if(!$forumPagination->hasValidOffset() && $forum['forum_topic_count'] > 0) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
$forumMayHaveTopics = forum_may_have_topics($forum['forum_type']);
|
||||
$topics = $forumMayHaveTopics
|
||||
? forum_topic_listing(
|
||||
$forum['forum_id'],
|
||||
$forumUserId,
|
||||
$forumPagination->getOffset(),
|
||||
$forumPagination->getRange(),
|
||||
perms_check($perms, MSZ_FORUM_PERM_DELETE_ANY_POST)
|
||||
)
|
||||
: [];
|
||||
|
||||
$forumMayHaveChildren = forum_may_have_children($forum['forum_type']);
|
||||
|
||||
if($forumMayHaveChildren) {
|
||||
$forum['forum_subforums'] = forum_get_children($forum['forum_id'], $forumUserId);
|
||||
|
||||
foreach($forum['forum_subforums'] as $skey => $subforum) {
|
||||
$forum['forum_subforums'][$skey]['forum_subforums']
|
||||
= forum_get_children($subforum['forum_id'], $forumUserId);
|
||||
}
|
||||
}
|
||||
|
||||
Template::render('forum.forum', [
|
||||
'forum_breadcrumbs' => forum_get_breadcrumbs($forum['forum_id']),
|
||||
'global_accent_colour' => forum_get_colour($forum['forum_id']),
|
||||
'forum_may_have_topics' => $forumMayHaveTopics,
|
||||
'forum_may_have_children' => $forumMayHaveChildren,
|
||||
'forum_info' => $forum,
|
||||
'forum_topics' => $topics,
|
||||
'forum_pagination' => $forumPagination,
|
||||
]);
|
|
@ -1,41 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Users\User;
|
||||
|
||||
require_once '../../misuzu.php';
|
||||
|
||||
$indexMode = !empty($_GET['m']) && is_string($_GET['m']) ? (string)$_GET['m'] : '';
|
||||
$forumId = !empty($_GET['f']) && is_string($_GET['f']) ? (int)$_GET['f'] : 0;
|
||||
|
||||
$currentUser = User::getCurrent();
|
||||
$currentUserId = $currentUser === null ? 0 : $currentUser->getId();
|
||||
|
||||
switch($indexMode) {
|
||||
case 'mark':
|
||||
url_redirect($forumId < 1 ? 'forum-mark-global' : 'forum-mark-single', ['forum' => $forumId]);
|
||||
break;
|
||||
|
||||
default:
|
||||
$categories = forum_get_root_categories($currentUserId);
|
||||
$blankForum = count($categories) < 1;
|
||||
|
||||
foreach($categories as $key => $category) {
|
||||
$categories[$key]['forum_subforums'] = forum_get_children($category['forum_id'], $currentUserId);
|
||||
|
||||
foreach($categories[$key]['forum_subforums'] as $skey => $sub) {
|
||||
if(!forum_may_have_children($sub['forum_type'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$categories[$key]['forum_subforums'][$skey]['forum_subforums']
|
||||
= forum_get_children($sub['forum_id'], $currentUserId);
|
||||
}
|
||||
}
|
||||
|
||||
Template::render('forum.index', [
|
||||
'forum_categories' => $categories,
|
||||
'forum_empty' => $blankForum,
|
||||
]);
|
||||
break;
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Users\User;
|
||||
|
||||
require_once '../../misuzu.php';
|
||||
|
||||
if(!User::hasCurrent() || !perms_check_user(MSZ_PERMS_FORUM, User::getCurrent()->getId(), MSZ_PERM_FORUM_VIEW_LEADERBOARD)) {
|
||||
echo render_error(403);
|
||||
return;
|
||||
}
|
||||
|
||||
$leaderboardMode = !empty($_GET['mode']) && is_string($_GET['mode']) && ctype_lower($_GET['mode']) ? $_GET['mode'] : '';
|
||||
$leaderboardId = !empty($_GET['id']) && is_string($_GET['id'])
|
||||
&& ctype_digit($_GET['id'])
|
||||
? $_GET['id']
|
||||
: MSZ_FORUM_LEADERBOARD_CATEGORY_ALL;
|
||||
$leaderboardIdLength = strlen($leaderboardId);
|
||||
|
||||
$leaderboardYear = $leaderboardIdLength === 4 || $leaderboardIdLength === 6 ? substr($leaderboardId, 0, 4) : null;
|
||||
$leaderboardMonth = $leaderboardIdLength === 6 ? substr($leaderboardId, 4, 2) : null;
|
||||
|
||||
if(empty($_GET['allow_unranked'])) {
|
||||
[
|
||||
'forum_leader.unranked.forum' => $unrankedForums,
|
||||
'forum_leader.unranked.topic' => $unrankedTopics,
|
||||
] = $cfg->getValues([
|
||||
'forum_leader.unranked.forum:a',
|
||||
'forum_leader.unranked.topic:a',
|
||||
]);
|
||||
} else $unrankedForums = $unrankedTopics = [];
|
||||
|
||||
$leaderboards = forum_leaderboard_categories();
|
||||
$leaderboard = forum_leaderboard_listing($leaderboardYear, $leaderboardMonth, $unrankedForums, $unrankedTopics);
|
||||
|
||||
$leaderboardName = 'All Time';
|
||||
|
||||
if($leaderboardYear) {
|
||||
$leaderboardName = "Leaderboard {$leaderboardYear}";
|
||||
|
||||
if($leaderboardMonth)
|
||||
$leaderboardName .= "-{$leaderboardMonth}";
|
||||
}
|
||||
|
||||
if($leaderboardMode === 'markdown') {
|
||||
$markdown = <<<MD
|
||||
# {$leaderboardName}
|
||||
|
||||
| Rank | Usename | Post count |
|
||||
| ----:|:------- | ----------:|
|
||||
|
||||
MD;
|
||||
|
||||
foreach($leaderboard as $user) {
|
||||
$markdown .= sprintf("| %s | [%s](%s%s) | %s |\r\n", $user['rank'], $user['username'], url_prefix(false), url('user-profile', ['user' => $user['user_id']]), $user['posts']);
|
||||
}
|
||||
|
||||
Template::set('leaderboard_markdown', $markdown);
|
||||
}
|
||||
|
||||
Template::render('forum.leaderboard', [
|
||||
'leaderboard_id' => $leaderboardId,
|
||||
'leaderboard_name' => $leaderboardName,
|
||||
'leaderboard_categories' => $leaderboards,
|
||||
'leaderboard_data' => $leaderboard,
|
||||
'leaderboard_mode' => $leaderboardMode,
|
||||
]);
|
|
@ -1,217 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Users\User;
|
||||
use Misuzu\Users\UserSession;
|
||||
|
||||
require_once '../../misuzu.php';
|
||||
|
||||
$postId = !empty($_GET['p']) && is_string($_GET['p']) ? (int)$_GET['p'] : 0;
|
||||
$postMode = !empty($_GET['m']) && is_string($_GET['m']) ? (string)$_GET['m'] : '';
|
||||
$submissionConfirmed = !empty($_GET['confirm']) && is_string($_GET['confirm']) && $_GET['confirm'] === '1';
|
||||
|
||||
$postRequestVerified = CSRF::validateRequest();
|
||||
|
||||
if(!empty($postMode) && !UserSession::hasCurrent()) {
|
||||
echo render_info('You must be logged in to manage posts.', 401);
|
||||
return;
|
||||
}
|
||||
|
||||
$currentUser = User::getCurrent();
|
||||
$currentUserId = $currentUser === null ? 0 : $currentUser->getId();
|
||||
|
||||
if(isset($currentUser) && $currentUser->isBanned()) {
|
||||
echo render_info('You have been banned, check your profile for more information.', 403);
|
||||
return;
|
||||
}
|
||||
if(isset($currentUser) && $currentUser->isSilenced()) {
|
||||
echo render_info('You have been silenced, check your profile for more information.', 403);
|
||||
return;
|
||||
}
|
||||
|
||||
$postInfo = forum_post_get($postId, true);
|
||||
$perms = empty($postInfo)
|
||||
? 0
|
||||
: forum_perms_get_user($postInfo['forum_id'], $currentUserId)[MSZ_FORUM_PERMS_GENERAL];
|
||||
|
||||
switch($postMode) {
|
||||
case 'delete':
|
||||
$canDelete = forum_post_can_delete($postInfo, $currentUserId);
|
||||
$canDeleteMsg = '';
|
||||
$responseCode = 200;
|
||||
|
||||
switch($canDelete) {
|
||||
case MSZ_E_FORUM_POST_DELETE_USER: // i don't think this is ever reached but we may as well have it
|
||||
$responseCode = 401;
|
||||
$canDeleteMsg = 'You must be logged in to delete posts.';
|
||||
break;
|
||||
case MSZ_E_FORUM_POST_DELETE_POST:
|
||||
$responseCode = 404;
|
||||
$canDeleteMsg = "This post doesn't exist.";
|
||||
break;
|
||||
case MSZ_E_FORUM_POST_DELETE_DELETED:
|
||||
$responseCode = 404;
|
||||
$canDeleteMsg = 'This post has already been marked as deleted.';
|
||||
break;
|
||||
case MSZ_E_FORUM_POST_DELETE_OWNER:
|
||||
$responseCode = 403;
|
||||
$canDeleteMsg = 'You can only delete your own posts.';
|
||||
break;
|
||||
case MSZ_E_FORUM_POST_DELETE_OLD:
|
||||
$responseCode = 401;
|
||||
$canDeleteMsg = 'This post has existed for too long. Ask a moderator to remove if it absolutely necessary.';
|
||||
break;
|
||||
case MSZ_E_FORUM_POST_DELETE_PERM:
|
||||
$responseCode = 401;
|
||||
$canDeleteMsg = 'You are not allowed to delete posts.';
|
||||
break;
|
||||
case MSZ_E_FORUM_POST_DELETE_OP:
|
||||
$responseCode = 403;
|
||||
$canDeleteMsg = 'This is the opening post of a topic, it may not be deleted without deleting the entire topic as well.';
|
||||
break;
|
||||
case MSZ_E_FORUM_POST_DELETE_OK:
|
||||
break;
|
||||
default:
|
||||
$responseCode = 500;
|
||||
$canDeleteMsg = sprintf('Unknown error \'%d\'', $canDelete);
|
||||
}
|
||||
|
||||
if($canDelete !== MSZ_E_FORUM_POST_DELETE_OK) {
|
||||
echo render_info($canDeleteMsg, $responseCode);
|
||||
break;
|
||||
}
|
||||
|
||||
if($postRequestVerified && !$submissionConfirmed) {
|
||||
url_redirect('forum-post', [
|
||||
'post' => $postInfo['post_id'],
|
||||
'post_fragment' => 'p' . $postInfo['post_id'],
|
||||
]);
|
||||
break;
|
||||
} elseif(!$postRequestVerified) {
|
||||
Template::render('forum.confirm', [
|
||||
'title' => 'Confirm post deletion',
|
||||
'class' => 'far fa-trash-alt',
|
||||
'message' => sprintf('You are about to delete post #%d. Are you sure about that?', $postInfo['post_id']),
|
||||
'params' => [
|
||||
'p' => $postInfo['post_id'],
|
||||
'm' => 'delete',
|
||||
],
|
||||
]);
|
||||
break;
|
||||
}
|
||||
|
||||
$deletePost = forum_post_delete($postInfo['post_id']);
|
||||
|
||||
if($deletePost) {
|
||||
$msz->createAuditLog('FORUM_POST_DELETE', [$postInfo['post_id']]);
|
||||
}
|
||||
|
||||
if(!$deletePost) {
|
||||
echo render_error(500);
|
||||
break;
|
||||
}
|
||||
|
||||
url_redirect('forum-topic', ['topic' => $postInfo['topic_id']]);
|
||||
break;
|
||||
|
||||
case 'nuke':
|
||||
if(!perms_check($perms, MSZ_FORUM_PERM_DELETE_ANY_POST)) {
|
||||
echo render_error(403);
|
||||
break;
|
||||
}
|
||||
|
||||
if($postRequestVerified && !$submissionConfirmed) {
|
||||
url_redirect('forum-post', [
|
||||
'post' => $postInfo['post_id'],
|
||||
'post_fragment' => 'p' . $postInfo['post_id'],
|
||||
]);
|
||||
break;
|
||||
} elseif(!$postRequestVerified) {
|
||||
Template::render('forum.confirm', [
|
||||
'title' => 'Confirm post nuke',
|
||||
'class' => 'fas fa-radiation',
|
||||
'message' => sprintf('You are about to PERMANENTLY DELETE post #%d. Are you sure about that?', $postInfo['post_id']),
|
||||
'params' => [
|
||||
'p' => $postInfo['post_id'],
|
||||
'm' => 'nuke',
|
||||
],
|
||||
]);
|
||||
break;
|
||||
}
|
||||
|
||||
$nukePost = forum_post_nuke($postInfo['post_id']);
|
||||
|
||||
if(!$nukePost) {
|
||||
echo render_error(500);
|
||||
break;
|
||||
}
|
||||
|
||||
$msz->createAuditLog('FORUM_POST_NUKE', [$postInfo['post_id']]);
|
||||
|
||||
url_redirect('forum-topic', ['topic' => $postInfo['topic_id']]);
|
||||
break;
|
||||
|
||||
case 'restore':
|
||||
if(!perms_check($perms, MSZ_FORUM_PERM_DELETE_ANY_POST)) {
|
||||
echo render_error(403);
|
||||
break;
|
||||
}
|
||||
|
||||
if($postRequestVerified && !$submissionConfirmed) {
|
||||
url_redirect('forum-post', [
|
||||
'post' => $postInfo['post_id'],
|
||||
'post_fragment' => 'p' . $postInfo['post_id'],
|
||||
]);
|
||||
break;
|
||||
} elseif(!$postRequestVerified) {
|
||||
Template::render('forum.confirm', [
|
||||
'title' => 'Confirm post restore',
|
||||
'class' => 'fas fa-magic',
|
||||
'message' => sprintf('You are about to restore post #%d. Are you sure about that?', $postInfo['post_id']),
|
||||
'params' => [
|
||||
'p' => $postInfo['post_id'],
|
||||
'm' => 'restore',
|
||||
],
|
||||
]);
|
||||
break;
|
||||
}
|
||||
|
||||
$restorePost = forum_post_restore($postInfo['post_id']);
|
||||
|
||||
if(!$restorePost) {
|
||||
echo render_error(500);
|
||||
break;
|
||||
}
|
||||
|
||||
$msz->createAuditLog('FORUM_POST_RESTORE', [$postInfo['post_id']]);
|
||||
|
||||
url_redirect('forum-topic', ['topic' => $postInfo['topic_id']]);
|
||||
break;
|
||||
|
||||
default: // function as an alt for topic.php?p= by default
|
||||
$canDeleteAny = perms_check($perms, MSZ_FORUM_PERM_DELETE_ANY_POST);
|
||||
|
||||
if(!empty($postInfo['post_deleted']) && !$canDeleteAny) {
|
||||
echo render_error(404);
|
||||
break;
|
||||
}
|
||||
|
||||
$postFind = forum_post_find($postInfo['post_id'], $currentUserId);
|
||||
|
||||
if(empty($postFind)) {
|
||||
echo render_error(404);
|
||||
break;
|
||||
}
|
||||
|
||||
if($canDeleteAny) {
|
||||
$postInfo['preceeding_post_count'] += $postInfo['preceeding_post_deleted_count'];
|
||||
}
|
||||
|
||||
unset($postInfo['preceeding_post_deleted_count']);
|
||||
|
||||
url_redirect('forum-topic', [
|
||||
'topic' => $postFind['topic_id'],
|
||||
'page' => floor($postFind['preceeding_post_count'] / MSZ_FORUM_POSTS_PER_PAGE) + 1,
|
||||
]);
|
||||
break;
|
||||
}
|
|
@ -1,276 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Parsers\Parser;
|
||||
use Misuzu\Users\User;
|
||||
|
||||
require_once '../../misuzu.php';
|
||||
|
||||
$currentUser = User::getCurrent();
|
||||
|
||||
if($currentUser === null) {
|
||||
echo render_error(401);
|
||||
return;
|
||||
}
|
||||
|
||||
$currentUserId = $currentUser->getId();
|
||||
|
||||
if($currentUser->hasActiveWarning()) {
|
||||
echo render_error(403);
|
||||
return;
|
||||
}
|
||||
|
||||
$forumPostingModes = [
|
||||
'create', 'edit', 'quote', 'preview',
|
||||
];
|
||||
|
||||
if(!empty($_POST)) {
|
||||
$mode = !empty($_POST['post']['mode']) && is_string($_POST['post']['mode']) ? $_POST['post']['mode'] : 'create';
|
||||
$postId = !empty($_POST['post']['id']) && is_string($_POST['post']['id']) ? (int)$_POST['post']['id'] : 0;
|
||||
$topicId = !empty($_POST['post']['topic']) && is_string($_POST['post']['topic']) ? (int)$_POST['post']['topic'] : 0;
|
||||
$forumId = !empty($_POST['post']['forum']) && is_string($_POST['post']['forum']) ? (int)$_POST['post']['forum'] : 0;
|
||||
} else {
|
||||
$mode = !empty($_GET['m']) && is_string($_GET['m']) ? $_GET['m'] : 'create';
|
||||
$postId = !empty($_GET['p']) && is_string($_GET['p']) ? (int)$_GET['p'] : 0;
|
||||
$topicId = !empty($_GET['t']) && is_string($_GET['t']) ? (int)$_GET['t'] : 0;
|
||||
$forumId = !empty($_GET['f']) && is_string($_GET['f']) ? (int)$_GET['f'] : 0;
|
||||
}
|
||||
|
||||
if(!in_array($mode, $forumPostingModes, true)) {
|
||||
echo render_error(400);
|
||||
return;
|
||||
}
|
||||
|
||||
if($mode === 'preview') {
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
|
||||
$postText = (string)($_POST['post']['text']);
|
||||
$postParser = (int)($_POST['post']['parser']);
|
||||
|
||||
if(!Parser::isValid($postParser)) {
|
||||
http_response_code(400);
|
||||
return;
|
||||
}
|
||||
|
||||
http_response_code(200);
|
||||
echo Parser::instance($postParser)->parseText(htmlspecialchars($postText));
|
||||
return;
|
||||
}
|
||||
|
||||
if(empty($postId) && empty($topicId) && empty($forumId)) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!empty($postId)) {
|
||||
$post = forum_post_get($postId);
|
||||
|
||||
if(isset($post['topic_id'])) { // should automatic cross-quoting be a thing? if so, check if $topicId is < 1 first
|
||||
$topicId = (int)$post['topic_id'];
|
||||
}
|
||||
}
|
||||
|
||||
if(!empty($topicId)) {
|
||||
$topic = forum_topic_get($topicId);
|
||||
|
||||
if(isset($topic['forum_id'])) {
|
||||
$forumId = (int)$topic['forum_id'];
|
||||
}
|
||||
}
|
||||
|
||||
if(!empty($forumId)) {
|
||||
$forum = forum_get($forumId);
|
||||
}
|
||||
|
||||
if(empty($forum)) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
$perms = forum_perms_get_user($forum['forum_id'], $currentUserId)[MSZ_FORUM_PERMS_GENERAL];
|
||||
|
||||
if($forum['forum_archived']
|
||||
|| (!empty($topic['topic_locked']) && !perms_check($perms, MSZ_FORUM_PERM_LOCK_TOPIC))
|
||||
|| !perms_check($perms, MSZ_FORUM_PERM_VIEW_FORUM | MSZ_FORUM_PERM_CREATE_POST)
|
||||
|| (empty($topic) && !perms_check($perms, MSZ_FORUM_PERM_CREATE_TOPIC))) {
|
||||
echo render_error(403);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!forum_may_have_topics($forum['forum_type'])) {
|
||||
echo render_error(400);
|
||||
return;
|
||||
}
|
||||
|
||||
$topicTypes = [];
|
||||
|
||||
if($mode === 'create' || $mode === 'edit') {
|
||||
$topicTypes[MSZ_TOPIC_TYPE_DISCUSSION] = 'Normal discussion';
|
||||
|
||||
if(perms_check($perms, MSZ_FORUM_PERM_STICKY_TOPIC)) {
|
||||
$topicTypes[MSZ_TOPIC_TYPE_STICKY] = 'Sticky topic';
|
||||
}
|
||||
if(perms_check($perms, MSZ_FORUM_PERM_ANNOUNCE_TOPIC)) {
|
||||
$topicTypes[MSZ_TOPIC_TYPE_ANNOUNCEMENT] = 'Announcement';
|
||||
}
|
||||
if(perms_check($perms, MSZ_FORUM_PERM_GLOBAL_ANNOUNCE_TOPIC)) {
|
||||
$topicTypes[MSZ_TOPIC_TYPE_GLOBAL_ANNOUNCEMENT] = 'Global Announcement';
|
||||
}
|
||||
}
|
||||
|
||||
// edit mode stuff
|
||||
if($mode === 'edit') {
|
||||
if(empty($post)) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!perms_check($perms, $post['poster_id'] === $currentUserId ? MSZ_FORUM_PERM_EDIT_POST : MSZ_FORUM_PERM_EDIT_ANY_POST)) {
|
||||
echo render_error(403);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$notices = [];
|
||||
|
||||
if(!empty($_POST)) {
|
||||
$topicTitle = $_POST['post']['title'] ?? '';
|
||||
$postText = $_POST['post']['text'] ?? '';
|
||||
$postParser = (int)($_POST['post']['parser'] ?? Parser::BBCODE);
|
||||
$topicType = isset($_POST['post']['type']) ? (int)$_POST['post']['type'] : null;
|
||||
$postSignature = isset($_POST['post']['signature']);
|
||||
|
||||
if(!CSRF::validateRequest()) {
|
||||
$notices[] = 'Could not verify request.';
|
||||
} else {
|
||||
$isEditingTopic = empty($topic) || ($mode === 'edit' && $post['is_opening_post']);
|
||||
|
||||
if($mode === 'create') {
|
||||
$timeoutCheck = max(1, forum_timeout($forumId, $currentUserId));
|
||||
|
||||
if($timeoutCheck < 5) {
|
||||
$notices[] = sprintf("You're posting too quickly! Please wait %s seconds before posting again.", number_format($timeoutCheck));
|
||||
$notices[] = "It's possible that your post went through successfully and you pressed the submit button twice by accident.";
|
||||
}
|
||||
}
|
||||
|
||||
if($isEditingTopic) {
|
||||
$originalTopicTitle = $topic['topic_title'] ?? null;
|
||||
$topicTitleChanged = $topicTitle !== $originalTopicTitle;
|
||||
$originalTopicType = (int)($topic['topic_type'] ?? MSZ_TOPIC_TYPE_DISCUSSION);
|
||||
$topicTypeChanged = $topicType !== null && $topicType !== $originalTopicType;
|
||||
|
||||
switch(forum_validate_title($topicTitle)) {
|
||||
case 'too-short':
|
||||
$notices[] = 'Topic title was too short.';
|
||||
break;
|
||||
|
||||
case 'too-long':
|
||||
$notices[] = 'Topic title was too long.';
|
||||
break;
|
||||
}
|
||||
|
||||
if($mode === 'create' && $topicType === null) {
|
||||
$topicType = array_key_first($topicTypes);
|
||||
} elseif(!array_key_exists($topicType, $topicTypes) && $topicTypeChanged) {
|
||||
$notices[] = 'You are not allowed to set this topic type.';
|
||||
}
|
||||
}
|
||||
|
||||
if(!Parser::isValid($postParser)) {
|
||||
$notices[] = 'Invalid parser selected.';
|
||||
}
|
||||
|
||||
switch(forum_validate_post($postText)) {
|
||||
case 'too-short':
|
||||
$notices[] = 'Post content was too short.';
|
||||
break;
|
||||
|
||||
case 'too-long':
|
||||
$notices[] = 'Post content was too long.';
|
||||
break;
|
||||
}
|
||||
|
||||
if(empty($notices)) {
|
||||
switch($mode) {
|
||||
case 'create':
|
||||
if(!empty($topic)) {
|
||||
forum_topic_bump($topic['topic_id']);
|
||||
} else {
|
||||
$topicId = forum_topic_create(
|
||||
$forum['forum_id'],
|
||||
$currentUserId,
|
||||
$topicTitle,
|
||||
$topicType
|
||||
);
|
||||
}
|
||||
|
||||
$postId = forum_post_create(
|
||||
$topicId,
|
||||
$forum['forum_id'],
|
||||
$currentUserId,
|
||||
$_SERVER['REMOTE_ADDR'],
|
||||
$postText,
|
||||
$postParser,
|
||||
$postSignature
|
||||
);
|
||||
forum_topic_mark_read($currentUserId, $topicId, $forum['forum_id']);
|
||||
forum_count_increase($forum['forum_id'], empty($topic));
|
||||
break;
|
||||
|
||||
case 'edit':
|
||||
$markUpdated = $post['poster_id'] === $currentUserId
|
||||
&& $post['post_created_unix'] < strtotime('-1 minutes')
|
||||
&& $postText !== $post['post_text'];
|
||||
|
||||
if(!forum_post_update($postId, $_SERVER['REMOTE_ADDR'], $postText, $postParser, $postSignature, $markUpdated)) {
|
||||
$notices[] = 'Post edit failed.';
|
||||
}
|
||||
|
||||
if($isEditingTopic && ($topicTitleChanged || $topicTypeChanged)) {
|
||||
if(!forum_topic_update($topicId, $topicTitle, $topicType)) {
|
||||
$notices[] = 'Topic update failed.';
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(empty($notices)) {
|
||||
$redirect = url(empty($topic) ? 'forum-topic' : 'forum-post', [
|
||||
'topic' => $topicId ?? 0,
|
||||
'post' => $postId ?? 0,
|
||||
'post_fragment' => 'p' . ($postId ?? 0),
|
||||
]);
|
||||
redirect($redirect);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!empty($topic)) {
|
||||
Template::set('posting_topic', $topic);
|
||||
}
|
||||
|
||||
if($mode === 'edit') { // $post is pretty much sure to be populated at this point
|
||||
Template::set('posting_post', $post);
|
||||
}
|
||||
|
||||
$displayInfo = forum_posting_info($currentUserId);
|
||||
|
||||
Template::render('forum.posting', [
|
||||
'posting_breadcrumbs' => forum_get_breadcrumbs($forumId),
|
||||
'global_accent_colour' => forum_get_colour($forumId),
|
||||
'posting_forum' => $forum,
|
||||
'posting_info' => $displayInfo,
|
||||
'posting_notices' => $notices,
|
||||
'posting_mode' => $mode,
|
||||
'posting_types' => $topicTypes,
|
||||
'posting_defaults' => [
|
||||
'title' => $topicTitle ?? null,
|
||||
'type' => $topicType ?? null,
|
||||
'text' => $postText ?? null,
|
||||
'parser' => $postParser ?? null,
|
||||
'signature' => $postSignature ?? null,
|
||||
],
|
||||
]);
|
|
@ -1,339 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Users\User;
|
||||
use Misuzu\Users\UserSession;
|
||||
|
||||
require_once '../../misuzu.php';
|
||||
|
||||
$postId = !empty($_GET['p']) && is_string($_GET['p']) ? (int)$_GET['p'] : 0;
|
||||
$topicId = !empty($_GET['t']) && is_string($_GET['t']) ? (int)$_GET['t'] : 0;
|
||||
$moderationMode = !empty($_GET['m']) && is_string($_GET['m']) ? (string)$_GET['m'] : '';
|
||||
$submissionConfirmed = !empty($_GET['confirm']) && is_string($_GET['confirm']) && $_GET['confirm'] === '1';
|
||||
|
||||
$topicUser = User::getCurrent();
|
||||
$topicUserId = $topicUser === null ? 0 : $topicUser->getId();
|
||||
|
||||
if($topicId < 1 && $postId > 0) {
|
||||
$postInfo = forum_post_find($postId, $topicUserId);
|
||||
|
||||
if(!empty($postInfo['topic_id'])) {
|
||||
$topicId = (int)$postInfo['topic_id'];
|
||||
}
|
||||
}
|
||||
|
||||
$topic = forum_topic_get($topicId, true);
|
||||
$perms = $topic
|
||||
? forum_perms_get_user($topic['forum_id'], $topicUserId)[MSZ_FORUM_PERMS_GENERAL]
|
||||
: 0;
|
||||
|
||||
if(isset($topicUser) && $topicUser->hasActiveWarning())
|
||||
$perms &= ~MSZ_FORUM_PERM_SET_WRITE;
|
||||
|
||||
$topicIsNuked = empty($topic['topic_id']);
|
||||
$topicIsDeleted = !empty($topic['topic_deleted']);
|
||||
$canDeleteAny = perms_check($perms, MSZ_FORUM_PERM_DELETE_ANY_POST);
|
||||
|
||||
if($topicIsNuked || $topicIsDeleted) {
|
||||
$topicRedirectInfo = forum_topic_redir_info($topicId);
|
||||
Template::set('topic_redir_info', $topicRedirectInfo);
|
||||
|
||||
if($topicIsNuked || !$canDeleteAny) {
|
||||
if(empty($topicRedirectInfo))
|
||||
echo render_error(404);
|
||||
else
|
||||
header('Location: ' . $topicRedirectInfo->topic_redir_url);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(!perms_check($perms, MSZ_FORUM_PERM_VIEW_FORUM)) {
|
||||
echo render_error(403);
|
||||
return;
|
||||
}
|
||||
|
||||
$topicIsLocked = !empty($topic['topic_locked']);
|
||||
$topicIsArchived = !empty($topic['topic_archived']);
|
||||
$topicPostsTotal = (int)($topic['topic_count_posts'] + $topic['topic_count_posts_deleted']);
|
||||
$topicIsFrozen = $topicIsArchived || $topicIsDeleted;
|
||||
$canDeleteOwn = !$topicIsFrozen && !$topicIsLocked && perms_check($perms, MSZ_FORUM_PERM_DELETE_POST);
|
||||
$canBumpTopic = !$topicIsFrozen && perms_check($perms, MSZ_FORUM_PERM_BUMP_TOPIC);
|
||||
$canLockTopic = !$topicIsFrozen && perms_check($perms, MSZ_FORUM_PERM_LOCK_TOPIC);
|
||||
$canNukeOrRestore = $canDeleteAny && $topicIsDeleted;
|
||||
$canDelete = !$topicIsDeleted && (
|
||||
$canDeleteAny || (
|
||||
$topicPostsTotal > 0
|
||||
&& $topicPostsTotal <= MSZ_FORUM_TOPIC_DELETE_POST_LIMIT
|
||||
&& $canDeleteOwn
|
||||
&& $topic['author_user_id'] === $topicUserId
|
||||
)
|
||||
);
|
||||
|
||||
$validModerationModes = [
|
||||
'delete', 'restore', 'nuke',
|
||||
'bump', 'lock', 'unlock',
|
||||
];
|
||||
|
||||
if(in_array($moderationMode, $validModerationModes, true)) {
|
||||
if(!CSRF::validateRequest()) {
|
||||
echo render_info("Couldn't verify this request, please refresh the page and try again.", 403);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!UserSession::hasCurrent()) {
|
||||
echo render_info('You must be logged in to manage posts.', 401);
|
||||
return;
|
||||
}
|
||||
|
||||
if($topicUser->isBanned()) {
|
||||
echo render_info('You have been banned, check your profile for more information.', 403);
|
||||
return;
|
||||
}
|
||||
if($topicUser->isSilenced()) {
|
||||
echo render_info('You have been silenced, check your profile for more information.', 403);
|
||||
return;
|
||||
}
|
||||
|
||||
switch($moderationMode) {
|
||||
case 'delete':
|
||||
$canDeleteCode = forum_topic_can_delete($topic, $topicUserId);
|
||||
$canDeleteMsg = '';
|
||||
$responseCode = 200;
|
||||
|
||||
switch($canDeleteCode) {
|
||||
case MSZ_E_FORUM_TOPIC_DELETE_USER:
|
||||
$responseCode = 401;
|
||||
$canDeleteMsg = 'You must be logged in to delete topics.';
|
||||
break;
|
||||
case MSZ_E_FORUM_TOPIC_DELETE_TOPIC:
|
||||
$responseCode = 404;
|
||||
$canDeleteMsg = "This topic doesn't exist.";
|
||||
break;
|
||||
case MSZ_E_FORUM_TOPIC_DELETE_DELETED:
|
||||
$responseCode = 404;
|
||||
$canDeleteMsg = 'This topic has already been marked as deleted.';
|
||||
break;
|
||||
case MSZ_E_FORUM_TOPIC_DELETE_OWNER:
|
||||
$responseCode = 403;
|
||||
$canDeleteMsg = 'You can only delete your own topics.';
|
||||
break;
|
||||
case MSZ_E_FORUM_TOPIC_DELETE_OLD:
|
||||
$responseCode = 401;
|
||||
$canDeleteMsg = 'This topic has existed for too long. Ask a moderator to remove if it absolutely necessary.';
|
||||
break;
|
||||
case MSZ_E_FORUM_TOPIC_DELETE_PERM:
|
||||
$responseCode = 401;
|
||||
$canDeleteMsg = 'You are not allowed to delete topics.';
|
||||
break;
|
||||
case MSZ_E_FORUM_TOPIC_DELETE_POSTS:
|
||||
$responseCode = 403;
|
||||
$canDeleteMsg = 'This topic already has replies, you may no longer delete it. Ask a moderator to remove if it absolutely necessary.';
|
||||
break;
|
||||
case MSZ_E_FORUM_TOPIC_DELETE_OK:
|
||||
break;
|
||||
default:
|
||||
$responseCode = 500;
|
||||
$canDeleteMsg = sprintf('Unknown error \'%d\'', $canDelete);
|
||||
}
|
||||
|
||||
if($canDeleteCode !== MSZ_E_FORUM_TOPIC_DELETE_OK) {
|
||||
echo render_info($canDeleteMsg, $responseCode);
|
||||
break;
|
||||
}
|
||||
|
||||
if(!isset($_GET['confirm'])) {
|
||||
Template::render('forum.confirm', [
|
||||
'title' => 'Confirm topic deletion',
|
||||
'class' => 'far fa-trash-alt',
|
||||
'message' => sprintf('You are about to delete topic #%d. Are you sure about that?', $topic['topic_id']),
|
||||
'params' => [
|
||||
't' => $topic['topic_id'],
|
||||
'm' => 'delete',
|
||||
],
|
||||
]);
|
||||
break;
|
||||
} elseif(!$submissionConfirmed) {
|
||||
url_redirect(
|
||||
'forum-topic',
|
||||
['topic' => $topic['topic_id']]
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
$deleteTopic = forum_topic_delete($topic['topic_id']);
|
||||
|
||||
if($deleteTopic)
|
||||
$msz->createAuditLog('FORUM_TOPIC_DELETE', [$topic['topic_id']]);
|
||||
|
||||
if(!$deleteTopic) {
|
||||
echo render_error(500);
|
||||
break;
|
||||
}
|
||||
|
||||
url_redirect('forum-category', [
|
||||
'forum' => $topic['forum_id'],
|
||||
]);
|
||||
break;
|
||||
|
||||
case 'restore':
|
||||
if(!$canNukeOrRestore) {
|
||||
echo render_error(403);
|
||||
break;
|
||||
}
|
||||
|
||||
if(!isset($_GET['confirm'])) {
|
||||
Template::render('forum.confirm', [
|
||||
'title' => 'Confirm topic restore',
|
||||
'class' => 'fas fa-magic',
|
||||
'message' => sprintf('You are about to restore topic #%d. Are you sure about that?', $topic['topic_id']),
|
||||
'params' => [
|
||||
't' => $topic['topic_id'],
|
||||
'm' => 'restore',
|
||||
],
|
||||
]);
|
||||
break;
|
||||
} elseif(!$submissionConfirmed) {
|
||||
url_redirect('forum-topic', [
|
||||
'topic' => $topic['topic_id'],
|
||||
]);
|
||||
break;
|
||||
}
|
||||
|
||||
$restoreTopic = forum_topic_restore($topic['topic_id']);
|
||||
|
||||
if(!$restoreTopic) {
|
||||
echo render_error(500);
|
||||
break;
|
||||
}
|
||||
|
||||
$msz->createAuditLog('FORUM_TOPIC_RESTORE', [$topic['topic_id']]);
|
||||
|
||||
url_redirect('forum-category', [
|
||||
'forum' => $topic['forum_id'],
|
||||
]);
|
||||
break;
|
||||
|
||||
case 'nuke':
|
||||
if(!$canNukeOrRestore) {
|
||||
echo render_error(403);
|
||||
break;
|
||||
}
|
||||
|
||||
if(!isset($_GET['confirm'])) {
|
||||
Template::render('forum.confirm', [
|
||||
'title' => 'Confirm topic nuke',
|
||||
'class' => 'fas fa-radiation',
|
||||
'message' => sprintf('You are about to PERMANENTLY DELETE topic #%d. Are you sure about that?', $topic['topic_id']),
|
||||
'params' => [
|
||||
't' => $topic['topic_id'],
|
||||
'm' => 'nuke',
|
||||
],
|
||||
]);
|
||||
break;
|
||||
} elseif(!$submissionConfirmed) {
|
||||
url_redirect('forum-topic', [
|
||||
'topic' => $topic['topic_id'],
|
||||
]);
|
||||
break;
|
||||
}
|
||||
|
||||
$nukeTopic = forum_topic_nuke($topic['topic_id']);
|
||||
|
||||
if(!$nukeTopic) {
|
||||
echo render_error(500);
|
||||
break;
|
||||
}
|
||||
|
||||
$msz->createAuditLog('FORUM_TOPIC_NUKE', [$topic['topic_id']]);
|
||||
|
||||
url_redirect('forum-category', [
|
||||
'forum' => $topic['forum_id'],
|
||||
]);
|
||||
break;
|
||||
|
||||
case 'bump':
|
||||
if($canBumpTopic && forum_topic_bump($topic['topic_id'])) {
|
||||
$msz->createAuditLog('FORUM_TOPIC_BUMP', [$topic['topic_id']]);
|
||||
}
|
||||
|
||||
url_redirect('forum-topic', [
|
||||
'topic' => $topic['topic_id'],
|
||||
]);
|
||||
break;
|
||||
|
||||
case 'lock':
|
||||
if($canLockTopic && !$topicIsLocked && forum_topic_lock($topic['topic_id'])) {
|
||||
$msz->createAuditLog('FORUM_TOPIC_LOCK', [$topic['topic_id']]);
|
||||
}
|
||||
|
||||
url_redirect('forum-topic', [
|
||||
'topic' => $topic['topic_id'],
|
||||
]);
|
||||
break;
|
||||
|
||||
case 'unlock':
|
||||
if($canLockTopic && $topicIsLocked && forum_topic_unlock($topic['topic_id'])) {
|
||||
$msz->createAuditLog('FORUM_TOPIC_UNLOCK', [$topic['topic_id']]);
|
||||
}
|
||||
|
||||
url_redirect('forum-topic', [
|
||||
'topic' => $topic['topic_id'],
|
||||
]);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$topicPosts = $topic['topic_count_posts'];
|
||||
|
||||
if($canDeleteAny) {
|
||||
$topicPosts += $topic['topic_count_posts_deleted'];
|
||||
}
|
||||
|
||||
$topicPagination = new Pagination($topicPosts, MSZ_FORUM_POSTS_PER_PAGE, 'page');
|
||||
|
||||
if(isset($postInfo['preceeding_post_count'])) {
|
||||
$preceedingPosts = $postInfo['preceeding_post_count'];
|
||||
|
||||
if($canDeleteAny) {
|
||||
$preceedingPosts += $postInfo['preceeding_post_deleted_count'];
|
||||
}
|
||||
|
||||
$topicPagination->setPage(floor($preceedingPosts / $topicPagination->getRange()), true);
|
||||
}
|
||||
|
||||
if(!$topicPagination->hasValidOffset()) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
Template::set('topic_perms', $perms);
|
||||
|
||||
$posts = forum_post_listing(
|
||||
$topic['topic_id'],
|
||||
$topicPagination->getOffset(),
|
||||
$topicPagination->getRange(),
|
||||
perms_check($perms, MSZ_FORUM_PERM_DELETE_ANY_POST)
|
||||
);
|
||||
|
||||
if(!$posts) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
$canReply = !$topicIsArchived && !$topicIsLocked && !$topicIsDeleted && perms_check($perms, MSZ_FORUM_PERM_CREATE_POST);
|
||||
|
||||
forum_topic_mark_read($topicUserId, $topic['topic_id'], $topic['forum_id']);
|
||||
|
||||
Template::render('forum.topic', [
|
||||
'topic_breadcrumbs' => forum_get_breadcrumbs($topic['forum_id']),
|
||||
'global_accent_colour' => forum_get_colour($topic['forum_id']),
|
||||
'topic_info' => $topic,
|
||||
'topic_posts' => $posts,
|
||||
'can_reply' => $canReply,
|
||||
'topic_pagination' => $topicPagination,
|
||||
'topic_can_delete' => $canDelete,
|
||||
'topic_can_nuke_or_restore' => $canNukeOrRestore,
|
||||
'topic_can_bump' => $canBumpTopic,
|
||||
'topic_can_lock' => $canLockTopic,
|
||||
]);
|
200
public/index.php
200
public/index.php
|
@ -1,9 +1,207 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Users\User;
|
||||
use Misuzu\Users\UserSession;
|
||||
|
||||
require_once __DIR__ . '/../misuzu.php';
|
||||
|
||||
set_exception_handler(function(\Throwable $ex) {
|
||||
http_response_code(500);
|
||||
ob_clean();
|
||||
|
||||
if(MSZ_DEBUG) {
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
echo (string)$ex;
|
||||
} else {
|
||||
header('Content-Type: text/html; charset=utf-8');
|
||||
echo file_get_contents(MSZ_TEMPLATES . '/500.html');
|
||||
}
|
||||
exit;
|
||||
});
|
||||
|
||||
// The whole wall of shit before the router setup and dispatch should be worked away
|
||||
// Lockdown things should be middleware when there's no more legacy files
|
||||
|
||||
$request = \Index\Http\HttpRequest::fromRequest();
|
||||
|
||||
$msz->setUpHttp(str_contains($request->getPath(), '.php'));
|
||||
ob_start();
|
||||
|
||||
if(file_exists(MSZ_ROOT . '/.migrating')) {
|
||||
http_response_code(503);
|
||||
if(!isset($_GET['_check'])) {
|
||||
header('Content-Type: text/html; charset=utf-8');
|
||||
echo file_get_contents(MSZ_TEMPLATES . '/503.html');
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
if(!MSZ_DEBUG) {
|
||||
$twigCacheDirSfx = GitInfo::hash(true);
|
||||
if(empty($twigCacheDirSfx))
|
||||
$twigCacheDirSfx = md5(MSZ_ROOT);
|
||||
|
||||
$twigCache = sys_get_temp_dir() . '/msz-tpl-' . $twigCacheDirSfx;
|
||||
if(!is_dir($twigCache))
|
||||
mkdir($twigCache, 0775, true);
|
||||
}
|
||||
|
||||
$globals = $cfg->getValues([
|
||||
['site.name:s', 'Misuzu'],
|
||||
'site.desc:s',
|
||||
'site.url:s',
|
||||
'sockChat.chatPath.normal:s',
|
||||
'eeprom.path:s',
|
||||
'eeprom.app:s',
|
||||
['auth.secret:s', 'meow'],
|
||||
['csrf.secret:s', 'soup'],
|
||||
'private.enabled:b',
|
||||
]);
|
||||
|
||||
Template::init($msz, $twigCache ?? null, MSZ_DEBUG);
|
||||
|
||||
Template::set('globals', [
|
||||
'site_name' => $globals['site.name'],
|
||||
'site_description' => $globals['site.desc'],
|
||||
'site_url' => $globals['site.url'],
|
||||
'site_chat' => $globals['sockChat.chatPath.normal'],
|
||||
'eeprom' => [
|
||||
'path' => $globals['eeprom.path'],
|
||||
'app' => $globals['eeprom.app'],
|
||||
],
|
||||
]);
|
||||
|
||||
$mszAssetsInfo = json_decode(file_get_contents(MSZ_ASSETS . '/current.json'));
|
||||
if(!empty($mszAssetsInfo))
|
||||
Template::set('assets', $mszAssetsInfo);
|
||||
unset($mszAssetsInfo);
|
||||
|
||||
Template::addPath(MSZ_TEMPLATES);
|
||||
|
||||
AuthToken::setSecretKey($globals['auth.secret']);
|
||||
|
||||
if(isset($_COOKIE['msz_uid']) && isset($_COOKIE['msz_sid'])) {
|
||||
$authToken = new AuthToken;
|
||||
$authToken->setUserId(filter_input(INPUT_COOKIE, 'msz_uid', FILTER_SANITIZE_NUMBER_INT) ?? 0);
|
||||
$authToken->setSessionToken(filter_input(INPUT_COOKIE, 'msz_sid') ?? '');
|
||||
|
||||
if($authToken->isValid())
|
||||
$authToken->applyCookie(strtotime('1 year'));
|
||||
|
||||
AuthToken::nukeCookieLegacy();
|
||||
}
|
||||
|
||||
if(!isset($authToken))
|
||||
$authToken = AuthToken::unpack(filter_input(INPUT_COOKIE, 'msz_auth') ?? '');
|
||||
|
||||
if($authToken->isValid()) {
|
||||
$authToken->setCurrent();
|
||||
|
||||
try {
|
||||
$sessionInfo = UserSession::byToken($authToken->getSessionToken());
|
||||
if($sessionInfo->hasExpired()) {
|
||||
$sessionInfo->delete();
|
||||
} elseif($sessionInfo->getUserId() === $authToken->getUserId()) {
|
||||
$userInfo = $sessionInfo->getUser();
|
||||
if(!$userInfo->isDeleted()) {
|
||||
$sessionInfo->setCurrent();
|
||||
$userInfo->setCurrent();
|
||||
$sessionInfo->bump($_SERVER['REMOTE_ADDR']);
|
||||
|
||||
if($sessionInfo->shouldBumpExpire())
|
||||
$authToken->applyCookie($sessionInfo->getExpiresTime());
|
||||
|
||||
// only allow impersonation when super user
|
||||
if($authToken->hasImpersonatedUserId() && $userInfo->isSuper()) {
|
||||
$userInfoReal = $userInfo;
|
||||
|
||||
try {
|
||||
$userInfo = User::byId($authToken->getImpersonatedUserId());
|
||||
} catch(UserNotFoundException $ex) {
|
||||
$userInfo = $userInfoReal;
|
||||
$authToken->removeImpersonatedUserId();
|
||||
$authToken->applyCookie();
|
||||
}
|
||||
|
||||
$userInfo->setCurrent();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(UserNotFoundException $ex) {
|
||||
UserSession::unsetCurrent();
|
||||
User::unsetCurrent();
|
||||
} catch(UserSessionNotFoundException $ex) {
|
||||
UserSession::unsetCurrent();
|
||||
User::unsetCurrent();
|
||||
}
|
||||
|
||||
if(UserSession::hasCurrent()) {
|
||||
$userInfo->bumpActivity($_SERVER['REMOTE_ADDR']);
|
||||
} else
|
||||
AuthToken::nukeCookie();
|
||||
}
|
||||
|
||||
CSRF::init(
|
||||
$globals['csrf.secret'],
|
||||
(UserSession::hasCurrent() ? UserSession::getCurrent()->getToken() : ($_SERVER['REMOTE_ADDR'] ?? '::1'))
|
||||
);
|
||||
|
||||
if($globals['private.enabled']) {
|
||||
$onLoginPage = $_SERVER['PHP_SELF'] === url('auth-login');
|
||||
$onPasswordPage = parse_url($_SERVER['PHP_SELF'], PHP_URL_PATH) === url('auth-forgot');
|
||||
$misuzuBypassLockdown = !empty($misuzuBypassLockdown) || $onLoginPage;
|
||||
|
||||
if(!$misuzuBypassLockdown) {
|
||||
if(UserSession::hasCurrent()) {
|
||||
['private.perm.cat' => $privatePermCat, 'private.perm.val' => $privatePermVal] = $cfg->getValues(['private.perm.cat:s', 'private.perm.val:i']);
|
||||
|
||||
if(!empty($privatePermCat) && $privatePermVal > 0) {
|
||||
if(!perms_check_user($privatePermCat, User::getCurrent()->getId(), $privatePermVal)) {
|
||||
// au revoir
|
||||
UserSession::unsetCurrent();
|
||||
User::unsetCurrent();
|
||||
}
|
||||
}
|
||||
} elseif(!$onLoginPage && !($onPasswordPage && $cfg->getBoolean('private.allow_password_reset', true))) {
|
||||
url_redirect('auth-login');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!empty($userInfo))
|
||||
Template::set('current_user', $userInfo);
|
||||
if(!empty($userInfoReal))
|
||||
Template::set('current_user_real', $userInfoReal);
|
||||
|
||||
$inManageMode = str_starts_with($_SERVER['REQUEST_URI'], '/manage');
|
||||
$hasManageAccess = User::hasCurrent()
|
||||
&& !User::getCurrent()->hasActiveWarning()
|
||||
&& perms_check_user(MSZ_PERMS_GENERAL, User::getCurrent()->getId(), MSZ_PERM_GENERAL_CAN_MANAGE);
|
||||
Template::set('has_manage_access', $hasManageAccess);
|
||||
|
||||
if($inManageMode) {
|
||||
if(!$hasManageAccess) {
|
||||
echo render_error(403);
|
||||
exit;
|
||||
}
|
||||
|
||||
Template::set('manage_menu', manage_get_menu(User::getCurrent()->getId()));
|
||||
}
|
||||
|
||||
$mszRequestPath = $request->getPath();
|
||||
$mszLegacyPathPrefix = MSZ_PUBLIC . '-legacy/';
|
||||
$mszLegacyPath = realpath($mszLegacyPathPrefix . $mszRequestPath);
|
||||
|
||||
if(!empty($mszLegacyPath) && str_starts_with($mszLegacyPath, $mszLegacyPathPrefix)) {
|
||||
if(is_dir($mszLegacyPath))
|
||||
$mszLegacyPath .= '/index.php';
|
||||
|
||||
if(is_file($mszLegacyPath)) {
|
||||
require_once $mszLegacyPath;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$msz->setUpHttp(str_contains($mszRequestPath, '.php'));
|
||||
$msz->dispatchHttp($request);
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
<?php
|
||||
require_once __DIR__ . '/index.php';
|
|
@ -1,121 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use DateTimeInterface;
|
||||
use RuntimeException;
|
||||
use Index\DateTime;
|
||||
use Misuzu\Changelog\Changelog;
|
||||
use Misuzu\Users\User;
|
||||
use Misuzu\Users\UserNotFoundException;
|
||||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
if(!User::hasCurrent() || !perms_check_user(MSZ_PERMS_CHANGELOG, User::getCurrent()->getId(), MSZ_PERM_CHANGELOG_MANAGE_CHANGES)) {
|
||||
echo render_error(403);
|
||||
return;
|
||||
}
|
||||
|
||||
$changeActions = [];
|
||||
foreach(Changelog::ACTIONS as $action)
|
||||
$changeActions[$action] = Changelog::actionText($action);
|
||||
|
||||
$changelog = $msz->getChangelog();
|
||||
$changeId = (string)filter_input(INPUT_GET, 'c', FILTER_SANITIZE_NUMBER_INT);
|
||||
$loadChangeInfo = fn() => $changelog->getChangeById($changeId, withTags: true);
|
||||
$changeTags = $changelog->getAllTags();
|
||||
|
||||
if(empty($changeId))
|
||||
$isNew = true;
|
||||
else
|
||||
try {
|
||||
$isNew = false;
|
||||
$changeInfo = $loadChangeInfo();
|
||||
} catch(RuntimeException $ex) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
if($_SERVER['REQUEST_METHOD'] === 'GET' && !empty($_GET['delete'])) {
|
||||
if(CSRF::validateRequest()) {
|
||||
$changelog->deleteChange($changeInfo);
|
||||
$msz->createAuditLog('CHANGELOG_ENTRY_DELETE', [$changeInfo->getId()]);
|
||||
url_redirect('manage-changelog-changes');
|
||||
} else render_error(403);
|
||||
return;
|
||||
}
|
||||
|
||||
// make errors not echos lol
|
||||
while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
|
||||
$action = trim((string)filter_input(INPUT_POST, 'cl_action'));
|
||||
$summary = trim((string)filter_input(INPUT_POST, 'cl_summary'));
|
||||
$body = trim((string)filter_input(INPUT_POST, 'cl_body'));
|
||||
$userId = (int)filter_input(INPUT_POST, 'cl_user', FILTER_SANITIZE_NUMBER_INT);
|
||||
$createdAt = trim((string)filter_input(INPUT_POST, 'cl_created'));
|
||||
$tags = filter_input(INPUT_POST, 'cl_tags', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY);
|
||||
|
||||
if($userId < 1) $userId = null;
|
||||
else $userId = (string)$userId;
|
||||
|
||||
if(empty($createdAt))
|
||||
$createdAt = null;
|
||||
else {
|
||||
$createdAt = DateTime::createFromFormat(DateTimeInterface::ATOM, $createdAt . ':00Z');
|
||||
if($createdAt->getUnixTimeSeconds() < 0)
|
||||
$createdAt = null;
|
||||
}
|
||||
|
||||
if($isNew) {
|
||||
$changeInfo = $changelog->createChange($action, $summary, $body, $userId, $createdAt);
|
||||
} else {
|
||||
if($action === $changeInfo->getAction())
|
||||
$action = null;
|
||||
if($summary === $changeInfo->getSummary())
|
||||
$summary = null;
|
||||
if($body === $changeInfo->getBody())
|
||||
$body = null;
|
||||
if($createdAt !== null && $createdAt->equals($changeInfo->getCreatedAt()))
|
||||
$createdAt = null;
|
||||
$updateUserInfo = $userId !== $changeInfo->getUserId();
|
||||
|
||||
if($action !== null || $summary !== null || $body !== null || $createdAt !== null || $updateUserInfo)
|
||||
$changelog->updateChange($changeInfo, $action, $summary, $body, $updateUserInfo, $userId, $createdAt);
|
||||
}
|
||||
|
||||
if(!empty($tags)) {
|
||||
$tCurrent = $changeInfo->getTagIds();
|
||||
$tApply = $tags;
|
||||
$tRemove = [];
|
||||
|
||||
foreach($tCurrent as $tag)
|
||||
if(!in_array($tag, $tApply)) {
|
||||
$tRemove[] = $tag;
|
||||
$changelog->removeTagFromChange($changeInfo, $tag);
|
||||
}
|
||||
|
||||
$tCurrent = array_diff($tCurrent, $tRemove);
|
||||
|
||||
foreach($tApply as $tag)
|
||||
if(!in_array($tag, $tCurrent)) {
|
||||
$changelog->addTagToChange($changeInfo, $tag);
|
||||
$tCurrent[] = $tag;
|
||||
}
|
||||
}
|
||||
|
||||
$msz->createAuditLog(
|
||||
$isNew ? 'CHANGELOG_ENTRY_CREATE' : 'CHANGELOG_ENTRY_EDIT',
|
||||
[$changeInfo->getId()]
|
||||
);
|
||||
|
||||
if($isNew) {
|
||||
url_redirect('manage-changelog-change', ['change' => $changeInfo->getId()]);
|
||||
return;
|
||||
} else $changeInfo = $loadChangeInfo();
|
||||
break;
|
||||
}
|
||||
|
||||
Template::render('manage.changelog.change', [
|
||||
'change_new' => $isNew,
|
||||
'change_info' => $changeInfo ?? null,
|
||||
'change_tags' => $changeTags,
|
||||
'change_actions' => $changeActions,
|
||||
]);
|
|
@ -1,49 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Users\User;
|
||||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
if(!User::hasCurrent() || !perms_check_user(MSZ_PERMS_CHANGELOG, User::getCurrent()->getId(), MSZ_PERM_CHANGELOG_MANAGE_CHANGES)) {
|
||||
echo render_error(403);
|
||||
return;
|
||||
}
|
||||
|
||||
$changelog = $msz->getChangelog();
|
||||
$changelogPagination = new Pagination($changelog->countAllChanges(), 30);
|
||||
|
||||
if(!$changelogPagination->hasValidOffset()) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
$changeInfos = $changelog->getAllChanges(withTags: true, pagination: $changelogPagination);
|
||||
$changes = [];
|
||||
$userInfos = [];
|
||||
|
||||
foreach($changeInfos as $changeInfo) {
|
||||
$userId = $changeInfo->getUserId();
|
||||
|
||||
if(array_key_exists($userId, $userInfos)) {
|
||||
$userInfo = $userInfos[$userId];
|
||||
} else {
|
||||
try {
|
||||
$userInfo = User::byId($userId);
|
||||
} catch(UserNotFoundException $ex) {
|
||||
$userInfo = null;
|
||||
}
|
||||
|
||||
$userInfos[$userId] = $userInfo;
|
||||
}
|
||||
|
||||
$changes[] = [
|
||||
'change' => $changeInfo,
|
||||
'user' => $userInfo,
|
||||
];
|
||||
}
|
||||
|
||||
Template::render('manage.changelog.changes', [
|
||||
'changelog_changes' => $changes,
|
||||
'changelog_pagination' => $changelogPagination,
|
||||
]);
|
|
@ -1,72 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use RuntimeException;
|
||||
use Misuzu\Users\User;
|
||||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
if(!User::hasCurrent() || !perms_check_user(MSZ_PERMS_CHANGELOG, User::getCurrent()->getId(), MSZ_PERM_CHANGELOG_MANAGE_TAGS)) {
|
||||
echo render_error(403);
|
||||
return;
|
||||
}
|
||||
|
||||
$changelog = $msz->getChangelog();
|
||||
$tagId = (string)filter_input(INPUT_GET, 't', FILTER_SANITIZE_NUMBER_INT);
|
||||
$loadTagInfo = fn() => $changelog->getTagById($tagId);
|
||||
|
||||
if(empty($tagId))
|
||||
$isNew = true;
|
||||
else
|
||||
try {
|
||||
$isNew = false;
|
||||
$tagInfo = $loadTagInfo();
|
||||
} catch(RuntimeException $ex) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
if($_SERVER['REQUEST_METHOD'] === 'GET' && !empty($_GET['delete'])) {
|
||||
if(CSRF::validateRequest()) {
|
||||
$changelog->deleteTag($tagInfo);
|
||||
$msz->createAuditLog('CHANGELOG_TAG_DELETE', [$tagInfo->getId()]);
|
||||
url_redirect('manage-changelog-tags');
|
||||
} else render_error(403);
|
||||
return;
|
||||
}
|
||||
|
||||
while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
|
||||
$name = trim((string)filter_input(INPUT_POST, 'ct_name'));
|
||||
$description = trim((string)filter_input(INPUT_POST, 'ct_desc'));
|
||||
$archive = !empty($_POST['ct_archive']);
|
||||
|
||||
if($isNew) {
|
||||
$tagInfo = $changelog->createTag($name, $description, $archive);
|
||||
} else {
|
||||
if($name === $tagInfo->getName())
|
||||
$name = null;
|
||||
if($description === $tagInfo->getDescription())
|
||||
$description = null;
|
||||
if($archive === $tagInfo->isArchived())
|
||||
$archive = null;
|
||||
|
||||
if($name !== null || $description !== null || $archive !== null)
|
||||
$changelog->updateTag($tagInfo, $name, $description, $archive);
|
||||
}
|
||||
|
||||
$msz->createAuditLog(
|
||||
$isNew ? 'CHANGELOG_TAG_CREATE' : 'CHANGELOG_TAG_EDIT',
|
||||
[$tagInfo->getId()]
|
||||
);
|
||||
|
||||
if($isNew) {
|
||||
url_redirect('manage-changelog-tag', ['tag' => $tagInfo->getId()]);
|
||||
return;
|
||||
} else $tagInfo = $loadTagInfo();
|
||||
break;
|
||||
}
|
||||
|
||||
Template::render('manage.changelog.tag', [
|
||||
'tag_new' => $isNew,
|
||||
'tag_info' => $tagInfo ?? null,
|
||||
]);
|
|
@ -1,15 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Users\User;
|
||||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
if(!User::hasCurrent() || !perms_check_user(MSZ_PERMS_CHANGELOG, User::getCurrent()->getId(), MSZ_PERM_CHANGELOG_MANAGE_TAGS)) {
|
||||
echo render_error(403);
|
||||
return;
|
||||
}
|
||||
|
||||
Template::render('manage.changelog.tags', [
|
||||
'changelog_tags' => $msz->getChangelog()->getAllTags(),
|
||||
]);
|
|
@ -1,26 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Users\User;
|
||||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
if(!User::hasCurrent() || !perms_check_user(MSZ_PERMS_GENERAL, User::getCurrent()->getId(), MSZ_PERM_FORUM_MANAGE_FORUMS)) {
|
||||
echo render_error(403);
|
||||
return;
|
||||
}
|
||||
|
||||
$getForum = DB::prepare('
|
||||
SELECT *
|
||||
FROM `msz_forum_categories`
|
||||
WHERE `forum_id` = :forum_id
|
||||
');
|
||||
$getForum->bind('forum_id', (int)($_GET['f'] ?? 0));
|
||||
$forum = $getForum->fetch();
|
||||
|
||||
if(!$forum) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
Template::render('manage.forum.forum', compact('forum'));
|
|
@ -1,23 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Users\User;
|
||||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
if(!User::hasCurrent() || !perms_check_user(MSZ_PERMS_GENERAL, User::getCurrent()->getId(), MSZ_PERM_FORUM_MANAGE_FORUMS)) {
|
||||
echo render_error(403);
|
||||
return;
|
||||
}
|
||||
|
||||
$forums = DB::query('SELECT * FROM `msz_forum_categories`')->fetchAll();
|
||||
$rawPerms = perms_create(MSZ_FORUM_PERM_MODES);
|
||||
$perms = manage_forum_perms_list($rawPerms);
|
||||
|
||||
if(!empty($_POST['perms']) && is_array($_POST['perms'])) {
|
||||
$finalPerms = manage_perms_apply($perms, $_POST['perms'], $rawPerms);
|
||||
$perms = manage_forum_perms_list($finalPerms);
|
||||
Template::set('calculated_perms', $finalPerms);
|
||||
}
|
||||
|
||||
Template::render('manage.forum.listing', compact('forums', 'perms'));
|
|
@ -1,51 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Users\User;
|
||||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
if(!User::hasCurrent() || !perms_check_user(MSZ_PERMS_GENERAL, User::getCurrent()->getId(), MSZ_PERM_FORUM_TOPIC_REDIRS)) {
|
||||
echo render_error(403);
|
||||
return;
|
||||
}
|
||||
|
||||
if($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if(!CSRF::validateRequest())
|
||||
throw new \Exception("Request verification failed.");
|
||||
|
||||
$rTopicId = (int)filter_input(INPUT_POST, 'topic_redir_id');
|
||||
$rTopicURL = trim((string)filter_input(INPUT_POST, 'topic_redir_url'));
|
||||
|
||||
if($rTopicId < 1)
|
||||
throw new \Exception("Invalid topic id.");
|
||||
|
||||
$msz->createAuditLog('FORUM_TOPIC_REDIR_CREATE', [$rTopicId]);
|
||||
forum_topic_redir_create($rTopicId, User::getCurrent()->getId(), $rTopicURL);
|
||||
url_redirect('manage-forum-topic-redirs');
|
||||
return;
|
||||
}
|
||||
|
||||
if(filter_input(INPUT_GET, 'm') === 'explode') {
|
||||
if(!CSRF::validateRequest())
|
||||
throw new \Exception("Request verification failed.");
|
||||
|
||||
$rTopicId = (int)filter_input(INPUT_GET, 't');
|
||||
$msz->createAuditLog('FORUM_TOPIC_REDIR_REMOVE', [$rTopicId]);
|
||||
forum_topic_redir_remove($rTopicId);
|
||||
url_redirect('manage-forum-topic-redirs');
|
||||
return;
|
||||
}
|
||||
|
||||
$pagination = new Pagination(forum_topic_redir_count(), 20);
|
||||
if(!$pagination->hasValidOffset()) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
$redirs = forum_topic_redir_all($pagination->getOffset(), $pagination->getRange());
|
||||
|
||||
Template::render('manage.forum.redirs', [
|
||||
'manage_redirs' => $redirs,
|
||||
'manage_redirs_pagination' => $pagination,
|
||||
]);
|
|
@ -1,113 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use RuntimeException;
|
||||
use Misuzu\Users\User;
|
||||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
if(!User::hasCurrent() || !perms_check_user(MSZ_PERMS_GENERAL, User::getCurrent()->getId(), MSZ_PERM_GENERAL_MANAGE_EMOTES)) {
|
||||
echo render_error(403);
|
||||
return;
|
||||
}
|
||||
|
||||
$emotes = $msz->getEmotes();
|
||||
$emoteId = (string)filter_input(INPUT_GET, 'e', FILTER_SANITIZE_NUMBER_INT);
|
||||
$loadEmoteInfo = fn() => $emotes->getEmoteById($emoteId, true);
|
||||
|
||||
if(empty($emoteId))
|
||||
$isNew = true;
|
||||
else
|
||||
try {
|
||||
$isNew = false;
|
||||
$emoteInfo = $loadEmoteInfo();
|
||||
} catch(RuntimeException $ex) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
// make errors not echos lol
|
||||
while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
|
||||
$order = (int)filter_input(INPUT_POST, 'em_order', FILTER_SANITIZE_NUMBER_INT);
|
||||
$minRank = (int)filter_input(INPUT_POST, 'em_minrank', FILTER_SANITIZE_NUMBER_INT);
|
||||
$url = trim((string)filter_input(INPUT_POST, 'em_url'));
|
||||
$strings = explode(' ', trim((string)filter_input(INPUT_POST, 'em_strings')));
|
||||
|
||||
if($isNew || $url !== $emoteInfo->getUrl()) {
|
||||
$checkUrl = $emotes->checkEmoteUrl($url);
|
||||
if($checkUrl !== '') {
|
||||
echo match($checkUrl) {
|
||||
'empty' => 'URL may not be empty.',
|
||||
'spaces' => 'URL may not end or start with spaces.',
|
||||
'used' => 'This URL already belongs to another emoticon.',
|
||||
default => 'URL cannot be accepted: ' . $checkUrl,
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if($order == 0)
|
||||
$order = null;
|
||||
|
||||
if($isNew) {
|
||||
$emoteInfo = $emotes->createEmote($url, $minRank, $order);
|
||||
} else {
|
||||
if($order === $emoteInfo->getOrder())
|
||||
$order = null;
|
||||
if($minRank === $emoteInfo->getMinRank())
|
||||
$minRank = null;
|
||||
if($url === $emoteInfo->getUrl())
|
||||
$url = null;
|
||||
|
||||
if($order !== null || $minRank !== null || $url !== null)
|
||||
$emotes->updateEmote($emoteInfo, $order, $minRank, $url);
|
||||
}
|
||||
|
||||
$sCurrent = $emoteInfo->getStringsRaw();
|
||||
$sApply = $strings;
|
||||
$sRemove = [];
|
||||
|
||||
foreach($sCurrent as $string)
|
||||
if(!in_array($string, $sApply)) {
|
||||
$sRemove[] = $string;
|
||||
$emotes->removeEmoteString($string);
|
||||
}
|
||||
|
||||
$sCurrent = array_diff($sCurrent, $sRemove);
|
||||
|
||||
foreach($sApply as $string)
|
||||
if(!in_array($string, $sCurrent)) {
|
||||
$checkString = $emotes->checkEmoteString($string);
|
||||
if($checkString === '') {
|
||||
$emotes->addEmoteString($emoteInfo, $string);
|
||||
} else {
|
||||
echo match($checkString) {
|
||||
'empty' => 'String may not be empty.',
|
||||
'spaces' => 'String may not end or start with spaces.',
|
||||
'case' => 'String must be lowercase.',
|
||||
'format' => 'String must follow proper formatting.',
|
||||
'used' => 'This string has already been used for another emoticon.',
|
||||
default => 'String cannot be accepted: ' . $checkString,
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
$sCurrent[] = $string;
|
||||
}
|
||||
|
||||
$msz->createAuditLog(
|
||||
$isNew ? 'EMOTICON_CREATE' : 'EMOTICON_EDIT',
|
||||
[$emoteInfo->getId()]
|
||||
);
|
||||
|
||||
if($isNew) {
|
||||
url_redirect('manage-general-emoticon', ['emote' => $emoteInfo->getId()]);
|
||||
return;
|
||||
} else $emoteInfo = $loadEmoteInfo();
|
||||
break;
|
||||
}
|
||||
|
||||
Template::render('manage.general.emoticon', [
|
||||
'emote_new' => $isNew,
|
||||
'emote_info' => $emoteInfo ?? null,
|
||||
]);
|
|
@ -1,52 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use RuntimeException;
|
||||
use Misuzu\Users\User;
|
||||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
if(!User::hasCurrent() || !perms_check_user(MSZ_PERMS_GENERAL, User::getCurrent()->getId(), MSZ_PERM_GENERAL_MANAGE_EMOTES)) {
|
||||
echo render_error(403);
|
||||
return;
|
||||
}
|
||||
|
||||
$emotes = $msz->getEmotes();
|
||||
|
||||
if(CSRF::validateRequest() && !empty($_GET['emote'])) {
|
||||
$emoteId = (string)filter_input(INPUT_GET, 'emote', FILTER_SANITIZE_NUMBER_INT);
|
||||
|
||||
try {
|
||||
$emoteInfo = $emotes->getEmoteById($emoteId);
|
||||
} catch(RuntimeException $ex) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!empty($_GET['delete'])) {
|
||||
$emotes->deleteEmote($emoteInfo);
|
||||
$msz->createAuditLog('EMOTICON_DELETE', [$emoteInfo->getId()]);
|
||||
} else {
|
||||
if(isset($_GET['order'])) {
|
||||
$order = filter_input(INPUT_GET, 'order');
|
||||
$offset = $order === 'i' ? 1 : ($order === 'd' ? -1 : 0);
|
||||
$emotes->updateEmoteOrderOffset($emoteInfo, $offset);
|
||||
$msz->createAuditLog('EMOTICON_ORDER', [$emoteInfo->getId()]);
|
||||
}
|
||||
|
||||
if(isset($_GET['alias'])) {
|
||||
$alias = (string)filter_input(INPUT_GET, 'alias');
|
||||
if($emotes->checkEmoteString($alias) === '') {
|
||||
$emotes->addEmoteString($emoteInfo, $alias);
|
||||
$msz->createAuditLog('EMOTICON_ALIAS', [$emoteInfo->getId(), $alias]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
url_redirect('manage-general-emoticons');
|
||||
return;
|
||||
}
|
||||
|
||||
Template::render('manage.general.emoticons', [
|
||||
'emotes' => $emotes->getAllEmotes(),
|
||||
]);
|
|
@ -1,168 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
$statistics = DB::query('
|
||||
SELECT
|
||||
(
|
||||
SELECT COUNT(`user_id`)
|
||||
FROM `msz_users`
|
||||
) AS `stat_users_total`,
|
||||
(
|
||||
SELECT COUNT(`user_id`)
|
||||
FROM `msz_users`
|
||||
WHERE `user_deleted` IS NOT NULL
|
||||
) AS `stat_users_deleted`,
|
||||
(
|
||||
SELECT COUNT(`user_id`)
|
||||
FROM `msz_users`
|
||||
WHERE `user_active` IS NOT NULL
|
||||
AND `user_deleted` IS NULL
|
||||
) AS `stat_users_active`,
|
||||
(
|
||||
SELECT COUNT(*)
|
||||
FROM `msz_audit_log`
|
||||
) AS `stat_audit_logs`,
|
||||
(
|
||||
SELECT COUNT(`change_id`)
|
||||
FROM `msz_changelog_changes`
|
||||
) AS `stat_changelog_entries`,
|
||||
(
|
||||
SELECT COUNT(`category_id`)
|
||||
FROM `msz_comments_categories`
|
||||
) AS `stat_comment_categories_total`,
|
||||
(
|
||||
SELECT COUNT(`category_id`)
|
||||
FROM `msz_comments_categories`
|
||||
WHERE `category_locked` IS NOT NULL
|
||||
) AS `stat_comment_categories_locked`,
|
||||
(
|
||||
SELECT COUNT(`comment_id`)
|
||||
FROM `msz_comments_posts`
|
||||
) AS `stat_comment_posts_total`,
|
||||
(
|
||||
SELECT COUNT(`comment_id`)
|
||||
FROM `msz_comments_posts`
|
||||
WHERE `comment_deleted` IS NOT NULL
|
||||
) AS `stat_comment_posts_deleted`,
|
||||
(
|
||||
SELECT COUNT(`comment_id`)
|
||||
FROM `msz_comments_posts`
|
||||
WHERE `comment_reply_to` IS NOT NULL
|
||||
) AS `stat_comment_posts_replies`,
|
||||
(
|
||||
SELECT COUNT(`comment_id`)
|
||||
FROM `msz_comments_posts`
|
||||
WHERE `comment_pinned` IS NOT NULL
|
||||
) AS `stat_comment_posts_pinned`,
|
||||
(
|
||||
SELECT COUNT(`comment_id`)
|
||||
FROM `msz_comments_posts`
|
||||
WHERE `comment_edited` IS NOT NULL
|
||||
) AS `stat_comment_posts_edited`,
|
||||
(
|
||||
SELECT COUNT(`user_id`)
|
||||
FROM `msz_comments_votes`
|
||||
WHERE `comment_vote` > 0
|
||||
) AS `stat_comment_likes`,
|
||||
(
|
||||
SELECT COUNT(`user_id`)
|
||||
FROM `msz_comments_votes`
|
||||
WHERE `comment_vote` < 0
|
||||
) AS `stat_comment_dislikes`,
|
||||
(
|
||||
SELECT COUNT(`post_id`)
|
||||
FROM `msz_forum_posts`
|
||||
) AS `stat_forum_posts_total`,
|
||||
(
|
||||
SELECT COUNT(`post_id`)
|
||||
FROM `msz_forum_posts`
|
||||
WHERE `post_deleted` IS NOT NULL
|
||||
) AS `stat_forum_posts_deleted`,
|
||||
(
|
||||
SELECT COUNT(`post_id`)
|
||||
FROM `msz_forum_posts`
|
||||
WHERE `post_edited` IS NOT NULL
|
||||
) AS `stat_forum_posts_edited`,
|
||||
(
|
||||
SELECT COUNT(`post_id`)
|
||||
FROM `msz_forum_posts`
|
||||
WHERE `post_parse` = 0
|
||||
) AS `stat_forum_posts_plain`,
|
||||
(
|
||||
SELECT COUNT(`post_id`)
|
||||
FROM `msz_forum_posts`
|
||||
WHERE `post_parse` = 1
|
||||
) AS `stat_forum_posts_bbcode`,
|
||||
(
|
||||
SELECT COUNT(`post_id`)
|
||||
FROM `msz_forum_posts`
|
||||
WHERE `post_parse` = 2
|
||||
) AS `stat_forum_posts_markdown`,
|
||||
(
|
||||
SELECT COUNT(`post_id`)
|
||||
FROM `msz_forum_posts`
|
||||
WHERE `post_display_signature` != 0
|
||||
) AS `stat_forum_posts_signature`,
|
||||
(
|
||||
SELECT COUNT(`topic_id`)
|
||||
FROM `msz_forum_topics`
|
||||
) AS `stat_forum_topics_total`,
|
||||
(
|
||||
SELECT COUNT(`topic_id`)
|
||||
FROM `msz_forum_topics`
|
||||
WHERE `topic_type` = 0
|
||||
) AS `stat_forum_topics_normal`,
|
||||
(
|
||||
SELECT COUNT(`topic_id`)
|
||||
FROM `msz_forum_topics`
|
||||
WHERE `topic_type` = 1
|
||||
) AS `stat_forum_topics_pinned`,
|
||||
(
|
||||
SELECT COUNT(`topic_id`)
|
||||
FROM `msz_forum_topics`
|
||||
WHERE `topic_type` = 2
|
||||
) AS `stat_forum_topics_announce`,
|
||||
(
|
||||
SELECT COUNT(`topic_id`)
|
||||
FROM `msz_forum_topics`
|
||||
WHERE `topic_type` = 3
|
||||
) AS `stat_forum_topics_global_announce`,
|
||||
(
|
||||
SELECT COUNT(`topic_id`)
|
||||
FROM `msz_forum_topics`
|
||||
WHERE `topic_deleted` IS NOT NULL
|
||||
) AS `stat_forum_topics_deleted`,
|
||||
(
|
||||
SELECT COUNT(`topic_id`)
|
||||
FROM `msz_forum_topics`
|
||||
WHERE `topic_locked` IS NOT NULL
|
||||
) AS `stat_forum_topics_locked`,
|
||||
(
|
||||
SELECT COUNT(*)
|
||||
FROM `msz_login_attempts`
|
||||
) AS `stat_login_attempts_total`,
|
||||
(
|
||||
SELECT COUNT(*)
|
||||
FROM `msz_login_attempts`
|
||||
WHERE `attempt_success` = 0
|
||||
) AS `stat_login_attempts_failed`,
|
||||
(
|
||||
SELECT COUNT(`session_id`)
|
||||
FROM `msz_sessions`
|
||||
) AS `stat_user_sessions`,
|
||||
(
|
||||
SELECT COUNT(`user_id`)
|
||||
FROM `msz_users_password_resets`
|
||||
) AS `stat_user_password_resets`,
|
||||
(
|
||||
SELECT COUNT(`warning_id`)
|
||||
FROM `msz_user_warnings`
|
||||
WHERE `warning_type` != 0
|
||||
) AS `stat_user_warnings`
|
||||
')->fetch();
|
||||
|
||||
Template::render('manage.general.overview', [
|
||||
'statistics' => $statistics,
|
||||
]);
|
|
@ -1,35 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Pagination;
|
||||
use Misuzu\Users\User;
|
||||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
if(!User::hasCurrent() || !perms_check_user(MSZ_PERMS_GENERAL, User::getCurrent()->getId(), MSZ_PERM_GENERAL_VIEW_LOGS)) {
|
||||
echo render_error(403);
|
||||
return;
|
||||
}
|
||||
|
||||
$auditLog = $msz->getAuditLog();
|
||||
$pagination = new Pagination($auditLog->countLogs(), 50);
|
||||
|
||||
if(!$pagination->hasValidOffset()) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
$logs = $auditLog->getLogs(pagination: $pagination);
|
||||
$userInfos = [];
|
||||
foreach($logs as $log)
|
||||
if($log->hasUserId()) {
|
||||
$userId = $log->getUserId();
|
||||
if(!array_key_exists($userId, $userInfos))
|
||||
$userInfos[$userId] = User::byId($userId);
|
||||
}
|
||||
|
||||
Template::render('manage.general.logs', [
|
||||
'global_logs' => $logs,
|
||||
'global_logs_pagination' => $pagination,
|
||||
'global_logs_users' => $userInfos,
|
||||
]);
|
|
@ -1,32 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Config\CfgTools;
|
||||
use Misuzu\Users\User;
|
||||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
if(!User::hasCurrent()
|
||||
|| !perms_check_user(MSZ_PERMS_GENERAL, User::getCurrent()->getId(), MSZ_PERM_GENERAL_MANAGE_CONFIG)) {
|
||||
echo render_error(403);
|
||||
return;
|
||||
}
|
||||
|
||||
$valueName = (string)filter_input(INPUT_GET, 'name');
|
||||
$valueInfo = $cfg->getValueInfo($valueName);
|
||||
if($valueInfo === null) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
if($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
|
||||
$valueName = $valueInfo->getName();
|
||||
$msz->createAuditLog('CONFIG_DELETE', [$valueName]);
|
||||
$cfg->removeValues($valueName);
|
||||
url_redirect('manage-general-settings');
|
||||
return;
|
||||
}
|
||||
|
||||
Template::render('manage.general.setting-delete', [
|
||||
'config_value' => $valueInfo,
|
||||
]);
|
|
@ -1,95 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Config\DbConfig;
|
||||
use Misuzu\Users\User;
|
||||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
if(!User::hasCurrent()
|
||||
|| !perms_check_user(MSZ_PERMS_GENERAL, User::getCurrent()->getId(), MSZ_PERM_GENERAL_MANAGE_CONFIG)) {
|
||||
echo render_error(403);
|
||||
return;
|
||||
}
|
||||
|
||||
$isNew = true;
|
||||
$sName = (string)filter_input(INPUT_GET, 'name');
|
||||
$sType = (string)filter_input(INPUT_GET, 'type');
|
||||
$sValue = null;
|
||||
$loadValueInfo = fn() => $cfg->getValueInfo($sName);
|
||||
|
||||
if(!empty($sName)) {
|
||||
$sInfo = $loadValueInfo();
|
||||
if($sInfo !== null) {
|
||||
$isNew = false;
|
||||
$sName = $sInfo->getName();
|
||||
$sType = $sInfo->getType();
|
||||
$sValue = $sInfo->getValue();
|
||||
}
|
||||
}
|
||||
|
||||
while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
|
||||
if($isNew) {
|
||||
$sName = trim((string)filter_input(INPUT_POST, 'conf_name'));
|
||||
if(!DbConfig::validateName($sName)) {
|
||||
echo 'Name contains invalid characters.';
|
||||
break;
|
||||
}
|
||||
|
||||
$sType = trim((string)filter_input(INPUT_POST, 'conf_type'));
|
||||
if(!in_array($sType, ['string', 'int', 'float', 'bool', 'array'])) {
|
||||
echo 'Invalid type specified.';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if($sType === 'array') {
|
||||
$applyFunc = $cfg->setArray(...);
|
||||
$sValue = [];
|
||||
$sRaw = filter_input(INPUT_POST, 'conf_value', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY);
|
||||
foreach($sRaw as $rValue) {
|
||||
if(strpos($rValue, ':') === 1) {
|
||||
$rType = $rValue[0];
|
||||
$rValue = substr($rValue, 2);
|
||||
$sValue[] = match($rType) {
|
||||
's' => $rValue,
|
||||
'i' => (int)$rValue,
|
||||
'd' => (float)$rValue,
|
||||
'f' => (float)$rValue,
|
||||
'b' => $rValue !== 'false' && $rValue !== '0' && $rValue !== '',
|
||||
default => '',
|
||||
};
|
||||
} else
|
||||
$sValue[] = $rValue;
|
||||
}
|
||||
} elseif($sType === 'bool') {
|
||||
$sValue = !empty($_POST['conf_value']);
|
||||
$applyFunc = $cfg->setBoolean(...);
|
||||
} else {
|
||||
$sValue = filter_input(INPUT_POST, 'conf_value');
|
||||
if($sType === 'int') {
|
||||
$applyFunc = $cfg->setInteger(...);
|
||||
$sValue = (int)$sValue;
|
||||
} elseif($sType === 'float') {
|
||||
$applyFunc = $cfg->setFloat(...);
|
||||
$sValue = (float)$sValue;
|
||||
} else
|
||||
$applyFunc = $cfg->setString(...);
|
||||
}
|
||||
|
||||
$msz->createAuditLog($isNew ? 'CONFIG_CREATE' : 'CONFIG_UPDATE', [$sName]);
|
||||
$applyFunc($sName, $sValue);
|
||||
url_redirect('manage-general-settings');
|
||||
return;
|
||||
}
|
||||
|
||||
if($sType === 'array' && !empty($sValue))
|
||||
foreach($sValue as $key => $value)
|
||||
$sValue[$key] = gettype($value)[0] . ':' . $value;
|
||||
|
||||
Template::render('manage.general.setting', [
|
||||
'config_new' => $isNew,
|
||||
'config_name' => $sName,
|
||||
'config_type' => $sType,
|
||||
'config_value' => $sValue,
|
||||
]);
|
|
@ -1,20 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Users\User;
|
||||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
if(!User::hasCurrent()
|
||||
|| !perms_check_user(MSZ_PERMS_GENERAL, User::getCurrent()->getId(), MSZ_PERM_GENERAL_MANAGE_CONFIG)) {
|
||||
echo render_error(403);
|
||||
return;
|
||||
}
|
||||
|
||||
$hidden = $cfg->getArray('settings.hidden');
|
||||
$vars = $cfg->getAllValueInfos();
|
||||
|
||||
Template::render('manage.general.settings', [
|
||||
'config_vars' => $vars,
|
||||
'config_hidden' => $hidden,
|
||||
]);
|
|
@ -1,6 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
require_once '../../misuzu.php';
|
||||
|
||||
url_redirect('manage-general-overview');
|
|
@ -1,26 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Users\User;
|
||||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
if(!User::hasCurrent() || !perms_check_user(MSZ_PERMS_NEWS, User::getCurrent()->getId(), MSZ_PERM_NEWS_MANAGE_CATEGORIES)) {
|
||||
echo render_error(403);
|
||||
return;
|
||||
}
|
||||
|
||||
$news = $msz->getNews();
|
||||
$pagination = new Pagination($news->countAllCategories(true), 15);
|
||||
|
||||
if(!$pagination->hasValidOffset()) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
$categories = $news->getAllCategories(true, $pagination);
|
||||
|
||||
Template::render('manage.news.categories', [
|
||||
'news_categories' => $categories,
|
||||
'categories_pagination' => $pagination,
|
||||
]);
|
|
@ -1,72 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use RuntimeException;
|
||||
use Misuzu\Users\User;
|
||||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
if(!User::hasCurrent() || !perms_check_user(MSZ_PERMS_NEWS, User::getCurrent()->getId(), MSZ_PERM_NEWS_MANAGE_CATEGORIES)) {
|
||||
echo render_error(403);
|
||||
return;
|
||||
}
|
||||
|
||||
$news = $msz->getNews();
|
||||
$categoryId = (string)filter_input(INPUT_GET, 'c', FILTER_SANITIZE_NUMBER_INT);
|
||||
$loadCategoryInfo = fn() => $news->getCategoryById($categoryId);
|
||||
|
||||
if(empty($categoryId))
|
||||
$isNew = true;
|
||||
else
|
||||
try {
|
||||
$isNew = false;
|
||||
$categoryInfo = $loadCategoryInfo();
|
||||
} catch(RuntimeException $ex) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
if($_SERVER['REQUEST_METHOD'] === 'GET' && !empty($_GET['delete'])) {
|
||||
if(CSRF::validateRequest()) {
|
||||
$news->deleteCategory($categoryInfo);
|
||||
$msz->createAuditLog('NEWS_CATEGORY_DELETE', [$categoryInfo->getId()]);
|
||||
url_redirect('manage-news-categories');
|
||||
} else render_error(403);
|
||||
return;
|
||||
}
|
||||
|
||||
while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
|
||||
$name = trim((string)filter_input(INPUT_POST, 'nc_name'));
|
||||
$description = trim((string)filter_input(INPUT_POST, 'nc_desc'));
|
||||
$hidden = !empty($_POST['nc_hidden']);
|
||||
|
||||
if($isNew) {
|
||||
$categoryInfo = $news->createCategory($name, $description, $hidden);
|
||||
} else {
|
||||
if($name === $categoryInfo->getName())
|
||||
$name = null;
|
||||
if($description === $categoryInfo->getDescription())
|
||||
$description = null;
|
||||
if($hidden === $categoryInfo->isHidden())
|
||||
$hidden = null;
|
||||
|
||||
if($name !== null || $description !== null || $hidden !== null)
|
||||
$news->updateCategory($categoryInfo, $name, $description, $hidden);
|
||||
}
|
||||
|
||||
$msz->createAuditLog(
|
||||
$isNew ? 'NEWS_CATEGORY_CREATE' : 'NEWS_CATEGORY_EDIT',
|
||||
[$categoryInfo->getId()]
|
||||
);
|
||||
|
||||
if($isNew) {
|
||||
url_redirect('manage-news-category', ['category' => $categoryInfo->getId()]);
|
||||
return;
|
||||
} else $categoryInfo = $loadCategoryInfo();
|
||||
break;
|
||||
}
|
||||
|
||||
Template::render('manage.news.category', [
|
||||
'category_new' => $isNew,
|
||||
'category_info' => $categoryInfo ?? null,
|
||||
]);
|
|
@ -1,6 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
url_redirect('manage-news-categories');
|
|
@ -1,84 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use RuntimeException;
|
||||
use Misuzu\Users\User;
|
||||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
if(!User::hasCurrent() || !perms_check_user(MSZ_PERMS_NEWS, User::getCurrent()->getId(), MSZ_PERM_NEWS_MANAGE_POSTS)) {
|
||||
echo render_error(403);
|
||||
return;
|
||||
}
|
||||
|
||||
$news = $msz->getNews();
|
||||
$postId = (string)filter_input(INPUT_GET, 'p', FILTER_SANITIZE_NUMBER_INT);
|
||||
$loadPostInfo = fn() => $news->getPostById($postId);
|
||||
|
||||
if(empty($postId))
|
||||
$isNew = true;
|
||||
else
|
||||
try {
|
||||
$isNew = false;
|
||||
$postInfo = $loadPostInfo();
|
||||
} catch(RuntimeException $ex) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
if($_SERVER['REQUEST_METHOD'] === 'GET' && !empty($_GET['delete'])) {
|
||||
if(CSRF::validateRequest()) {
|
||||
$news->deletePost($postInfo);
|
||||
$msz->createAuditLog('NEWS_POST_DELETE', [$postInfo->getId()]);
|
||||
url_redirect('manage-news-posts');
|
||||
} else render_error(403);
|
||||
return;
|
||||
}
|
||||
|
||||
while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
|
||||
$title = trim((string)filter_input(INPUT_POST, 'np_title'));
|
||||
$category = (string)filter_input(INPUT_POST, 'np_category', FILTER_SANITIZE_NUMBER_INT);
|
||||
$featured = !empty($_POST['np_featured']);
|
||||
$body = trim((string)filter_input(INPUT_POST, 'np_body'));
|
||||
|
||||
if($isNew) {
|
||||
$postInfo = $news->createPost($category, $title, $body, $featured, User::getCurrent());
|
||||
} else {
|
||||
if($category === $postInfo->getCategoryId())
|
||||
$category = null;
|
||||
if($title === $postInfo->getTitle())
|
||||
$title = null;
|
||||
if($body === $postInfo->getBody())
|
||||
$body = null;
|
||||
if($featured === $postInfo->isFeatured())
|
||||
$featured = null;
|
||||
|
||||
if($category !== null || $title !== null || $body !== null || $featured !== null)
|
||||
$news->updatePost($postInfo, $category, $title, $body, $featured);
|
||||
}
|
||||
|
||||
$msz->createAuditLog(
|
||||
$isNew ? 'NEWS_POST_CREATE' : 'NEWS_POST_EDIT',
|
||||
[$postInfo->getId()]
|
||||
);
|
||||
|
||||
if($isNew) {
|
||||
if($postInfo->isFeatured()) {
|
||||
// Twitter integration used to be here, replace with Railgun Pulse integration
|
||||
}
|
||||
|
||||
url_redirect('manage-news-post', ['post' => $postInfo->getId()]);
|
||||
return;
|
||||
} else $postInfo = $loadPostInfo();
|
||||
break;
|
||||
}
|
||||
|
||||
$categories = [];
|
||||
foreach($news->getAllCategories(true) as $categoryInfo)
|
||||
$categories[$categoryInfo->getId()] = $categoryInfo->getName();
|
||||
|
||||
Template::render('manage.news.post', [
|
||||
'categories' => $categories,
|
||||
'post_new' => $isNew,
|
||||
'post_info' => $postInfo ?? null,
|
||||
]);
|
|
@ -1,33 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Users\User;
|
||||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
if(!User::hasCurrent() || !perms_check_user(MSZ_PERMS_NEWS, User::getCurrent()->getId(), MSZ_PERM_NEWS_MANAGE_POSTS)) {
|
||||
echo render_error(403);
|
||||
return;
|
||||
}
|
||||
|
||||
$news = $msz->getNews();
|
||||
$pagination = new Pagination($news->countAllPosts(
|
||||
includeScheduled: true,
|
||||
includeDeleted: true
|
||||
), 15);
|
||||
|
||||
if(!$pagination->hasValidOffset()) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
$posts = $news->getAllPosts(
|
||||
includeScheduled: true,
|
||||
includeDeleted: true,
|
||||
pagination: $pagination
|
||||
);
|
||||
|
||||
Template::render('manage.news.posts', [
|
||||
'news_posts' => $posts,
|
||||
'posts_pagination' => $pagination,
|
||||
]);
|
|
@ -1,23 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Users\User;
|
||||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
if(!User::hasCurrent() || !perms_check_user(MSZ_PERMS_USER, User::getCurrent()->getId(), MSZ_PERM_USER_MANAGE_USERS)) {
|
||||
echo render_error(403);
|
||||
return;
|
||||
}
|
||||
|
||||
$pagination = new Pagination(User::countAll(true), 30);
|
||||
|
||||
if(!$pagination->hasValidOffset()) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
Template::render('manage.users.users', [
|
||||
'manage_users' => User::all(true, $pagination),
|
||||
'manage_users_pagination' => $pagination,
|
||||
]);
|
|
@ -1,136 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Index\Colour\Colour;
|
||||
use Index\Colour\ColourRGB;
|
||||
use Misuzu\Users\User;
|
||||
use Misuzu\Users\UserRole;
|
||||
use Misuzu\Users\UserRoleNotFoundException;
|
||||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
if(!User::hasCurrent() || !perms_check_user(MSZ_PERMS_USER, User::getCurrent()->getId(), MSZ_PERM_USER_MANAGE_ROLES)) {
|
||||
echo render_error(403);
|
||||
return;
|
||||
}
|
||||
|
||||
$roleId = (int)filter_input(INPUT_GET, 'r', FILTER_SANITIZE_NUMBER_INT);
|
||||
|
||||
if($roleId > 0)
|
||||
try {
|
||||
$roleInfo = UserRole::byId($roleId);
|
||||
} catch(UserRoleNotFoundException $ex) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
$currentUser = User::getCurrent();
|
||||
$currentUserId = $currentUser->getId();
|
||||
$canEditPerms = perms_check_user(MSZ_PERMS_USER, $currentUserId, MSZ_PERM_USER_MANAGE_PERMS);
|
||||
|
||||
if($canEditPerms)
|
||||
$permissions = manage_perms_list(perms_get_role_raw($roleId ?? 0));
|
||||
|
||||
if(!empty($_POST['role']) && is_array($_POST['role']) && CSRF::validateRequest()) {
|
||||
$roleHierarchy = (int)($_POST['role']['hierarchy'] ?? -1);
|
||||
|
||||
if(!$currentUser->isSuper() && (isset($roleInfo) ? $roleInfo->hasAuthorityOver($currentUser) : $currentUser->getRank() <= $roleHierarchy)) {
|
||||
echo 'You don\'t hold authority over this role.';
|
||||
return;
|
||||
}
|
||||
|
||||
$roleName = $_POST['role']['name'] ?? '';
|
||||
$roleNameLength = strlen($roleName);
|
||||
|
||||
if($roleNameLength < 1 || $roleNameLength > 255) {
|
||||
echo 'invalid name length';
|
||||
return;
|
||||
}
|
||||
|
||||
$roleSecret = !empty($_POST['role']['secret']);
|
||||
|
||||
if($roleHierarchy < 1 || $roleHierarchy > 100) {
|
||||
echo 'Invalid hierarchy value.';
|
||||
return;
|
||||
}
|
||||
|
||||
if(!empty($_POST['role']['colour']['inherit'])) {
|
||||
$roleColour = \Index\Colour\Colour::none();
|
||||
} else {
|
||||
$roleColour = new ColourRGB(
|
||||
(int)($_POST['role']['colour']['red'] ?? -1),
|
||||
(int)($_POST['role']['colour']['green'] ?? -1),
|
||||
(int)($_POST['role']['colour']['blue'] ?? -1)
|
||||
);
|
||||
}
|
||||
|
||||
$roleDescription = $_POST['role']['description'] ?? '';
|
||||
$roleTitle = $_POST['role']['title'] ?? '';
|
||||
|
||||
if($roleDescription !== null) {
|
||||
$rdLength = strlen($roleDescription);
|
||||
|
||||
if($rdLength > 1000) {
|
||||
echo 'description is too long';
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if($roleTitle !== null) {
|
||||
$rtLength = strlen($roleTitle);
|
||||
|
||||
if($rtLength > 64) {
|
||||
echo 'title is too long';
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(!isset($roleInfo))
|
||||
$roleInfo = new UserRole;
|
||||
|
||||
$roleInfo->setName($roleName)
|
||||
->setRank($roleHierarchy)
|
||||
->setHidden($roleSecret)
|
||||
->setColour($roleColour)
|
||||
->setDescription($roleDescription)
|
||||
->setTitle($roleTitle)
|
||||
->save();
|
||||
|
||||
if(!empty($permissions) && !empty($_POST['perms']) && is_array($_POST['perms'])) {
|
||||
$perms = manage_perms_apply($permissions, $_POST['perms']);
|
||||
|
||||
if($perms !== null) {
|
||||
$permKeys = array_keys($perms);
|
||||
$setPermissions = DB::prepare('
|
||||
REPLACE INTO `msz_permissions`
|
||||
(`role_id`, `user_id`, `' . implode('`, `', $permKeys) . '`)
|
||||
VALUES
|
||||
(:role_id, NULL, :' . implode(', :', $permKeys) . ')
|
||||
');
|
||||
$setPermissions->bind('role_id', $roleInfo->getId());
|
||||
|
||||
foreach($perms as $key => $value) {
|
||||
$setPermissions->bind($key, $value);
|
||||
}
|
||||
|
||||
$setPermissions->execute();
|
||||
} else {
|
||||
$deletePermissions = DB::prepare('
|
||||
DELETE FROM `msz_permissions`
|
||||
WHERE `role_id` = :role_id
|
||||
AND `user_id` IS NULL
|
||||
');
|
||||
$deletePermissions->bind('role_id', $roleInfo->getId());
|
||||
$deletePermissions->execute();
|
||||
}
|
||||
}
|
||||
|
||||
url_redirect('manage-role', ['role' => $roleInfo->getId()]);
|
||||
return;
|
||||
}
|
||||
|
||||
Template::render('manage.users.role', [
|
||||
'role_info' => $roleInfo ?? null,
|
||||
'can_manage_perms' => $canEditPerms,
|
||||
'permissions' => $permissions ?? [],
|
||||
]);
|
|
@ -1,24 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Users\User;
|
||||
use Misuzu\Users\UserRole;
|
||||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
if(!User::hasCurrent() || !perms_check_user(MSZ_PERMS_USER, User::getCurrent()->getId(), MSZ_PERM_USER_MANAGE_ROLES)) {
|
||||
echo render_error(403);
|
||||
return;
|
||||
}
|
||||
|
||||
$pagination = new Pagination(UserRole::countAll(true), 10);
|
||||
|
||||
if(!$pagination->hasValidOffset()) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
Template::render('manage.users.roles', [
|
||||
'manage_roles' => UserRole::all(true, $pagination),
|
||||
'manage_roles_pagination' => $pagination,
|
||||
]);
|
|
@ -1,210 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Index\Colour\Colour;
|
||||
use Misuzu\Users\User;
|
||||
use Misuzu\Users\UserNotFoundException;
|
||||
use Misuzu\Users\UserRole;
|
||||
use Misuzu\Users\UserRoleNotFoundException;
|
||||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
if(!User::hasCurrent() || !perms_check_user(MSZ_PERMS_USER, User::getCurrent()->getId(), MSZ_PERM_USER_MANAGE_USERS)) {
|
||||
echo render_error(403);
|
||||
return;
|
||||
}
|
||||
|
||||
$notices = [];
|
||||
$userId = (int)filter_input(INPUT_GET, 'u', FILTER_SANITIZE_NUMBER_INT);
|
||||
$currentUser = User::getCurrent();
|
||||
$currentUserId = $currentUser->getId();
|
||||
|
||||
try {
|
||||
$userInfo = User::byId($userId);
|
||||
} catch(UserNotFoundException $ex) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
$canEdit = $currentUser->hasAuthorityOver($userInfo);
|
||||
$canEditPerms = $canEdit && perms_check_user(MSZ_PERMS_USER, $currentUserId, MSZ_PERM_USER_MANAGE_PERMS);
|
||||
$permissions = manage_perms_list(perms_get_user_raw($userId));
|
||||
|
||||
if(CSRF::validateRequest() && $canEdit) {
|
||||
if(!empty($_POST['impersonate_user'])) {
|
||||
if(!$currentUser->isSuper()) {
|
||||
$notices[] = 'You must be a super user to do this.';
|
||||
} elseif(!is_string($_POST['impersonate_user']) || $_POST['impersonate_user'] !== 'meow') {
|
||||
$notices[] = 'You didn\'t say the magic word.';
|
||||
} else {
|
||||
$authToken->setImpersonatedUserId($userInfo->getId());
|
||||
$authToken->applyCookie();
|
||||
url_redirect('index');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(!empty($_POST['send_test_email'])) {
|
||||
if(!$currentUser->isSuper()) {
|
||||
$notices[] = 'You must be a super user to do this.';
|
||||
} elseif(!is_string($_POST['send_test_email']) || $_POST['send_test_email'] !== 'yes_send_it') {
|
||||
$notices[] = 'Invalid request thing shut the fuck up.';
|
||||
} else {
|
||||
$testMail = Mailer::sendMessage(
|
||||
[$userInfo->getEMailAddress() => $userInfo->getUsername()],
|
||||
'Flashii Test E-mail',
|
||||
'You were sent this e-mail to validate if you can receive e-mails from Flashii. You may discard it.'
|
||||
);
|
||||
|
||||
if(!$testMail)
|
||||
$notices[] = 'Failed to send test e-mail.';
|
||||
}
|
||||
}
|
||||
|
||||
if(!empty($_POST['roles']) && is_array($_POST['roles'])) {
|
||||
// Read user input array and throw intval on em
|
||||
$applyRoles = [];
|
||||
foreach($_POST['roles'] as $item) {
|
||||
if(!ctype_digit($item))
|
||||
die('Invalid item encountered in roles list.');
|
||||
$applyRoles[] = (int)$item;
|
||||
}
|
||||
|
||||
// Fetch existing roles
|
||||
$existingRoles = $userInfo->getRoles();
|
||||
|
||||
// Initialise set array with existing roles
|
||||
$setRoles = $existingRoles;
|
||||
|
||||
// Storage array for roles to dump
|
||||
$removeRoles = [];
|
||||
|
||||
// STEP 1: Check for roles to be removed in the existing set.
|
||||
// Roles that the current users isn't allowed to touch (hierarchy) will stay.
|
||||
foreach($setRoles as $role) {
|
||||
// Also prevent the main role from being removed.
|
||||
if($role->isDefault() || !$currentUser->hasAuthorityOver($role))
|
||||
continue;
|
||||
if(!in_array($role->getId(), $applyRoles))
|
||||
$removeRoles[] = $role;
|
||||
}
|
||||
|
||||
// STEP 2: Purge the ones marked for removal.
|
||||
$setRoles = array_diff($setRoles, $removeRoles);
|
||||
|
||||
// STEP 3: Add roles to the set array from the user input, if the user has authority over the given roles.
|
||||
foreach($applyRoles as $roleId) {
|
||||
try {
|
||||
$role = $existingRoles[$roleId] ?? UserRole::byId($roleId);
|
||||
} catch(UserRoleNotFoundException $ex) {
|
||||
continue;
|
||||
}
|
||||
if(!$currentUser->hasAuthorityOver($role))
|
||||
continue;
|
||||
if(!in_array($role, $setRoles))
|
||||
$setRoles[] = $role;
|
||||
}
|
||||
|
||||
foreach($removeRoles as $role)
|
||||
$userInfo->removeRole($role);
|
||||
|
||||
foreach($setRoles as $role)
|
||||
$userInfo->addRole($role);
|
||||
}
|
||||
|
||||
if(!empty($_POST['user']) && is_array($_POST['user'])) {
|
||||
//$setUsername = (string)($_POST['user']['username'] ?? '');
|
||||
//$setEMailAddress = (string)($_POST['user']['email'] ?? '');
|
||||
$setCountry = (string)($_POST['user']['country'] ?? '');
|
||||
$setTitle = (string)($_POST['user']['title'] ?? '');
|
||||
|
||||
$displayRole = (int)($_POST['user']['display_role'] ?? 0);
|
||||
|
||||
try {
|
||||
$userInfo->setDisplayRole(UserRole::byId($displayRole));
|
||||
} catch(UserRoleNotFoundException $ex) {}
|
||||
|
||||
//$usernameValidation = User::validateUsername($setUsername);
|
||||
//$emailValidation = User::validateEMailAddress($setEMailAddress);
|
||||
$countryValidation = strlen($setCountry) === 2
|
||||
&& ctype_alpha($setCountry)
|
||||
&& ctype_upper($setCountry);
|
||||
|
||||
//if(!empty($usernameValidation))
|
||||
// $notices[] = User::usernameValidationErrorString($usernameValidation);
|
||||
|
||||
// if(!empty($emailValidation)) {
|
||||
// $notices[] = $emailValidation === 'in-use'
|
||||
// ? 'This e-mail address has already been used!'
|
||||
// : 'This e-mail address is invalid!';
|
||||
// }
|
||||
|
||||
if(!$countryValidation)
|
||||
$notices[] = 'Country code was invalid.';
|
||||
|
||||
if(strlen($setTitle) > 64)
|
||||
$notices[] = 'User title was invalid.';
|
||||
|
||||
if(empty($notices))
|
||||
$userInfo
|
||||
// ->setUsername((string)($_POST['user']['username'] ?? ''))
|
||||
// ->setEMailAddress((string)($_POST['user']['email'] ?? ''))
|
||||
->setCountry((string)($_POST['user']['country'] ?? ''))
|
||||
->setTitle((string)($_POST['user']['title'] ?? ''))
|
||||
->setDisplayRole(UserRole::byId((int)($_POST['user']['display_role'] ?? 0)));
|
||||
}
|
||||
|
||||
if(!empty($_POST['colour']) && is_array($_POST['colour'])) {
|
||||
$setColour = null;
|
||||
|
||||
if(!empty($_POST['colour']['enable'])) {
|
||||
$setColour = \Index\Colour\Colour::parse((string)($_POST['colour']['hex'] ?? ''));
|
||||
if($setColour->shouldInherit())
|
||||
$notices[] = 'Invalid colour specified.';
|
||||
}
|
||||
|
||||
if(empty($notices))
|
||||
$userInfo->setColour($setColour);
|
||||
}
|
||||
|
||||
if(!empty($_POST['password']) && is_array($_POST['password'])) {
|
||||
$passwordNewValue = (string)($_POST['password']['new'] ?? '');
|
||||
$passwordConfirmValue = (string)($_POST['password']['confirm'] ?? '');
|
||||
|
||||
if(!empty($passwordNewValue)) {
|
||||
if($passwordNewValue !== $passwordConfirmValue)
|
||||
$notices[] = 'Confirm password does not match.';
|
||||
elseif(!empty(User::validatePassword($passwordNewValue)))
|
||||
$notices[] = 'New password is too weak.';
|
||||
else
|
||||
$userInfo->setPassword($passwordNewValue);
|
||||
}
|
||||
}
|
||||
|
||||
if(empty($notices))
|
||||
$userInfo->save();
|
||||
|
||||
if($canEditPerms && !empty($_POST['perms']) && is_array($_POST['perms'])) {
|
||||
$perms = manage_perms_apply($permissions, $_POST['perms']);
|
||||
|
||||
if($perms !== null) {
|
||||
if(!perms_set_user_raw($userId, $perms))
|
||||
$notices[] = 'Failed to update permissions.';
|
||||
} else {
|
||||
if(!perms_delete_user($userId))
|
||||
$notices[] = 'Failed to remove permissions.';
|
||||
}
|
||||
|
||||
// this smells, make it refresh/apply in a non-retarded way
|
||||
$permissions = manage_perms_list(perms_get_user_raw($userId));
|
||||
}
|
||||
}
|
||||
|
||||
Template::render('manage.users.user', [
|
||||
'user_info' => $userInfo,
|
||||
'manage_notices' => $notices,
|
||||
'manage_roles' => UserRole::all(true),
|
||||
'can_edit_user' => $canEdit,
|
||||
'can_edit_perms' => $canEdit && $canEditPerms,
|
||||
'permissions' => $permissions ?? [],
|
||||
]);
|
|
@ -1,165 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Misuzu\Users\User;
|
||||
use Misuzu\Users\UserNotFoundException;
|
||||
use Misuzu\Users\UserWarning;
|
||||
use Misuzu\Users\UserWarningNotFoundException;
|
||||
use Misuzu\Users\UserWarningCreationFailedException;
|
||||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
if(!User::hasCurrent() || !perms_check_user(MSZ_PERMS_USER, User::getCurrent()->getId(), MSZ_PERM_USER_MANAGE_WARNINGS)) {
|
||||
echo render_error(403);
|
||||
return;
|
||||
}
|
||||
|
||||
$notices = [];
|
||||
$currentUser = User::getCurrent();
|
||||
$currentUserId = $currentUser->getId();
|
||||
|
||||
if(!empty($_POST['lookup']) && is_string($_POST['lookup'])) {
|
||||
try {
|
||||
$userId = User::byUsername((string)filter_input(INPUT_POST, 'lookup'))->getId();
|
||||
} catch(UserNotFoundException $ex) {
|
||||
$userId = 0;
|
||||
}
|
||||
url_redirect('manage-users-warnings', ['user' => $userId]);
|
||||
return;
|
||||
}
|
||||
|
||||
// instead of just kinda taking $_GET['w'] this should really fetch the info from the database
|
||||
// and make sure that the user has authority
|
||||
if(!empty($_GET['delete'])) {
|
||||
try {
|
||||
UserWarning::byId((int)filter_input(INPUT_GET, 'w', FILTER_SANITIZE_NUMBER_INT))->delete();
|
||||
} catch(UserWarningNotFoundException $ex) {}
|
||||
redirect($_SERVER['HTTP_REFERER'] ?? url('manage-users-warnings'));
|
||||
return;
|
||||
}
|
||||
|
||||
if(!empty($_POST['warning']) && is_array($_POST['warning'])) {
|
||||
$warningType = (int)($_POST['warning']['type'] ?? 0);
|
||||
$warningDuration = 0;
|
||||
$warningDuration = (int)($_POST['warning']['duration'] ?? 0);
|
||||
|
||||
if($warningDuration < -1) {
|
||||
$customDuration = $_POST['warning']['duration_custom'] ?? '';
|
||||
|
||||
if(!empty($customDuration)) {
|
||||
switch($warningDuration) {
|
||||
case -100: // YYYY-MM-DD
|
||||
$splitDate = explode('-', $customDuration, 3);
|
||||
if(count($splitDate) !== 3)
|
||||
die('Invalid date specified.');
|
||||
|
||||
$wYear = (int)$splitDate[0];
|
||||
$wMonth = (int)$splitDate[1];
|
||||
$wDay = (int)$splitDate[2];
|
||||
|
||||
if(checkdate($wMonth, $wDay, $wYear))
|
||||
$warningDuration = mktime(0, 0, 0, $wMonth, $wDay, $wYear) - time();
|
||||
else
|
||||
die('Invalid date specified.');
|
||||
break;
|
||||
|
||||
case -200: // Raw seconds
|
||||
$warningDuration = (int)$customDuration;
|
||||
break;
|
||||
|
||||
case -300: // strtotime
|
||||
$warningDuration = strtotime($customDuration) - time();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$warningsUserInfo = User::byId((int)($_POST['warning']['user'] ?? 0));
|
||||
$warningsUser = $warningsUserInfo->getId();
|
||||
|
||||
if(!$currentUser->hasAuthorityOver($warningsUserInfo))
|
||||
$notices[] = 'You do not have authority over this user.';
|
||||
} catch(UserNotFoundException $ex) {
|
||||
$notices[] = 'This user doesn\'t exist.';
|
||||
}
|
||||
|
||||
|
||||
if(empty($notices) && !empty($warningsUserInfo)) {
|
||||
try {
|
||||
$warningInfo = UserWarning::create(
|
||||
$warningsUserInfo,
|
||||
$currentUser,
|
||||
$warningType,
|
||||
$warningDuration,
|
||||
$_POST['warning']['note'],
|
||||
$_POST['warning']['private']
|
||||
);
|
||||
} catch(InvalidArgumentException $ex) {
|
||||
$notices[] = $ex->getMessage();
|
||||
} catch(UserWarningCreationFailedException $ex) {
|
||||
$notices[] = 'Warning creation failed.';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(empty($warningsUser))
|
||||
$warningsUser = max(0, (int)($_GET['u'] ?? 0));
|
||||
|
||||
if(empty($warningsUserInfo))
|
||||
try {
|
||||
$warningsUserInfo = User::byId($warningsUser);
|
||||
} catch(UserNotFoundException $ex) {
|
||||
$warningsUserInfo = null;
|
||||
}
|
||||
|
||||
$warningsPagination = new Pagination(UserWarning::countAll($warningsUserInfo), 10);
|
||||
|
||||
if(!$warningsPagination->hasValidOffset()) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
// calling array_flip since the input_select macro wants value => display, but this looks cuter
|
||||
$warningDurations = array_flip([
|
||||
'Pick a duration...' => 0,
|
||||
'5 Minutes' => 60 * 5,
|
||||
'15 Minutes' => 60 * 15,
|
||||
'30 Minutes' => 60 * 30,
|
||||
'45 Minutes' => 60 * 45,
|
||||
'1 Hour' => 60 * 60,
|
||||
'2 Hours' => 60 * 60 * 2,
|
||||
'3 Hours' => 60 * 60 * 3,
|
||||
'6 Hours' => 60 * 60 * 6,
|
||||
'12 Hours' => 60 * 60 * 12,
|
||||
'1 Day' => 60 * 60 * 24,
|
||||
'2 Days' => 60 * 60 * 24 * 2,
|
||||
'1 Week' => 60 * 60 * 24 * 7,
|
||||
'2 Weeks' => 60 * 60 * 24 * 7 * 2,
|
||||
'1 Month' => 60 * 60 * 24 * 365 / 12,
|
||||
'3 Months' => 60 * 60 * 24 * 365 / 12 * 3,
|
||||
'6 Months' => 60 * 60 * 24 * 365 / 12 * 6,
|
||||
'9 Months' => 60 * 60 * 24 * 365 / 12 * 9,
|
||||
'1 Year' => 60 * 60 * 24 * 365,
|
||||
'Permanent' => -1,
|
||||
'Until (YYYY-MM-DD) ->' => -100,
|
||||
'Until (Seconds) ->' => -200,
|
||||
'Until (strtotime) ->' => -300,
|
||||
]);
|
||||
|
||||
Template::render('manage.users.warnings', [
|
||||
'warnings' => [
|
||||
'notices' => $notices,
|
||||
'pagination' => $warningsPagination,
|
||||
'list' => UserWarning::all($warningsUserInfo, $warningsPagination),
|
||||
'user' => $warningsUserInfo,
|
||||
'durations' => $warningDurations,
|
||||
'types' => [
|
||||
UserWarning::TYPE_NOTE => 'Note',
|
||||
UserWarning::TYPE_WARN => 'Warning',
|
||||
UserWarning::TYPE_MUTE => 'Silence',
|
||||
UserWarning::TYPE_BAHN => 'Ban',
|
||||
],
|
||||
],
|
||||
]);
|
|
@ -1,139 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Users\User;
|
||||
use Misuzu\Users\UserRole;
|
||||
use Misuzu\Users\UserRoleNotFoundException;
|
||||
|
||||
require_once '../misuzu.php';
|
||||
|
||||
$roleId = !empty($_GET['r']) && is_string($_GET['r']) ? (int)$_GET['r'] : UserRole::DEFAULT;
|
||||
$orderBy = !empty($_GET['ss']) && is_string($_GET['ss']) ? mb_strtolower($_GET['ss']) : '';
|
||||
$orderDir = !empty($_GET['sd']) && is_string($_GET['sd']) ? mb_strtolower($_GET['sd']) : '';
|
||||
|
||||
$orderDirs = [
|
||||
'asc' => 'Ascending',
|
||||
'desc' => 'Descending',
|
||||
];
|
||||
|
||||
$defaultOrder = 'last-online';
|
||||
$orderFields = [
|
||||
'id' => [
|
||||
'column' => 'u.`user_id`',
|
||||
'default-dir' => 'asc',
|
||||
'title' => 'User ID',
|
||||
],
|
||||
'name' => [
|
||||
'column' => 'u.`username`',
|
||||
'default-dir' => 'asc',
|
||||
'title' => 'Username',
|
||||
],
|
||||
'country' => [
|
||||
'column' => 'u.`user_country`',
|
||||
'default-dir' => 'asc',
|
||||
'title' => 'Country',
|
||||
],
|
||||
'registered' => [
|
||||
'column' => 'u.`user_created`',
|
||||
'default-dir' => 'desc',
|
||||
'title' => 'Registration Date',
|
||||
],
|
||||
'last-online' => [
|
||||
'column' => 'u.`user_active`',
|
||||
'default-dir' => 'desc',
|
||||
'title' => 'Last Online',
|
||||
],
|
||||
'forum-topics' => [
|
||||
'column' => '`user_count_topics`',
|
||||
'default-dir' => 'desc',
|
||||
'title' => 'Forum Topics',
|
||||
],
|
||||
'forum-posts' => [
|
||||
'column' => '`user_count_posts`',
|
||||
'default-dir' => 'desc',
|
||||
'title' => 'Forum Posts',
|
||||
],
|
||||
];
|
||||
|
||||
if(empty($orderBy)) {
|
||||
$orderBy = $defaultOrder;
|
||||
} elseif(!array_key_exists($orderBy, $orderFields)) {
|
||||
echo render_error(400);
|
||||
return;
|
||||
}
|
||||
|
||||
if(empty($orderDir)) {
|
||||
$orderDir = $orderFields[$orderBy]['default-dir'];
|
||||
} elseif(!array_key_exists($orderDir, $orderDirs)) {
|
||||
echo render_error(400);
|
||||
return;
|
||||
}
|
||||
|
||||
$canManageUsers = perms_check_user(MSZ_PERMS_USER, User::hasCurrent() ? User::getCurrent()->getId() : 0, MSZ_PERM_USER_MANAGE_USERS);
|
||||
|
||||
try {
|
||||
$roleInfo = UserRole::byId($roleId);
|
||||
} catch(UserRoleNotFoundException $ex) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
$pagination = new Pagination($roleInfo->getUserCount(), 15);
|
||||
|
||||
$roles = UserRole::all();
|
||||
|
||||
$getUsers = DB::prepare(sprintf(
|
||||
'
|
||||
SELECT
|
||||
:current_user_id AS `current_user_id`,
|
||||
u.`user_id`, u.`username`, u.`user_country`,
|
||||
u.`user_created`, u.`user_active`, r.`role_id`,
|
||||
COALESCE(u.`user_title`, r.`role_title`) AS `user_title`,
|
||||
COALESCE(u.`user_colour`, r.`role_colour`) AS `user_colour`,
|
||||
(
|
||||
SELECT COUNT(`topic_id`)
|
||||
FROM `msz_forum_topics`
|
||||
WHERE `user_id` = u.`user_id`
|
||||
AND `topic_deleted` IS NULL
|
||||
) AS `user_count_topics`,
|
||||
(
|
||||
SELECT COUNT(`post_Id`)
|
||||
FROM `msz_forum_posts`
|
||||
WHERE `user_id` = u.`user_id`
|
||||
AND `post_deleted` IS NULL
|
||||
) AS `user_count_posts`
|
||||
FROM `msz_users` AS u
|
||||
LEFT JOIN `msz_roles` AS r
|
||||
ON r.`role_id` = u.`display_role`
|
||||
LEFT JOIN `msz_user_roles` AS ur
|
||||
ON ur.`user_id` = u.`user_id`
|
||||
WHERE ur.`role_id` = :role_id
|
||||
%1$s
|
||||
ORDER BY %2$s %3$s
|
||||
LIMIT %4$d, %5$d
|
||||
',
|
||||
$canManageUsers ? '' : 'AND u.`user_deleted` IS NULL',
|
||||
$orderFields[$orderBy]['column'],
|
||||
$orderDir,
|
||||
$pagination->getOffset(),
|
||||
$pagination->getRange()
|
||||
));
|
||||
$getUsers->bind('role_id', $roleInfo->getId());
|
||||
$getUsers->bind('current_user_id', User::hasCurrent() ? User::getCurrent()->getId() : 0);
|
||||
$users = $getUsers->fetchAll();
|
||||
|
||||
if(empty($users))
|
||||
http_response_code(404);
|
||||
|
||||
Template::render('user.listing', [
|
||||
'roles' => $roles,
|
||||
'role' => $roleInfo,
|
||||
'users' => $users,
|
||||
'order_fields' => $orderFields,
|
||||
'order_directions' => $orderDirs,
|
||||
'order_field' => $orderBy,
|
||||
'order_direction' => $orderDir,
|
||||
'order_default' => $defaultOrder,
|
||||
'can_manage_users' => $canManageUsers,
|
||||
'users_pagination' => $pagination,
|
||||
]);
|
|
@ -1,2 +0,0 @@
|
|||
<?php
|
||||
require_once __DIR__ . '/index.php';
|
|
@ -1,2 +0,0 @@
|
|||
<?php
|
||||
require_once __DIR__ . '/../index.php';
|
|
@ -1,2 +0,0 @@
|
|||
<?php
|
||||
require_once __DIR__ . '/../index.php';
|
|
@ -1,2 +0,0 @@
|
|||
<?php
|
||||
require_once __DIR__ . '/../index.php';
|
|
@ -1,2 +0,0 @@
|
|||
<?php
|
||||
require_once __DIR__ . '/../index.php';
|
|
@ -1,386 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Index\ByteFormat;
|
||||
use Misuzu\Parsers\Parser;
|
||||
use Misuzu\Users\User;
|
||||
use Misuzu\Users\UserNotFoundException;
|
||||
use Misuzu\Users\UserSession;
|
||||
use Misuzu\Users\Assets\UserBackgroundAsset;
|
||||
use Misuzu\Users\Assets\UserImageAssetException;
|
||||
use Misuzu\Users\Assets\UserImageAssetInvalidImageException;
|
||||
use Misuzu\Users\Assets\UserImageAssetInvalidTypeException;
|
||||
use Misuzu\Users\Assets\UserImageAssetInvalidDimensionsException;
|
||||
use Misuzu\Users\Assets\UserImageAssetFileTooLargeException;
|
||||
|
||||
require_once '../misuzu.php';
|
||||
|
||||
$userId = !empty($_GET['u']) && is_string($_GET['u']) ? trim($_GET['u']) : 0;
|
||||
$profileMode = !empty($_GET['m']) && is_string($_GET['m']) ? (string)$_GET['m'] : '';
|
||||
$isEditing = !empty($_GET['edit']) && is_string($_GET['edit']) ? (bool)$_GET['edit'] : !empty($_POST) && is_array($_POST);
|
||||
|
||||
try {
|
||||
$profileUser = User::findForProfile($userId);
|
||||
} catch(UserNotFoundException $ex) {
|
||||
http_response_code(404);
|
||||
Template::render('profile.index');
|
||||
return;
|
||||
}
|
||||
|
||||
if($profileUser->isDeleted()) {
|
||||
http_response_code(404);
|
||||
Template::render('profile.index');
|
||||
return;
|
||||
}
|
||||
|
||||
$notices = [];
|
||||
|
||||
$currentUser = User::getCurrent();
|
||||
$viewingAsGuest = $currentUser === null;
|
||||
$currentUserId = $viewingAsGuest ? 0 : $currentUser->getId();
|
||||
$viewingOwnProfile = $currentUserId === $profileUser->getId();
|
||||
$isBanned = $profileUser->hasActiveWarning();
|
||||
$userPerms = perms_get_user($currentUserId)[MSZ_PERMS_USER];
|
||||
$canManageWarnings = perms_check($userPerms, MSZ_PERM_USER_MANAGE_WARNINGS);
|
||||
$canEdit = !$isBanned
|
||||
&& UserSession::hasCurrent()
|
||||
&& ($viewingOwnProfile || $currentUser->isSuper() || (
|
||||
perms_check($userPerms, MSZ_PERM_USER_MANAGE_USERS)
|
||||
&& $currentUser->hasAuthorityOver($profileUser)
|
||||
));
|
||||
|
||||
if($isEditing) {
|
||||
if(!$canEdit) {
|
||||
echo render_error(403);
|
||||
return;
|
||||
}
|
||||
|
||||
$perms = perms_check_bulk($userPerms, [
|
||||
'edit_profile' => MSZ_PERM_USER_EDIT_PROFILE,
|
||||
'edit_avatar' => MSZ_PERM_USER_CHANGE_AVATAR,
|
||||
'edit_background' => MSZ_PERM_USER_CHANGE_BACKGROUND,
|
||||
'edit_about' => MSZ_PERM_USER_EDIT_ABOUT,
|
||||
'edit_birthdate' => MSZ_PERM_USER_EDIT_BIRTHDATE,
|
||||
'edit_signature' => MSZ_PERM_USER_EDIT_SIGNATURE,
|
||||
]);
|
||||
|
||||
Template::set([
|
||||
'perms' => $perms,
|
||||
'background_attachments' => UserBackgroundAsset::getAttachmentStringOptions(),
|
||||
]);
|
||||
|
||||
if(!empty($_POST) && is_array($_POST)) {
|
||||
if(!CSRF::validateRequest()) {
|
||||
$notices[] = 'Couldn\'t verify you, please refresh the page and retry.';
|
||||
} else {
|
||||
if(!empty($_POST['profile']) && is_array($_POST['profile'])) {
|
||||
if(!$perms['edit_profile']) {
|
||||
$notices[] = 'You\'re not allowed to edit your profile';
|
||||
} else {
|
||||
$profileFields = $profileUser->profileFields(false);
|
||||
|
||||
foreach($profileFields as $profileField) {
|
||||
if(isset($_POST['profile'][$profileField->field_key])
|
||||
&& $profileField->field_value !== $_POST['profile'][$profileField->field_key]
|
||||
&& !$profileField->setFieldValue($_POST['profile'][$profileField->field_key])) {
|
||||
$notices[] = sprintf('%s was formatted incorrectly!', $profileField->field_title);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!empty($_POST['about']) && is_array($_POST['about'])) {
|
||||
if(!$perms['edit_about']) {
|
||||
$notices[] = 'You\'re not allowed to edit your about page.';
|
||||
} else {
|
||||
$aboutText = (string)($_POST['about']['text'] ?? '');
|
||||
$aboutParse = (int)($_POST['about']['parser'] ?? Parser::PLAIN);
|
||||
$aboutValid = User::validateProfileAbout($aboutParse, $aboutText);
|
||||
|
||||
if($aboutValid === '')
|
||||
$currentUser->setProfileAboutText($aboutText)->setProfileAboutParser($aboutParse);
|
||||
else switch($aboutValid) {
|
||||
case 'parser':
|
||||
$notices[] = 'The selected about section parser is invalid.';
|
||||
break;
|
||||
case 'long':
|
||||
$notices[] = sprintf('Please keep the length of your about section below %d characters.', User::PROFILE_ABOUT_MAX_LENGTH);
|
||||
break;
|
||||
default:
|
||||
$notices[] = 'Failed to update about section, contact an administator.';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!empty($_POST['signature']) && is_array($_POST['signature'])) {
|
||||
if(!$perms['edit_signature']) {
|
||||
$notices[] = 'You\'re not allowed to edit your forum signature.';
|
||||
} else {
|
||||
$sigText = (string)($_POST['signature']['text'] ?? '');
|
||||
$sigParse = (int)($_POST['signature']['parser'] ?? Parser::PLAIN);
|
||||
$sigValid = User::validateForumSignature($sigParse, $sigText);
|
||||
|
||||
if($sigValid === '')
|
||||
$currentUser->setForumSignatureText($sigText)->setForumSignatureParser($sigParse);
|
||||
else switch($sigValid) {
|
||||
case 'parser':
|
||||
$notices[] = 'The selected forum signature parser is invalid.';
|
||||
break;
|
||||
case 'long':
|
||||
$notices[] = sprintf('Please keep the length of your signature below %d characters.', User::FORUM_SIGNATURE_MAX_LENGTH);
|
||||
break;
|
||||
default:
|
||||
$notices[] = 'Failed to update signature, contact an administator.';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!empty($_POST['birthdate']) && is_array($_POST['birthdate'])) {
|
||||
if(!$perms['edit_birthdate']) {
|
||||
$notices[] = "You aren't allow to change your birthdate.";
|
||||
} else {
|
||||
$birthYear = (int)($_POST['birthdate']['year'] ?? 0);
|
||||
$birthMonth = (int)($_POST['birthdate']['month'] ?? 0);
|
||||
$birthDay = (int)($_POST['birthdate']['day'] ?? 0);
|
||||
$birthValid = User::validateBirthdate($birthYear, $birthMonth, $birthDay);
|
||||
|
||||
if($birthValid === '')
|
||||
$currentUser->setBirthdate($birthYear, $birthMonth, $birthDay);
|
||||
else switch($birthValid) {
|
||||
case 'year':
|
||||
$notices[] = 'The given birth year is invalid.';
|
||||
break;
|
||||
case 'date':
|
||||
$notices[] = 'The given birthdate is invalid.';
|
||||
break;
|
||||
default:
|
||||
$notices[] = 'Something unexpected happened while setting your birthdate.';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!empty($_FILES['avatar'])) {
|
||||
$avatarInfo = $profileUser->getAvatarInfo();
|
||||
|
||||
if(!empty($_POST['avatar']['delete'])) {
|
||||
$avatarInfo->delete();
|
||||
} else {
|
||||
if(!$perms['edit_avatar']) {
|
||||
$notices[] = 'You aren\'t allow to change your avatar.';
|
||||
} elseif(!empty($_FILES['avatar'])
|
||||
&& is_array($_FILES['avatar'])
|
||||
&& !empty($_FILES['avatar']['name']['file'])) {
|
||||
if($_FILES['avatar']['error']['file'] !== UPLOAD_ERR_OK) {
|
||||
switch($_FILES['avatar']['error']['file']) {
|
||||
case UPLOAD_ERR_NO_FILE:
|
||||
$notices[] = 'Select a file before hitting upload!';
|
||||
break;
|
||||
case UPLOAD_ERR_PARTIAL:
|
||||
$notices[] = 'The upload was interrupted, please try again!';
|
||||
break;
|
||||
case UPLOAD_ERR_INI_SIZE:
|
||||
case UPLOAD_ERR_FORM_SIZE:
|
||||
$notices[] = sprintf('Your avatar is not allowed to be larger in file size than %s!', ByteFormat::format($avatarInfo->getMaxBytes()));
|
||||
break;
|
||||
default:
|
||||
$notices[] = 'Unable to save your avatar, contact an administator!';
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
$avatarInfo->setFromPath($_FILES['avatar']['tmp_name']['file']);
|
||||
} catch(UserImageAssetInvalidImageException $ex) {
|
||||
$notices[] = 'The file you uploaded was not an image!';
|
||||
} catch(UserImageAssetInvalidTypeException $ex) {
|
||||
$notices[] = 'This type of image is not supported, keep to PNG, JPG or GIF!';
|
||||
} catch(UserImageAssetInvalidDimensionsException $ex) {
|
||||
$notices[] = sprintf('Your avatar can\'t be larger than %dx%d!', $avatarInfo->getMaxWidth(), $avatarInfo->getMaxHeight());
|
||||
} catch(UserImageAssetFileTooLargeException $ex) {
|
||||
$notices[] = sprintf('Your avatar is not allowed to be larger in file size than %s!', ByteFormat::format($avatarInfo->getMaxBytes()));
|
||||
} catch(UserImageAssetException $ex) {
|
||||
$notices[] = 'Unable to save your avatar, contact an administator!';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!empty($_FILES['background'])) {
|
||||
$backgroundInfo = $profileUser->getBackgroundInfo();
|
||||
|
||||
if((int)($_POST['background']['attach'] ?? -1) === 0) {
|
||||
$backgroundInfo->delete();
|
||||
} else {
|
||||
if(!$perms['edit_background']) {
|
||||
$notices[] = 'You aren\'t allow to change your background.';
|
||||
} elseif(!empty($_FILES['background']) && is_array($_FILES['background'])) {
|
||||
if(!empty($_FILES['background']['name']['file'])) {
|
||||
if($_FILES['background']['error']['file'] !== UPLOAD_ERR_OK) {
|
||||
switch($_FILES['background']['error']['file']) {
|
||||
case UPLOAD_ERR_NO_FILE:
|
||||
$notices[] = 'Select a file before hitting upload!';
|
||||
break;
|
||||
case UPLOAD_ERR_PARTIAL:
|
||||
$notices[] = 'The upload was interrupted, please try again!';
|
||||
break;
|
||||
case UPLOAD_ERR_INI_SIZE:
|
||||
case UPLOAD_ERR_FORM_SIZE:
|
||||
$notices[] = sprintf('Your background is not allowed to be larger in file size than %s!', ByteFormat::format($backgroundProps['max_size']));
|
||||
break;
|
||||
default:
|
||||
$notices[] = 'Unable to save your background, contact an administator!';
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
$backgroundInfo->setFromPath($_FILES['background']['tmp_name']['file']);
|
||||
} catch(UserImageAssetInvalidImageException $ex) {
|
||||
$notices[] = 'The file you uploaded was not an image!';
|
||||
} catch(UserImageAssetInvalidTypeException $ex) {
|
||||
$notices[] = 'This type of image is not supported, keep to PNG, JPG or GIF!';
|
||||
} catch(UserImageAssetInvalidDimensionsException $ex) {
|
||||
$notices[] = sprintf('Your background can\'t be larger than %dx%d!', $backgroundInfo->getMaxWidth(), $backgroundInfo->getMaxHeight());
|
||||
} catch(UserImageAssetFileTooLargeException $ex) {
|
||||
$notices[] = sprintf('Your background is not allowed to be larger in file size than %2$s!', ByteFormat::format($backgroundInfo->getMaxBytes()));
|
||||
} catch(UserImageAssetException $ex) {
|
||||
$notices[] = 'Unable to save your background, contact an administator!';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$backgroundInfo->setAttachment((int)($_POST['background']['attach'] ?? 0))
|
||||
->setBlend(!empty($_POST['background']['attr']['blend']))
|
||||
->setSlide(!empty($_POST['background']['attr']['slide']));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$profileUser->saveProfile();
|
||||
}
|
||||
|
||||
// Unset $isEditing and hope the user doesn't refresh their profile!
|
||||
if(empty($notices))
|
||||
$isEditing = false;
|
||||
}
|
||||
}
|
||||
|
||||
$profileStats = DB::prepare('
|
||||
SELECT (
|
||||
SELECT COUNT(`topic_id`)
|
||||
FROM `msz_forum_topics`
|
||||
WHERE `user_id` = u.`user_id`
|
||||
AND `topic_deleted` IS NULL
|
||||
) AS `forum_topic_count`,
|
||||
(
|
||||
SELECT COUNT(`post_id`)
|
||||
FROM `msz_forum_posts`
|
||||
WHERE `user_id` = u.`user_id`
|
||||
AND `post_deleted` IS NULL
|
||||
) AS `forum_post_count`,
|
||||
(
|
||||
SELECT COUNT(`change_id`)
|
||||
FROM `msz_changelog_changes`
|
||||
WHERE `user_id` = u.`user_id`
|
||||
) AS `changelog_count`,
|
||||
(
|
||||
SELECT COUNT(`comment_id`)
|
||||
FROM `msz_comments_posts`
|
||||
WHERE `user_id` = u.`user_id`
|
||||
AND `comment_deleted` IS NULL
|
||||
) AS `comments_count`
|
||||
FROM `msz_users` AS u
|
||||
WHERE `user_id` = :user_id
|
||||
')->bind('user_id', $profileUser->getId())->fetch();
|
||||
|
||||
switch($profileMode) {
|
||||
default:
|
||||
echo render_error(404);
|
||||
return;
|
||||
|
||||
case 'forum-topics':
|
||||
$template = 'profile.topics';
|
||||
$topicsCount = forum_topic_count_user($profileUser->getId(), $currentUserId);
|
||||
$topicsPagination = new Pagination($topicsCount, 20);
|
||||
|
||||
if(!$topicsPagination->hasValidOffset()) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
$topics = forum_topic_listing_user(
|
||||
$profileUser->getId(), $currentUserId,
|
||||
$topicsPagination->getOffset(), $topicsPagination->getRange()
|
||||
);
|
||||
|
||||
Template::set([
|
||||
'title' => $profileUser->getUsername() . ' / topics',
|
||||
'canonical_url' => url('user-profile-forum-topics', ['user' => $profileUser->getId(), 'page' => Pagination::param()]),
|
||||
'profile_topics' => $topics,
|
||||
'profile_topics_pagination' => $topicsPagination,
|
||||
]);
|
||||
break;
|
||||
|
||||
case 'forum-posts':
|
||||
$template = 'profile.posts';
|
||||
$postsCount = forum_post_count_user($profileUser->getId());
|
||||
$postsPagination = new Pagination($postsCount, 20);
|
||||
|
||||
if(!$postsPagination->hasValidOffset()) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
||||
$posts = forum_post_listing(
|
||||
$profileUser->getId(),
|
||||
$postsPagination->getOffset(),
|
||||
$postsPagination->getRange(),
|
||||
false,
|
||||
true
|
||||
);
|
||||
|
||||
Template::set([
|
||||
'title' => $profileUser->getUsername() . ' / posts',
|
||||
'canonical_url' => url('user-profile-forum-posts', ['user' => $profileUser->getId(), 'page' => Pagination::param()]),
|
||||
'profile_posts' => $posts,
|
||||
'profile_posts_pagination' => $postsPagination,
|
||||
]);
|
||||
break;
|
||||
|
||||
case '':
|
||||
$template = 'profile.index';
|
||||
$warnings = $profileUser->getProfileWarnings($currentUser);
|
||||
|
||||
$activeCategoryStats = $viewingAsGuest ? null : forum_get_user_most_active_category_info($profileUser->getId());
|
||||
$activeCategoryInfo = empty($activeCategoryStats->forum_id) ? null : forum_get($activeCategoryStats->forum_id);
|
||||
|
||||
$activeTopicStats = $viewingAsGuest ? null : forum_get_user_most_active_topic_info($profileUser->getId());
|
||||
$activeTopicInfo = empty($activeTopicStats->topic_id) ? null : forum_topic_get($activeTopicStats->topic_id);
|
||||
|
||||
Template::set([
|
||||
'profile_warnings' => $warnings,
|
||||
'profile_warnings_view_private' => $canManageWarnings,
|
||||
'profile_warnings_can_manage' => $canManageWarnings,
|
||||
'profile_active_category_stats' => $activeCategoryStats,
|
||||
'profile_active_category_info' => $activeCategoryInfo,
|
||||
'profile_active_topic_stats' => $activeTopicStats,
|
||||
'profile_active_topic_info' => $activeTopicInfo,
|
||||
]);
|
||||
break;
|
||||
}
|
||||
|
||||
if(!empty($template)) {
|
||||
Template::render($template, [
|
||||
'profile_viewer' => $currentUser,
|
||||
'profile_user' => $profileUser,
|
||||
'profile_stats' => $profileStats,
|
||||
'profile_mode' => $profileMode,
|
||||
'profile_notices' => $notices,
|
||||
'profile_can_edit' => $canEdit,
|
||||
'profile_is_editing' => $isEditing,
|
||||
'profile_is_banned' => $isBanned,
|
||||
]);
|
||||
}
|
|
@ -1,91 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use RuntimeException;
|
||||
use Misuzu\Comments\CommentsCategory;
|
||||
use Misuzu\Users\User;
|
||||
|
||||
require_once '../misuzu.php';
|
||||
|
||||
$searchQuery = !empty($_GET['q']) && is_string($_GET['q']) ? $_GET['q'] : '';
|
||||
|
||||
if(!empty($searchQuery)) {
|
||||
$forumTopics = forum_topic_listing_search($searchQuery, User::hasCurrent() ? User::getCurrent()->getId() : 0);
|
||||
$forumPosts = forum_post_search($searchQuery);
|
||||
|
||||
// this sure is an expansion
|
||||
$news = $msz->getNews();
|
||||
$comments = $msz->getComments();
|
||||
$newsPosts = [];
|
||||
$newsPostInfos = $news->getPostsBySearchQuery($searchQuery);
|
||||
$newsUserInfos = [];
|
||||
$newsCategoryInfos = [];
|
||||
|
||||
foreach($newsPostInfos as $postInfo) {
|
||||
$userId = $postInfo->getUserId();
|
||||
$categoryId = $postInfo->getCategoryId();
|
||||
|
||||
if(array_key_exists($userId, $newsUserInfos)) {
|
||||
$userInfo = $newsUserInfos[$userId];
|
||||
} else {
|
||||
try {
|
||||
$userInfo = User::byId($userId);
|
||||
} catch(UserNotFoundException $ex) {
|
||||
$userInfo = null;
|
||||
}
|
||||
|
||||
$newsUserInfos[$userId] = $userInfo;
|
||||
}
|
||||
|
||||
if(array_key_exists($categoryId, $newsCategoryInfos))
|
||||
$categoryInfo = $newsCategoryInfos[$categoryId];
|
||||
else
|
||||
$newsCategoryInfos[$categoryId] = $categoryInfo = $news->getCategoryByPost($postInfo);
|
||||
|
||||
$commentsCount = $postInfo->hasCommentsCategoryId()
|
||||
? $comments->countPosts($postInfo->getCommentsCategoryId(), includeReplies: true) : 0;
|
||||
|
||||
$newsPosts[] = [
|
||||
'post' => $postInfo,
|
||||
'category' => $categoryInfo,
|
||||
'user' => $userInfo,
|
||||
'comments_count' => $commentsCount,
|
||||
];
|
||||
}
|
||||
|
||||
$findUsers = DB::prepare('
|
||||
SELECT u.`user_id`, u.`username`, u.`user_country`,
|
||||
u.`user_created`, u.`user_active`, r.`role_id`,
|
||||
COALESCE(u.`user_title`, r.`role_title`) AS `user_title`,
|
||||
COALESCE(u.`user_colour`, r.`role_colour`) AS `user_colour`,
|
||||
(
|
||||
SELECT COUNT(`topic_id`)
|
||||
FROM `msz_forum_topics`
|
||||
WHERE `user_id` = u.`user_id`
|
||||
AND `topic_deleted` IS NULL
|
||||
) AS `user_count_topics`,
|
||||
(
|
||||
SELECT COUNT(`post_Id`)
|
||||
FROM `msz_forum_posts`
|
||||
WHERE `user_id` = u.`user_id`
|
||||
AND `post_deleted` IS NULL
|
||||
) AS `user_count_posts`
|
||||
FROM `msz_users` AS u
|
||||
LEFT JOIN `msz_roles` AS r
|
||||
ON r.`role_id` = u.`display_role`
|
||||
LEFT JOIN `msz_user_roles` AS ur
|
||||
ON ur.`user_id` = u.`user_id`
|
||||
WHERE LOWER(u.`username`) LIKE CONCAT("%%", LOWER(:query), "%%")
|
||||
GROUP BY u.`user_id`
|
||||
');
|
||||
$findUsers->bind('query', $searchQuery);
|
||||
$users = $findUsers->fetchAll();
|
||||
}
|
||||
|
||||
Template::render('home.search', [
|
||||
'search_query' => $searchQuery,
|
||||
'forum_topics' => $forumTopics ?? [],
|
||||
'forum_posts' => $forumPosts ?? [],
|
||||
'users' => $users ?? [],
|
||||
'news_posts' => $newsPosts ?? [],
|
||||
]);
|
|
@ -1,2 +0,0 @@
|
|||
<?php
|
||||
require_once __DIR__ . '/index.php';
|
|
@ -1,136 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Users\User;
|
||||
use Misuzu\Users\UserRole;
|
||||
use Misuzu\Users\UserRoleNotFoundException;
|
||||
use Misuzu\Users\UserSession;
|
||||
use chillerlan\QRCode\QRCode;
|
||||
use chillerlan\QRCode\QROptions;
|
||||
|
||||
require_once '../../misuzu.php';
|
||||
|
||||
if(!UserSession::hasCurrent()) {
|
||||
echo render_error(401);
|
||||
return;
|
||||
}
|
||||
|
||||
$errors = [];
|
||||
$currentUser = User::getCurrent();
|
||||
$currentUserId = $currentUser->getId();
|
||||
$isRestricted = $currentUser->hasActiveWarning();
|
||||
$isVerifiedRequest = CSRF::validateRequest();
|
||||
|
||||
if(!$isRestricted && $isVerifiedRequest && !empty($_POST['role'])) {
|
||||
try {
|
||||
$roleInfo = UserRole::byId((int)($_POST['role']['id'] ?? 0));
|
||||
} catch(UserRoleNotFoundException $ex) {}
|
||||
|
||||
if(empty($roleInfo) || !$currentUser->hasRole($roleInfo))
|
||||
$errors[] = "You're trying to modify a role that hasn't been assigned to you.";
|
||||
else {
|
||||
switch($_POST['role']['mode'] ?? '') {
|
||||
case 'display':
|
||||
$currentUser->setDisplayRole($roleInfo);
|
||||
break;
|
||||
|
||||
case 'leave':
|
||||
if($roleInfo->getCanLeave())
|
||||
$currentUser->removeRole($roleInfo);
|
||||
else
|
||||
$errors[] = "You're not allow to leave this role, an administrator has to remove it for you.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($isVerifiedRequest && isset($_POST['tfa']['enable']) && $currentUser->hasTOTP() !== (bool)$_POST['tfa']['enable']) {
|
||||
if((bool)$_POST['tfa']['enable']) {
|
||||
$tfaKey = TOTP::generateKey();
|
||||
$tfaIssuer = $cfg->getString('site.name', 'Misuzu');
|
||||
$tfaQrcode = (new QRCode(new QROptions([
|
||||
'version' => 5,
|
||||
'outputType' => QRCode::OUTPUT_IMAGE_JPG,
|
||||
'eccLevel' => QRCode::ECC_L,
|
||||
])))->render(sprintf('otpauth://totp/%s:%s?%s', $tfaIssuer, $currentUser->getUsername(), http_build_query([
|
||||
'secret' => $tfaKey,
|
||||
'issuer' => $tfaIssuer,
|
||||
])));
|
||||
|
||||
Template::set([
|
||||
'settings_2fa_code' => $tfaKey,
|
||||
'settings_2fa_image' => $tfaQrcode,
|
||||
]);
|
||||
|
||||
$currentUser->setTOTPKey($tfaKey);
|
||||
} else {
|
||||
$currentUser->removeTOTPKey();
|
||||
}
|
||||
}
|
||||
|
||||
if($isVerifiedRequest && !empty($_POST['current_password'])) {
|
||||
if(!$currentUser->checkPassword($_POST['current_password'] ?? '')) {
|
||||
$errors[] = 'Your password was incorrect.';
|
||||
} else {
|
||||
// Changing e-mail
|
||||
if(!empty($_POST['email']['new'])) {
|
||||
if(empty($_POST['email']['confirm']) || $_POST['email']['new'] !== $_POST['email']['confirm']) {
|
||||
$errors[] = 'The addresses you entered did not match each other.';
|
||||
} elseif($currentUser->getEMailAddress() === mb_strtolower($_POST['email']['confirm'])) {
|
||||
$errors[] = 'This is already your e-mail address!';
|
||||
} else {
|
||||
$checkMail = User::validateEMailAddress($_POST['email']['new'], true);
|
||||
|
||||
if($checkMail !== '') {
|
||||
switch($checkMail) {
|
||||
case 'dns':
|
||||
$errors[] = 'No valid MX record exists for this domain.';
|
||||
break;
|
||||
|
||||
case 'format':
|
||||
$errors[] = 'The given e-mail address was incorrectly formatted.';
|
||||
break;
|
||||
|
||||
case 'in-use':
|
||||
$errors[] = 'This e-mail address is already in use.';
|
||||
break;
|
||||
|
||||
default:
|
||||
$errors[] = 'Unknown e-mail validation error.';
|
||||
}
|
||||
} else {
|
||||
$currentUser->setEMailAddress($_POST['email']['new']);
|
||||
$msz->createAuditLog('PERSONAL_EMAIL_CHANGE', [
|
||||
$_POST['email']['new'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Changing password
|
||||
if(!empty($_POST['password']['new'])) {
|
||||
if(empty($_POST['password']['confirm']) || $_POST['password']['new'] !== $_POST['password']['confirm']) {
|
||||
$errors[] = 'The new passwords you entered did not match each other.';
|
||||
} else {
|
||||
$checkPassword = User::validatePassword($_POST['password']['new']);
|
||||
|
||||
if($checkPassword !== '') {
|
||||
$errors[] = 'The given passwords was too weak.';
|
||||
} else {
|
||||
$currentUser->setPassword($_POST['password']['new']);
|
||||
$msz->createAuditLog('PERSONAL_PASSWORD_CHANGE');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// THIS FUCKING SUCKS AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
if($_SERVER['REQUEST_METHOD'] === 'POST' && $isVerifiedRequest)
|
||||
$currentUser->save();
|
||||
|
||||
Template::render('settings.account', [
|
||||
'errors' => $errors,
|
||||
'settings_user' => $currentUser,
|
||||
'is_restricted' => $isRestricted,
|
||||
]);
|
|
@ -1,174 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use ZipArchive;
|
||||
use Index\XString;
|
||||
use Index\IO\FileStream;
|
||||
use Misuzu\Users\User;
|
||||
use Misuzu\Users\UserSession;
|
||||
|
||||
require_once '../../misuzu.php';
|
||||
|
||||
if(!UserSession::hasCurrent()) {
|
||||
echo render_error(401);
|
||||
return;
|
||||
}
|
||||
|
||||
$dbConn = $msz->getDbConn();
|
||||
|
||||
function db_to_zip(ZipArchive $archive, int $userId, string $baseName, array $fieldInfos, string $userIdField = 'user_id'): string {
|
||||
global $dbConn;
|
||||
|
||||
$fields = [];
|
||||
|
||||
foreach($fieldInfos as $key => $fieldInfo) {
|
||||
$fieldInfo = explode(':', $fieldInfo);
|
||||
$fieldInfo = [
|
||||
'name' => $fieldInfo[0],
|
||||
'type' => $fieldInfo[1] ?? 'n',
|
||||
'nullable' => ($fieldInfo[2] ?? '') === 'n',
|
||||
];
|
||||
$fieldInfo['is_null'] = $fieldInfo['type'] === 'n';
|
||||
|
||||
if(!$fieldInfo['is_null']) {
|
||||
$selectField = $fieldInfo['name'];
|
||||
|
||||
if($fieldInfo['type'] === 'a')
|
||||
$selectField = sprintf('INET6_NTOA(%s)', $selectField);
|
||||
elseif($fieldInfo['type'] === 't')
|
||||
$selectField = sprintf('UNIX_TIMESTAMP(%s)', $selectField);
|
||||
|
||||
$fields[] = $selectField;
|
||||
}
|
||||
|
||||
$fieldInfos[$key] = $fieldInfo;
|
||||
}
|
||||
|
||||
$tmpName = sys_get_temp_dir() . DIRECTORY_SEPARATOR . sprintf('msz-user-data-%d-%s-%s.tmp', $userId, $baseName, XString::random(8));
|
||||
$tmpStream = FileStream::newWrite($tmpName);
|
||||
|
||||
try {
|
||||
$stmt = $dbConn->prepare(sprintf('SELECT %s FROM msz_%s WHERE %s = ?', implode(', ', $fields), $baseName, $userIdField));
|
||||
$stmt->addParameter(1, $userId);
|
||||
$stmt->execute();
|
||||
$result = $stmt->getResult();
|
||||
|
||||
while($result->next()) {
|
||||
$args = -1;
|
||||
$row = [];
|
||||
|
||||
foreach($fieldInfos as $fieldInfo) {
|
||||
$fieldValue = null;
|
||||
|
||||
if(!$fieldInfo['is_null']) {
|
||||
++$args;
|
||||
|
||||
if(!$fieldInfo['nullable'] || !$result->isNull($args)) {
|
||||
if($fieldInfo['type'] === 's' || $fieldInfo['type'] === 'a' || $fieldInfo['type'] === 'j') {
|
||||
$fieldValue = $result->getString($args);
|
||||
|
||||
if($fieldInfo['type'] === 'j')
|
||||
$fieldValue = json_decode($fieldValue);
|
||||
} elseif($fieldInfo['type'] === 't' || $fieldInfo['type'] === 'b' || $fieldInfo['type'] === 'i') {
|
||||
$fieldValue = $result->getInteger($args);
|
||||
|
||||
if($fieldInfo['type'] === 'b')
|
||||
$fieldValue = $fieldValue !== 0;
|
||||
elseif($fieldInfo['type'] === 't') {
|
||||
$fieldValue = date(DATE_ATOM, $fieldValue);
|
||||
if(str_ends_with($fieldValue, '+00:00'))
|
||||
$fieldValue = substr($fieldValue, 0, -6) . 'Z';
|
||||
}
|
||||
} else {
|
||||
$fieldValue = '?';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$row[$fieldInfo['name']] = $fieldValue;
|
||||
}
|
||||
|
||||
$tmpStream->write(json_encode($row, JSON_INVALID_UTF8_SUBSTITUTE));
|
||||
$tmpStream->write("\n");
|
||||
}
|
||||
} finally {
|
||||
$tmpStream->flush();
|
||||
$tmpStream->close();
|
||||
}
|
||||
|
||||
$archive->addFile($tmpName, $baseName . '.jsonl');
|
||||
|
||||
return $tmpName;
|
||||
}
|
||||
|
||||
$errors = [];
|
||||
$currentUser = User::getCurrent();
|
||||
$currentUserId = $currentUser->getId();
|
||||
|
||||
if(isset($_POST['action']) && is_string($_POST['action'])) {
|
||||
if(isset($_POST['password']) && is_string($_POST['password'])
|
||||
&& ($currentUser->checkPassword($_POST['password'] ?? ''))) {
|
||||
switch($_POST['action']) {
|
||||
case 'data':
|
||||
$msz->createAuditLog('PERSONAL_DATA_DOWNLOAD');
|
||||
|
||||
$timeStamp = floor(time() / 3600) * 3600;
|
||||
$fileName = sprintf('msz-user-data-%d-%d.zip', $currentUserId, $timeStamp);
|
||||
$filePath = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $fileName;
|
||||
$archive = new ZipArchive;
|
||||
|
||||
if(!is_file($filePath)) {
|
||||
if($archive->open($filePath, ZipArchive::CREATE | ZIPARCHIVE::OVERWRITE) === true) {
|
||||
$tmpFiles = [];
|
||||
|
||||
try {
|
||||
$tmpFiles[] = db_to_zip($archive, $currentUserId, 'audit_log', ['user_id:s:n', 'log_action:s', 'log_params:j', 'log_created:t', 'log_ip:a:n', 'log_country:s']);
|
||||
$tmpFiles[] = db_to_zip($archive, $currentUserId, 'auth_tfa', ['user_id:s', 'tfa_token:n', 'tfa_created:t']);
|
||||
$tmpFiles[] = db_to_zip($archive, $currentUserId, 'changelog_changes', ['change_id:s', 'user_id:s:n', 'change_action:s:n', 'change_created:t', 'change_log:s', 'change_text:s:n']);
|
||||
$tmpFiles[] = db_to_zip($archive, $currentUserId, 'comments_categories', ['category_id:s', 'category_name:s', 'owner_id:s:n', 'category_created:t', 'category_locked:t:n'], 'owner_id');
|
||||
$tmpFiles[] = db_to_zip($archive, $currentUserId, 'comments_posts', ['comment_id:s', 'category_id:s', 'user_id:s:n', 'comment_reply_to:s:n', 'comment_text:s', 'comment_created:t', 'comment_pinned:t:n', 'comment_edited:t:n', 'comment_deleted:t:n']);
|
||||
$tmpFiles[] = db_to_zip($archive, $currentUserId, 'comments_votes', ['comment_id:s', 'user_id:s', 'comment_vote:i']);
|
||||
$tmpFiles[] = db_to_zip($archive, $currentUserId, 'forum_permissions', ['user_id:s:n', 'role_id:s:n', 'forum_id:s', 'forum_perms_allow:i', 'forum_perms_deny:i']);
|
||||
$tmpFiles[] = db_to_zip($archive, $currentUserId, 'forum_posts', ['post_id:s', 'topic_id:s', 'forum_id:s', 'user_id:s:n', 'post_ip:a', 'post_text:s', 'post_parse:i', 'post_display_signature:b', 'post_created:t', 'post_edited:t:n', 'post_deleted:t:n']);
|
||||
$tmpFiles[] = db_to_zip($archive, $currentUserId, 'forum_topics', ['topic_id:s', 'forum_id:s', 'user_id:s:n', 'topic_type:i', 'topic_title:s', 'topic_count_views:i', 'topic_created:t', 'topic_bumped:t', 'topic_deleted:t:n', 'topic_locked:t:n']);
|
||||
$tmpFiles[] = db_to_zip($archive, $currentUserId, 'forum_topics_redirects', ['topic_id:s', 'user_id:s:n', 'topic_redir_url:s', 'topic_redir_created:t']);
|
||||
$tmpFiles[] = db_to_zip($archive, $currentUserId, 'forum_topics_track', ['user_id:s', 'topic_id:s', 'forum_id:s', 'track_last_read:t']);
|
||||
$tmpFiles[] = db_to_zip($archive, $currentUserId, 'login_attempts', ['user_id:s:n', 'attempt_success:b', 'attempt_ip:a', 'attempt_country:s', 'attempt_created:t', 'attempt_user_agent:s']);
|
||||
$tmpFiles[] = db_to_zip($archive, $currentUserId, 'news_posts', ['post_id:s', 'category_id:s', 'user_id:s:n', 'comment_section_id:s:n', 'post_is_featured:b', 'post_title:s', 'post_text:s', 'post_scheduled:t', 'post_created:t', 'post_updated:t', 'post_deleted:t:n']);
|
||||
$tmpFiles[] = db_to_zip($archive, $currentUserId, 'permissions', ['user_id:s:n', 'role_id:s:n', 'general_perms_allow:i', 'general_perms_deny:i', 'user_perms_allow:i', 'user_perms_deny:i', 'changelog_perms_allow:i', 'changelog_perms_deny:i', 'news_perms_allow:i', 'news_perms_deny:i', 'forum_perms_allow:i', 'forum_perms_deny:i', 'comments_perms_allow:i', 'comments_perms_deny:i']);
|
||||
$tmpFiles[] = db_to_zip($archive, $currentUserId, 'profile_fields_values', ['field_id:s', 'user_id:s', 'format_id:s', 'field_value:s']);
|
||||
$tmpFiles[] = db_to_zip($archive, $currentUserId, 'sessions', ['session_id:s', 'user_id:s', 'session_key:n', 'session_ip:a', 'session_ip_last:a:n', 'session_user_agent:s', 'session_country:s', 'session_expires:t', 'session_expires_bump:b', 'session_created:t', 'session_active:t:n']);
|
||||
$tmpFiles[] = db_to_zip($archive, $currentUserId, 'users', ['user_id:s', 'username:s', 'password:n', 'email:s', 'register_ip:a', 'last_ip:a', 'user_super:b', 'user_country:s', 'user_colour:i:n', 'user_created:t', 'user_active:t:n', 'user_deleted:t:n', 'display_role:s:n', 'user_totp_key:n', 'user_about_content:s:n', 'user_about_parser:i', 'user_signature_content:s:n', 'user_signature_parser:i', 'user_birthdate:s:n', 'user_background_settings:i:n', 'user_title:s:n']);
|
||||
$tmpFiles[] = db_to_zip($archive, $currentUserId, 'users_password_resets', ['user_id:s', 'reset_ip:a', 'reset_requested:t', 'verification_code:n']);
|
||||
$tmpFiles[] = db_to_zip($archive, $currentUserId, 'user_roles', ['user_id:s', 'role_id:s']);
|
||||
$tmpFiles[] = db_to_zip($archive, $currentUserId, 'user_warnings', ['warning_id:s', 'user_id:s', 'user_ip:a', 'issuer_id:n', 'issuer_ip:n', 'warning_created:t', 'warning_duration:t:n', 'warning_type:i', 'warning_note:s', 'warning_note_private:s:n']);
|
||||
|
||||
$archive->close();
|
||||
} finally {
|
||||
foreach($tmpFiles as $tmpFile)
|
||||
if(is_file($tmpFile))
|
||||
unlink($tmpFile);
|
||||
}
|
||||
} else {
|
||||
$errors[] = 'Something went wrong while creating your account archive.';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
header('Content-Type: application/zip');
|
||||
header(sprintf('Content-Disposition: inline; filename="%s"', $fileName));
|
||||
echo file_get_contents($filePath);
|
||||
return;
|
||||
|
||||
case 'deactivate':
|
||||
// deactivation
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
$errors[] = 'Incorrect password.';
|
||||
}
|
||||
}
|
||||
|
||||
Template::render('settings.data', [
|
||||
'errors' => $errors,
|
||||
]);
|
|
@ -1,13 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Users\UserSession;
|
||||
|
||||
require_once '../../misuzu.php';
|
||||
|
||||
if(!UserSession::hasCurrent()) {
|
||||
echo render_error(401);
|
||||
return;
|
||||
}
|
||||
|
||||
url_redirect('settings-account');
|
|
@ -1,29 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Pagination;
|
||||
use Misuzu\Users\User;
|
||||
use Misuzu\Users\UserLoginAttempt;
|
||||
|
||||
require_once '../../misuzu.php';
|
||||
|
||||
$currentUser = User::getCurrent();
|
||||
|
||||
if($currentUser === null) {
|
||||
echo render_error(401);
|
||||
return;
|
||||
}
|
||||
|
||||
$auditLog = $msz->getAuditLog();
|
||||
|
||||
$loginHistoryPagination = new Pagination(UserLoginAttempt::countAll($currentUser), 15, 'hp');
|
||||
$accountLogPagination = new Pagination($auditLog->countLogs(userInfo: $currentUser), 15, 'ap');
|
||||
|
||||
$auditLogs = $auditLog->getLogs(userInfo: $currentUser, pagination: $accountLogPagination);
|
||||
|
||||
Template::render('settings.logs', [
|
||||
'login_history_list' => UserLoginAttempt::all($loginHistoryPagination, $currentUser),
|
||||
'login_history_pagination' => $loginHistoryPagination,
|
||||
'account_log_list' => $auditLogs,
|
||||
'account_log_pagination' => $accountLogPagination,
|
||||
]);
|
|
@ -1,61 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Users\User;
|
||||
use Misuzu\Users\UserSession;
|
||||
use Misuzu\Users\UserSessionNotFoundException;
|
||||
|
||||
require_once '../../misuzu.php';
|
||||
|
||||
if(!User::hasCurrent()) {
|
||||
echo render_error(401);
|
||||
return;
|
||||
}
|
||||
|
||||
$errors = [];
|
||||
$currentUser = User::getCurrent();
|
||||
$currentSession = UserSession::getCurrent();
|
||||
$currentUserId = $currentUser->getId();
|
||||
$sessionActive = $currentSession->getId();;
|
||||
|
||||
if(!empty($_POST['session']) && CSRF::validateRequest()) {
|
||||
$currentSessionKilled = false;
|
||||
|
||||
if(is_array($_POST['session'])) {
|
||||
foreach($_POST['session'] as $sessionId) {
|
||||
$sessionId = (int)$sessionId;
|
||||
|
||||
try {
|
||||
$sessionInfo = UserSession::byId($sessionId);
|
||||
} catch(UserSessionNotFoundException $ex) {}
|
||||
|
||||
if(empty($sessionInfo) || $sessionInfo->getUserId() !== $currentUser->getId()) {
|
||||
$errors[] = "Session #{$sessionId} does not exist.";
|
||||
continue;
|
||||
} elseif($sessionInfo->getId() === $sessionActive) {
|
||||
$currentSessionKilled = true;
|
||||
}
|
||||
|
||||
$sessionInfo->delete();
|
||||
$msz->createAuditLog('PERSONAL_SESSION_DESTROY', [$sessionInfo->getId()]);
|
||||
}
|
||||
} elseif($_POST['session'] === 'all') {
|
||||
$currentSessionKilled = true;
|
||||
UserSession::purgeUser($currentUser);
|
||||
$msz->createAuditLog('PERSONAL_SESSION_DESTROY_ALL');
|
||||
}
|
||||
|
||||
if($currentSessionKilled) {
|
||||
url_redirect('index');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$pagination = new Pagination(UserSession::countAll($currentUser), 15);
|
||||
|
||||
Template::render('settings.sessions', [
|
||||
'errors' => $errors,
|
||||
'session_list' => UserSession::all($pagination, $currentUser),
|
||||
'session_current' => $currentSession,
|
||||
'session_pagination' => $pagination,
|
||||
]);
|
|
@ -1,2 +0,0 @@
|
|||
<?php
|
||||
require_once __DIR__ . '/index.php';
|
Loading…
Add table
Add a link
Reference in a new issue