migration and the software itself works!
114
app/BBcode.php
|
@ -7,12 +7,8 @@
|
||||||
|
|
||||||
namespace Sakura;
|
namespace Sakura;
|
||||||
|
|
||||||
use JBBCode\CodeDefinitionBuilder;
|
|
||||||
use JBBCode\DefaultCodeDefinitionSet;
|
|
||||||
use JBBCode\Parser;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sakura wrapper for JBBCode.
|
* BBcode handler.
|
||||||
*
|
*
|
||||||
* @package Sakura
|
* @package Sakura
|
||||||
* @author Julian van de Groep <me@flash.moe>
|
* @author Julian van de Groep <me@flash.moe>
|
||||||
|
@ -20,22 +16,17 @@ use JBBCode\Parser;
|
||||||
class BBcode
|
class BBcode
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The container for JBBCode.
|
* BBcodes, also for backwards compatibility.
|
||||||
*
|
*
|
||||||
* @var Parser
|
* @var array
|
||||||
*/
|
*/
|
||||||
private static $bbcode = null;
|
protected static $bbcodes = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialiser.
|
* Initialiser.
|
||||||
*/
|
*/
|
||||||
public static function init()
|
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
|
// Parse all emoticons
|
||||||
foreach ($emotes as $emote) {
|
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, '#');
|
$icon = preg_quote($emote->emote_string, '#');
|
||||||
$text = preg_replace("#$icon#", $image, $text);
|
$text = preg_replace("#$icon#", $image, $text);
|
||||||
}
|
}
|
||||||
|
@ -62,52 +53,6 @@ class BBcode
|
||||||
return $text;
|
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.
|
* Set the text to parse.
|
||||||
*
|
*
|
||||||
|
@ -115,12 +60,7 @@ class BBcode
|
||||||
*/
|
*/
|
||||||
public static function text($text)
|
public static function text($text)
|
||||||
{
|
{
|
||||||
// Check if $bbcode is still null
|
return $text;
|
||||||
if (!self::$bbcode) {
|
|
||||||
self::init();
|
|
||||||
}
|
|
||||||
|
|
||||||
self::$bbcode->parse($text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -132,17 +72,19 @@ class BBcode
|
||||||
*/
|
*/
|
||||||
public static function toHTML($text = null)
|
public static function toHTML($text = null)
|
||||||
{
|
{
|
||||||
// Check if text isn't null
|
// // Check if text isn't null
|
||||||
if ($text !== null) {
|
// if ($text !== null) {
|
||||||
self::text($text);
|
// self::text($text);
|
||||||
}
|
// }
|
||||||
|
|
||||||
$parsed = nl2br(self::$bbcode->getAsHtml());
|
// $parsed = nl2br(self::$bbcode->getAsHtml());
|
||||||
|
|
||||||
$parsed = self::fixCodeTags($parsed);
|
// $parsed = self::fixCodeTags($parsed);
|
||||||
$parsed = self::parseEmoticons($parsed);
|
// $parsed = self::parseEmoticons($parsed);
|
||||||
|
|
||||||
return $parsed;
|
// return $parsed;
|
||||||
|
|
||||||
|
return $text;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -154,12 +96,14 @@ class BBcode
|
||||||
*/
|
*/
|
||||||
public static function toEditor($text = null)
|
public static function toEditor($text = null)
|
||||||
{
|
{
|
||||||
// Check if text isn't null
|
// // Check if text isn't null
|
||||||
if ($text !== null) {
|
// if ($text !== null) {
|
||||||
self::text($text);
|
// 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)
|
public static function toPlain($text = null)
|
||||||
{
|
{
|
||||||
// Check if text isn't null
|
// // Check if text isn't null
|
||||||
if ($text !== null) {
|
// if ($text !== null) {
|
||||||
self::text($text);
|
// self::text($text);
|
||||||
}
|
// }
|
||||||
|
|
||||||
return self::$bbcode->getAsText();
|
// return self::$bbcode->getAsText();
|
||||||
|
return $text;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clean up the contents of <code> tags.
|
* Clean up the contents of <code> tags.
|
||||||
|
* See if this can be deprecated with a custom implementation!
|
||||||
*
|
*
|
||||||
* @param string $text Dirty
|
* @param string $text Dirty
|
||||||
*
|
*
|
||||||
|
|
|
@ -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>";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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>";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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>";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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>";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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>";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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>";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -7,8 +7,6 @@
|
||||||
|
|
||||||
namespace Sakura;
|
namespace Sakura;
|
||||||
|
|
||||||
use Sakura\Hashing;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to generate and validate CSRF tokens.
|
* Used to generate and validate CSRF tokens.
|
||||||
*
|
*
|
||||||
|
@ -56,7 +54,7 @@ class CSRF
|
||||||
*/
|
*/
|
||||||
public static function generate()
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the slowEquals function from the hashing lib to validate
|
return hash_equals($token, $_SESSION[$id]);
|
||||||
return Hashing::slowEquals($token, $_SESSION[$id]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,15 +25,8 @@ class Application extends \CLIFramework\Application
|
||||||
*/
|
*/
|
||||||
const VERSION = SAKURA_VERSION;
|
const VERSION = SAKURA_VERSION;
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* CLI initialiser
|
* Enable command autoloading
|
||||||
*/
|
*/
|
||||||
public function init()
|
protected $commandAutoloadEnabled = true;
|
||||||
{
|
|
||||||
// Execute the original init function
|
|
||||||
parent::init();
|
|
||||||
|
|
||||||
// Add commands with class reference because the autoloader is retarded
|
|
||||||
$this->command('serve', Command\ServeCommand::class);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
26
app/Console/Command/DatabaseInstallCommand.php
Normal 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!");
|
||||||
|
}
|
||||||
|
}
|
40
app/Console/Command/DatabaseMigrateCommand.php
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,7 +8,6 @@
|
||||||
namespace Sakura\Console\Command;
|
namespace Sakura\Console\Command;
|
||||||
|
|
||||||
use CLIFramework\Command;
|
use CLIFramework\Command;
|
||||||
use Sakura\Config;
|
|
||||||
|
|
||||||
class ServeCommand extends Command
|
class ServeCommand extends Command
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,7 +11,6 @@ use Sakura\ActionCode;
|
||||||
use Sakura\ActiveUser;
|
use Sakura\ActiveUser;
|
||||||
use Sakura\Config;
|
use Sakura\Config;
|
||||||
use Sakura\DB;
|
use Sakura\DB;
|
||||||
use Sakura\Hashing;
|
|
||||||
use Sakura\Net;
|
use Sakura\Net;
|
||||||
use Sakura\Perms\Site;
|
use Sakura\Perms\Site;
|
||||||
use Sakura\Router;
|
use Sakura\Router;
|
||||||
|
@ -125,31 +124,21 @@ class AuthController extends Controller
|
||||||
return Template::render('global/information');
|
return Template::render('global/information');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate password
|
if (strlen($user->password) < 1) {
|
||||||
switch ($user->passwordAlgo) {
|
$message = 'Your password expired.';
|
||||||
// Disabled
|
$redirect = Router::route('auth.resetpassword');
|
||||||
case 'disabled':
|
|
||||||
$this->touchRateLimit($user->id);
|
|
||||||
$message = 'Logging into this account is disabled.';
|
|
||||||
Template::vars(compact('message', 'redirect'));
|
Template::vars(compact('message', 'redirect'));
|
||||||
|
|
||||||
return Template::render('global/information');
|
return Template::render('global/information');
|
||||||
|
}
|
||||||
|
|
||||||
// Default hashing method
|
if (!password_verify($password, $user->password)) {
|
||||||
default:
|
|
||||||
if (!Hashing::validatePassword($password, [
|
|
||||||
$user->passwordAlgo,
|
|
||||||
$user->passwordIter,
|
|
||||||
$user->passwordSalt,
|
|
||||||
$user->passwordHash,
|
|
||||||
])) {
|
|
||||||
$this->touchRateLimit($user->id);
|
$this->touchRateLimit($user->id);
|
||||||
$message = 'The password you entered was invalid.';
|
$message = 'The password you entered was invalid.';
|
||||||
Template::vars(compact('message', 'redirect'));
|
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
|
// Check if the user has the required privs to log in
|
||||||
if ($user->permission(Site::DEACTIVATED)) {
|
if ($user->permission(Site::DEACTIVATED)) {
|
||||||
|
@ -564,16 +553,13 @@ class AuthController extends Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash the password
|
// Hash the password
|
||||||
$pw = Hashing::createHash($password);
|
$password = password_hash($password, PASSWORD_BCRYPT);
|
||||||
|
|
||||||
// Update the user
|
// Update the user
|
||||||
DB::table('users')
|
DB::table('users')
|
||||||
->where('user_id', $user->id)
|
->where('user_id', $user->id)
|
||||||
->update([
|
->update([
|
||||||
'password_hash' => $pw[3],
|
'password' => $password,
|
||||||
'password_salt' => $pw[2],
|
|
||||||
'password_algo' => $pw[0],
|
|
||||||
'password_iter' => $pw[1],
|
|
||||||
'password_chan' => time(),
|
'password_chan' => time(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ class FileController extends Controller
|
||||||
*/
|
*/
|
||||||
public function avatar($id = 0)
|
public function avatar($id = 0)
|
||||||
{
|
{
|
||||||
$noAvatar = ROOT . str_replace(
|
$noAvatar = ROOT . 'public/' . str_replace(
|
||||||
'%tplname%',
|
'%tplname%',
|
||||||
Template::$name,
|
Template::$name,
|
||||||
config('user.avatar_none')
|
config('user.avatar_none')
|
||||||
|
@ -56,18 +56,7 @@ class FileController extends Controller
|
||||||
'mime' => getimagesizefromstring($noAvatar)['mime'],
|
'mime' => getimagesizefromstring($noAvatar)['mime'],
|
||||||
];
|
];
|
||||||
|
|
||||||
$deactivePath = ROOT . str_replace(
|
$bannedPath = ROOT . 'public/' . 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(
|
|
||||||
'%tplname%',
|
'%tplname%',
|
||||||
Template::$name,
|
Template::$name,
|
||||||
config('user.avatar_ban')
|
config('user.avatar_ban')
|
||||||
|
@ -80,15 +69,11 @@ class FileController extends Controller
|
||||||
|
|
||||||
$user = User::construct($id);
|
$user = User::construct($id);
|
||||||
|
|
||||||
if ($user->permission(Site::DEACTIVATED)) {
|
|
||||||
return $this->serve($deactive['data'], $deactive['mime'], $deactive['name']);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($user->permission(Site::RESTRICTED)) {
|
if ($user->permission(Site::RESTRICTED)) {
|
||||||
return $this->serve($banned['data'], $banned['mime'], $banned['name']);
|
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']);
|
return $this->serve($none['data'], $none['mime'], $none['name']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +93,7 @@ class FileController extends Controller
|
||||||
*/
|
*/
|
||||||
public function background($id = 0)
|
public function background($id = 0)
|
||||||
{
|
{
|
||||||
$noBg = ROOT . "public/content/pixel.png";
|
$noBg = ROOT . "public/images/pixel.png";
|
||||||
$none = [
|
$none = [
|
||||||
'name' => basename($noBg),
|
'name' => basename($noBg),
|
||||||
'data' => file_get_contents($noBg),
|
'data' => file_get_contents($noBg),
|
||||||
|
@ -143,7 +128,7 @@ class FileController extends Controller
|
||||||
*/
|
*/
|
||||||
public function header($id = 0)
|
public function header($id = 0)
|
||||||
{
|
{
|
||||||
$noHeader = ROOT . "public/content/pixel.png";
|
$noHeader = ROOT . "public/images/pixel.png";
|
||||||
$none = [
|
$none = [
|
||||||
'name' => basename($noHeader),
|
'name' => basename($noHeader),
|
||||||
'data' => file_get_contents($noHeader),
|
'data' => file_get_contents($noHeader),
|
||||||
|
|
|
@ -10,7 +10,6 @@ namespace Sakura\Controllers\Settings;
|
||||||
use Sakura\ActiveUser;
|
use Sakura\ActiveUser;
|
||||||
use Sakura\Config;
|
use Sakura\Config;
|
||||||
use Sakura\DB;
|
use Sakura\DB;
|
||||||
use Sakura\Hashing;
|
|
||||||
use Sakura\Perms\Site;
|
use Sakura\Perms\Site;
|
||||||
use Sakura\Router;
|
use Sakura\Router;
|
||||||
use Sakura\Template;
|
use Sakura\Template;
|
||||||
|
@ -237,12 +236,7 @@ class AccountController extends Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check current password
|
// Check current password
|
||||||
if (!Hashing::validatePassword($current, [
|
if (!password_verify($current, ActiveUser::$user->password)) {
|
||||||
ActiveUser::$user->passwordAlgo,
|
|
||||||
ActiveUser::$user->passwordIter,
|
|
||||||
ActiveUser::$user->passwordSalt,
|
|
||||||
ActiveUser::$user->passwordHash,
|
|
||||||
])) {
|
|
||||||
$message = "Your password was invalid!";
|
$message = "Your password was invalid!";
|
||||||
Template::vars(compact('redirect', 'message'));
|
Template::vars(compact('redirect', 'message'));
|
||||||
return Template::render('global/information');
|
return Template::render('global/information');
|
||||||
|
|
|
@ -9,7 +9,6 @@ namespace Sakura\Controllers\Settings;
|
||||||
|
|
||||||
use Sakura\ActiveUser;
|
use Sakura\ActiveUser;
|
||||||
use Sakura\DB;
|
use Sakura\DB;
|
||||||
use Sakura\Hashing;
|
|
||||||
use Sakura\Perms\Site;
|
use Sakura\Perms\Site;
|
||||||
use Sakura\Router;
|
use Sakura\Router;
|
||||||
use Sakura\Template;
|
use Sakura\Template;
|
||||||
|
@ -115,12 +114,7 @@ class AdvancedController extends Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check password
|
// Check password
|
||||||
if (!Hashing::validatePassword($password, [
|
if (!password_verify($password, ActiveUser::$user->password)) {
|
||||||
ActiveUser::$user->passwordAlgo,
|
|
||||||
ActiveUser::$user->passwordIter,
|
|
||||||
ActiveUser::$user->passwordSalt,
|
|
||||||
ActiveUser::$user->passwordHash,
|
|
||||||
])) {
|
|
||||||
$message = "Your password was invalid!";
|
$message = "Your password was invalid!";
|
||||||
Template::vars(compact('redirect', 'message'));
|
Template::vars(compact('redirect', 'message'));
|
||||||
return Template::render('global/information');
|
return Template::render('global/information');
|
||||||
|
|
17
app/DB.php
|
@ -7,7 +7,9 @@
|
||||||
|
|
||||||
namespace Sakura;
|
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.
|
* The Illuminate (Laravel) database wrapper.
|
||||||
|
@ -17,5 +19,16 @@ use \Illuminate\Database\Capsule\Manager;
|
||||||
*/
|
*/
|
||||||
class DB extends 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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,35 +78,35 @@ class Forum
|
||||||
*
|
*
|
||||||
* @var Post
|
* @var Post
|
||||||
*/
|
*/
|
||||||
private $_firstPost = null;
|
private $firstPostCache = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A cached instance of the last post in this forum.
|
* A cached instance of the last post in this forum.
|
||||||
*
|
*
|
||||||
* @var Post
|
* @var Post
|
||||||
*/
|
*/
|
||||||
private $_lastPost = null;
|
private $lastPostCache = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cached instances of the subforums.
|
* Cached instances of the subforums.
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
private $_forums = [];
|
private $forumsCache = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cached instances of the threads in this forum.
|
* Cached instances of the threads in this forum.
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
private $_threads = [];
|
private $threadsCache = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The permission container.
|
* The permission container.
|
||||||
*
|
*
|
||||||
* @var Perms
|
* @var Perms
|
||||||
*/
|
*/
|
||||||
private $_permissions;
|
private $permissionsCache;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
|
@ -121,7 +121,7 @@ class Forum
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
// Create permissions object
|
// Create permissions object
|
||||||
$this->_permissions = new Perms(Perms::FORUM);
|
$this->permissionsCache = new Perms(Perms::FORUM);
|
||||||
|
|
||||||
// Populate the variables
|
// Populate the variables
|
||||||
if ($forumRow) {
|
if ($forumRow) {
|
||||||
|
@ -159,9 +159,9 @@ class Forum
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bitwise OR it with the permissions for this 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()
|
public function forums()
|
||||||
{
|
{
|
||||||
// Check if _forums is populated
|
// Check if forumsCache is populated
|
||||||
if (!count($this->_forums)) {
|
if (!count($this->forumsCache)) {
|
||||||
// Get all rows with the category id set to the forum id
|
// Get all rows with the category id set to the forum id
|
||||||
$forumRows = DB::table('forums')
|
$forumRows = DB::table('forums')
|
||||||
->where('forum_category', $this->id)
|
->where('forum_category', $this->id)
|
||||||
|
@ -187,9 +187,9 @@ class Forum
|
||||||
$forums[$forum->forum_id] = new Forum($forum->forum_id);
|
$forums[$forum->forum_id] = new Forum($forum->forum_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->_forums = $forums;
|
$this->forumsCache = $forums;
|
||||||
} else {
|
} else {
|
||||||
$forums = $this->_forums;
|
$forums = $this->forumsCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the forum objects
|
// Return the forum objects
|
||||||
|
@ -203,8 +203,8 @@ class Forum
|
||||||
*/
|
*/
|
||||||
public function threads()
|
public function threads()
|
||||||
{
|
{
|
||||||
// Check if _threads is populated
|
// Check if threadsCache is populated
|
||||||
if (!count($this->_threads)) {
|
if (!count($this->threadsCache)) {
|
||||||
// Get all rows with the forum id for this forum
|
// Get all rows with the forum id for this forum
|
||||||
$threadRows = DB::table('topics')
|
$threadRows = DB::table('topics')
|
||||||
->where('forum_id', $this->id)
|
->where('forum_id', $this->id)
|
||||||
|
@ -220,9 +220,9 @@ class Forum
|
||||||
$threads[$thread->topic_id] = new Thread($thread->topic_id);
|
$threads[$thread->topic_id] = new Thread($thread->topic_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->_threads = $threads;
|
$this->threadsCache = $threads;
|
||||||
} else {
|
} else {
|
||||||
$threads = $this->_threads;
|
$threads = $this->threadsCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the thread objects
|
// Return the thread objects
|
||||||
|
@ -236,8 +236,8 @@ class Forum
|
||||||
*/
|
*/
|
||||||
public function firstPost()
|
public function firstPost()
|
||||||
{
|
{
|
||||||
// Check if _firstPost is set
|
// Check if firstPostCache is set
|
||||||
if ($this->_firstPost === null) {
|
if ($this->firstPostCache === null) {
|
||||||
// Get the row
|
// Get the row
|
||||||
$firstPost = DB::table('posts')
|
$firstPost = DB::table('posts')
|
||||||
->where('forum_id', $this->id)
|
->where('forum_id', $this->id)
|
||||||
|
@ -249,12 +249,12 @@ class Forum
|
||||||
$post = new Post(empty($firstPost) ? 0 : $firstPost[0]->post_id);
|
$post = new Post(empty($firstPost) ? 0 : $firstPost[0]->post_id);
|
||||||
|
|
||||||
// Assign it to a "cache" variable
|
// Assign it to a "cache" variable
|
||||||
$this->_firstPost = $post;
|
$this->firstPostCache = $post;
|
||||||
|
|
||||||
// Return the post object
|
// Return the post object
|
||||||
return $post;
|
return $post;
|
||||||
} else {
|
} else {
|
||||||
return $this->_firstPost;
|
return $this->firstPostCache;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,8 +265,8 @@ class Forum
|
||||||
*/
|
*/
|
||||||
public function lastPost()
|
public function lastPost()
|
||||||
{
|
{
|
||||||
// Check if _lastPost is set
|
// Check if lastPostCache is set
|
||||||
if ($this->_lastPost === null) {
|
if ($this->lastPostCache === null) {
|
||||||
// Get the row
|
// Get the row
|
||||||
$lastPost = DB::table('posts')
|
$lastPost = DB::table('posts')
|
||||||
->where('forum_id', $this->id)
|
->where('forum_id', $this->id)
|
||||||
|
@ -278,12 +278,12 @@ class Forum
|
||||||
$post = new Post(empty($lastPost) ? 0 : $lastPost[0]->post_id);
|
$post = new Post(empty($lastPost) ? 0 : $lastPost[0]->post_id);
|
||||||
|
|
||||||
// Assign it to a "cache" variable
|
// Assign it to a "cache" variable
|
||||||
$this->_lastPost = $post;
|
$this->lastPostCache = $post;
|
||||||
|
|
||||||
// Return the post object
|
// Return the post object
|
||||||
return $post;
|
return $post;
|
||||||
} else {
|
} else {
|
||||||
return $this->_lastPost;
|
return $this->lastPostCache;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -104,21 +104,21 @@ class Thread
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
private $_posts = [];
|
private $postsCache = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A cached instance of opening post.
|
* A cached instance of opening post.
|
||||||
*
|
*
|
||||||
* @var Post
|
* @var Post
|
||||||
*/
|
*/
|
||||||
private $_firstPost = null;
|
private $firstPostCache = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A cached instance of the last reply.
|
* A cached instance of the last reply.
|
||||||
*
|
*
|
||||||
* @var Post
|
* @var Post
|
||||||
*/
|
*/
|
||||||
private $_lastPost = null;
|
private $lastPostCache = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
|
@ -244,8 +244,8 @@ class Thread
|
||||||
*/
|
*/
|
||||||
public function posts()
|
public function posts()
|
||||||
{
|
{
|
||||||
// Check if _posts is something
|
// Check if postsCache is something
|
||||||
if (!count($this->_posts)) {
|
if (!count($this->postsCache)) {
|
||||||
// Get all rows with the thread id
|
// Get all rows with the thread id
|
||||||
$postRows = DB::table('posts')
|
$postRows = DB::table('posts')
|
||||||
->where('topic_id', $this->id)
|
->where('topic_id', $this->id)
|
||||||
|
@ -259,9 +259,9 @@ class Thread
|
||||||
$posts[$post->post_id] = new Post($post->post_id);
|
$posts[$post->post_id] = new Post($post->post_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->_posts = $posts;
|
$this->postsCache = $posts;
|
||||||
} else {
|
} else {
|
||||||
$posts = $this->_posts;
|
$posts = $this->postsCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the post objects
|
// Return the post objects
|
||||||
|
@ -276,8 +276,8 @@ class Thread
|
||||||
public function firstPost()
|
public function firstPost()
|
||||||
{
|
{
|
||||||
// Check if the cache var is set
|
// Check if the cache var is set
|
||||||
if ($this->_firstPost !== null) {
|
if ($this->firstPostCache !== null) {
|
||||||
return $this->_firstPost;
|
return $this->firstPostCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the row from the database
|
// Get the row from the database
|
||||||
|
@ -291,7 +291,7 @@ class Thread
|
||||||
$post = new Post($post ? $post[0]->post_id : 0);
|
$post = new Post($post ? $post[0]->post_id : 0);
|
||||||
|
|
||||||
// Assign it to the cache var
|
// Assign it to the cache var
|
||||||
$this->_firstPost = $post;
|
$this->firstPostCache = $post;
|
||||||
|
|
||||||
// Return
|
// Return
|
||||||
return $post;
|
return $post;
|
||||||
|
@ -305,8 +305,8 @@ class Thread
|
||||||
public function lastPost()
|
public function lastPost()
|
||||||
{
|
{
|
||||||
// Check if the cache var is set
|
// Check if the cache var is set
|
||||||
if ($this->_lastPost !== null) {
|
if ($this->lastPostCache !== null) {
|
||||||
return $this->_lastPost;
|
return $this->lastPostCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the row from the database
|
// Get the row from the database
|
||||||
|
@ -320,7 +320,7 @@ class Thread
|
||||||
$post = new Post($post ? $post[0]->post_id : 0);
|
$post = new Post($post ? $post[0]->post_id : 0);
|
||||||
|
|
||||||
// Assign it to the cache var
|
// Assign it to the cache var
|
||||||
$this->_lastPost = $post;
|
$this->lastPostCache = $post;
|
||||||
|
|
||||||
// Return
|
// Return
|
||||||
return $post;
|
return $post;
|
||||||
|
|
226
app/Hashing.php
|
@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -42,13 +42,6 @@ class Template
|
||||||
*/
|
*/
|
||||||
public static $name;
|
public static $name;
|
||||||
|
|
||||||
/**
|
|
||||||
* The path to the client side resources
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public static $resources;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The file extension used by template files
|
* The file extension used by template files
|
||||||
*/
|
*/
|
||||||
|
@ -64,9 +57,6 @@ class Template
|
||||||
// Set variables
|
// Set variables
|
||||||
self::$name = $name;
|
self::$name = $name;
|
||||||
|
|
||||||
// Set reources path
|
|
||||||
self::$resources = '/content/data/' . self::$name;
|
|
||||||
|
|
||||||
// Reinitialise
|
// Reinitialise
|
||||||
self::init();
|
self::init();
|
||||||
}
|
}
|
||||||
|
@ -77,14 +67,14 @@ class Template
|
||||||
public static function init()
|
public static function init()
|
||||||
{
|
{
|
||||||
// Initialise Twig Filesystem Loader
|
// 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
|
// Environment variable
|
||||||
$twigEnv = [];
|
$twigEnv = [];
|
||||||
|
|
||||||
// Enable caching
|
// Enable caching
|
||||||
if (config("performance.template_cache")) {
|
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
|
// And now actually initialise the templating engine
|
||||||
|
@ -94,18 +84,11 @@ class Template
|
||||||
self::$engine->addExtension(new Twig_Extension_StringLoader());
|
self::$engine->addExtension(new Twig_Extension_StringLoader());
|
||||||
|
|
||||||
// Add route function
|
// Add route function
|
||||||
self::$engine->addFunction(new Twig_SimpleFunction('route', function ($name, $args = null) {
|
self::$engine->addFunction(new Twig_SimpleFunction('route', 'route'));
|
||||||
return Router::route($name, $args);
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Add config function
|
// Add config function
|
||||||
self::$engine->addFunction(new Twig_SimpleFunction('config', 'config'));
|
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
|
// Method of getting the currently active session id
|
||||||
self::$engine->addFunction(new Twig_SimpleFunction('session_id', 'session_id'));
|
self::$engine->addFunction(new Twig_SimpleFunction('session_id', 'session_id'));
|
||||||
|
|
||||||
|
@ -135,10 +118,6 @@ class Template
|
||||||
*/
|
*/
|
||||||
public static function render($file)
|
public static function render($file)
|
||||||
{
|
{
|
||||||
try {
|
|
||||||
return self::$engine->render($file . self::FILE_EXT, self::$vars);
|
return self::$engine->render($file . self::FILE_EXT, self::$vars);
|
||||||
} catch (\Exception $e) {
|
|
||||||
return trigger_error($e->getMessage(), E_USER_ERROR);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
84
app/User.php
|
@ -219,7 +219,7 @@ class User
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected static $_userCache = [];
|
protected static $userCache = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cached constructor.
|
* Cached constructor.
|
||||||
|
@ -232,13 +232,13 @@ class User
|
||||||
public static function construct($uid, $forceRefresh = false)
|
public static function construct($uid, $forceRefresh = false)
|
||||||
{
|
{
|
||||||
// Check if a user object isn't present in cache
|
// 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
|
// 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 the cached object
|
||||||
return self::$_userCache[$uid];
|
return self::$userCache[$uid];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -256,17 +256,14 @@ class User
|
||||||
// Set a few variables
|
// Set a few variables
|
||||||
$usernameClean = clean_string($username, true);
|
$usernameClean = clean_string($username, true);
|
||||||
$emailClean = clean_string($email, 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
|
// Insert the user into the database and get the id
|
||||||
$userId = DB::table('users')
|
$userId = DB::table('users')
|
||||||
->insertGetId([
|
->insertGetId([
|
||||||
'username' => $username,
|
'username' => $username,
|
||||||
'username_clean' => $usernameClean,
|
'username_clean' => $usernameClean,
|
||||||
'password_hash' => $password[3],
|
'password' => $password,
|
||||||
'password_salt' => $password[2],
|
|
||||||
'password_algo' => $password[0],
|
|
||||||
'password_iter' => $password[1],
|
|
||||||
'email' => $emailClean,
|
'email' => $emailClean,
|
||||||
'rank_main' => 0,
|
'rank_main' => 0,
|
||||||
'register_ip' => Net::pton(Net::ip()),
|
'register_ip' => Net::pton(Net::ip()),
|
||||||
|
@ -308,11 +305,7 @@ class User
|
||||||
$this->id = $userRow->user_id;
|
$this->id = $userRow->user_id;
|
||||||
$this->username = $userRow->username;
|
$this->username = $userRow->username;
|
||||||
$this->usernameClean = $userRow->username_clean;
|
$this->usernameClean = $userRow->username_clean;
|
||||||
$this->passwordHash = $userRow->password_hash;
|
$this->password = $userRow->password;
|
||||||
$this->passwordSalt = $userRow->password_salt;
|
|
||||||
$this->passwordAlgo = $userRow->password_algo;
|
|
||||||
$this->passwordIter = $userRow->password_iter;
|
|
||||||
$this->passwordChan = $userRow->password_chan;
|
|
||||||
$this->email = $userRow->email;
|
$this->email = $userRow->email;
|
||||||
$this->mainRankId = $userRow->rank_main;
|
$this->mainRankId = $userRow->rank_main;
|
||||||
$this->colour = $userRow->user_colour;
|
$this->colour = $userRow->user_colour;
|
||||||
|
@ -1015,62 +1008,6 @@ class User
|
||||||
return $return;
|
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.
|
* Parse the user's userpage.
|
||||||
*
|
*
|
||||||
|
@ -1155,16 +1092,13 @@ class User
|
||||||
public function setPassword($password)
|
public function setPassword($password)
|
||||||
{
|
{
|
||||||
// Create hash
|
// Create hash
|
||||||
$password = Hashing::createHash($password);
|
$password = password_hash($password, PASSWORD_BCRYPT);
|
||||||
|
|
||||||
// Update userrow
|
// Update userrow
|
||||||
DB::table('users')
|
DB::table('users')
|
||||||
->where('user_id', $this->id)
|
->where('user_id', $this->id)
|
||||||
->update([
|
->update([
|
||||||
'password_hash' => $password[3],
|
'password' => $password,
|
||||||
'password_salt' => $password[2],
|
|
||||||
'password_algo' => $password[0],
|
|
||||||
'password_iter' => $password[1],
|
|
||||||
'password_chan' => time(),
|
'password_chan' => time(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,13 +12,15 @@
|
||||||
"ext-curl": "*",
|
"ext-curl": "*",
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
"twig/twig": "*",
|
"twig/twig": "*",
|
||||||
"phpmailer/phpmailer": "*",
|
|
||||||
"paypal/rest-api-sdk-php": "*",
|
"paypal/rest-api-sdk-php": "*",
|
||||||
"jbbcode/jbbcode": "*",
|
|
||||||
"corneltek/cliframework": "*",
|
|
||||||
"phroute/phroute": "^2.1",
|
"phroute/phroute": "^2.1",
|
||||||
"illuminate/database": "5.2.*",
|
"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": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
|
263
composer.lock
generated
|
@ -4,8 +4,8 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"hash": "765ea465939a65048ac736e4fbc6c167",
|
"hash": "1f0887a5e8183bc1ca24e7c50e054ba1",
|
||||||
"content-hash": "1af681873ad63e53d42dfd445d67b388",
|
"content-hash": "271f4f1bcfb9fdc9446336132938b762",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "corneltek/class-template",
|
"name": "corneltek/class-template",
|
||||||
|
@ -725,6 +725,56 @@
|
||||||
],
|
],
|
||||||
"time": "2014-09-09 13:34:57"
|
"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",
|
"name": "illuminate/container",
|
||||||
"version": "v5.2.37",
|
"version": "v5.2.37",
|
||||||
|
@ -870,6 +920,56 @@
|
||||||
],
|
],
|
||||||
"time": "2016-06-06 13:12:46"
|
"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",
|
"name": "illuminate/support",
|
||||||
"version": "v5.2.37",
|
"version": "v5.2.37",
|
||||||
|
@ -926,52 +1026,6 @@
|
||||||
"homepage": "http://laravel.com",
|
"homepage": "http://laravel.com",
|
||||||
"time": "2016-05-30 02:40:53"
|
"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",
|
"name": "nesbot/carbon",
|
||||||
"version": "1.21.0",
|
"version": "1.21.0",
|
||||||
|
@ -1116,66 +1170,6 @@
|
||||||
],
|
],
|
||||||
"time": "2016-07-15 20:42:18"
|
"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",
|
"name": "phroute/phroute",
|
||||||
"version": "v2.1.0",
|
"version": "v2.1.0",
|
||||||
|
@ -1304,6 +1298,59 @@
|
||||||
],
|
],
|
||||||
"time": "2012-12-21 11:40:51"
|
"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",
|
"name": "symfony/class-loader",
|
||||||
"version": "v3.0.0",
|
"version": "v3.0.0",
|
||||||
|
|
|
@ -1,38 +1,41 @@
|
||||||
; Database configuration according to https://laravel.com/docs/5.2/database#introduction
|
; Database configuration according to https://laravel.com/docs/5.2/database#introduction
|
||||||
; Put some here in advance, uncomment the one you need.
|
; Put some here in advance, uncomment the one you need.
|
||||||
[database]
|
[database]
|
||||||
;; mysql
|
; ; mysql
|
||||||
;driver = mysql
|
; driver = mysql
|
||||||
;host = localhost
|
; host = localhost
|
||||||
;port = 3306
|
; port = 3306
|
||||||
;username = sakura
|
; username = sakura
|
||||||
;password = password
|
; password = password
|
||||||
;prefix = sakura_
|
; prefix = sakura_
|
||||||
;database = sakura-development
|
; database = sakura-development
|
||||||
;charset = utf8
|
; charset = utf8
|
||||||
;collation = utf8_unicode_ci
|
; collation = utf8_unicode_ci
|
||||||
|
|
||||||
;; sqlite
|
; ; sqlite
|
||||||
;driver = sqlite
|
; driver = sqlite
|
||||||
;database = sakura.sq3
|
; database = sakura.sq3
|
||||||
;prefix = sakura_
|
; prefix = sakura_
|
||||||
|
|
||||||
;; postgres
|
; ; postgres
|
||||||
;driver = pgsql
|
; driver = pgsql
|
||||||
;host = localhost
|
; host = localhost
|
||||||
;port = 5432
|
; port = 5432
|
||||||
;username = sakura
|
; username = sakura
|
||||||
;password = password
|
; password = password
|
||||||
;prefix = sakura_
|
; prefix = sakura_
|
||||||
;database = sakura-development
|
; database = sakura-development
|
||||||
;charset = utf8
|
; charset = utf8
|
||||||
;schema = public
|
; schema = public
|
||||||
|
|
||||||
; General site settings
|
; General site settings
|
||||||
[general]
|
[general]
|
||||||
; Name of the site
|
; Name of the site
|
||||||
name = Sakura
|
name = Sakura
|
||||||
|
|
||||||
|
; Logo of the site
|
||||||
|
logo =
|
||||||
|
|
||||||
; Description of the site
|
; Description of the site
|
||||||
description = Test site
|
description = Test site
|
||||||
|
|
||||||
|
@ -48,6 +51,9 @@ cover =
|
||||||
; Close the site for maintenance
|
; Close the site for maintenance
|
||||||
maintenance = false
|
maintenance = false
|
||||||
|
|
||||||
|
; URL of the sakurako chat (full path) without trailing slash
|
||||||
|
chat = http://chat.localghost
|
||||||
|
|
||||||
; Cookie settings
|
; Cookie settings
|
||||||
[cookie]
|
[cookie]
|
||||||
prefix = sakura_
|
prefix = sakura_
|
||||||
|
@ -90,7 +96,7 @@ username =
|
||||||
password =
|
password =
|
||||||
server =
|
server =
|
||||||
port = 25
|
port = 25
|
||||||
secure = true
|
secure = tls
|
||||||
|
|
||||||
; File settings
|
; File settings
|
||||||
[file]
|
[file]
|
||||||
|
@ -116,10 +122,9 @@ max_width = 2048
|
||||||
|
|
||||||
; User settings
|
; User settings
|
||||||
[user]
|
[user]
|
||||||
; Avatars
|
; Avatars (relative to public/)
|
||||||
avatar_ban = public/content/data/%tplname%/images/banned-av.png
|
avatar_ban = images/%tplname%-ban.png
|
||||||
avatar_none = public/content/data/%tplname%/images/no-av.png
|
avatar_none = images/%tplname%-none.png
|
||||||
avatar_inactive = public/content/data/%tplname%/images/deactivated-av.png
|
|
||||||
|
|
||||||
; Username constraints
|
; Username constraints
|
||||||
name_min = 3
|
name_min = 3
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
var
|
var
|
||||||
elixir = require('laravel-elixir'),
|
elixir = require('laravel-elixir'),
|
||||||
elixirTypscript = require('elixir-typescript'),
|
elixirTypscript = require('elixir-typescript'),
|
||||||
nodePath = '../../node_modules/';
|
nodePath = '../../../node_modules/';
|
||||||
|
|
||||||
elixir.config.assetsPath = './assets';
|
|
||||||
|
|
||||||
elixir(function(mix) {
|
elixir(function(mix) {
|
||||||
|
|
||||||
mix
|
mix
|
||||||
.less('aitemu/master.less', 'public/css/aitemu.css')
|
.less('aitemu/master.less', 'public/css/aitemu.css')
|
||||||
.less('yuuno/master.less', 'public/css/yuuno.css')
|
.less('yuuno/master.less', 'public/css/yuuno.css')
|
||||||
|
@ -14,6 +11,6 @@ elixir(function(mix) {
|
||||||
.typescript('aitemu/**/*.ts', 'public/js/aitemu.js')
|
.typescript('aitemu/**/*.ts', 'public/js/aitemu.js')
|
||||||
.typescript('yuuno/**/*.ts', 'public/js/yuuno.js')
|
.typescript('yuuno/**/*.ts', 'public/js/yuuno.js')
|
||||||
.scripts([
|
.scripts([
|
||||||
nodePath + 'turbolinks/dist/turbolinks.js'
|
nodePath + 'turbolinks/dist/turbolinks.js',
|
||||||
], 'public/js/libraries.js');
|
], 'public/js/libraries.js');
|
||||||
});
|
});
|
||||||
|
|
18
mahou
|
@ -13,23 +13,15 @@ namespace Sakura;
|
||||||
use Sakura\Console\Application;
|
use Sakura\Console\Application;
|
||||||
use GetOptionKit\Exception\InvalidOptionException;
|
use GetOptionKit\Exception\InvalidOptionException;
|
||||||
|
|
||||||
// Define that this page won't require templating
|
|
||||||
define('SAKURA_NO_TPL', true);
|
|
||||||
|
|
||||||
// Include components
|
// Include components
|
||||||
require_once 'sakura.php';
|
require_once 'sakura.php';
|
||||||
|
|
||||||
// Check if we're using console
|
// Create an instance
|
||||||
if (php_sapi_name() === 'cli') {
|
$console = new Application;
|
||||||
// Create an instance
|
|
||||||
$console = new Application;
|
|
||||||
|
|
||||||
// Attempt to run
|
// Attempt to run
|
||||||
try {
|
try {
|
||||||
$console->run($argv);
|
$console->run($argv);
|
||||||
} catch (InvalidOptionException $e) {
|
} catch (InvalidOptionException $e) {
|
||||||
die($e->getMessage());
|
die($e->getMessage());
|
||||||
}
|
|
||||||
} else {
|
|
||||||
echo 'Why would you even try to run a console app through a browser?';
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,9 @@
|
||||||
"dev": "gulp watch"
|
"dev": "gulp watch"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"elixir-typescript": "^2.0.0",
|
||||||
"gulp": "^3.9.1",
|
"gulp": "^3.9.1",
|
||||||
"laravel-elixir": "^5.0.0",
|
"laravel-elixir": "^5.0.0",
|
||||||
"elixir-typescript": "^2.0.0",
|
|
||||||
"turbolinks": "^5.0.0"
|
"turbolinks": "^5.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
Before Width: | Height: | Size: 93 KiB |
Before Width: | Height: | Size: 223 KiB |
Before Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 398 B |
Before Width: | Height: | Size: 496 B |
Before Width: | Height: | Size: 479 B |
Before Width: | Height: | Size: 649 B |
Before Width: | Height: | Size: 652 B |
5
public/content/libraries/d3.js
vendored
|
@ -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;
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
})();
|
|
107
public/error.css
Normal 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;
|
||||||
|
}
|
Before Width: | Height: | Size: 87 B After Width: | Height: | Size: 87 B |
Before Width: | Height: | Size: 260 B After Width: | Height: | Size: 260 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
BIN
public/images/access-denied.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
public/images/aitemu-ban.png
Normal file
After Width: | Height: | Size: 6.1 KiB |
BIN
public/images/aitemu-none.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
public/images/default-banner.png
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
public/images/enable-javascript.png
Normal file
After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 643 B After Width: | Height: | Size: 643 B |
Before Width: | Height: | Size: 408 B After Width: | Height: | Size: 408 B |
Before Width: | Height: | Size: 604 B After Width: | Height: | Size: 604 B |
Before Width: | Height: | Size: 591 B After Width: | Height: | Size: 591 B |
Before Width: | Height: | Size: 643 B After Width: | Height: | Size: 643 B |
Before Width: | Height: | Size: 600 B After Width: | Height: | Size: 600 B |
Before Width: | Height: | Size: 497 B After Width: | Height: | Size: 497 B |
Before Width: | Height: | Size: 488 B After Width: | Height: | Size: 488 B |
Before Width: | Height: | Size: 428 B After Width: | Height: | Size: 428 B |
Before Width: | Height: | Size: 506 B After Width: | Height: | Size: 506 B |
Before Width: | Height: | Size: 647 B After Width: | Height: | Size: 647 B |
Before Width: | Height: | Size: 403 B After Width: | Height: | Size: 403 B |
Before Width: | Height: | Size: 673 B After Width: | Height: | Size: 673 B |
Before Width: | Height: | Size: 524 B After Width: | Height: | Size: 524 B |
Before Width: | Height: | Size: 663 B After Width: | Height: | Size: 663 B |
Before Width: | Height: | Size: 589 B After Width: | Height: | Size: 589 B |
Before Width: | Height: | Size: 593 B After Width: | Height: | Size: 593 B |
Before Width: | Height: | Size: 585 B After Width: | Height: | Size: 585 B |
Before Width: | Height: | Size: 504 B After Width: | Height: | Size: 504 B |
Before Width: | Height: | Size: 449 B After Width: | Height: | Size: 449 B |
Before Width: | Height: | Size: 497 B After Width: | Height: | Size: 497 B |
Before Width: | Height: | Size: 462 B After Width: | Height: | Size: 462 B |
Before Width: | Height: | Size: 457 B After Width: | Height: | Size: 457 B |
Before Width: | Height: | Size: 675 B After Width: | Height: | Size: 675 B |
Before Width: | Height: | Size: 486 B After Width: | Height: | Size: 486 B |
Before Width: | Height: | Size: 611 B After Width: | Height: | Size: 611 B |
Before Width: | Height: | Size: 639 B After Width: | Height: | Size: 639 B |
Before Width: | Height: | Size: 500 B After Width: | Height: | Size: 500 B |
Before Width: | Height: | Size: 593 B After Width: | Height: | Size: 593 B |
Before Width: | Height: | Size: 526 B After Width: | Height: | Size: 526 B |
Before Width: | Height: | Size: 631 B After Width: | Height: | Size: 631 B |
Before Width: | Height: | Size: 512 B After Width: | Height: | Size: 512 B |
Before Width: | Height: | Size: 443 B After Width: | Height: | Size: 443 B |
Before Width: | Height: | Size: 514 B After Width: | Height: | Size: 514 B |
Before Width: | Height: | Size: 600 B After Width: | Height: | Size: 600 B |
Before Width: | Height: | Size: 628 B After Width: | Height: | Size: 628 B |
Before Width: | Height: | Size: 625 B After Width: | Height: | Size: 625 B |
Before Width: | Height: | Size: 528 B After Width: | Height: | Size: 528 B |
Before Width: | Height: | Size: 614 B After Width: | Height: | Size: 614 B |
Before Width: | Height: | Size: 521 B After Width: | Height: | Size: 521 B |
Before Width: | Height: | Size: 367 B After Width: | Height: | Size: 367 B |