and just a heap of shit

This commit is contained in:
flash 2016-09-14 00:05:03 +02:00
parent 6a4d984189
commit 8b603d5e37
41 changed files with 405 additions and 606 deletions

View file

@ -259,7 +259,7 @@ class Settings
"{$cpfx}id", "{$cpfx}id",
"{$cpfx}session", "{$cpfx}session",
]; ];
$this->avatarUrl = route('file.avatar', '{0}', true); $this->avatarUrl = route('user.avatar', '{0}', true);
$this->profileUrl = route('user.profile', '{0}', true); $this->profileUrl = route('user.profile', '{0}', true);
$this->development = config('dev.show_errors'); $this->development = config('dev.show_errors');
$this->languagePath = config('chat.language_path'); $this->languagePath = config('chat.language_path');

View file

@ -51,8 +51,7 @@ class AuthController extends Controller
CurrentSession::stop(); CurrentSession::stop();
// Return true indicating a successful logout // Return true indicating a successful logout
header('Location: ' . route('auth.login')); redirect(route('auth.login'));
return;
} }
/** /**

View file

@ -36,7 +36,7 @@ class ChatController extends Controller
*/ */
public function redirect() public function redirect()
{ {
header('Location: ' . config('chat.webclient')); redirect(config('chat.webclient'));
} }
/** /**

View file

@ -6,8 +6,15 @@
namespace Sakura\Controllers; namespace Sakura\Controllers;
use Phroute\Phroute\Exception\HttpMethodNotAllowedException;
use Phroute\Phroute\Exception\HttpRouteNotFoundException;
use Sakura\Config; use Sakura\Config;
use Sakura\CurrentSession;
use Sakura\DB;
use Sakura\Exceptions\FileException;
use Sakura\File; use Sakura\File;
use Sakura\Perms;
use Sakura\Perms\Manage;
use Sakura\Perms\Site; use Sakura\Perms\Site;
use Sakura\Template; use Sakura\Template;
use Sakura\User; use Sakura\User;
@ -19,6 +26,15 @@ use Sakura\User;
*/ */
class FileController extends Controller class FileController extends Controller
{ {
/**
* Possible modes.
*/
const MODES = [
'avatar',
'background',
'header',
];
/** /**
* The base for serving a file. * The base for serving a file.
* @param string $data * @param string $data
@ -28,126 +44,171 @@ class FileController extends Controller
*/ */
private function serve($data, $mime, $name) private function serve($data, $mime, $name)
{ {
// Add original filename
header("Content-Disposition: inline; filename={$name}"); header("Content-Disposition: inline; filename={$name}");
// Set content type
header("Content-Type: {$mime}"); header("Content-Type: {$mime}");
// Return image data
return $data; return $data;
} }
/** /**
* Attempt to get an avatar. * Handles file uploads.
* @param int $id * @param string $mode
* @return string * @param array $file
* @return array
*/ */
public function avatar($id = 0) private function upload($mode, $file, $user)
{ {
$noAvatar = ROOT . 'public/' . str_replace( $error = null;
'%tplname%',
Template::$name,
config('user.avatar_none')
);
$none = [
'name' => basename($noAvatar),
'data' => file_get_contents($noAvatar),
'mime' => getimagesizefromstring($noAvatar)['mime'],
];
$bannedPath = ROOT . 'public/' . str_replace( // Handle errors
'%tplname%', switch ($file['error']) {
Template::$name, case UPLOAD_ERR_OK:
config('user.avatar_ban') break;
);
$banned = [
'name' => basename($bannedPath),
'data' => file_get_contents($bannedPath),
'mime' => getimagesizefromstring($bannedPath)['mime'],
];
$user = User::construct($id); case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
throw new FileException("Your file was too large!");
if ($user->permission(Site::RESTRICTED)) { case UPLOAD_ERR_PARTIAL:
return $this->serve($banned['data'], $banned['mime'], $banned['name']); throw new FileException("The upload failed!");
case UPLOAD_ERR_NO_TMP_DIR:
case UPLOAD_ERR_CANT_WRITE:
throw new FileException("Wasn't able to save the file, contact a staff member!");
case UPLOAD_ERR_EXTENSION:
default:
throw new FileException("Something prevented the file upload!");
} }
if ($user->id < 1 || !$user->avatar || $user->permission(Site::DEACTIVATED)) { // Get the temp filename
return $this->serve($none['data'], $none['mime'], $none['name']); $tmpName = $file['tmp_name'];
// Get the image meta data
$meta = getimagesize($tmpName);
// Check if image
if (!$meta
|| (
$meta[2] !== IMAGETYPE_GIF
&& $meta[2] !== IMAGETYPE_JPEG
&& $meta[2] !== IMAGETYPE_PNG
)
) {
throw new FileException("Please upload a valid image!");
} }
$serve = new File($user->avatar); // Check dimensions
$maxWidth = config("file.{$mode}.max_width");
$maxHeight = config("file.{$mode}.max_height");
if (!$serve->id) { if ($meta[0] > $maxWidth
return $this->serve($none['data'], $none['mime'], $none['name']); || $meta[1] > $maxHeight) {
throw new FileException("Your image can't be bigger than {$maxWidth}x{$maxHeight}" .
", yours was {$meta[0]}x{$meta[1]}!");
} }
return $this->serve($serve->data, $serve->mime, $serve->name); // Check file size
$maxFileSize = config("file.{$mode}.max_file_size");
if (filesize($tmpName) > $maxFileSize) {
$maxSizeFmt = byte_symbol($maxFileSize);
throw new FileException("Your image is not allowed to be larger than {$maxSizeFmt}!");
}
$userId = $user->id;
$ext = image_type_to_extension($meta[2]);
$filename = "{$mode}_{$userId}{$ext}";
// Create the file
$file = File::create(file_get_contents($tmpName), $filename, $user);
// Delete the old file
$this->delete($mode, $user);
$column = "user_{$mode}";
// Save new avatar
DB::table('users')
->where('user_id', $user->id)
->update([
$column => $file->id,
]);
} }
/** /**
* Attempt to get a background. * Deletes a file.
* @param int $id * @param string $mode
* @return string
*/ */
public function background($id = 0) public function delete($mode, $user)
{ {
$noBg = ROOT . "public/images/pixel.png"; $fileId = $user->{$mode};
$none = [
'name' => basename($noBg),
'data' => file_get_contents($noBg),
'mime' => getimagesizefromstring($noBg)['mime'],
];
if (!$id) { if ($fileId) {
return $this->serve($none['data'], $none['mime'], $none['name']); (new File($fileId))->delete();
} }
$user = User::construct($id);
if ($user->permission(Site::DEACTIVATED)
|| $user->permission(Site::RESTRICTED)
|| !$user->background) {
return $this->serve($none['data'], $none['mime'], $none['name']);
}
$serve = new File($user->background);
if (!$serve->id) {
return $this->serve($none['data'], $none['mime'], $none['name']);
}
return $this->serve($serve->data, $serve->mime, $serve->name);
} }
/** /**
* Attempt to get a profile header. * Catchall serve.
* @param int $id * @param string $method
* @param array $params
* @todo add a specific permission for mods to override uploads.
* @return string * @return string
*/ */
public function header($id = 0) public function __call($method, $params)
{ {
$noHeader = ROOT . "public/images/pixel.png"; if (!in_array($method, self::MODES)) {
$none = [ throw new HttpRouteNotFoundException;
'name' => basename($noHeader),
'data' => file_get_contents($noHeader),
'mime' => getimagesizefromstring($noHeader)['mime'],
];
if (!$id) {
return $this->serve($none['data'], $none['mime'], $none['name']);
} }
$user = User::construct($id); $user = User::construct($params[0] ?? 0);
if (session_check()) {
if (!CurrentSession::$user->permission(Manage::USE_MANAGE, Perms::MANAGE)
&& ($user->id !== CurrentSession::$user->id
|| !$user->permission(constant("Sakura\Perms\Site::CHANGE_" . strtoupper($method)))
|| $user->permission(Site::DEACTIVATED)
|| $user->permission(Site::RESTRICTED))
) {
throw new HttpMethodNotAllowedException;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$error = null;
try {
$this->upload($method, $_FILES['file'] ?? null, $user);
} catch (FileException $e) {
$error = $e->getMessage();
}
return $this->json(compact('error'));
} elseif ($_SERVER['REQUEST_METHOD'] === 'DELETE') {
$this->delete($method, $user);
return;
}
}
$noFile = ROOT . 'public/' . str_replace(
'%tplname%',
Template::$name,
config("user.{$method}_none")
);
$none = [
'name' => basename($noFile),
'data' => file_get_contents($noFile),
'mime' => getimagesizefromstring($noFile)['mime'],
];
if ($user->permission(Site::DEACTIVATED) if ($user->permission(Site::DEACTIVATED)
|| $user->permission(Site::RESTRICTED) || $user->permission(Site::RESTRICTED)
|| !$user->header) { || !$user->{$method}) {
return $this->serve($none['data'], $none['mime'], $none['name']); return $this->serve($none['data'], $none['mime'], $none['name']);
} }
$serve = new File($user->header); $serve = new File($user->{$method});
if (!$serve->id) { if (!$serve->id) {
return $this->serve($none['data'], $none['mime'], $none['name']); return $this->serve($none['data'], $none['mime'], $none['name']);

View file

@ -118,7 +118,7 @@ class ForumController extends Controller
// Redirect forum id 0 to the main page // Redirect forum id 0 to the main page
if ($forum->id === 0) { if ($forum->id === 0) {
header("Location: " . route('forums.index')); redirect(route('forums.index'));
return; return;
} }
@ -130,7 +130,7 @@ class ForumController extends Controller
// Check if the forum isn't a link // Check if the forum isn't a link
if ($forum->type === 2) { if ($forum->type === 2) {
header("Location: {$forum->link}"); redirect($forum->link);
return; return;
} }
@ -158,6 +158,6 @@ class ForumController extends Controller
$forum->trackUpdateAll(CurrentSession::$user->id); $forum->trackUpdateAll(CurrentSession::$user->id);
header("Location: " . route('forums.forum', $forum->id)); redirect(route('forums.forum', $forum->id));
} }
} }

View file

@ -57,7 +57,7 @@ class PostController extends Controller
$topicLink .= "?page={$postAt}"; $topicLink .= "?page={$postAt}";
} }
return header("Location: {$topicLink}#p{$post->id}"); redirect("{$topicLink}#p{$post->id}");
} }
/** /**
@ -178,7 +178,7 @@ class PostController extends Controller
$postLink = route('forums.post', $post->id); $postLink = route('forums.post', $post->id);
return header("Location: {$postLink}"); redirect($postLink);
} }
/** /**
@ -211,30 +211,19 @@ class PostController extends Controller
throw new HttpMethodNotAllowedException(); throw new HttpMethodNotAllowedException();
} }
if (session_check('sessionid')) { // Check if the topic only has 1 post
if (isset($_POST['yes'])) { if ($topic->replyCount() === 1) {
// Check if the topic only has 1 post // Delete the entire topic
if ($topic->replyCount() === 1) { $topic->delete();
// Delete the entire topic
$topic->delete();
$redirect = route('forums.forum', $forum->id); $redirect = route('forums.forum', $forum->id);
} else { } else {
// Just delete the post // Just delete the post (replace this with soft deleting)
$post->delete(); $post->purge();
$redirect = route('forums.topic', $topic->id); $redirect = route('forums.topic', $topic->id);
}
} else {
$redirect = route('forums.post', $post->id);
}
header("Location: {$redirect}");
return;
} }
$message = "Are you sure?"; redirect($redirect);
return view('global/confirm', compact('message'));
} }
} }

View file

@ -6,6 +6,8 @@
namespace Sakura\Controllers\Forum; namespace Sakura\Controllers\Forum;
use Phroute\Phroute\Exception\HttpMethodNotAllowedException;
use Phroute\Phroute\Exception\HttpRouteNotFoundException;
use Sakura\CurrentSession; use Sakura\CurrentSession;
use Sakura\Forum\Forum; use Sakura\Forum\Forum;
use Sakura\Forum\Post; use Sakura\Forum\Post;
@ -22,6 +24,7 @@ class TopicController extends Controller
/** /**
* Views a topic. * Views a topic.
* @param int $id * @param int $id
* @throws HttpRouteNotFoundException
* @return string * @return string
*/ */
public function view($id = 0) public function view($id = 0)
@ -32,10 +35,7 @@ class TopicController extends Controller
// Check if the forum exists // Check if the forum exists
if ($topic->id === 0 if ($topic->id === 0
|| !$forum->permission(ForumPerms::VIEW, CurrentSession::$user->id)) { || !$forum->permission(ForumPerms::VIEW, CurrentSession::$user->id)) {
$message = "This topic doesn't exist or you don't have access to it!"; throw new HttpRouteNotFoundException;
$redirect = route('forums.index');
return view('global/information', compact('message', 'redirect'));
} }
$topic->trackUpdate(CurrentSession::$user->id); $topic->trackUpdate(CurrentSession::$user->id);
@ -47,6 +47,7 @@ class TopicController extends Controller
/** /**
* Checks if a user can moderate the topic. * Checks if a user can moderate the topic.
* @param int $id * @param int $id
* @throws HttpRouteNotFoundException
* @return array * @return array
*/ */
private function modBase($id) private function modBase($id)
@ -60,194 +61,136 @@ class TopicController extends Controller
return compact('topic', 'forum'); return compact('topic', 'forum');
} }
return false; throw new HttpRouteNotFoundException;
} }
/** /**
* Sticky a topic. * Sticky a topic.
* @param int $id * @param int $id
* @throws HttpMethodNotAllowedException
* @return string * @return string
*/ */
public function sticky($id) public function sticky($id)
{ {
$modBase = $this->modBase($id); extract($this->modBase($id));
$redirect = route('forums.index');
$message = "This forum doesn't exist or you don't have access to it.";
if ($modBase !== false) { if (!$forum->permission(ForumPerms::STICKY, CurrentSession::$user->id)) {
extract($modBase); throw new HttpMethodNotAllowedException;
$redirect = route('forums.topic', $topic->id);
if ($forum->permission(ForumPerms::STICKY, CurrentSession::$user->id)) {
$topic->type = $topic->type !== 1 ? 1 : 0;
$topic->update();
$message = $topic->type
? 'Changed the topic to sticky!' : 'Reverted the topic back to normal!';
} else {
$message = "You aren't allowed to sticky topics!";
}
} }
return view('global/information', compact('message', 'redirect')); $topic->type = $topic->type !== 1 ? 1 : 0;
$topic->update();
redirect(route('forums.topic', $topic->id));
} }
/** /**
* Announce a topic. * Announce a topic.
* @param int $id * @param int $id
* @throws HttpMethodNotAllowedException
* @return string * @return string
*/ */
public function announce($id) public function announce($id)
{ {
$modBase = $this->modBase($id); extract($this->modBase($id));
$redirect = route('forums.index');
$message = "This forum doesn't exist or you don't have access to it.";
if ($modBase !== false) { if (!$forum->permission(ForumPerms::ANNOUNCEMENT, CurrentSession::$user->id)) {
extract($modBase); throw new HttpMethodNotAllowedException;
$redirect = route('forums.topic', $topic->id);
if ($forum->permission(ForumPerms::ANNOUNCEMENT, CurrentSession::$user->id)) {
$topic->type = $topic->type !== 2 ? 2 : 0;
$topic->update();
$message = $topic->type
? 'Changed the topic to an announcement!' : 'Reverted the topic back to normal!';
} else {
$message = "You aren't allowed to announce topics!";
}
} }
return view('global/information', compact('message', 'redirect')); $topic->type = $topic->type !== 2 ? 2 : 0;
$topic->update();
redirect(route('forums.topic', $topic->id));
} }
/** /**
* Lock a topic. * Lock a topic.
* @param int $id * @param int $id
* @throws HttpMethodNotAllowedException
* @return string * @return string
*/ */
public function lock($id) public function lock($id)
{ {
$modBase = $this->modBase($id); extract($this->modBase($id));
$redirect = route('forums.index');
$message = "This forum doesn't exist or you don't have access to it.";
if ($modBase !== false) { if (!$forum->permission(ForumPerms::LOCK, CurrentSession::$user->id)) {
extract($modBase); throw new HttpMethodNotAllowedException;
$redirect = route('forums.topic', $topic->id);
if ($forum->permission(ForumPerms::LOCK, CurrentSession::$user->id)) {
$topic->status = $topic->status !== 1 ? 1 : 0;
$topic->update();
$message = ($topic->status ? 'Locked' : 'Unlocked') . ' the topic!';
} else {
$message = "You aren't allowed to lock topics!";
}
} }
return view('global/information', compact('message', 'redirect')); $topic->status = $topic->status !== 1 ? 1 : 0;
$topic->update();
redirect(route('forums.topic', $topic->id));
} }
/** /**
* Delete an entire topic. * Delete an entire topic.
* @param int $id * @param int $id
* @throws HttpMethodNotAllowedException
* @return string * @return string
*/ */
public function delete($id) public function delete($id)
{ {
$modBase = $this->modBase($id); extract($this->modBase($id));
$redirect = route('forums.index');
$message = "This forum doesn't exist or you don't have access to it.";
if ($modBase !== false) { $trash = intval(config('forum.trash'));
extract($modBase);
$trash = config('forum.trash');
// Check if we're operating from the trash if ($topic->forum === $trash
if ($topic->forum === $trash) { && $forum->permission(ForumPerms::DELETE_ANY, CurrentSession::$user->id)) {
if ($forum->permission(ForumPerms::DELETE_ANY, CurrentSession::$user->id)) { $redirect = route('forums.forum', $trash);
$topic->delete(); $topic->delete();
$message = "Deleted the topic!"; } elseif ($forum->permission(ForumPerms::MOVE, CurrentSession::$user->id)) {
$redirect = route('forums.forum', $trash); $redirect = route('forums.topic', $topic->id);
} else { $topic->move($trash);
$message = "You aren't allowed to delete topics!"; } else {
} throw new HttpMethodNotAllowedException;
} else {
$redirect = route('forums.topic', $topic->id);
if ($forum->permission(ForumPerms::MOVE, CurrentSession::$user->id)) {
$topic->move($trash);
$message = "Moved the topic to the trash!";
} else {
$message = "You're not allowed to do this!";
}
}
} }
return view('global/information', compact('message', 'redirect')); redirect($redirect);
} }
/** /**
* Restore a topic to its previous location. * Restore a topic to its previous location.
* @param int $id * @param int $id
* @throws HttpMethodNotAllowedException
* @return string * @return string
*/ */
public function restore($id) public function restore($id)
{ {
$modBase = $this->modBase($id); extract($this->modBase($id));
$redirect = route('forums.index');
$message = "This forum doesn't exist or you don't have access to it.";
if ($modBase !== false) { if (!$forum->permission(ForumPerms::MOVE, CurrentSession::$user->id)) {
extract($modBase); throw new HttpMethodNotAllowedException;
$redirect = route('forums.topic', $topic->id);
if ($forum->permission(ForumPerms::MOVE, CurrentSession::$user->id)) {
if ($topic->oldForum) {
$topic->move($topic->oldForum, false);
$message = "Moved the topic back to it's old location!";
} else {
$message = "This topic has never been moved!";
}
} else {
$message = "You aren't allowed to move threads!";
}
} }
return view('global/information', compact('message', 'redirect')); if ($topic->oldForum) {
$topic->move($topic->oldForum, false);
}
redirect(route('forums.topic', $topic->id));
} }
/** /**
* Move a topic. * Move a topic.
* @param int $id * @param int $id
* @throws HttpMethodNotAllowedException
* @return string * @return string
*/ */
public function move($id) public function move($id)
{ {
$modBase = $this->modBase($id); extract($this->modBase($id));
$redirect = route('forums.index'); $dest_forum = new Forum($_REQUEST['forum_id'] ?? 0);
$message = "This forum doesn't exist or you don't have access to it.";
if ($modBase !== false) { if (!$forum->permission(ForumPerms::MOVE, CurrentSession::$user->id)
extract($modBase); || $dest_forum->id === 0
$redirect = route('forums.topic', $topic->id); || $dest_forum->permission(ForumPerms::VIEW, CurrentSession::$user->id)) {
throw new HttpMethodNotAllowedException;
if ($forum->permission(ForumPerms::MOVE, CurrentSession::$user->id)) {
$dest_forum = new Forum($_REQUEST['forum_id'] ?? 0);
if ($dest_forum->id === 0
|| $dest_forum->permission(ForumPerms::VIEW, CurrentSession::$user->id)) {
$topic->move($dest_forum->id);
$message = "Moved to the topic to {$dest_forum->name}!";
} else {
$message = "The destination forum doesn't exist or you don't have access to it.";
}
} else {
$message = "You aren't allowed to move threads!";
}
} }
return view('global/information', compact('message', 'redirect')); $topic->move($dest_forum->id);
redirect(route('forums.topic', $topic->id));
} }
/** /**
@ -330,7 +273,7 @@ class TopicController extends Controller
$postLink = route('forums.post', $post->id); $postLink = route('forums.post', $post->id);
// Head to the post // Head to the post
return header("Location: {$postLink}"); redirect($postLink);
} }
/** /**
@ -418,7 +361,8 @@ class TopicController extends Controller
$postLink = route('forums.post', $post->id); $postLink = route('forums.post', $post->id);
// Head to the post // Head to the post
return header("Location: {$postLink}"); redirect($postLink);
return;
} }
return view('forum/topic', compact('forum')); return view('forum/topic', compact('forum'));

View file

@ -33,7 +33,7 @@ class FriendsController extends Controller
$alert->time = time(); $alert->time = time();
$alert->title = $title; $alert->title = $title;
$alert->text = $text; $alert->text = $text;
$alert->image = route('file.avatar', $user->id); $alert->image = route('user.avatar', $user->id);
$alert->timeout = 60000; $alert->timeout = 60000;
$alert->link = route('user.profile', $user->id); $alert->link = route('user.profile', $user->id);

View file

@ -67,7 +67,7 @@ class PremiumController extends Controller
// Check months // Check months
if ($months < 1 if ($months < 1
|| $months > $amountLimit) { || $months > $amountLimit) {
header("Location: " . route('premium.error')); redirect(route('premium.error'));
return; return;
} }
@ -97,14 +97,14 @@ class PremiumController extends Controller
// Attempt to create a transaction // Attempt to create a transaction
if (!$transaction) { if (!$transaction) {
header("Location: " . route('premium.error')); redirect(route('premium.error'));
return; return;
} }
// Store the amount of months in the global session array // Store the amount of months in the global session array
$_SESSION['premiumMonths'] = (int) $months; $_SESSION['premiumMonths'] = (int) $months;
return header("Location: {$transaction}"); redirect($transaction);
} }
/** /**
@ -125,7 +125,8 @@ class PremiumController extends Controller
|| !$payment || !$payment
|| !$payer || !$payer
|| !$months) { || !$months) {
return header("Location: {$failRoute}"); redirect($failRoute);
return;
} }
// Attempt to complete the transaction // Attempt to complete the transaction
@ -136,12 +137,13 @@ class PremiumController extends Controller
} }
if (!$finalise) { if (!$finalise) {
return header("Location: {$failRoute}"); redirect($failRoute);
return;
} }
CurrentSession::$user->addPremium(self::PERIOD_PER_PAYMENT * $months); CurrentSession::$user->addPremium(self::PERIOD_PER_PAYMENT * $months);
return header("Location: {$successRoute}"); redirect($successRoute);
} }
/** /**

View file

@ -80,7 +80,7 @@ class AccountController extends Controller
]); ]);
} }
header("Location: {$redirect}"); redirect($redirect);
return; return;
} }
@ -212,7 +212,7 @@ class AccountController extends Controller
$user->setPassword($password); $user->setPassword($password);
} }
header("Location: {$redirect}"); redirect($redirect);
return; return;
} }
@ -271,7 +271,7 @@ class AccountController extends Controller
CurrentSession::$user->setMainRank($rank); CurrentSession::$user->setMainRank($rank);
header("Location: {$redirect}"); redirect($redirect);
return; return;
} }

View file

@ -54,7 +54,7 @@ class AdvancedController extends Controller
// Delete it // Delete it
$session->delete(); $session->delete();
header("Location: {$redirect}"); redirect($redirect);
return; return;
} }
@ -94,9 +94,7 @@ class AdvancedController extends Controller
// Destroy all active sessions // Destroy all active sessions
CurrentSession::$user->purgeSessions(); CurrentSession::$user->purgeSessions();
$redirect = route('main.index'); return view('settings/advanced/deactivate_bye');
$message = "Farewell!";
return view('global/information', compact('message', 'redirect'));
} }
return view('settings/advanced/deactivate'); return view('settings/advanced/deactivate');

View file

@ -9,7 +9,6 @@ namespace Sakura\Controllers\Settings;
use Phroute\Phroute\Exception\HttpMethodNotAllowedException; use Phroute\Phroute\Exception\HttpMethodNotAllowedException;
use Sakura\CurrentSession; use Sakura\CurrentSession;
use Sakura\DB; use Sakura\DB;
use Sakura\File;
use Sakura\Perms\Site; use Sakura\Perms\Site;
/** /**
@ -19,196 +18,6 @@ use Sakura\Perms\Site;
*/ */
class AppearanceController extends Controller class AppearanceController extends Controller
{ {
/**
* Handles file uploads.
* @param string $mode
* @param array $file
* @return array
*/
private function handleUpload($mode, $file)
{
// Handle errors
switch ($file['error']) {
case UPLOAD_ERR_OK:
break;
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
return "Your file was too large!";
case UPLOAD_ERR_PARTIAL:
return "The upload failed!";
case UPLOAD_ERR_NO_TMP_DIR:
case UPLOAD_ERR_CANT_WRITE:
return "Wasn't able to save the file, contact a staff member!";
case UPLOAD_ERR_EXTENSION:
default:
return "Something prevented the file upload!";
}
// Get the temp filename
$tmpName = $_FILES[$mode]['tmp_name'];
// Get the image meta data
$meta = getimagesize($tmpName);
// Check if image
if (!$meta
|| (
$meta[2] !== IMAGETYPE_GIF
&& $meta[2] !== IMAGETYPE_JPEG
&& $meta[2] !== IMAGETYPE_PNG
)
) {
return "Please upload a valid image!";
}
$confp = $mode === 'header' ? 'cover' : $mode;
// Check dimensions
$maxWidth = config("file.{$confp}.max_width");
$maxHeight = config("file.{$confp}.max_height");
if ($meta[0] > $maxWidth
|| $meta[1] > $maxHeight) {
return "Your image has to be at least {$minWidth}x{$minHeight}"
. " and not bigger than {$maxWidth}x{$maxHeight}, yours was {$meta[0]}x{$meta[1]}!";
}
// Check file size
$maxFileSize = config("file.{$confp}.max_file_size");
if (filesize($tmpName) > $maxFileSize) {
$maxSizeFmt = byte_symbol($maxFileSize);
return "Your image is not allowed to be larger than {$maxSizeFmt}!";
}
$userId = CurrentSession::$user->id;
$ext = image_type_to_extension($meta[2]);
$filename = "{$mode}_{$userId}{$ext}";
// Create the file
$file = File::create(file_get_contents($tmpName), $filename, CurrentSession::$user);
// Delete the old file
$this->deleteFile($mode);
$column = "user_{$mode}";
// Save new avatar
DB::table('users')
->where('user_id', CurrentSession::$user->id)
->update([
$column => $file->id,
]);
return null;
}
/**
* Deletes a file.
* @param string $mode
*/
public function deleteFile($mode)
{
$fileId = CurrentSession::$user->{$mode};
if ($fileId) {
(new File($fileId))->delete();
}
}
/**
* Renders the avatar changing page
* @return string
*/
public function avatar()
{
// Check permission
if (!CurrentSession::$user->permission(Site::CHANGE_AVATAR)) {
throw new HttpMethodNotAllowedException();
}
if (session_check()) {
$avatar = $_FILES['avatar'] ?? null;
$redirect = route('settings.appearance.avatar');
if ($avatar && $avatar['error'] !== UPLOAD_ERR_NO_FILE) {
$upload = $this->handleUpload('avatar', $_FILES['avatar']);
$message = $upload !== null ? $upload : "Changed your avatar!";
} else {
$this->deleteFile('avatar');
$message = "Deleted your avatar!";
}
return view('global/information', compact('message', 'redirect'));
}
return view('settings/appearance/avatar');
}
/**
* Renders the background changing page.
* @return string
*/
public function background()
{
// Check permission
if (!CurrentSession::$user->permission(Site::CHANGE_BACKGROUND)) {
throw new HttpMethodNotAllowedException();
}
if (session_check()) {
$background = $_FILES['background'] ?? null;
$redirect = route('settings.appearance.background');
if ($background && $background['error'] !== UPLOAD_ERR_NO_FILE) {
$upload = $this->handleUpload('background', $_FILES['background']);
$message = $upload !== null ? $upload : "Changed your background!";
} else {
$this->deleteFile('background');
$message = "Deleted your background!";
}
return view('global/information', compact('message', 'redirect'));
}
return view('settings/appearance/background');
}
/**
* Renders the banner changing page.
* @return string
*/
public function header()
{
// Check permission
if (!CurrentSession::$user->permission(Site::CHANGE_HEADER)) {
throw new HttpMethodNotAllowedException();
}
if (session_check()) {
$header = $_FILES['header'] ?? null;
$redirect = route('settings.appearance.header');
if ($header && $header['error'] !== UPLOAD_ERR_NO_FILE) {
$upload = $this->handleUpload('header', $_FILES['header']);
$message = $upload !== null ? $upload : "Changed your header!";
} else {
$this->deleteFile('header');
$message = "Deleted your header!";
}
return view('global/information', compact('message', 'redirect'));
}
return view('settings/appearance/header');
}
/** /**
* Renders the userpage editing page. * Renders the userpage editing page.
*/ */

View file

@ -55,15 +55,6 @@ class Controller extends BaseController
} }
// Appearance // Appearance
if (CurrentSession::$user->permission(Site::CHANGE_AVATAR)) {
$nav["Appearance"]["Avatar"] = route('settings.appearance.avatar');
}
if (CurrentSession::$user->permission(Site::CHANGE_BACKGROUND)) {
$nav["Appearance"]["Background"] = route('settings.appearance.background');
}
if (CurrentSession::$user->permission(Site::CHANGE_HEADER)) {
$nav["Appearance"]["Header"] = route('settings.appearance.header');
}
if (( if ((
CurrentSession::$user->page CurrentSession::$user->page
&& CurrentSession::$user->permission(Site::CHANGE_USERPAGE) && CurrentSession::$user->permission(Site::CHANGE_USERPAGE)

View file

@ -6,6 +6,7 @@
namespace Sakura\Controllers; namespace Sakura\Controllers;
use Phroute\Phroute\Exception\HttpMethodNotAllowedException;
use Sakura\Config; use Sakura\Config;
use Sakura\CurrentSession; use Sakura\CurrentSession;
use Sakura\DB; use Sakura\DB;
@ -31,7 +32,7 @@ class UserController extends Controller
$profile = User::construct($id); $profile = User::construct($id);
// If the user id is zero check if there was a namechange // If the user id is zero check if there was a namechange
if ($profile->id == 0) { if ($profile->id === 0) {
// Fetch from username_history // Fetch from username_history
$check = DB::table('username_history') $check = DB::table('username_history')
->where('username_old_clean', clean_string($id, true, true)) ->where('username_old_clean', clean_string($id, true, true))
@ -40,9 +41,8 @@ class UserController extends Controller
// Redirect if so // Redirect if so
if ($check) { if ($check) {
$message = "This user changed their username! Redirecting you to their new profile."; redirect(route('user.profile', $check->user_id));
$redirect = route('user.profile', $check->user_id); return;
return view('global/information', compact('message', 'redirect'));
} }
} }
@ -52,13 +52,14 @@ class UserController extends Controller
/** /**
* Display the memberlist. * Display the memberlist.
* @param int $rank * @param int $rank
* @throws HttpMethodNotAllowedException
* @return string * @return string
*/ */
public function members($rank = null) public function members($rank = null)
{ {
// Check permission // Check permission
if (!CurrentSession::$user->permission(Site::VIEW_MEMBERLIST)) { if (!CurrentSession::$user->permission(Site::VIEW_MEMBERLIST)) {
return view('global/restricted'); throw new HttpMethodNotAllowedException;
} }
// Get all ranks // Get all ranks
@ -79,10 +80,7 @@ class UserController extends Controller
// Get the active rank // Get the active rank
$rank = array_key_exists($rank, $ranks) ? $rank : ($rank ? 0 : intval(config("rank.regular"))); $rank = array_key_exists($rank, $ranks) ? $rank : ($rank ? 0 : intval(config("rank.regular")));
// Get members per page return view('user/members', compact('ranks', 'rank'));
$membersPerPage = 30;
return view('user/members', compact('ranks', 'rank', 'membersPerPage'));
} }
/** /**

View file

@ -0,0 +1,16 @@
<?php
/**
* Holds an exception class.
* @package Sakura
*/
namespace Sakura\Exceptions;
/**
* Thrown when the config file doesn't exist.
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class FileException extends BaseException
{
}

View file

@ -91,6 +91,12 @@ class Post
*/ */
public $editUser = null; public $editUser = null;
/**
* Post deleted?
* @var bool
*/
public $deleted = false;
/** /**
* Constructor. * Constructor.
* @param int $postId * @param int $postId
@ -114,6 +120,7 @@ class Post
$this->editTime = intval($postRow->post_edit_time); $this->editTime = intval($postRow->post_edit_time);
$this->editReason = $postRow->post_edit_reason; $this->editReason = $postRow->post_edit_reason;
$this->editUser = User::construct($postRow->post_edit_user); $this->editUser = User::construct($postRow->post_edit_user);
$this->deleted = boolval($postRow->post_deleted);
// Temporary backwards compatible IP storage system // Temporary backwards compatible IP storage system
try { try {
@ -199,10 +206,34 @@ class Post
return new Post($this->id); return new Post($this->id);
} }
/**
* Undo deletion.
*/
public function restore()
{
DB::table('posts')
->where('post_id', $this->id)
->update([
'post_deleted' => 0,
]);
}
/** /**
* delete this. * delete this.
*/ */
public function delete() public function delete()
{
DB::table('posts')
->where('post_id', $this->id)
->update([
'post_deleted' => 1,
]);
}
/**
* DELETE THIS.
*/
public function purge()
{ {
DB::table('posts') DB::table('posts')
->where('post_id', $this->id) ->where('post_id', $this->id)

View file

@ -124,17 +124,18 @@ max_file_size = 5242880
max_height = 1440 max_height = 1440
max_width = 2560 max_width = 2560
; Cover requirements ; Header requirements
[file.cover] [file.header]
max_file_size = 2097152 max_file_size = 2097152
max_height = 500 max_height = 500
max_width = 2048 max_width = 2048
; User settings ; User settings
[user] [user]
; Avatars (relative to public/) ; Default avatar, background and header (relative to public/)
avatar_ban = images/%tplname%-ban.png
avatar_none = images/%tplname%-none.png avatar_none = images/%tplname%-none.png
background_none = images/pixel.png
header_none = images/pixel.png
; Username constraints ; Username constraints
name_min = 3 name_min = 3

View file

@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Sakura\DB;
class SoftDeleting extends Migration
{
/**
* Run the migrations.
* @return void
*/
public function up()
{
$schema = DB::getSchemaBuilder();
$schema->table('posts', function (Blueprint $table) {
$table->tinyInteger('post_deleted')
->nullable()
->default(0);
});
}
/**
* Reverse the migrations.
* @return void
*/
public function down()
{
$schema = DB::getSchemaBuilder();
$schema->table('posts', function (Blueprint $table) {
$table->dropColumn('post_deleted');
});
}
}

View file

@ -36,7 +36,7 @@
{% if user.isActive %} {% if user.isActive %}
<a class="header__user" href="{{ route('user.profile', user.id) }}" style="color: {{ user.colour }}"> <a class="header__user" href="{{ route('user.profile', user.id) }}" style="color: {{ user.colour }}">
<div class="header__username">{{ user.username }}</div> <div class="header__username">{{ user.username }}</div>
<div class="header__avatar" style="background-image: url('{{ route('file.avatar', user.id) }}')"></div> <div class="header__avatar" style="background-image: url('{{ route('user.avatar', user.id) }}')"></div>
</a> </a>
{% else %} {% else %}
<a class="header__user" href="{{ route('auth.login') }}"> <a class="header__user" href="{{ route('auth.login') }}">

View file

@ -115,7 +115,7 @@
{% block content %} {% block content %}
<div class="profile__content"> <div class="profile__content">
<div class="profile__container profile__container--left"> <div class="profile__container profile__container--left">
<div class="profile__avatar" style="background-image: url('{{ route('file.avatar', profile.id) }}');"></div> <div class="profile__avatar" style="background-image: url('{{ route('user.avatar', profile.id) }}');"></div>
<div class="platform profile__platform profile__hierarchies">{# <div class="platform profile__platform profile__hierarchies">{#
#}{% for id, data in hierarchies %}{# #}{% for id, data in hierarchies %}{#
#}{% if data.display %} #}{% if data.display %}

View file

@ -1,6 +1,6 @@
<li id="comment-{{ comment.id }}"> <li id="comment-{{ comment.id }}">
<div class="comment"> <div class="comment">
<a class="comment-avatar clean" href="{{ route('user.profile', comment.userData.id) }}" style="background-image: url('{{ route('file.avatar', comment.userData.id) }}');"><span style="color: {{ comment.userData.colour }};">{{ comment.userData.username }}</span></a> <a class="comment-avatar clean" href="{{ route('user.profile', comment.userData.id) }}" style="background-image: url('{{ route('user.avatar', comment.userData.id) }}');"><span style="color: {{ comment.userData.colour }};">{{ comment.userData.username }}</span></a>
<div class="comment-pointer"></div> <div class="comment-pointer"></div>
<div class="comment-content"> <div class="comment-content">
<div class="comment-controls"> <div class="comment-controls">
@ -10,7 +10,7 @@
{% else %} {% else %}
<li><a href="{{ route('user.report', comment.userData.id) }}" class="clean fa fa-exclamation-circle" title="Report"></a></li> <li><a href="{{ route('user.report', comment.userData.id) }}" class="clean fa fa-exclamation-circle" title="Report"></a></li>
{% endif %} {% endif %}
<li><a href="javascript:void(0);" onclick="commentReply({{ comment.id }}, '{{ session_id() }}', '{{ route('file.avatar', user.id) }}');" class="clean fa fa-reply" title="Reply" id="comment-action-reply-{{ comment.id }}"></a></li> <li><a href="javascript:void(0);" onclick="commentReply({{ comment.id }}, '{{ session_id() }}', '{{ route('user.avatar', user.id) }}');" class="clean fa fa-reply" title="Reply" id="comment-action-reply-{{ comment.id }}"></a></li>
<li class="shown voting like"><a href="javascript:void(0);" onclick="commentVote({{ comment.id }}, 1);" class="clean"><span class="fa fa-thumbs-up"></span> <span id="comment-{{ comment.id }}-likes">{{ comment.upvotes }}</span></a></li> <li class="shown voting like"><a href="javascript:void(0);" onclick="commentVote({{ comment.id }}, 1);" class="clean"><span class="fa fa-thumbs-up"></span> <span id="comment-{{ comment.id }}-likes">{{ comment.upvotes }}</span></a></li>
<li class="shown voting dislike"><a href="javascript:void(0);" onclick="commentVote({{ comment.id }}, 0);" class="clean"><span class="fa fa-thumbs-down"></span> <span id="comment-{{ comment.id }}-dislikes">{{ comment.downvotes }}</span></a></li> <li class="shown voting dislike"><a href="javascript:void(0);" onclick="commentVote({{ comment.id }}, 0);" class="clean"><span class="fa fa-thumbs-down"></span> <span id="comment-{{ comment.id }}-dislikes">{{ comment.downvotes }}</span></a></li>
</ul> </ul>

View file

@ -2,7 +2,7 @@
<div class="comment-input-section"> <div class="comment-input-section">
{% if user.isActive %} {% if user.isActive %}
<div class="comment"> <div class="comment">
<div class="comment-avatar" style="background-image: url('{{ route('file.avatar', user.id) }}');"></div> <div class="comment-avatar" style="background-image: url('{{ route('user.avatar', user.id) }}');"></div>
<div class="comment-pointer"></div> <div class="comment-pointer"></div>
<textarea class="comment-content" name="text" placeholder="Join the conversation..."></textarea> <textarea class="comment-content" name="text" placeholder="Join the conversation..."></textarea>
<button class="comment-submit new" name="session" value="{{ session_id() }}" onclick="commentPost(this.parentNode, '{{ route('comments.category.post', commentsCategory) }}');">&#xf1d8;</button> <button class="comment-submit new" name="session" value="{{ session_id() }}" onclick="commentPost(this.parentNode, '{{ route('comments.category.post', commentsCategory) }}');">&#xf1d8;</button>
@ -65,7 +65,7 @@
var avatar = document.createElement('a'); var avatar = document.createElement('a');
avatar.className = 'comment-avatar clean'; avatar.className = 'comment-avatar clean';
avatar.href = "{{ route('user.profile', 1) }}".replace(1, obj.user); avatar.href = "{{ route('user.profile', 1) }}".replace(1, obj.user);
avatar.style.backgroundImage = "url('{{ route('file.avatar', 1) }}')".replace(1, obj.user); avatar.style.backgroundImage = "url('{{ route('user.avatar', 1) }}')".replace(1, obj.user);
inner.appendChild(avatar); inner.appendChild(avatar);
var pointer = document.createElement('div'); var pointer = document.createElement('div');
@ -104,7 +104,7 @@
controlsReply.href = 'javascript:void(0);'; controlsReply.href = 'javascript:void(0);';
controlsReply.title = 'Reply'; controlsReply.title = 'Reply';
controlsReply.className = 'clean fa fa-reply'; controlsReply.className = 'clean fa fa-reply';
controlsReply.setAttribute('onclick', 'commentReply(' + obj.id + ', "{{ session_id() }}", "{{ route("file.avatar", user.id) }}");'); controlsReply.setAttribute('onclick', 'commentReply(' + obj.id + ', "{{ session_id() }}", "{{ route("user.avatar", user.id) }}");');
controlsReplyContainer.appendChild(controlsReply); controlsReplyContainer.appendChild(controlsReply);
controlsInner.appendChild(controlsReplyContainer); controlsInner.appendChild(controlsReplyContainer);

View file

@ -1,7 +1,7 @@
<div id="indexPanel"> <div id="indexPanel">
{% if user.isActive %} {% if user.isActive %}
<div class="user-container" style="background-image: url({{ route('user.header', user.id) }});"> <div class="user-container" style="background-image: url({{ route('user.header', user.id) }});">
<div class="default-avatar-setting user-container-avatar" style="background-image: url({{ route('file.avatar', user.id) }}); box-shadow: 0 0 5px {{ user.colour }};"><a href="{{ route('settings.appearance.avatar') }}" class="clean" style="display: block; height: 100%; width: 100%;"></a></div> <div class="default-avatar-setting user-container-avatar" style="background-image: url({{ route('user.avatar', user.id) }}); box-shadow: 0 0 5px {{ user.colour }};"><a href="{{ route('user.profile', user.id) }}" class="clean" style="display: block; height: 100%; width: 100%;"></a></div>
<div class="user-container-info"> <div class="user-container-info">
<a href="{{ route('user.profile', user.id) }}" class="clean"><h1 style="color: {{ user.colour }}; text-shadow: 0 0 7px {% if user.colour != 'inherit' %}{{ user.colour }}{% else %}#222{% endif %}; padding: 0 0 2px;">{{ user.username }}</h1></a> <a href="{{ route('user.profile', user.id) }}" class="clean"><h1 style="color: {{ user.colour }}; text-shadow: 0 0 7px {% if user.colour != 'inherit' %}{{ user.colour }}{% else %}#222{% endif %}; padding: 0 0 2px;">{{ user.username }}</h1></a>
{% set friendRequests = user.friends(-1, true)|length %} {% set friendRequests = user.friends(-1, true)|length %}

View file

@ -2,7 +2,7 @@
<div class="news-body"> <div class="news-body">
<a class="no-underline" href="{{ route('user.profile', post.userData.id) }}"> <a class="no-underline" href="{{ route('user.profile', post.userData.id) }}">
<div class="news-poster"> <div class="news-poster">
<img src="{{ route('file.avatar', post.userData.id) }}" alt="{{ post.userData.username }}" class="default-avatar-setting"> <img src="{{ route('user.avatar', post.userData.id) }}" alt="{{ post.userData.username }}" class="default-avatar-setting">
<h1 style="color: {{ post.userData.colour }}; text-shadow: 0 0 7px {% if post.userData.colour != 'inherit' %}{{ post.userData.colour }}{% else %}#222{% endif %}; padding: 0 0 10px;">{{ post.userData.username }}</h1> <h1 style="color: {{ post.userData.colour }}; text-shadow: 0 0 7px {% if post.userData.colour != 'inherit' %}{{ post.userData.colour }}{% else %}#222{% endif %}; padding: 0 0 10px;">{{ post.userData.username }}</h1>
</div> </div>
</a> </a>

View file

@ -46,7 +46,7 @@
{% if activePoster.id %} {% if activePoster.id %}
<a class="clean" href="{{ route('user.profile', activePoster.id) }}"> <a class="clean" href="{{ route('user.profile', activePoster.id) }}">
<div class="user-container" style="background-image: url({{ route('user.header', activePoster.id) }});"> <div class="user-container" style="background-image: url({{ route('user.header', activePoster.id) }});">
<div class="default-avatar-setting user-container-avatar" style="background-image: url({{ route('file.avatar', activePoster.id) }}); box-shadow: 0 0 5px #{% if activePoster.isOnline %}484{% else %}844{% endif %};"></div> <div class="default-avatar-setting user-container-avatar" style="background-image: url({{ route('user.avatar', activePoster.id) }}); box-shadow: 0 0 5px #{% if activePoster.isOnline %}484{% else %}844{% endif %};"></div>
<div class="user-container-info"> <div class="user-container-info">
<h1 style="color: {{ activePoster.colour }}; text-shadow: 0 0 7px {% if activePoster.colour != 'inherit' %}{{ activePoster.colour }}{% else %}#222{% endif %}; padding: 0 0 2px;" {% if activePoster.getUsernameHistory %} title="Known as {{ activePoster.getUsernameHistory[0].username_old }} before {{ activePoster.getUsernameHistory[0].change_time|date(config('general.date_format')) }}." {% endif %}>{{ activePoster.username }}</h1> <h1 style="color: {{ activePoster.colour }}; text-shadow: 0 0 7px {% if activePoster.colour != 'inherit' %}{{ activePoster.colour }}{% else %}#222{% endif %}; padding: 0 0 2px;" {% if activePoster.getUsernameHistory %} title="Known as {{ activePoster.getUsernameHistory[0].username_old }} before {{ activePoster.getUsernameHistory[0].change_time|date(config('general.date_format')) }}." {% endif %}>{{ activePoster.username }}</h1>
{% if activePoster.isPremium %}<img src="/images/tenshi.png" alt="Tenshi" style="vertical-align: middle;"> {% endif %}<img src="/images/flags/{{ activePoster.country|lower }}.png" alt="{{ activePoster.country }}" style="vertical-align: middle;" title="{{ activePoster.country(true) }}"> <span style="font-size: .8em;">{{ activePoster.title }}</span> {% if activePoster.isPremium %}<img src="/images/tenshi.png" alt="Tenshi" style="vertical-align: middle;"> {% endif %}<img src="/images/flags/{{ activePoster.country|lower }}.png" alt="{{ activePoster.country }}" style="vertical-align: middle;" title="{{ activePoster.country(true) }}"> <span style="font-size: .8em;">{{ activePoster.title }}</span>

View file

@ -80,7 +80,7 @@
<tr class="post" id="p{{ post.id }}"> <tr class="post" id="p{{ post.id }}">
<td class="userpanel"> <td class="userpanel">
{% if not post.poster.permission(constant('Sakura\\Perms\\Site::DEACTIVATED')) or post.poster.permission(constant('Sakura\\Perms\\Site::RESTRICTED')) %}<a href="{{ route('user.profile', post.poster.id) }}" class="default username" style="color: {{ post.poster.colour }}; text-shadow: 0 0 5px {% if post.poster.colour != 'inherit' %}{{ post.poster.colour }}{% else %}#222{% endif %};" title="Go to {{ post.poster.username }}'s profile">{{ post.poster.username }}</a> {% if not post.poster.permission(constant('Sakura\\Perms\\Site::DEACTIVATED')) or post.poster.permission(constant('Sakura\\Perms\\Site::RESTRICTED')) %}<a href="{{ route('user.profile', post.poster.id) }}" class="default username" style="color: {{ post.poster.colour }}; text-shadow: 0 0 5px {% if post.poster.colour != 'inherit' %}{{ post.poster.colour }}{% else %}#222{% endif %};" title="Go to {{ post.poster.username }}'s profile">{{ post.poster.username }}</a>
<img src="{{ route('file.avatar', post.poster.id) }}" alt="{{ post.poster.username }}" class="avatar" style="box-shadow: 0 3px 7px #{% if post.poster.isOnline %}484{% else %}844{% endif %};"> <img src="{{ route('user.avatar', post.poster.id) }}" alt="{{ post.poster.username }}" class="avatar" style="box-shadow: 0 3px 7px #{% if post.poster.isOnline %}484{% else %}844{% endif %};">
{% else %} {% else %}
<a class="username">[deleted user]</a> <a class="username">[deleted user]</a>
{% endif %} {% endif %}
@ -136,7 +136,7 @@
<tr class="post" id="postingPreview" style="display: none;"> <tr class="post" id="postingPreview" style="display: none;">
<td class="userpanel"> <td class="userpanel">
<a class="default username" href="{{ route('user.profile', user.id) }}" style="color: {{ user.colour }}; text-shadow: 0 0 5px {% if user.colour != 'inherit' %}{{ user.colour }}{% else %}#222{% endif %};" title="Go to {{ user.username }}'s profile">{{ user.username }}</a> <a class="default username" href="{{ route('user.profile', user.id) }}" style="color: {{ user.colour }}; text-shadow: 0 0 5px {% if user.colour != 'inherit' %}{{ user.colour }}{% else %}#222{% endif %};" title="Go to {{ user.username }}'s profile">{{ user.username }}</a>
<img src="{{ route('file.avatar', user.id) }}" alt="{{ user.username }}" class="avatar" style="box-shadow: 0 3px 7px #484;"> <img src="{{ route('user.avatar', user.id) }}" alt="{{ user.username }}" class="avatar" style="box-shadow: 0 3px 7px #484;">
<div class="userdata"> <div class="userdata">
<div class="usertitle">{{ user.title }}</div> <div class="usertitle">{{ user.title }}</div>
<img src="/images/tenshi.png" alt="Tenshi"{% if not user.isPremium %} style="opacity: 0;"{% endif %}> <img src="/images/flags/{{ user.country|lower }}.png" alt="{{ user.country(true) }}">{% if user.id == (topic.posts|first).poster.id %} <img src="/images/op.png" alt="OP" title="Original Poster">{% endif %} <img src="/images/tenshi.png" alt="Tenshi"{% if not user.isPremium %} style="opacity: 0;"{% endif %}> <img src="/images/flags/{{ user.country|lower }}.png" alt="{{ user.country(true) }}">{% if user.id == (topic.posts|first).poster.id %} <img src="/images/op.png" alt="OP" title="Original Poster">{% endif %}

View file

@ -1,22 +0,0 @@
{% extends 'master.twig' %}
{% set title = 'Confirmation' %}
{% block content %}
<div class="content standalone">
<div>
<h1>{% block header %}Confirmation{% endblock %}</h1>
<hr class="default">
{{ message }}
<form method="post" action="{{ server['REQUEST_URI'] }}" id="confirmationForm">
<input type="hidden" name="sessionid" value="{{ session_id() }}">
<input type="hidden" name="timestamp" value="{{ date().timestamp }}">
{% for key,value in conditions %}
<input type="hidden" name="{{ key }}" value="{{ value }}">
{% endfor %}
<input type="submit" class="inputStyling" name="yes" value="Yes">
<input type="submit" class="inputStyling" name="no" value="No">
</form>
</div>
</div>
{% endblock %}

View file

@ -1,12 +0,0 @@
{% extends 'master.twig' %}
{% set title = 'Restricted' %}
{% block content %}
<div class="content standalone">
<div>
<h1>You aren't allowed to view this page!</h1>
Please make sure that you're logged in and are supposed to be able to access this page. If you think this was a mistake please contact a staff member.
</div>
</div>
{% endblock %}

View file

@ -47,7 +47,7 @@
<div class="menu-ucp" id="navMenuUser"> <div class="menu-ucp" id="navMenuUser">
<!-- User menu, displayed on right side of the bar. --> <!-- User menu, displayed on right side of the bar. -->
{% if user.isActive %} {% if user.isActive %}
<a class="menu-item avatar" href="{{ route('user.profile', user.id) }}" title="Logged in as {{ user.username }}" style="background-image: url('{{ route('file.avatar', user.id) }}'); width: auto; color: {{ user.colour }}; border-color: {{ user.colour }}; font-weight: 700;"></a> <a class="menu-item avatar" href="{{ route('user.profile', user.id) }}" title="Logged in as {{ user.username }}" style="background-image: url('{{ route('user.avatar', user.id) }}'); width: auto; color: {{ user.colour }}; border-color: {{ user.colour }}; font-weight: 700;"></a>
{#<a class="menu-item fa-envelope" href="#" title="Messages"></a>#} {#<a class="menu-item fa-envelope" href="#" title="Messages"></a>#}
{% if user.permission(constant('Sakura\\Perms\\Manage::USE_MANAGE'), constant('Sakura\\Perms::MANAGE')) %} {% if user.permission(constant('Sakura\\Perms\\Manage::USE_MANAGE'), constant('Sakura\\Perms::MANAGE')) %}
<a class="menu-item fa-gavel" href="{{ route('manage.index') }}" title="Manage"></a> <a class="menu-item fa-gavel" href="{{ route('manage.index') }}" title="Manage"></a>
@ -64,7 +64,7 @@
<div id="contentwrapper"> <div id="contentwrapper">
<div id="notifications"></div> <div id="notifications"></div>
{% if profile is defined ? profile.background : (user.permission(constant('Sakura\\Perms\\Site::CHANGE_BACKGROUND')) and (user.backgroundSitewide or showBG) and user.background) %} {% if profile is defined ? profile.background : (user.permission(constant('Sakura\\Perms\\Site::CHANGE_BACKGROUND')) and (user.backgroundSitewide or showBG) and user.background) %}
<div id="userBackground" style="background-image: url('{{ route('file.background', (profile is defined ? profile : user).id) }}');"></div> <div id="userBackground" style="background-image: url('{{ route('user.background', (profile is defined ? profile : user).id) }}');"></div>
{% endif %} {% endif %}
{% if not user.isActive and server['REQUEST_URI'] != route('auth.login') %} {% if not user.isActive and server['REQUEST_URI'] != route('auth.login') %}
<div class="headerLoginContainer"> <div class="headerLoginContainer">

View file

@ -6,6 +6,27 @@
{% include 'elements/indexPanel.twig' %} {% include 'elements/indexPanel.twig' %}
</div> </div>
<div class="content-left content-column"> <div class="content-left content-column">
<div class="head">Temporary avatar upload test form!!</div>
<form enctype="multipart/form-data" method="post" action="{{ route('user.avatar', user.id) }}" style="margin: 1em">
<input type="hidden" name="MAX_FILE_SIZE" value="{{ config('file.avatar.max_file_size') }}">
<input type="hidden" name="session" value="{{ session_id() }}">
<input type="file" name="file">
<button>upload</button>
</form>
<div class="head">Temporary <del>avatar</del> background upload test form!!</div>
<form enctype="multipart/form-data" method="post" action="{{ route('user.background', user.id) }}" style="margin: 1em">
<input type="hidden" name="MAX_FILE_SIZE" value="{{ config('file.background.max_file_size') }}">
<input type="hidden" name="session" value="{{ session_id() }}">
<input type="file" name="file">
<button>upload</button>
</form>
<div class="head">Temporary <del>avatar background</del> header upload test form!!</div>
<form enctype="multipart/form-data" method="post" action="{{ route('user.header', user.id) }}" style="margin: 1em">
<input type="hidden" name="MAX_FILE_SIZE" value="{{ config('file.header.max_file_size') }}">
<input type="hidden" name="session" value="{{ session_id() }}">
<input type="file" name="file">
<button>upload</button>
</form>
<div class="head">News</div> <div class="head">News</div>
{% for post in news %} {% for post in news %}
{% include 'elements/newsPost.twig' %} {% include 'elements/newsPost.twig' %}

View file

@ -10,7 +10,7 @@
{% for friend in friends[get.page|default(1) - 1] %} {% for friend in friends[get.page|default(1) - 1] %}
<div class="friend-container" id="friendslist-friend-{{ friend.id }}"> <div class="friend-container" id="friendslist-friend-{{ friend.id }}">
<a class="friends-list-data clean" href="{{ route('user.profile', friend.id) }}"> <a class="friends-list-data clean" href="{{ route('user.profile', friend.id) }}">
<img src="{{ route('file.avatar', friend.id) }}" alt="{{ friend.username }}" class="friends-list-avatar default-avatar-setting" style="width: 150px; height: 150px;"> <img src="{{ route('user.avatar', friend.id) }}" alt="{{ friend.username }}" class="friends-list-avatar default-avatar-setting" style="width: 150px; height: 150px;">
<div class="friends-list-name" style="color: {{ friend.colour }};">{{ friend.username }}</div> <div class="friends-list-name" style="color: {{ friend.colour }};">{{ friend.username }}</div>
</a> </a>
</div> </div>

View file

@ -0,0 +1,11 @@
{% extends 'master.twig' %}
{% set title = 'Farewell!' %}
{% block content %}
<div class="content standalone" style="text-align: center;">
<h1 class="stylised" style="margin: 1em auto;">Farewell!</h1>
<h1 class="fa fa-frown-o stylised" style="font-size: 20em;"></h1>
<h3>Remember that you can reactivate your account anytime if you decide to come back!</h3>
</div>
{% endblock %}

View file

@ -1,27 +0,0 @@
{% extends 'settings/appearance/master.twig' %}
{% set mode = 'Avatar' %}
{% block description %}
<p>Maximum image size is {{ config('file.avatar.max_width') }}x{{ config('file.avatar.max_height') }} and can't be larger than {{ config('file.avatar.max_file_size')|byte_symbol }}.</p>
{% endblock %}
{% block settingsContent %}
<form enctype="multipart/form-data" method="post" action="{{ route('settings.appearance.avatar') }}">
<input type="hidden" name="MAX_FILE_SIZE" value="{{ config('file.avatar.max_file_size') }}">
<div style="text-align: center;">
<div>
<img src="{{ route('file.avatar', user.id) }}" alt="Your Avatar" class="default-avatar-setting">
</div>
<div>
<input type="file" name="avatar">
<div style="font-size: .8em;">
(Leave upload box empty to remove avatar)
</div>
</div>
<div>
<button value="{{ session_id() }}" name="session" class="inputStyling">Upload</button>
</div>
</div>
</form>
{% endblock %}

View file

@ -1,25 +0,0 @@
{% extends 'settings/appearance/master.twig' %}
{% set mode = 'Background' %}
{% set showBG = true %}
{% block description %}
<p>Maximum image size is {{ config('file.background.max_width') }}x{{ config('file.background.max_height') }} and can't be larger than {{ config('file.background.max_file_size')|byte_symbol }}.</p>
{% endblock %}
{% block settingsContent %}
<form enctype="multipart/form-data" method="post" action="{{ route('settings.appearance.background') }}">
<input type="hidden" name="MAX_FILE_SIZE" value="{{ config('file.background.max_file_size') }}">
<div style="text-align: center;">
<div>
<input type="file" name="background">
<div style="font-size: .8em;">
(Leave upload box empty to remove background)
</div>
</div>
<div>
<button value="{{ session_id() }}" name="session" class="inputStyling">Upload</button>
</div>
</div>
</form>
{% endblock %}

View file

@ -1,27 +0,0 @@
{% extends 'settings/appearance/master.twig' %}
{% set mode = 'Header' %}
{% block description %}
<p>Maximum image size is {{ config('file.cover.max_width') }}x{{ config('file.cover.max_height') }} and can't be larger than {{ config('file.cover.max_file_size')|byte_symbol }}.</p>
{% endblock %}
{% block settingsContent %}
<form enctype="multipart/form-data" method="post" action="{{ route('settings.appearance.header') }}">
<input type="hidden" name="MAX_FILE_SIZE" value="{{ config('file.cover.max_file_size') }}">
<div style="text-align: center;">
<div>
<img src="{{ route('user.header', user.id) }}" alt="Your Header" class="default-avatar-setting" style="max-width: 90%; max-height: 90%;">
</div>
<div>
<input type="file" name="header">
<div style="font-size: .8em;">
(Leave upload box empty to remove header)
</div>
</div>
<div>
<button value="{{ session_id() }}" name="session" class="inputStyling">Upload</button>
</div>
</div>
</form>
{% endblock %}

View file

@ -17,7 +17,7 @@
{% for friend in friends[get.page|default(1) - 1] %} {% for friend in friends[get.page|default(1) - 1] %}
<div class="friend-container" id="friendslist-friend-{{ friend.id }}"> <div class="friend-container" id="friendslist-friend-{{ friend.id }}">
<a class="friends-list-data clean" href="{{ route('user.profile', friend.id) }}"> <a class="friends-list-data clean" href="{{ route('user.profile', friend.id) }}">
<img src="{{ route('file.avatar', friend.id) }}" alt="{{ friend.username }}" class="friends-list-avatar default-avatar-setting" style="width: 150px; height: 150px;"> <img src="{{ route('user.avatar', friend.id) }}" alt="{{ friend.username }}" class="friends-list-avatar default-avatar-setting" style="width: 150px; height: 150px;">
<div class="friends-list-name" style="color: {{ friend.colour }};">{{ friend.username }}</div> <div class="friends-list-name" style="color: {{ friend.colour }};">{{ friend.username }}</div>
</a> </a>
<div class="friends-list-actions"> <div class="friends-list-actions">

View file

@ -17,7 +17,7 @@
{% for friend in friends[get.page|default(1) - 1] %} {% for friend in friends[get.page|default(1) - 1] %}
<div class="friend-container" id="friend-{{ friend.id }}"> <div class="friend-container" id="friend-{{ friend.id }}">
<a class="friends-list-data clean" href="{{ route('user.profile', friend.id) }}"> <a class="friends-list-data clean" href="{{ route('user.profile', friend.id) }}">
<img src="{{ route('file.avatar', friend.id) }}" alt="{{ friend.username }}" class="friends-list-avatar default-avatar-setting" style="width: 150px; height: 150px;"> <img src="{{ route('user.avatar', friend.id) }}" alt="{{ friend.username }}" class="friends-list-avatar default-avatar-setting" style="width: 150px; height: 150px;">
<div class="friends-list-name" style="color: {{ friend.colour }};">{{ friend.username }}</div> <div class="friends-list-name" style="color: {{ friend.colour }};">{{ friend.username }}</div>
</a> </a>
<div class="friends-list-actions"> <div class="friends-list-actions">

View file

@ -13,7 +13,7 @@
{% if notfound %}The requested rank could not be found!{% else %}{{ ranks[rank].description }}{% endif %} {% if notfound %}The requested rank could not be found!{% else %}{{ ranks[rank].description }}{% endif %}
{% endset %} {% endset %}
{% set users = ranks[rank].users|batch(membersPerPage) %} {% set users = ranks[rank].users|batch(30) %}
{% set currPage = get.page|default(1) - 1 %} {% set currPage = get.page|default(1) - 1 %}
@ -99,7 +99,7 @@
{% for user in users[currPage] %} {% for user in users[currPage] %}
<a href="{{ route('user.profile', user.id) }}">{# These comment tags are here to prevent the link extending too far <a href="{{ route('user.profile', user.id) }}">{# These comment tags are here to prevent the link extending too far
#}<div class="userBox" id="u{{ user.id }}">{# #}<div class="userBox" id="u{{ user.id }}">{#
#}<img src="/images/pixel.png" alt="{{ user.username }}" style="background: url('{{ route('file.avatar', user.id) }}') no-repeat center / contain;">{# #}<img src="/images/pixel.png" alt="{{ user.username }}" style="background: url('{{ route('user.avatar', user.id) }}') no-repeat center / contain;">{#
#}<span class="userBoxUserName" style="color: {{ user.colour }};">{# #}<span class="userBoxUserName" style="color: {{ user.colour }};">{#
#}{{ user.username }}{# #}{{ user.username }}{#
#}</span>{# #}</span>{#

View file

@ -142,7 +142,7 @@
<div class="new-profile-container"> <div class="new-profile-container">
<div class="new-profile-header" style="background-image: url({{ route('user.header', profile.id) }});"> <div class="new-profile-header" style="background-image: url({{ route('user.header', profile.id) }});">
<div class="new-profile-info"> <div class="new-profile-info">
<div class="default-avatar-setting new-profile-avatar" style="background-image: url({{ route('file.avatar', profile.id) }}); box-shadow: 0 0 5px #{% if profile.isOnline %}484{% else %}844{% endif %};"></div> <div class="default-avatar-setting new-profile-avatar" style="background-image: url({{ route('user.avatar', profile.id) }}); box-shadow: 0 0 5px #{% if profile.isOnline %}484{% else %}844{% endif %};"></div>
<div class="new-profile-username"> <div class="new-profile-username">
<h1 style="color: {{ profile.colour }}; text-shadow: 0 0 7px {% if profile.colour != 'inherit' %}{{ profile.colour }}{% else %}#222{% endif %}; padding: 0 0 2px;" {% if profile.getUsernameHistory %} title="Known as {{ profile.getUsernameHistory[0].username_old }} before {{ profile.getUsernameHistory[0].change_time|date(config('general.date_format')) }}." {% endif %}>{{ profile.username }}</h1> <h1 style="color: {{ profile.colour }}; text-shadow: 0 0 7px {% if profile.colour != 'inherit' %}{{ profile.colour }}{% else %}#222{% endif %}; padding: 0 0 2px;" {% if profile.getUsernameHistory %} title="Known as {{ profile.getUsernameHistory[0].username_old }} before {{ profile.getUsernameHistory[0].change_time|date(config('general.date_format')) }}." {% endif %}>{{ profile.username }}</h1>
{% if profile.isPremium %}<img src="/images/tenshi.png" alt="Tenshi" style="vertical-align: middle;"> {% endif %}<img src="/images/flags/{{ profile.country|lower }}.png" alt="{{ profile.country }}" style="vertical-align: middle;" title="{{ profile.country(true) }}"> <span style="font-size: .8em;">{{ profile.title }}</span> {% if profile.isPremium %}<img src="/images/tenshi.png" alt="Tenshi" style="vertical-align: middle;"> {% endif %}<img src="/images/flags/{{ profile.country|lower }}.png" alt="{{ profile.country }}" style="vertical-align: middle;" title="{{ profile.country(true) }}"> <span style="font-size: .8em;">{{ profile.title }}</span>

View file

@ -163,7 +163,18 @@ Routerv1::group(['before' => 'maintenance'], function () {
Routerv1::group(['prefix' => 'u'], function () { Routerv1::group(['prefix' => 'u'], function () {
Routerv1::get('/{id}', 'UserController@profile', 'user.profile'); Routerv1::get('/{id}', 'UserController@profile', 'user.profile');
Routerv1::get('/{id}/report', 'UserController@report', 'user.report'); Routerv1::get('/{id}/report', 'UserController@report', 'user.report');
Routerv1::get('/{id}/avatar', 'FileController@avatar', 'user.avatar');
Routerv1::post('/{id}/avatar', 'FileController@avatar', 'user.avatar');
Routerv1::delete('/{id}/avatar', 'FileController@avatar', 'user.avatar');
Routerv1::get('/{id}/background', 'FileController@background', 'user.background');
Routerv1::post('/{id}/background', 'FileController@background', 'user.background');
Routerv1::delete('/{id}/background', 'FileController@background', 'user.background');
Routerv1::get('/{id}/header', 'FileController@header', 'user.header'); Routerv1::get('/{id}/header', 'FileController@header', 'user.header');
Routerv1::post('/{id}/header', 'FileController@header', 'user.header');
Routerv1::delete('/{id}/header', 'FileController@header', 'user.header');
}); });
// Notifications // Notifications
@ -185,10 +196,6 @@ Routerv1::group(['before' => 'maintenance'], function () {
Routerv1::post('/{id:i}/remove', 'FriendsController@remove', 'friends.remove'); Routerv1::post('/{id:i}/remove', 'FriendsController@remove', 'friends.remove');
}); });
// Files
Routerv1::get('/a/{id}', 'FileController@avatar', 'file.avatar');
Routerv1::get('/bg/{id}', 'FileController@background', 'file.background');
// Premium // Premium
Routerv1::group(['prefix' => 'support', 'before' => 'loginCheck'], function () { Routerv1::group(['prefix' => 'support', 'before' => 'loginCheck'], function () {
Routerv1::get('/', 'PremiumController@index', 'premium.index'); Routerv1::get('/', 'PremiumController@index', 'premium.index');
@ -256,12 +263,6 @@ Routerv1::group(['before' => 'maintenance'], function () {
return header("Location: {$route}"); return header("Location: {$route}");
}); });
Routerv1::get('/avatar', 'Settings.AppearanceController@avatar', 'settings.appearance.avatar');
Routerv1::post('/avatar', 'Settings.AppearanceController@avatar', 'settings.appearance.avatar');
Routerv1::get('/background', 'Settings.AppearanceController@background', 'settings.appearance.background');
Routerv1::post('/background', 'Settings.AppearanceController@background', 'settings.appearance.background');
Routerv1::get('/header', 'Settings.AppearanceController@header', 'settings.appearance.header');
Routerv1::post('/header', 'Settings.AppearanceController@header', 'settings.appearance.header');
Routerv1::get('/userpage', 'Settings.AppearanceController@userpage', 'settings.appearance.userpage'); Routerv1::get('/userpage', 'Settings.AppearanceController@userpage', 'settings.appearance.userpage');
Routerv1::post('/userpage', 'Settings.AppearanceController@userpage', 'settings.appearance.userpage'); Routerv1::post('/userpage', 'Settings.AppearanceController@userpage', 'settings.appearance.userpage');
Routerv1::get('/signature', 'Settings.AppearanceController@signature', 'settings.appearance.signature'); Routerv1::get('/signature', 'Settings.AppearanceController@signature', 'settings.appearance.signature');

View file

@ -73,6 +73,13 @@ function clean_string($string, $lower = false, $noSpecial = false, $replaceSpeci
return $string; return $string;
} }
// Redirect with turbolinks header
function redirect($url)
{
header("Turbolinks-Location: {$url}");
header("Location: {$url}");
}
function check_mx_record($email) function check_mx_record($email)
{ {
// Get the domain from the e-mail address // Get the domain from the e-mail address
@ -126,12 +133,10 @@ function get_country_name($code)
} }
} }
// Count the amount of unique characters in the password string and calculate the entropy
function password_entropy($password) function password_entropy($password)
{ {
$password = utf8_decode($password); return count(count_chars(utf8_decode($password), 1)) * log(256, 2);
// Count the amount of unique characters in the password string and calculate the entropy
return count(count_chars($password, 1)) * log(256, 2);
} }
function byte_symbol($bytes) function byte_symbol($bytes)