migration and the software itself works!

This commit is contained in:
flash 2016-07-29 21:31:36 +02:00
parent bd5faa9e00
commit 93a7ba8389
405 changed files with 5107 additions and 4260 deletions

View file

@ -7,12 +7,8 @@
namespace Sakura;
use JBBCode\CodeDefinitionBuilder;
use JBBCode\DefaultCodeDefinitionSet;
use JBBCode\Parser;
/**
* Sakura wrapper for JBBCode.
* BBcode handler.
*
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
@ -20,22 +16,17 @@ use JBBCode\Parser;
class BBcode
{
/**
* The container for JBBCode.
* BBcodes, also for backwards compatibility.
*
* @var Parser
* @var array
*/
private static $bbcode = null;
protected static $bbcodes = [];
/**
* Initialiser.
*/
public static function init()
{
// Create new parser class
self::$bbcode = new Parser();
// Add the standard definitions
self::loadStandardCodes();
}
/**
@ -53,7 +44,7 @@ class BBcode
// Parse all emoticons
foreach ($emotes as $emote) {
$image = "<img src='{$emote->emote_path}' alt='{$emote->emote_string}' class='emoticon' />";
$image = "<img src='{$emote->emote_path}' alt='{$emote->emote_string}' class='emoticon'>";
$icon = preg_quote($emote->emote_string, '#');
$text = preg_replace("#$icon#", $image, $text);
}
@ -62,52 +53,6 @@ class BBcode
return $text;
}
/**
* Adds the standard BBcode.
*/
public static function loadStandardCodes()
{
// Add the standard definitions
self::$bbcode->addCodeDefinitionSet(new DefaultCodeDefinitionSet());
$simpleCodes = [
['header', '<h1>{param}</h1>'],
['s', '<del>{param}</del>'],
['spoiler', '<span class="spoiler">{param}</span>'],
['box', '<div class="spoiler-box-container">
<div class="spoiler-box-title" onclick="Sakura.toggleClass(this.parentNode.children[1], \'hidden\');">'
. 'Click to open</div><div class="spoiler-box-content hidden">{param}</div></div>'],
['box', '<div class="spoiler-box-container"><div class="spoiler-box-title"'
. ' onclick="Sakura.toggleClass(this.parentNode.children[1], \'hidden\');">{option}</div>'
. '<div class="spoiler-box-content hidden">{param}</div></div>'],
['quote', '<blockquote><div class="quotee">Quote</div><div class="quote">{param}</div></blockquote>'],
];
foreach ($simpleCodes as $code) {
$builder = new CodeDefinitionBuilder($code[0], $code[1]);
if (strstr($code[1], '{option}')) {
$builder->setUseOption(true);
}
self::$bbcode->addCodeDefinition($builder->build());
}
// Add special definitions (PHP files MUST have the same name as the definition class
foreach (glob(ROOT . 'libraries/BBcodeDefinitions/*.php') as $ext) {
// Clean the file path
$ext = str_replace(ROOT . 'libraries/', '', $ext);
$ext = str_replace('.php', '', $ext);
$ext = str_replace('/', '\\', $ext);
// Build the classname
$className = __NAMESPACE__ . '\\' . $ext;
// Add the BBcode definition
self::$bbcode->addCodeDefinition(new $className);
}
}
/**
* Set the text to parse.
*
@ -115,12 +60,7 @@ class BBcode
*/
public static function text($text)
{
// Check if $bbcode is still null
if (!self::$bbcode) {
self::init();
}
self::$bbcode->parse($text);
return $text;
}
/**
@ -132,17 +72,19 @@ class BBcode
*/
public static function toHTML($text = null)
{
// Check if text isn't null
if ($text !== null) {
self::text($text);
}
// // Check if text isn't null
// if ($text !== null) {
// self::text($text);
// }
$parsed = nl2br(self::$bbcode->getAsHtml());
// $parsed = nl2br(self::$bbcode->getAsHtml());
$parsed = self::fixCodeTags($parsed);
$parsed = self::parseEmoticons($parsed);
// $parsed = self::fixCodeTags($parsed);
// $parsed = self::parseEmoticons($parsed);
return $parsed;
// return $parsed;
return $text;
}
/**
@ -154,12 +96,14 @@ class BBcode
*/
public static function toEditor($text = null)
{
// Check if text isn't null
if ($text !== null) {
self::text($text);
}
// // Check if text isn't null
// if ($text !== null) {
// self::text($text);
// }
return self::$bbcode->getAsBBCode();
// return self::$bbcode->getAsBBCode();
return $text;
}
/**
@ -171,16 +115,18 @@ class BBcode
*/
public static function toPlain($text = null)
{
// Check if text isn't null
if ($text !== null) {
self::text($text);
}
// // Check if text isn't null
// if ($text !== null) {
// self::text($text);
// }
return self::$bbcode->getAsText();
// return self::$bbcode->getAsText();
return $text;
}
/**
* Clean up the contents of <code> tags.
* See if this can be deprecated with a custom implementation!
*
* @param string $text Dirty
*

View file

@ -1,60 +0,0 @@
<?php
/**
* Holds the text alignment bbcode class.
*
* @package Sakura
*/
namespace Sakura\BBcodeDefinitions;
use JBBCode\CodeDefinition;
use JBBCode\ElementNode;
/**
* Text alignment bbcode for JBBCode
*
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class Align extends CodeDefinition
{
/**
* Constructor.
*/
public function __construct()
{
parent::__construct();
$this->setTagName("align");
$this->setUseOption(true);
}
/**
* Creates compiled HTML from the align bbcode.
*
* @param ElementNode $el The JBBCode element node.
*
* @return string Compiled HTML.
*/
public function asHtml(ElementNode $el)
{
$alignments = [
'left',
'center',
'right',
];
$content = "";
foreach ($el->getChildren() as $child) {
$content .= $child->getAsHTML();
}
$alignment = $el->getAttribute()['align'];
if (!in_array($alignment, $alignments)) {
return $el->getAsBBCode();
}
return "<div style='text-align: {$alignment};'>{$content}</div>";
}
}

View file

@ -1,47 +0,0 @@
<?php
/**
* Holds the code format bbcode class.
*
* @package Sakura
*/
namespace Sakura\BBcodeDefinitions;
use JBBCode\CodeDefinition;
use JBBCode\ElementNode;
/**
* Code bbcode for JBBCode
*
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class Code extends CodeDefinition
{
/**
* Constructor.
*/
public function __construct()
{
parent::__construct();
$this->setTagName("code");
}
/**
* Compiles the code bbcode to HTML.
*
* @param ElementNode $el The JBBCode element node.
*
* @return mixed The compiled HTML.
*/
public function asHtml(ElementNode $el)
{
$content = "";
foreach ($el->getChildren() as $child) {
$content .= $child->getAsBBCode();
}
return "<pre class='code'><code>{$content}</code></pre>";
}
}

View file

@ -1,65 +0,0 @@
<?php
/**
* Holds the list bbcode class.
*
* @package Sakura
*/
namespace Sakura\BBcodeDefinitions;
use JBBCode\CodeDefinition;
use JBBCode\ElementNode;
/**
* Implements a [list] code definition that provides the following syntax:
*
* [list]
* [*] first item
* [*] second item
* [*] third item
* [/list]
*
* @package Sakura
* @author Jackson Owens <jackson_owens@alumni.brown.edu>
*/
class Lists extends CodeDefinition
{
/**
* Constructor
*/
public function __construct()
{
$this->parseContent = true;
$this->useOption = false;
$this->setTagName('list');
$this->nestLimit = -1;
}
/**
* Compiles the list bbcode to HTML.
*
* @param ElementNode $el The JBBCode element node.
*
* @return string The compiled HTML list.
*/
public function asHtml(ElementNode $el)
{
$bodyHtml = '';
foreach ($el->getChildren() as $child) {
$bodyHtml .= $child->getAsHTML();
}
$listPieces = explode('[*]', $bodyHtml);
unset($listPieces[0]);
$listPieces = array_map(function ($li) {
return "<li>{$li}</li>";
}, $listPieces);
$list = implode('', $listPieces);
return "<ul>{$list}</ul>";
}
}

View file

@ -1,75 +0,0 @@
<?php
/**
* Holds the forum post quoting bbcode class.
*
* @package Sakura
*/
namespace Sakura\BBcodeDefinitions;
use JBBCode\CodeDefinition;
use JBBCode\ElementNode;
use Sakura\ActiveUser;
use Sakura\Forum\Forum;
use Sakura\Forum\Post;
use Sakura\Perms\Forum as ForumPerms;
use Sakura\Router;
/**
* Quote BBcode for JBBCode.
*
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class Quote extends CodeDefinition
{
/**
* Constructor.
*/
public function __construct()
{
parent::__construct();
$this->setTagName("quote");
$this->setUseOption(true);
$this->setParseContent(true);
}
/**
* Compiles the user bbcode to HTML
*
* @param ElementNode $el The JBBCode element node.
*
* @return string The compiled HTML.
*/
public function asHtml(ElementNode $el)
{
$attr = $el->getAttribute()['quote'];
if (substr($attr, 0, 1) === '#') {
$postId = substr($attr, 1);
$post = new Post($postId);
$forum = new Forum($post->forum);
if ($post->id !== 0
&& $forum->permission(ForumPerms::VIEW, ActiveUser::$user->id)) {
$postLink = Router::route('forums.post', $post->id);
$content = "<blockquote><div class='quotee'><a href='{$postLink}' style='color: inherit;'>"
. "<span style='color: {$post->poster->colour}'>"
. "{$post->poster->username}</span> wrote</a></div>"
. "<div class='quote'>{$post->parsed}</div></blockquote>";
return $content;
}
}
$content = "";
foreach ($el->getChildren() as $child) {
$content .= $child->getAsHTML();
}
return "<blockquote><div class='quotee'>{$attr} wrote</div>
<div class='quote'>{$content}</div></blockquote>";
}
}

View file

@ -1,59 +0,0 @@
<?php
/**
* Holds the font size bbcode class.
*
* @package Sakura
*/
namespace Sakura\BBcodeDefinitions;
use JBBCode\CodeDefinition;
use JBBCode\ElementNode;
/**
* Size BBcode for JBBCode.
*
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class Size extends CodeDefinition
{
/**
* Constructor.
*/
public function __construct()
{
parent::__construct();
$this->setTagName("size");
$this->setUseOption(true);
}
/**
* Compiles the size bbcode to HTML
*
* @param ElementNode $el The JBBCode element node.
*
* @return string The compiled HTML.
*/
public function asHtml(ElementNode $el)
{
$minSize = 0;
$maxSize = 200;
$content = "";
foreach ($el->getChildren() as $child) {
$content .= $child->getAsHTML();
}
$size = $el->getAttribute()['size'];
if ($size < $minSize || $size > $maxSize) {
return $el->getAsBBCode();
}
$size = $size / 100;
return "<span style='font-size: {$size}em;'>{$content}</span>";
}
}

View file

@ -1,55 +0,0 @@
<?php
/**
* Holds the username linking bbcode class.
*
* @package Sakura
*/
namespace Sakura\BBcodeDefinitions;
use JBBCode\CodeDefinition;
use JBBCode\ElementNode;
use Sakura\Router;
use Sakura\User as SakuraUser;
/**
* Username BBcode for JBBCode.
*
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class User extends CodeDefinition
{
/**
* Constructor.
*/
public function __construct()
{
parent::__construct();
$this->setTagName("user");
$this->setUseOption(false);
$this->setParseContent(false);
}
/**
* Compiles the user bbcode to HTML
*
* @param ElementNode $el The JBBCode element node.
*
* @return string The compiled HTML.
*/
public function asHtml(ElementNode $el)
{
$content = "";
foreach ($el->getChildren() as $child) {
$content .= clean_string($child->getAsText(), true);
}
$user = SakuraUser::construct($content);
$profile = Router::route('user.profile', $user->id);
return "<a class='default username' href='{$profile} style='color: {$user->colour};
text-shadow: 0 0 .3em {$user->colour}; font-weight: bold;'>{$user->username}</a>";
}
}

View file

@ -1,54 +0,0 @@
<?php
/**
* Holds the YouTube embed bbcode class.
*
* @package Sakura
*/
namespace Sakura\BBcodeDefinitions;
use JBBCode\CodeDefinition;
use JBBCode\ElementNode;
/**
* YouTube video embedding bbcode for JBBCode
*
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class YouTube extends CodeDefinition
{
/**
* Constructor.
*/
public function __construct()
{
parent::__construct();
$this->setTagName("youtube");
}
/**
* Compiles the YouTube bbcode to HTML
*
* @param ElementNode $el The JBBCode element node.
*
* @return string The compiled HTML.
*/
public function asHtml(ElementNode $el)
{
$content = "";
foreach ($el->getChildren() as $child) {
$content .= $child->getAsBBCode();
}
$foundMatch = preg_match('/^([A-z0-9=\-]+?)$/i', $content, $matches);
if (!$foundMatch) {
return $el->getAsBBCode();
} else {
return "<iframe width='640' height='390' src='https://www.youtube.com/embed/{$matches[1]}'
frameborder='0' allowfullscreen></iframe>";
}
}
}

View file

@ -7,8 +7,6 @@
namespace Sakura;
use Sakura\Hashing;
/**
* Used to generate and validate CSRF tokens.
*
@ -56,7 +54,7 @@ class CSRF
*/
public static function generate()
{
return bin2hex(\mcrypt_create_iv(self::RANDOM_SIZE, MCRYPT_DEV_URANDOM));
return bin2hex(random_bytes(self::RANDOM_SIZE));
}
/**
@ -77,7 +75,6 @@ class CSRF
return false;
}
// Use the slowEquals function from the hashing lib to validate
return Hashing::slowEquals($token, $_SESSION[$id]);
return hash_equals($token, $_SESSION[$id]);
}
}

View file

@ -25,15 +25,8 @@ class Application extends \CLIFramework\Application
*/
const VERSION = SAKURA_VERSION;
/**
* CLI initialiser
/*
* Enable command autoloading
*/
public function init()
{
// Execute the original init function
parent::init();
// Add commands with class reference because the autoloader is retarded
$this->command('serve', Command\ServeCommand::class);
}
protected $commandAutoloadEnabled = true;
}

View file

@ -0,0 +1,26 @@
<?php
/**
* Holds the migration repository installer command controller.
*
* @package Sakura
*/
namespace Sakura\Console\Command;
use CLIFramework\Command;
use Sakura\DB;
class DatabaseInstallCommand extends Command
{
public function brief()
{
return 'Create the migration repository';
}
public function execute()
{
$repository = DB::getMigrationRepository();
$repository->createRepository();
$this->getLogger()->writeln("Created the migration repository!");
}
}

View file

@ -0,0 +1,40 @@
<?php
/**
* Holds the migration command controller.
*
* @package Sakura
*/
namespace Sakura\Console\Command;
use CLIFramework\Command;
use Illuminate\Database\Migrations\Migrator;
use Illuminate\Filesystem\Filesystem;
use Sakura\DB;
class DatabaseMigrateCommand extends Command
{
const MIGRATIONS = "database/";
public function brief()
{
return 'Run the database migrations';
}
public function execute()
{
$repository = DB::getMigrationRepository();
$migrator = new Migrator($repository, $repository->getConnectionResolver(), new Filesystem);
if (!$migrator->repositoryExists()) {
$this->getLogger()->writeln("Run 'database-install' first!");
return;
}
$migrator->run(ROOT . self::MIGRATIONS);
foreach ($migrator->getNotes() as $note) {
$this->getLogger()->writeln($note);
}
}
}

View file

@ -8,7 +8,6 @@
namespace Sakura\Console\Command;
use CLIFramework\Command;
use Sakura\Config;
class ServeCommand extends Command
{

View file

@ -11,7 +11,6 @@ use Sakura\ActionCode;
use Sakura\ActiveUser;
use Sakura\Config;
use Sakura\DB;
use Sakura\Hashing;
use Sakura\Net;
use Sakura\Perms\Site;
use Sakura\Router;
@ -125,30 +124,20 @@ class AuthController extends Controller
return Template::render('global/information');
}
// Validate password
switch ($user->passwordAlgo) {
// Disabled
case 'disabled':
$this->touchRateLimit($user->id);
$message = 'Logging into this account is disabled.';
Template::vars(compact('message', 'redirect'));
if (strlen($user->password) < 1) {
$message = 'Your password expired.';
$redirect = Router::route('auth.resetpassword');
Template::vars(compact('message', 'redirect'));
return Template::render('global/information');
return Template::render('global/information');
}
// Default hashing method
default:
if (!Hashing::validatePassword($password, [
$user->passwordAlgo,
$user->passwordIter,
$user->passwordSalt,
$user->passwordHash,
])) {
$this->touchRateLimit($user->id);
$message = 'The password you entered was invalid.';
Template::vars(compact('message', 'redirect'));
if (!password_verify($password, $user->password)) {
$this->touchRateLimit($user->id);
$message = 'The password you entered was invalid.';
Template::vars(compact('message', 'redirect'));
return Template::render('global/information');
}
return Template::render('global/information');
}
// Check if the user has the required privs to log in
@ -564,16 +553,13 @@ class AuthController extends Controller
}
// Hash the password
$pw = Hashing::createHash($password);
$password = password_hash($password, PASSWORD_BCRYPT);
// Update the user
DB::table('users')
->where('user_id', $user->id)
->update([
'password_hash' => $pw[3],
'password_salt' => $pw[2],
'password_algo' => $pw[0],
'password_iter' => $pw[1],
'password' => $password,
'password_chan' => time(),
]);

View file

@ -45,7 +45,7 @@ class FileController extends Controller
*/
public function avatar($id = 0)
{
$noAvatar = ROOT . str_replace(
$noAvatar = ROOT . 'public/' . str_replace(
'%tplname%',
Template::$name,
config('user.avatar_none')
@ -56,18 +56,7 @@ class FileController extends Controller
'mime' => getimagesizefromstring($noAvatar)['mime'],
];
$deactivePath = ROOT . str_replace(
'%tplname%',
Template::$name,
config('user.avatar_inactive')
);
$deactive = [
'name' => basename($deactivePath),
'data' => file_get_contents($deactivePath),
'mime' => getimagesizefromstring($deactivePath)['mime'],
];
$bannedPath = ROOT . str_replace(
$bannedPath = ROOT . 'public/' . str_replace(
'%tplname%',
Template::$name,
config('user.avatar_ban')
@ -80,15 +69,11 @@ class FileController extends Controller
$user = User::construct($id);
if ($user->permission(Site::DEACTIVATED)) {
return $this->serve($deactive['data'], $deactive['mime'], $deactive['name']);
}
if ($user->permission(Site::RESTRICTED)) {
return $this->serve($banned['data'], $banned['mime'], $banned['name']);
}
if (!$user->avatar) {
if ($user->id < 1 || !$user->avatar || $user->permission(Site::DEACTIVATED)) {
return $this->serve($none['data'], $none['mime'], $none['name']);
}
@ -108,7 +93,7 @@ class FileController extends Controller
*/
public function background($id = 0)
{
$noBg = ROOT . "public/content/pixel.png";
$noBg = ROOT . "public/images/pixel.png";
$none = [
'name' => basename($noBg),
'data' => file_get_contents($noBg),
@ -143,7 +128,7 @@ class FileController extends Controller
*/
public function header($id = 0)
{
$noHeader = ROOT . "public/content/pixel.png";
$noHeader = ROOT . "public/images/pixel.png";
$none = [
'name' => basename($noHeader),
'data' => file_get_contents($noHeader),

View file

@ -10,7 +10,6 @@ namespace Sakura\Controllers\Settings;
use Sakura\ActiveUser;
use Sakura\Config;
use Sakura\DB;
use Sakura\Hashing;
use Sakura\Perms\Site;
use Sakura\Router;
use Sakura\Template;
@ -237,12 +236,7 @@ class AccountController extends Controller
}
// Check current password
if (!Hashing::validatePassword($current, [
ActiveUser::$user->passwordAlgo,
ActiveUser::$user->passwordIter,
ActiveUser::$user->passwordSalt,
ActiveUser::$user->passwordHash,
])) {
if (!password_verify($current, ActiveUser::$user->password)) {
$message = "Your password was invalid!";
Template::vars(compact('redirect', 'message'));
return Template::render('global/information');

View file

@ -9,7 +9,6 @@ namespace Sakura\Controllers\Settings;
use Sakura\ActiveUser;
use Sakura\DB;
use Sakura\Hashing;
use Sakura\Perms\Site;
use Sakura\Router;
use Sakura\Template;
@ -115,12 +114,7 @@ class AdvancedController extends Controller
}
// Check password
if (!Hashing::validatePassword($password, [
ActiveUser::$user->passwordAlgo,
ActiveUser::$user->passwordIter,
ActiveUser::$user->passwordSalt,
ActiveUser::$user->passwordHash,
])) {
if (!password_verify($password, ActiveUser::$user->password)) {
$message = "Your password was invalid!";
Template::vars(compact('redirect', 'message'));
return Template::render('global/information');

View file

@ -7,7 +7,9 @@
namespace Sakura;
use \Illuminate\Database\Capsule\Manager;
use Illuminate\Database\Capsule\Manager;
use Illuminate\Database\ConnectionResolver;
use Illuminate\Database\Migrations\DatabaseMigrationRepository;
/**
* The Illuminate (Laravel) database wrapper.
@ -17,5 +19,16 @@ use \Illuminate\Database\Capsule\Manager;
*/
class DB extends Manager
{
// This class solely exists as an alias
public static function getMigrationRepository()
{
$resolver = new ConnectionResolver(['database' => self::connection()]);
$repository = new DatabaseMigrationRepository($resolver, 'migrations');
$repository->setSource('database');
return $repository;
}
public static function getSchemaBuilder()
{
return self::connection()->getSchemaBuilder();
}
}

View file

@ -78,35 +78,35 @@ class Forum
*
* @var Post
*/
private $_firstPost = null;
private $firstPostCache = null;
/**
* A cached instance of the last post in this forum.
*
* @var Post
*/
private $_lastPost = null;
private $lastPostCache = null;
/**
* Cached instances of the subforums.
*
* @var array
*/
private $_forums = [];
private $forumsCache = [];
/**
* Cached instances of the threads in this forum.
*
* @var array
*/
private $_threads = [];
private $threadsCache = [];
/**
* The permission container.
*
* @var Perms
*/
private $_permissions;
private $permissionsCache;
/**
* Constructor.
@ -121,7 +121,7 @@ class Forum
->get();
// Create permissions object
$this->_permissions = new Perms(Perms::FORUM);
$this->permissionsCache = new Perms(Perms::FORUM);
// Populate the variables
if ($forumRow) {
@ -159,9 +159,9 @@ class Forum
}
// Bitwise OR it with the permissions for this forum
$perm = $perm | $this->_permissions->user($user, ['forum_id' => [$this->id, '=']]);
$perm = $perm | $this->permissionsCache->user($user, ['forum_id' => [$this->id, '=']]);
return $raw ? $perm : $this->_permissions->check($flag, $perm);
return $raw ? $perm : $this->permissionsCache->check($flag, $perm);
}
/**
@ -171,8 +171,8 @@ class Forum
*/
public function forums()
{
// Check if _forums is populated
if (!count($this->_forums)) {
// Check if forumsCache is populated
if (!count($this->forumsCache)) {
// Get all rows with the category id set to the forum id
$forumRows = DB::table('forums')
->where('forum_category', $this->id)
@ -187,9 +187,9 @@ class Forum
$forums[$forum->forum_id] = new Forum($forum->forum_id);
}
$this->_forums = $forums;
$this->forumsCache = $forums;
} else {
$forums = $this->_forums;
$forums = $this->forumsCache;
}
// Return the forum objects
@ -203,8 +203,8 @@ class Forum
*/
public function threads()
{
// Check if _threads is populated
if (!count($this->_threads)) {
// Check if threadsCache is populated
if (!count($this->threadsCache)) {
// Get all rows with the forum id for this forum
$threadRows = DB::table('topics')
->where('forum_id', $this->id)
@ -220,9 +220,9 @@ class Forum
$threads[$thread->topic_id] = new Thread($thread->topic_id);
}
$this->_threads = $threads;
$this->threadsCache = $threads;
} else {
$threads = $this->_threads;
$threads = $this->threadsCache;
}
// Return the thread objects
@ -236,8 +236,8 @@ class Forum
*/
public function firstPost()
{
// Check if _firstPost is set
if ($this->_firstPost === null) {
// Check if firstPostCache is set
if ($this->firstPostCache === null) {
// Get the row
$firstPost = DB::table('posts')
->where('forum_id', $this->id)
@ -249,12 +249,12 @@ class Forum
$post = new Post(empty($firstPost) ? 0 : $firstPost[0]->post_id);
// Assign it to a "cache" variable
$this->_firstPost = $post;
$this->firstPostCache = $post;
// Return the post object
return $post;
} else {
return $this->_firstPost;
return $this->firstPostCache;
}
}
@ -265,8 +265,8 @@ class Forum
*/
public function lastPost()
{
// Check if _lastPost is set
if ($this->_lastPost === null) {
// Check if lastPostCache is set
if ($this->lastPostCache === null) {
// Get the row
$lastPost = DB::table('posts')
->where('forum_id', $this->id)
@ -278,12 +278,12 @@ class Forum
$post = new Post(empty($lastPost) ? 0 : $lastPost[0]->post_id);
// Assign it to a "cache" variable
$this->_lastPost = $post;
$this->lastPostCache = $post;
// Return the post object
return $post;
} else {
return $this->_lastPost;
return $this->lastPostCache;
}
}

View file

@ -104,21 +104,21 @@ class Thread
*
* @var array
*/
private $_posts = [];
private $postsCache = [];
/**
* A cached instance of opening post.
*
* @var Post
*/
private $_firstPost = null;
private $firstPostCache = null;
/**
* A cached instance of the last reply.
*
* @var Post
*/
private $_lastPost = null;
private $lastPostCache = null;
/**
* Constructor.
@ -244,8 +244,8 @@ class Thread
*/
public function posts()
{
// Check if _posts is something
if (!count($this->_posts)) {
// Check if postsCache is something
if (!count($this->postsCache)) {
// Get all rows with the thread id
$postRows = DB::table('posts')
->where('topic_id', $this->id)
@ -259,9 +259,9 @@ class Thread
$posts[$post->post_id] = new Post($post->post_id);
}
$this->_posts = $posts;
$this->postsCache = $posts;
} else {
$posts = $this->_posts;
$posts = $this->postsCache;
}
// Return the post objects
@ -276,8 +276,8 @@ class Thread
public function firstPost()
{
// Check if the cache var is set
if ($this->_firstPost !== null) {
return $this->_firstPost;
if ($this->firstPostCache !== null) {
return $this->firstPostCache;
}
// Get the row from the database
@ -291,7 +291,7 @@ class Thread
$post = new Post($post ? $post[0]->post_id : 0);
// Assign it to the cache var
$this->_firstPost = $post;
$this->firstPostCache = $post;
// Return
return $post;
@ -305,8 +305,8 @@ class Thread
public function lastPost()
{
// Check if the cache var is set
if ($this->_lastPost !== null) {
return $this->_lastPost;
if ($this->lastPostCache !== null) {
return $this->lastPostCache;
}
// Get the row from the database
@ -320,7 +320,7 @@ class Thread
$post = new Post($post ? $post[0]->post_id : 0);
// Assign it to the cache var
$this->_lastPost = $post;
$this->lastPostCache = $post;
// Return
return $post;

View file

@ -1,226 +0,0 @@
<?php
/**
* Password Hashing With PBKDF2 (https://defuse.ca/php-pbkdf2.htm).
* Copyright (c) 2013, Taylor Hornby
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package Sakura
*/
namespace Sakura;
/**
* PBKDF2 password hashing implementation.
*
* @package Sakura
* @author Taylor Hornby <havoc@defuse.ca>
* @author Julian van de Groep <me@flash.moe>
*/
class Hashing
{
/**
* Hashing algorithm that should be used.
*
* @var string
*/
private static $hashAlgorithm = 'sha256';
/**
* Iterations.
*
* @var int
*/
private static $iterations = 1000;
/**
* The amount of bytes the salt should be.
*
* @var int
*/
private static $saltBytes = 24;
/**
* The amount of bytes the hash should be.
*
* @var int
*/
private static $hashBytes = 24;
/**
* Creates a hash.
*
* @param string $pass The password that should be hashed.
*
* @return array An array containing the algorithm, iterations, salt and hash.
*/
public static function createHash($pass)
{
$salt = base64_encode(
\mcrypt_create_iv(
self::$saltBytes,
MCRYPT_DEV_URANDOM
)
);
$hash = base64_encode(
self::pbkdf2(
self::$hashAlgorithm,
$pass,
$salt,
self::$iterations,
self::$hashBytes,
true
)
);
$passwordData = [
self::$hashAlgorithm,
self::$iterations,
$salt,
$hash,
];
return $passwordData;
}
/**
* Validate a password.
*
* @param string $password The password that is being validated.
* @param array $params The parametres in the order of algorithm, iterations, salt and hash.
*
* @return bool Correct?
*/
public static function validatePassword($password, $params)
{
if (count($params) < 4) {
return false;
}
$pbkdf2 = base64_decode($params[3]);
$validate = self::slowEquals(
$pbkdf2,
self::pbkdf2(
$params[0],
$password,
$params[2],
(int) $params[1],
strlen($pbkdf2),
true
)
);
return $validate;
}
/**
* Compares two strings $a and $b in length-constant time.
*
* @param string $a String A.
* @param string $b String B.
*
* @return bool Boolean indicating difference.
*/
public static function slowEquals($a, $b)
{
$diff = strlen($a) ^ strlen($b);
for ($i = 0; $i < strlen($a) && $i < strlen($b); $i++) {
$diff |= ord($a[$i]) ^ ord($b[$i]);
}
return $diff === 0;
}
/**
* PBKDF2 key derivation function as defined by RSA's PKCS #5: https://www.ietf.org/rfc/rfc2898.txt
*
* This implementation of PBKDF2 was originally created by https://defuse.ca
* With improvements by http://www.variations-of-shadow.com
*
* @param mixed $algorithm The hash algorithm to use. Recommended: SHA256.
* @param mixed $password The password.
* @param mixed $salt A salt that is unique to the password.
* @param mixed $count Iteration count. Higher is better, but slower. Recommended: At least 1000.
* @param mixed $key_length The length of the derived key in bytes.
* @param mixed $raw_output A $key_length-byte key derived from the password and salt.
*
* @return string The PBKDF2 derivation.
*/
private static function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false)
{
$algorithm = strtolower($algorithm);
if (!in_array($algorithm, hash_algos(), true)) {
trigger_error(
'PBKDF2 ERROR: Invalid hash algorithm.',
E_USER_ERROR
);
}
if ($count <= 0 || $key_length <= 0) {
trigger_error(
'PBKDF2 ERROR: Invalid parameters.',
E_USER_ERROR
);
}
if (function_exists('hash_pbkdf2')) {
// The output length is in NIBBLES (4-bits) if $raw_output is false!
if (!$raw_output) {
$key_length = $key_length * 2;
}
return hash_pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output);
}
$hash_length = strlen(hash($algorithm, '', true));
$block_count = ceil($key_length / $hash_length);
$output = '';
for ($i = 1; $i <= $block_count; $i++) {
// $i encoded as 4 bytes, big endian.
$last = $salt . pack('N', $i);
// First iteration
$last = $xorsum = hash_hmac($algorithm, $last, $password, true);
// Perform the other $count - 1 interations
for ($j = 1; $j < $count; $j++) {
$xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
}
$output .= $xorsum;
if ($raw_output) {
return substr($output, 0, $key_length);
}
return bin2hex(substr($output, 0, $key_length));
}
}
}

View file

@ -42,13 +42,6 @@ class Template
*/
public static $name;
/**
* The path to the client side resources
*
* @var string
*/
public static $resources;
/**
* The file extension used by template files
*/
@ -64,9 +57,6 @@ class Template
// Set variables
self::$name = $name;
// Set reources path
self::$resources = '/content/data/' . self::$name;
// Reinitialise
self::init();
}
@ -77,14 +67,14 @@ class Template
public static function init()
{
// Initialise Twig Filesystem Loader
$twigLoader = new Twig_Loader_Filesystem(ROOT . 'views/' . self::$name);
$twigLoader = new Twig_Loader_Filesystem(ROOT . 'resources/views/' . self::$name);
// Environment variable
$twigEnv = [];
// Enable caching
if (config("performance.template_cache")) {
$twigEnv['cache'] = ROOT . config("performance.cache_dir") . 'twig';
$twigEnv['cache'] = ROOT . config("performance.cache_dir") . 'views';
}
// And now actually initialise the templating engine
@ -94,18 +84,11 @@ class Template
self::$engine->addExtension(new Twig_Extension_StringLoader());
// Add route function
self::$engine->addFunction(new Twig_SimpleFunction('route', function ($name, $args = null) {
return Router::route($name, $args);
}));
self::$engine->addFunction(new Twig_SimpleFunction('route', 'route'));
// Add config function
self::$engine->addFunction(new Twig_SimpleFunction('config', 'config'));
// Add resource function
self::$engine->addFunction(new Twig_SimpleFunction('resource', function ($path = "") {
return self::$resources . "/{$path}";
}));
// Method of getting the currently active session id
self::$engine->addFunction(new Twig_SimpleFunction('session_id', 'session_id'));
@ -135,10 +118,6 @@ class Template
*/
public static function render($file)
{
try {
return self::$engine->render($file . self::FILE_EXT, self::$vars);
} catch (\Exception $e) {
return trigger_error($e->getMessage(), E_USER_ERROR);
}
return self::$engine->render($file . self::FILE_EXT, self::$vars);
}
}

View file

@ -219,7 +219,7 @@ class User
*
* @var array
*/
protected static $_userCache = [];
protected static $userCache = [];
/**
* Cached constructor.
@ -232,13 +232,13 @@ class User
public static function construct($uid, $forceRefresh = false)
{
// Check if a user object isn't present in cache
if ($forceRefresh || !array_key_exists($uid, self::$_userCache)) {
if ($forceRefresh || !array_key_exists($uid, self::$userCache)) {
// If not create a new object and cache it
self::$_userCache[$uid] = new User($uid);
self::$userCache[$uid] = new User($uid);
}
// Return the cached object
return self::$_userCache[$uid];
return self::$userCache[$uid];
}
/**
@ -256,17 +256,14 @@ class User
// Set a few variables
$usernameClean = clean_string($username, true);
$emailClean = clean_string($email, true);
$password = Hashing::createHash($password);
$password = password_hash($password, PASSWORD_BCRYPT);
// Insert the user into the database and get the id
$userId = DB::table('users')
->insertGetId([
'username' => $username,
'username_clean' => $usernameClean,
'password_hash' => $password[3],
'password_salt' => $password[2],
'password_algo' => $password[0],
'password_iter' => $password[1],
'password' => $password,
'email' => $emailClean,
'rank_main' => 0,
'register_ip' => Net::pton(Net::ip()),
@ -308,11 +305,7 @@ class User
$this->id = $userRow->user_id;
$this->username = $userRow->username;
$this->usernameClean = $userRow->username_clean;
$this->passwordHash = $userRow->password_hash;
$this->passwordSalt = $userRow->password_salt;
$this->passwordAlgo = $userRow->password_algo;
$this->passwordIter = $userRow->password_iter;
$this->passwordChan = $userRow->password_chan;
$this->password = $userRow->password;
$this->email = $userRow->email;
$this->mainRankId = $userRow->rank_main;
$this->colour = $userRow->user_colour;
@ -1015,62 +1008,6 @@ class User
return $return;
}
/**
* Get the open warnings on this user.
*
* @return array The warnings.
*/
public function getWarnings()
{
// Do the database query
$getWarnings = DB::table('warnings')
->where('user_id', $this->id)
->get();
// Storage array
$warnings = [];
// Add special stuff
foreach ($getWarnings as $warning) {
// Check if it hasn't expired
if ($warning->warning_expires < time()) {
DB::table('warnings')
->where('warning_id', $warning['warning_id'])
->delete();
continue;
}
// Text action
switch ($warning->warning_action) {
default:
case '0':
$warning->warning_action_text = 'Warning';
break;
case '1':
$warning->warning_action_text = 'Silence';
break;
case '2':
$warning->warning_action_text = 'Restriction';
break;
case '3':
$warning->warning_action_text = 'Ban';
break;
case '4':
$warning->warning_action_text = 'Abyss';
break;
}
// Text expiration
$warning->warning_length = round(($warning->warning_expires - $warning->warning_issued) / 60);
// Add to array
$warnings[$warning->warning_id] = $warning;
}
// Return all the warnings
return $warnings;
}
/**
* Parse the user's userpage.
*
@ -1155,16 +1092,13 @@ class User
public function setPassword($password)
{
// Create hash
$password = Hashing::createHash($password);
$password = password_hash($password, PASSWORD_BCRYPT);
// Update userrow
DB::table('users')
->where('user_id', $this->id)
->update([
'password_hash' => $password[3],
'password_salt' => $password[2],
'password_algo' => $password[0],
'password_iter' => $password[1],
'password' => $password,
'password_chan' => time(),
]);
}

View file

@ -12,13 +12,15 @@
"ext-curl": "*",
"ext-json": "*",
"twig/twig": "*",
"phpmailer/phpmailer": "*",
"paypal/rest-api-sdk-php": "*",
"jbbcode/jbbcode": "*",
"corneltek/cliframework": "*",
"phroute/phroute": "^2.1",
"illuminate/database": "5.2.*",
"doctrine/dbal": "~2.4"
"doctrine/dbal": "~2.4",
"golonka/bbcodeparser": "^2.2",
"nesbot/carbon": "^1.21",
"swiftmailer/swiftmailer": "^5.4",
"corneltek/cliframework": "^3.0",
"illuminate/filesystem": "^5.2"
},
"autoload": {
"psr-4": {

263
composer.lock generated
View file

@ -4,8 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "765ea465939a65048ac736e4fbc6c167",
"content-hash": "1af681873ad63e53d42dfd445d67b388",
"hash": "1f0887a5e8183bc1ca24e7c50e054ba1",
"content-hash": "271f4f1bcfb9fdc9446336132938b762",
"packages": [
{
"name": "corneltek/class-template",
@ -725,6 +725,56 @@
],
"time": "2014-09-09 13:34:57"
},
{
"name": "golonka/bbcodeparser",
"version": "v2.2.2",
"source": {
"type": "git",
"url": "https://github.com/golonka/BBCodeParser.git",
"reference": "769c4ebe6207ffa20298b84b90eafca87ce2fb95"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/golonka/BBCodeParser/zipball/769c4ebe6207ffa20298b84b90eafca87ce2fb95",
"reference": "769c4ebe6207ffa20298b84b90eafca87ce2fb95",
"shasum": ""
},
"require": {
"php": ">=5.4.0"
},
"require-dev": {
"phpunit/phpunit": "~4",
"squizlabs/php_codesniffer": "~2"
},
"type": "library",
"autoload": {
"psr-4": {
"Golonka\\BBCode\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Joseph Landberg",
"email": "joseph.landberg@gmail.com",
"homepage": "http://github.com/golonka/"
}
],
"description": "Parse your BBCode easy with this library.",
"homepage": "http://github.com/golonka/bbcodeparser",
"keywords": [
"PSR-1",
"PSR-2",
"PSR-4",
"bbcode",
"laravel",
"parser"
],
"time": "2016-03-03 09:56:19"
},
{
"name": "illuminate/container",
"version": "v5.2.37",
@ -870,6 +920,56 @@
],
"time": "2016-06-06 13:12:46"
},
{
"name": "illuminate/filesystem",
"version": "v5.2.37",
"source": {
"type": "git",
"url": "https://github.com/illuminate/filesystem.git",
"reference": "e9c3ba4fce5853f559f805a5626b18517a55b8b3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/illuminate/filesystem/zipball/e9c3ba4fce5853f559f805a5626b18517a55b8b3",
"reference": "e9c3ba4fce5853f559f805a5626b18517a55b8b3",
"shasum": ""
},
"require": {
"illuminate/contracts": "5.2.*",
"illuminate/support": "5.2.*",
"php": ">=5.5.9",
"symfony/finder": "2.8.*|3.0.*"
},
"suggest": {
"league/flysystem": "Required to use the Flysystem local and FTP drivers (~1.0).",
"league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (~1.0).",
"league/flysystem-rackspace": "Required to use the Flysystem Rackspace driver (~1.0)."
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "5.2-dev"
}
},
"autoload": {
"psr-4": {
"Illuminate\\Filesystem\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Taylor Otwell",
"email": "taylorotwell@gmail.com"
}
],
"description": "The Illuminate Filesystem package.",
"homepage": "http://laravel.com",
"time": "2016-05-31 15:08:27"
},
{
"name": "illuminate/support",
"version": "v5.2.37",
@ -926,52 +1026,6 @@
"homepage": "http://laravel.com",
"time": "2016-05-30 02:40:53"
},
{
"name": "jbbcode/jbbcode",
"version": "v1.3.0",
"source": {
"type": "git",
"url": "https://github.com/jbowens/jBBCode.git",
"reference": "645b6a1c0afa92b7d029d3417ebd8b60a5c578b3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/jbowens/jBBCode/zipball/645b6a1c0afa92b7d029d3417ebd8b60a5c578b3",
"reference": "645b6a1c0afa92b7d029d3417ebd8b60a5c578b3",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": "3.7.*"
},
"type": "library",
"autoload": {
"psr-0": {
"JBBCode": "."
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jackson Owens",
"email": "jackson_owens@alumni.brown.edu",
"homepage": "http://jbowens.org/",
"role": "Developer"
}
],
"description": "A lightweight but extensible BBCode parser written in PHP 5.3.",
"homepage": "http://jbbcode.com/",
"keywords": [
"BB",
"bbcode"
],
"time": "2014-07-06 05:48:20"
},
{
"name": "nesbot/carbon",
"version": "1.21.0",
@ -1116,66 +1170,6 @@
],
"time": "2016-07-15 20:42:18"
},
{
"name": "phpmailer/phpmailer",
"version": "v5.2.16",
"source": {
"type": "git",
"url": "https://github.com/PHPMailer/PHPMailer.git",
"reference": "1d85f9ef3ecfc42bbc4f3c70d5e37ca9a65f629a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/1d85f9ef3ecfc42bbc4f3c70d5e37ca9a65f629a",
"reference": "1d85f9ef3ecfc42bbc4f3c70d5e37ca9a65f629a",
"shasum": ""
},
"require": {
"php": ">=5.0.0"
},
"require-dev": {
"phpdocumentor/phpdocumentor": "*",
"phpunit/phpunit": "4.7.*"
},
"suggest": {
"league/oauth2-google": "Needed for Google XOAUTH2 authentication"
},
"type": "library",
"autoload": {
"classmap": [
"class.phpmailer.php",
"class.phpmaileroauth.php",
"class.phpmaileroauthgoogle.php",
"class.smtp.php",
"class.pop3.php",
"extras/EasyPeasyICS.php",
"extras/ntlm_sasl_client.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-2.1"
],
"authors": [
{
"name": "Jim Jagielski",
"email": "jimjag@gmail.com"
},
{
"name": "Marcus Bointon",
"email": "phpmailer@synchromedia.co.uk"
},
{
"name": "Andy Prevost",
"email": "codeworxtech@users.sourceforge.net"
},
{
"name": "Brent R. Matzelle"
}
],
"description": "PHPMailer is a full-featured email creation and transfer class for PHP",
"time": "2016-06-06 09:09:37"
},
{
"name": "phroute/phroute",
"version": "v2.1.0",
@ -1304,6 +1298,59 @@
],
"time": "2012-12-21 11:40:51"
},
{
"name": "swiftmailer/swiftmailer",
"version": "v5.4.3",
"source": {
"type": "git",
"url": "https://github.com/swiftmailer/swiftmailer.git",
"reference": "4cc92842069c2bbc1f28daaaf1d2576ec4dfe153"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/4cc92842069c2bbc1f28daaaf1d2576ec4dfe153",
"reference": "4cc92842069c2bbc1f28daaaf1d2576ec4dfe153",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"mockery/mockery": "~0.9.1"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "5.4-dev"
}
},
"autoload": {
"files": [
"lib/swift_required.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Chris Corbyn"
},
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"description": "Swiftmailer, free feature-rich PHP mailer",
"homepage": "http://swiftmailer.org",
"keywords": [
"email",
"mail",
"mailer"
],
"time": "2016-07-08 11:51:25"
},
{
"name": "symfony/class-loader",
"version": "v3.0.0",

View file

@ -1,38 +1,41 @@
; Database configuration according to https://laravel.com/docs/5.2/database#introduction
; Put some here in advance, uncomment the one you need.
[database]
;; mysql
;driver = mysql
;host = localhost
;port = 3306
;username = sakura
;password = password
;prefix = sakura_
;database = sakura-development
;charset = utf8
;collation = utf8_unicode_ci
; ; mysql
; driver = mysql
; host = localhost
; port = 3306
; username = sakura
; password = password
; prefix = sakura_
; database = sakura-development
; charset = utf8
; collation = utf8_unicode_ci
;; sqlite
;driver = sqlite
;database = sakura.sq3
;prefix = sakura_
; ; sqlite
; driver = sqlite
; database = sakura.sq3
; prefix = sakura_
;; postgres
;driver = pgsql
;host = localhost
;port = 5432
;username = sakura
;password = password
;prefix = sakura_
;database = sakura-development
;charset = utf8
;schema = public
; ; postgres
; driver = pgsql
; host = localhost
; port = 5432
; username = sakura
; password = password
; prefix = sakura_
; database = sakura-development
; charset = utf8
; schema = public
; General site settings
[general]
; Name of the site
name = Sakura
; Logo of the site
logo =
; Description of the site
description = Test site
@ -48,6 +51,9 @@ cover =
; Close the site for maintenance
maintenance = false
; URL of the sakurako chat (full path) without trailing slash
chat = http://chat.localghost
; Cookie settings
[cookie]
prefix = sakura_
@ -90,7 +96,7 @@ username =
password =
server =
port = 25
secure = true
secure = tls
; File settings
[file]
@ -116,10 +122,9 @@ max_width = 2048
; User settings
[user]
; Avatars
avatar_ban = public/content/data/%tplname%/images/banned-av.png
avatar_none = public/content/data/%tplname%/images/no-av.png
avatar_inactive = public/content/data/%tplname%/images/deactivated-av.png
; Avatars (relative to public/)
avatar_ban = images/%tplname%-ban.png
avatar_none = images/%tplname%-none.png
; Username constraints
name_min = 3

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,12 +1,9 @@
var
elixir = require('laravel-elixir'),
elixirTypscript = require('elixir-typescript'),
nodePath = '../../node_modules/';
elixir.config.assetsPath = './assets';
nodePath = '../../../node_modules/';
elixir(function(mix) {
mix
.less('aitemu/master.less', 'public/css/aitemu.css')
.less('yuuno/master.less', 'public/css/yuuno.css')
@ -14,6 +11,6 @@ elixir(function(mix) {
.typescript('aitemu/**/*.ts', 'public/js/aitemu.js')
.typescript('yuuno/**/*.ts', 'public/js/yuuno.js')
.scripts([
nodePath + 'turbolinks/dist/turbolinks.js'
nodePath + 'turbolinks/dist/turbolinks.js',
], 'public/js/libraries.js');
});

22
mahou
View file

@ -13,23 +13,15 @@ namespace Sakura;
use Sakura\Console\Application;
use GetOptionKit\Exception\InvalidOptionException;
// Define that this page won't require templating
define('SAKURA_NO_TPL', true);
// Include components
require_once 'sakura.php';
// Check if we're using console
if (php_sapi_name() === 'cli') {
// Create an instance
$console = new Application;
// Create an instance
$console = new Application;
// Attempt to run
try {
$console->run($argv);
} catch (InvalidOptionException $e) {
die($e->getMessage());
}
} else {
echo 'Why would you even try to run a console app through a browser?';
// Attempt to run
try {
$console->run($argv);
} catch (InvalidOptionException $e) {
die($e->getMessage());
}

View file

@ -5,9 +5,9 @@
"dev": "gulp watch"
},
"dependencies": {
"elixir-typescript": "^2.0.0",
"gulp": "^3.9.1",
"laravel-elixir": "^5.0.0",
"elixir-typescript": "^2.0.0",
"turbolinks": "^5.0.0"
}
}

View file

@ -1,103 +0,0 @@
html,
body {
min-height: 100%;
width: 90;
}
html {
background: url('/content/images/satori-error.png') top right no-repeat #FFF;
font-family: 'verdana', sans-serif;
font-size: .8em;
}
body {
margin: 0 2em;
}
#wrap {
max-width: 34em;
}
h1,
h2,
h3,
p {
margin: 0;
padding: 0;
font-size: 1em;
font-weight: normal;
}
h1 {
font-size: 1.5em;
margin: 1.33em 0;
}
h1 img {
margin: 0 .5em -.75em 0;
}
p {
padding: 0;
margin: 2em 0;
line-height: 1.33em;
}
hr {
margin: 1.9em 0;
background: #BBB;
border: none;
}
ul {
padding: .75em 0 0 0;
}
li {
margin: 0 0 .8em 3.46em;
line-height: 1.32em;
}
a {
color: red;
}
img+a:before {
content: " ";
}
h3 {
margin: 2.5em 0;
}
li:nth-child(3) img {
margin: -0.2em 0;
}
li:nth-child(4) img {
margin: -0.5em 0;
}
table {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
opacity: 0;
display: none;
}
table,
tr,
td {
background: rgba(0, 0, 0, .2);
height: 100%;
width: 100%;
text-align: center;
}
table img {
border-radius: 32px;
box-shadow: 0 4px 32px #888;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 223 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 398 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 496 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 479 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 649 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 652 B

File diff suppressed because one or more lines are too long

View file

@ -1,83 +0,0 @@
/*
Monokai Sublime style. Derived from Monokai by noformnocontent http://nn.mit-license.org/
*/
.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
background: #23241f;
}
.hljs,
.hljs-tag,
.hljs-subst {
color: #f8f8f2;
}
.hljs-strong,
.hljs-emphasis {
color: #a8a8a2;
}
.hljs-bullet,
.hljs-quote,
.hljs-number,
.hljs-regexp,
.hljs-literal,
.hljs-link {
color: #ae81ff;
}
.hljs-code,
.hljs-title,
.hljs-section,
.hljs-selector-class {
color: #a6e22e;
}
.hljs-strong {
font-weight: bold;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-keyword,
.hljs-selector-tag,
.hljs-name,
.hljs-attr {
color: #f92672;
}
.hljs-symbol,
.hljs-attribute {
color: #66d9ef;
}
.hljs-params,
.hljs-class .hljs-title {
color: #f8f8f2;
}
.hljs-string,
.hljs-type,
.hljs-built_in,
.hljs-builtin-name,
.hljs-selector-id,
.hljs-selector-attr,
.hljs-selector-pseudo,
.hljs-addition,
.hljs-variable,
.hljs-template-variable {
color: #e6db74;
}
.hljs-comment,
.hljs-deletion,
.hljs-meta {
color: #75715e;
}

File diff suppressed because one or more lines are too long

View file

@ -1,273 +0,0 @@
/*
* Shared client side code
*/
// Meta functions
var Sakura = (function () {
function Sakura() {
}
// Get or set a cookie value
Sakura.cookie = function (name, value) {
if (value === void 0) { value = null; }
// If value is null only get the cookie's value
if (value) {
// Delete the old instance
document.cookie = this.cookiePrefix + name + '=;expires=Thu, 01 Jan 1970 00:00:01 GMT; path=' + this.cookiePath;
// Assign the cookie
document.cookie = this.cookiePrefix + name + '=' + value + '; path=' + this.cookiePath;
// Pass the value through
return value;
}
else {
// Perform a regex on document.cookie
var get = new RegExp('(^|; )' + encodeURIComponent(this.cookiePrefix + name) + '=([^;]*)').exec(document.cookie);
// If anything was returned return it (professional phrasing)
return get ? get[2] : '';
}
};
// Unix timestamp
Sakura.epoch = function () {
return Math.floor(Date.now() / 1000);
};
// Toggle a class
Sakura.toggleClass = function (element, name) {
// Check if the class already exists and if not add it
if (element.className.indexOf(name) < 0) {
element.className += ' ' + name;
}
else {
element.className = element.className.replace(name, '').trim();
}
};
// Remove every element with a specific class name
Sakura.removeByClass = function (name) {
// Get the elements
var objs = document.getElementsByClassName(name);
// Use a while loop to remove each element
while (objs.length > 0) {
objs[0].parentNode.removeChild(objs[0]);
}
};
// Remove a single element with a specific id
Sakura.removeById = function (id) {
// Get the element
var obj = document.getElementById(id);
// If the element exists use the parent node to remove it
if (typeof (obj) != "undefined" && obj !== null) {
obj.parentNode.removeChild(obj);
}
};
// Alternative for Math.log2() since it's still experimental
Sakura.log2 = function (num) {
return Math.log(num) / Math.log(2);
};
// Get the number of unique characters in a string
Sakura.unique = function (string) {
// Store the already found character
var used = [];
// The amount of characters we've already found
var count = 0;
// Count the amount of unique characters
for (var i = 0; i < string.length; i++) {
// Check if we already counted this character
if (used.indexOf(string[i]) == -1) {
// Push the character into the used array
used.push(string[i]);
// Up the count
count++;
}
}
// Return the count
return count;
};
// Calculate password entropy
Sakura.entropy = function (string) {
// Decode utf-8 encoded characters
string = utf8.decode(string);
// Count the unique characters in the string
var unique = this.unique(string);
// Do the entropy calculation
return unique * this.log2(256);
};
// Validate string lengths
Sakura.stringLength = function (string, minimum, maximum) {
// Get length of string
var length = string.length;
// Check if it meets the minimum/maximum
if (length < minimum || length > maximum) {
return false;
}
// If it passes both return true
return true;
};
// Validate email address formats
Sakura.validateEmail = function (email) {
// RFC compliant e-mail address regex
var re = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,48})+$/;
// Test it on the email var which'll return a boolean
return re.test(email);
};
// Calculate the time that has elapsed since a certain data (doesn't take leap years in account).
Sakura.timeElapsed = function (timestamp, append, none) {
if (append === void 0) { append = ' ago'; }
if (none === void 0) { none = 'Just now'; }
// Subtract the entered timestamp from the current timestamp
var time = this.epoch() - timestamp;
// If the new timestamp is below 1 return a standard string
if (time < 1) {
return none;
}
// Times array
var times = {
31536000: ['year', 'a'],
2592000: ['month', 'a'],
604800: ['week', 'a'],
86400: ['day', 'a'],
3600: ['hour', 'an'],
60: ['minute', 'a'],
1: ['second', 'a']
};
//
var timeKeys = Object.keys(times).reverse();
// Iterate over the times
for (var i in timeKeys) {
// Do a devision to check if the given timestamp fits in the current "type"
var calc = time / parseInt(timeKeys[i]);
// Check if we have a match
if (calc >= 1) {
// Round the number
var display = Math.floor(calc);
// Return the formatted string
return (display === 1 ? times[timeKeys[i]][1] : display) + " " + times[timeKeys[i]][0] + (display === 1 ? '' : 's') + append;
}
}
// If everything fails just return none
return none;
};
Sakura.cookiePrefix = ""; // Cookie prefix, gets prepended to cookie names
Sakura.cookiePath = "/"; // Cookie path, can in most cases be left untouched
return Sakura;
})();
// UTF-8 functions
var utf8 = (function () {
function utf8() {
}
// Encode a utf-8 string
utf8.encode = function (string) {
return unescape(encodeURIComponent(string));
};
// Decode a utf-8 string
utf8.decode = function (string) {
return decodeURIComponent(escape(string));
};
return utf8;
})();
// HTTP methods
var HTTPMethods;
(function (HTTPMethods) {
HTTPMethods[HTTPMethods["GET"] = 0] = "GET";
HTTPMethods[HTTPMethods["HEAD"] = 1] = "HEAD";
HTTPMethods[HTTPMethods["POST"] = 2] = "POST";
HTTPMethods[HTTPMethods["PUT"] = 3] = "PUT";
HTTPMethods[HTTPMethods["DELETE"] = 4] = "DELETE";
})(HTTPMethods || (HTTPMethods = {}));
// AJAX functions
var AJAX = (function () {
// Prepares the XMLHttpRequest and stuff
function AJAX() {
this.send = null;
this.request = new XMLHttpRequest();
this.callbacks = new Object();
this.headers = new Object();
}
// Start
AJAX.prototype.start = function (method) {
var _this = this;
// Open the connection
this.request.open(HTTPMethods[method], this.url, true);
// Set headers
this.prepareHeaders();
// Watch the ready state
this.request.onreadystatechange = function () {
// Only invoke when complete
if (_this.request.readyState === 4) {
// Check if a callback if present
if ((typeof _this.callbacks[_this.request.status]).toLowerCase() === 'function') {
_this.callbacks[_this.request.status]();
}
else {
if ((typeof _this.callbacks['0']).toLowerCase() === 'function') {
// Call that
_this.callbacks['0']();
}
}
}
};
this.request.send(this.send);
};
// Stop
AJAX.prototype.stop = function () {
this.request = null;
};
// Add post data
AJAX.prototype.setSend = function (data) {
// Storage array
var store = new Array();
// Iterate over the object and them in the array with an equals sign inbetween
for (var item in data) {
store.push(encodeURIComponent(item) + "=" + encodeURIComponent(data[item]));
}
// Assign to send
this.send = store.join('&');
};
// Set raw post
AJAX.prototype.setRawSend = function (data) {
this.send = data;
};
// Get response
AJAX.prototype.response = function () {
return this.request.responseText;
};
// Set charset
AJAX.prototype.contentType = function (type, charset) {
if (charset === void 0) { charset = null; }
this.addHeader('Content-Type', type + ';charset=' + (charset ? charset : 'utf-8'));
};
// Add a header
AJAX.prototype.addHeader = function (name, value) {
// Attempt to remove a previous instance
this.removeHeader(name);
// Add the new header
this.headers[name] = value;
};
// Remove a header
AJAX.prototype.removeHeader = function (name) {
if ((typeof this.headers[name]).toLowerCase() !== 'undefined') {
delete this.headers[name];
}
};
// Prepare request headers
AJAX.prototype.prepareHeaders = function () {
for (var header in this.headers) {
this.request.setRequestHeader(header, this.headers[header]);
}
};
// Adds a callback
AJAX.prototype.addCallback = function (status, callback) {
// Attempt to remove previous instances
this.removeCallback(status);
// Add the new callback
this.callbacks[status] = callback;
};
// Delete a callback
AJAX.prototype.removeCallback = function (status) {
// Delete the callback if present
if ((typeof this.callbacks[status]).toLowerCase() === 'function') {
delete this.callbacks[status];
}
};
// Sets the URL
AJAX.prototype.setUrl = function (url) {
this.url = url;
};
return AJAX;
})();

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

107
public/error.css Normal file
View file

@ -0,0 +1,107 @@
/*
* i'll just throw this here for now since i haven't properly figured out something yet
*/
html,
body {
min-height: 100%;
width: 90;
}
html {
background: url('/images/satori-error.png') top right no-repeat #FFF;
font-family: 'verdana', sans-serif;
font-size: .8em;
}
body {
margin: 0 2em;
}
#wrap {
max-width: 34em;
}
h1,
h2,
h3,
p {
margin: 0;
padding: 0;
font-size: 1em;
font-weight: normal;
}
h1 {
font-size: 1.5em;
margin: 1.33em 0;
}
h1 img {
margin: 0 .5em -.75em 0;
}
p {
padding: 0;
margin: 2em 0;
line-height: 1.33em;
}
hr {
margin: 1.9em 0;
background: #BBB;
border: none;
}
ul {
padding: .75em 0 0 0;
}
li {
margin: 0 0 .8em 3.46em;
line-height: 1.32em;
}
a {
color: red;
}
img+a:before {
content: " ";
}
h3 {
margin: 2.5em 0;
}
li:nth-child(3) img {
margin: -0.2em 0;
}
li:nth-child(4) img {
margin: -0.5em 0;
}
table {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
opacity: 0;
display: none;
}
table,
tr,
td {
background: rgba(0, 0, 0, .2);
height: 100%;
width: 100%;
text-align: center;
}
table img {
border-radius: 32px;
box-shadow: 0 4px 32px #888;
}

View file

Before

Width:  |  Height:  |  Size: 87 B

After

Width:  |  Height:  |  Size: 87 B

View file

Before

Width:  |  Height:  |  Size: 260 B

After

Width:  |  Height:  |  Size: 260 B

View file

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View file

Before

Width:  |  Height:  |  Size: 643 B

After

Width:  |  Height:  |  Size: 643 B

View file

Before

Width:  |  Height:  |  Size: 408 B

After

Width:  |  Height:  |  Size: 408 B

View file

Before

Width:  |  Height:  |  Size: 604 B

After

Width:  |  Height:  |  Size: 604 B

View file

Before

Width:  |  Height:  |  Size: 591 B

After

Width:  |  Height:  |  Size: 591 B

View file

Before

Width:  |  Height:  |  Size: 643 B

After

Width:  |  Height:  |  Size: 643 B

View file

Before

Width:  |  Height:  |  Size: 600 B

After

Width:  |  Height:  |  Size: 600 B

View file

Before

Width:  |  Height:  |  Size: 497 B

After

Width:  |  Height:  |  Size: 497 B

View file

Before

Width:  |  Height:  |  Size: 488 B

After

Width:  |  Height:  |  Size: 488 B

View file

Before

Width:  |  Height:  |  Size: 428 B

After

Width:  |  Height:  |  Size: 428 B

View file

Before

Width:  |  Height:  |  Size: 506 B

After

Width:  |  Height:  |  Size: 506 B

View file

Before

Width:  |  Height:  |  Size: 647 B

After

Width:  |  Height:  |  Size: 647 B

View file

Before

Width:  |  Height:  |  Size: 403 B

After

Width:  |  Height:  |  Size: 403 B

View file

Before

Width:  |  Height:  |  Size: 673 B

After

Width:  |  Height:  |  Size: 673 B

View file

Before

Width:  |  Height:  |  Size: 524 B

After

Width:  |  Height:  |  Size: 524 B

View file

Before

Width:  |  Height:  |  Size: 663 B

After

Width:  |  Height:  |  Size: 663 B

View file

Before

Width:  |  Height:  |  Size: 589 B

After

Width:  |  Height:  |  Size: 589 B

View file

Before

Width:  |  Height:  |  Size: 593 B

After

Width:  |  Height:  |  Size: 593 B

View file

Before

Width:  |  Height:  |  Size: 585 B

After

Width:  |  Height:  |  Size: 585 B

View file

Before

Width:  |  Height:  |  Size: 504 B

After

Width:  |  Height:  |  Size: 504 B

View file

Before

Width:  |  Height:  |  Size: 449 B

After

Width:  |  Height:  |  Size: 449 B

View file

Before

Width:  |  Height:  |  Size: 497 B

After

Width:  |  Height:  |  Size: 497 B

View file

Before

Width:  |  Height:  |  Size: 462 B

After

Width:  |  Height:  |  Size: 462 B

View file

Before

Width:  |  Height:  |  Size: 457 B

After

Width:  |  Height:  |  Size: 457 B

View file

Before

Width:  |  Height:  |  Size: 675 B

After

Width:  |  Height:  |  Size: 675 B

View file

Before

Width:  |  Height:  |  Size: 486 B

After

Width:  |  Height:  |  Size: 486 B

View file

Before

Width:  |  Height:  |  Size: 611 B

After

Width:  |  Height:  |  Size: 611 B

View file

Before

Width:  |  Height:  |  Size: 639 B

After

Width:  |  Height:  |  Size: 639 B

View file

Before

Width:  |  Height:  |  Size: 500 B

After

Width:  |  Height:  |  Size: 500 B

View file

Before

Width:  |  Height:  |  Size: 593 B

After

Width:  |  Height:  |  Size: 593 B

View file

Before

Width:  |  Height:  |  Size: 526 B

After

Width:  |  Height:  |  Size: 526 B

View file

Before

Width:  |  Height:  |  Size: 631 B

After

Width:  |  Height:  |  Size: 631 B

View file

Before

Width:  |  Height:  |  Size: 512 B

After

Width:  |  Height:  |  Size: 512 B

View file

Before

Width:  |  Height:  |  Size: 443 B

After

Width:  |  Height:  |  Size: 443 B

View file

Before

Width:  |  Height:  |  Size: 514 B

After

Width:  |  Height:  |  Size: 514 B

View file

Before

Width:  |  Height:  |  Size: 600 B

After

Width:  |  Height:  |  Size: 600 B

View file

Before

Width:  |  Height:  |  Size: 628 B

After

Width:  |  Height:  |  Size: 628 B

View file

Before

Width:  |  Height:  |  Size: 625 B

After

Width:  |  Height:  |  Size: 625 B

View file

Before

Width:  |  Height:  |  Size: 528 B

After

Width:  |  Height:  |  Size: 528 B

View file

Before

Width:  |  Height:  |  Size: 614 B

After

Width:  |  Height:  |  Size: 614 B

View file

Before

Width:  |  Height:  |  Size: 521 B

After

Width:  |  Height:  |  Size: 521 B

View file

Before

Width:  |  Height:  |  Size: 367 B

After

Width:  |  Height:  |  Size: 367 B

Some files were not shown because too many files have changed in this diff Show more