improved the bbcode framework

This commit is contained in:
flash 2016-08-06 19:31:32 +02:00
parent aa5239e0b6
commit 0446bc4f41
27 changed files with 801 additions and 181 deletions

93
app/BBCode/Parser.php Normal file
View file

@ -0,0 +1,93 @@
<?php
/**
* Holds the BBcode handler.
* @package Sakura
*/
namespace Sakura\BBCode;
use Sakura\DB;
/**
* BBcode handler.
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class Parser
{
/**
* Holds the bbcode parsers.
* @var array
*/
public static $parsers = [
// Basic markup
Tags\Bold::class,
Tags\Italics::class,
Tags\Underline::class,
Tags\Strike::class,
Tags\Header::class,
Tags\Image::class,
Tags\Spoiler::class,
// More advanced
Tags\Colour::class,
Tags\Align::class,
Tags\Size::class,
Tags\YouTube::class,
// Links
Tags\NamedLink::class,
Tags\Link::class,
// Quotes
Tags\NamedQuote::class,
Tags\Quote::class,
// Advanced parsing
Tags\Box::class,
Tags\Code::class,
Tags\ListTag::class,
Tags\User::class,
// Newline must always be last
Tags\Newline::class,
];
/**
* Parse the emoticons.
* @param string $text
* @return string
*/
public static function parseEmoticons($text)
{
// Get emoticons from the database
$emotes = DB::table('emoticons')
->get();
// Parse all emoticons
foreach ($emotes as $emote) {
$image = "<img src='{$emote->emote_path}' alt='{$emote->emote_string}' class='emoticon'>";
$icon = preg_quote($emote->emote_string, '#');
$text = preg_replace("#{$icon}#", $image, $text);
}
// Return the parsed text
return $text;
}
/**
* Convert the parsed text to HTML.
* @param string $text
* @return string
*/
public static function toHTML($text)
{
$text = self::parseEmoticons($text);
foreach (self::$parsers as $parser) {
$text = call_user_func([$parser, 'parse'], $text);
}
return $text;
}
}

37
app/BBCode/TagBase.php Normal file
View file

@ -0,0 +1,37 @@
<?php
/**
* Holds the tag base.
* @package Sakura
*/
namespace Sakura\BBCode;
/**
* Interface for tags.
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class TagBase
{
/**
* The pattern to match.
* @var string
*/
public static $pattern = "";
/**
* The string to replace it with.
* @var string
*/
public static $replace = "";
/**
* Parses the bbcode.
* @param string $text
* @return string
*/
public static function parse($text)
{
return preg_replace(static::$pattern, static::$replace, $text);
}
}

37
app/BBCode/Tags/Align.php Normal file
View file

@ -0,0 +1,37 @@
<?php
/**
* Holds the align tag.
* @package Sakura
*/
namespace Sakura\BBCode\Tags;
use Sakura\BBCode\TagBase;
/**
* Align tag.
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class Align extends TagBase
{
/**
* Parses the bbcode.
* @param string $text
* @return string
*/
public static function parse($text)
{
return preg_replace_callback(
'/\[align\=(left|center|centre|right)\](.*?)\[\/align\]/s',
function ($matches) {
if ($matches[1] === 'centre') {
$matches[1] = 'center';
}
return "<div style='text-align: {$matches[1]}'>{$matches[2]}</div>";
},
$text
);
}
}

29
app/BBCode/Tags/Bold.php Normal file
View file

@ -0,0 +1,29 @@
<?php
/**
* Holds the bold tag.
* @package Sakura
*/
namespace Sakura\BBCode\Tags;
use Sakura\BBCode\TagBase;
/**
* Bold tag.
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class Bold extends TagBase
{
/**
* The pattern to match.
* @var string
*/
public static $pattern = "/\[b\](.*?)\[\/b\]/s";
/**
* The string to replace it with.
* @var string
*/
public static $replace = "<b>$1</b>";
}

38
app/BBCode/Tags/Box.php Normal file
View file

@ -0,0 +1,38 @@
<?php
/**
* Holds the box tag.
* @package Sakura
*/
namespace Sakura\BBCode\Tags;
use Sakura\BBCode\TagBase;
/**
* Box tag.
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class Box extends TagBase
{
/**
* Parses the bbcode.
* @param string $text
* @return string
*/
public static function parse($text)
{
return preg_replace_callback(
'/\[box(?:\=(.*?))?\](.*?)\[\/box\]/s',
function ($matches) {
$title = strlen($matches[1]) ? $matches[1] : 'Click to open';
return "<div class='spoiler-box-container'>"
. "<div class='spoiler-box-title' onclick='alert(\"reimplement the toggle system\");'>{$title}</div>"
. "<div class='spoiler-box-content hidden'>{$matches[2]}</div>"
. "</div>";
},
$text
);
}
}

36
app/BBCode/Tags/Code.php Normal file
View file

@ -0,0 +1,36 @@
<?php
/**
* Holds the code tag.
* @package Sakura
*/
namespace Sakura\BBCode\Tags;
use Sakura\BBCode\TagBase;
/**
* Code tag.
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class Code extends TagBase
{
/**
* Parses the bbcode.
* @param string $text
* @return string
*/
public static function parse($text)
{
return preg_replace_callback(
'/\[code(?:\=([a-z]+))?\](.*?)\[\/code\]/s',
function ($matches) {
$class = strlen($matches[1]) ? " class='lang-{$matches[1]}'" : '';
// htmlencode bbcode characters here as well
return "<pre><code{$class}>{$matches[2]}</code></pre>";
},
$text
);
}
}

View file

@ -0,0 +1,29 @@
<?php
/**
* Holds the colour tag.
* @package Sakura
*/
namespace Sakura\BBCode\Tags;
use Sakura\BBCode\TagBase;
/**
* Colour tag.
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class Colour extends TagBase
{
/**
* The pattern to match.
* @var string
*/
public static $pattern = "/\[colou?r\=(#[A-f0-9]{6}|#[A-f0-9]{3})\](.*?)\[\/colou?r\]/s";
/**
* The string to replace it with.
* @var string
*/
public static $replace = "<span style='color: $1'>$2</span>";
}

View file

@ -0,0 +1,29 @@
<?php
/**
* Holds the header tag.
* @package Sakura
*/
namespace Sakura\BBCode\Tags;
use Sakura\BBCode\TagBase;
/**
* Header tag.
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class Header extends TagBase
{
/**
* The pattern to match.
* @var string
*/
public static $pattern = "/\[header\](.*?)\[\/header\]/s";
/**
* The string to replace it with.
* @var string
*/
public static $replace = "<h1>$1</h1>";
}

29
app/BBCode/Tags/Image.php Normal file
View file

@ -0,0 +1,29 @@
<?php
/**
* Holds the image tag.
* @package Sakura
*/
namespace Sakura\BBCode\Tags;
use Sakura\BBCode\TagBase;
/**
* Image tag.
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class Image extends TagBase
{
/**
* The pattern to match.
* @var string
*/
public static $pattern = "/\[img\](.*?)\[\/img\]/s";
/**
* The string to replace it with.
* @var string
*/
public static $replace = "<img src='$1' alt='$1'>";
}

View file

@ -0,0 +1,29 @@
<?php
/**
* Holds the italics tag.
* @package Sakura
*/
namespace Sakura\BBCode\Tags;
use Sakura\BBCode\TagBase;
/**
* Italics tag.
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class Italics extends TagBase
{
/**
* The pattern to match.
* @var string
*/
public static $pattern = "/\[i\](.*?)\[\/i\]/s";
/**
* The string to replace it with.
* @var string
*/
public static $replace = "<i>$1</i>";
}

29
app/BBCode/Tags/Link.php Normal file
View file

@ -0,0 +1,29 @@
<?php
/**
* Holds the link tag.
* @package Sakura
*/
namespace Sakura\BBCode\Tags;
use Sakura\BBCode\TagBase;
/**
* Zelda tag.
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class Link extends TagBase
{
/**
* The pattern to match.
* @var string
*/
public static $pattern = "/\[url\](.*?)\[\/url\]/s";
/**
* The string to replace it with.
* @var string
*/
public static $replace = "<a href='$1'>$1</a>";
}

View file

@ -0,0 +1,39 @@
<?php
/**
* Holds the list tag.
* @package Sakura
*/
namespace Sakura\BBCode\Tags;
use Sakura\BBCode\TagBase;
/**
* List tag. Name is suffixed with Tag since "list" is a language construct.
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class ListTag extends TagBase
{
/**
* Parses the bbcode.
* @param string $text
* @return string
*/
public static function parse($text)
{
return preg_replace_callback(
'/\[list(?:\=(1|A|a|I|i))?\](.*?)\[\/list\]/s',
function ($matches) {
$content = preg_replace('/\[\*\](.*)/', '<li>$1</li>', $matches[2]);
if ($matches[1] !== '') {
return "<ol type='{$matches[1]}'>{$content}</ol>";
}
return "<ul>{$content}</ul>";
},
$text
);
}
}

View file

@ -0,0 +1,29 @@
<?php
/**
* Holds the named link tag.
* @package Sakura
*/
namespace Sakura\BBCode\Tags;
use Sakura\BBCode\TagBase;
/**
* Named link tag.
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class NamedLink extends TagBase
{
/**
* The pattern to match.
* @var string
*/
public static $pattern = "/\[url\=(.*?)\](.*?)\[\/url\]/s";
/**
* The string to replace it with.
* @var string
*/
public static $replace = "<a href='$1'>$2</a>";
}

View file

@ -0,0 +1,29 @@
<?php
/**
* Holds the named quote tag.
* @package Sakura
*/
namespace Sakura\BBCode\Tags;
use Sakura\BBCode\TagBase;
/**
* Named quote tag.
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class NamedQuote extends TagBase
{
/**
* The pattern to match.
* @var string
*/
public static $pattern = "/\[quote\=(.*?)\](.*)\[\/quote\]/s";
/**
* The string to replace it with.
* @var string
*/
public static $replace = "<blockquote><small>$1</small>$2</blockquote>";
}

View file

@ -0,0 +1,29 @@
<?php
/**
* Holds the newline tag.
* @package Sakura
*/
namespace Sakura\BBCode\Tags;
use Sakura\BBCode\TagBase;
/**
* Newline.
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class Newline extends TagBase
{
/**
* The pattern to match.
* @var string
*/
public static $pattern = "/\r\n|\r|\n/";
/**
* The string to replace it with.
* @var string
*/
public static $replace = "<br>";
}

29
app/BBCode/Tags/Quote.php Normal file
View file

@ -0,0 +1,29 @@
<?php
/**
* Holds the quote tag.
* @package Sakura
*/
namespace Sakura\BBCode\Tags;
use Sakura\BBCode\TagBase;
/**
* Quote tag.
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class Quote extends TagBase
{
/**
* The pattern to match.
* @var string
*/
public static $pattern = "/\[quote\](.*?)\[\/quote\]/s";
/**
* The string to replace it with.
* @var string
*/
public static $replace = "<blockquote>$1</blockquote>";
}

70
app/BBCode/Tags/Size.php Normal file
View file

@ -0,0 +1,70 @@
<?php
/**
* Holds the size tag.
* @package Sakura
*/
namespace Sakura\BBCode\Tags;
use Sakura\BBCode\TagBase;
/**
* Size tag.
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class Size extends TagBase
{
/**
* The maximum size a user can specify.
* @var int
*/
private static $maxSize = 200;
/**
* The minimum size a user can specify.
* @var int
*/
private static $minSize = 1;
/**
* Text aliases for the various sizes.
* @var array
*/
private static $aliases = [
'tiny' => 50,
'small' => 85,
'normal' => 100,
'large' => 150,
'huge' => 200,
];
/**
* Parses the bbcode.
* @param string $text
* @return string
*/
public static function parse($text)
{
return preg_replace_callback(
'/\[size\=([a-z0-9]+)\](.*?)\[\/size\]/s',
function ($matches) {
if (is_numeric($matches[1])) {
$size = intval($matches[1]);
if ($size < self::$minSize || $size > self::$maxSize) {
return $matches[0];
}
} elseif (in_array($matches[1], self::$aliases)) {
$size = self::$aliases[$matches[1]];
} else {
return $matches[0];
}
// we'll just use per cent for now, don't let this make it to production though
return "<div style='font-size: {$size}%'>{$matches[2]}</div>";
},
$text
);
}
}

View file

@ -0,0 +1,29 @@
<?php
/**
* Holds the spoiler tag.
* @package Sakura
*/
namespace Sakura\BBCode\Tags;
use Sakura\BBCode\TagBase;
/**
* Spoiler tag.
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class Spoiler extends TagBase
{
/**
* The pattern to match.
* @var string
*/
public static $pattern = "/\[spoiler\](.*?)\[\/spoiler\]/s";
/**
* The string to replace it with.
* @var string
*/
public static $replace = "<span class='spoiler'>$1</span>";
}

View file

@ -0,0 +1,29 @@
<?php
/**
* Holds the strike tag.
* @package Sakura
*/
namespace Sakura\BBCode\Tags;
use Sakura\BBCode\TagBase;
/**
* Strike tag.
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class Strike extends TagBase
{
/**
* The pattern to match.
* @var string
*/
public static $pattern = "/\[s\](.*?)\[\/s\]/s";
/**
* The string to replace it with.
* @var string
*/
public static $replace = "<del>$1</del>";
}

View file

@ -0,0 +1,29 @@
<?php
/**
* Holds the underline tag.
* @package Sakura
*/
namespace Sakura\BBCode\Tags;
use Sakura\BBCode\TagBase;
/**
* Underline tag.
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class Underline extends TagBase
{
/**
* The pattern to match.
* @var string
*/
public static $pattern = "/\[u\](.*?)\[\/u\]/s";
/**
* The string to replace it with.
* @var string
*/
public static $replace = "<u>$1</u>";
}

38
app/BBCode/Tags/User.php Normal file
View file

@ -0,0 +1,38 @@
<?php
/**
* Holds the user tag.
* @package Sakura
*/
namespace Sakura\BBCode\Tags;
use Sakura\BBCode\TagBase;
use Sakura\User as UserObject;
/**
* User tag.
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class User extends TagBase
{
/**
* Parses the bbcode.
* @param string $text
* @return string
*/
public static function parse($text)
{
return preg_replace_callback(
'/\[user\]([0-9]+)\[\/user\]/s',
function ($matches) {
$user = UserObject::construct($matches[1]);
$route = route('user.profile', $user->id);
return "<a href='{$route}' class='default username' style='color: {$user->colour}; "
. "text-shadow: 0 0 .3em {$user->colour}; font-weight: bold'>{$user->username}</a>";
},
$text
);
}
}

View file

@ -0,0 +1,30 @@
<?php
/**
* Holds the YouTube tag.
* @package Sakura
*/
namespace Sakura\BBCode\Tags;
use Sakura\BBCode\TagBase;
/**
* YouTube tag.
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class YouTube extends TagBase
{
/**
* The pattern to match.
* @var string
*/
public static $pattern = "/\[youtube\]([A-Za-z0-9\-\_]+)\[\/youtube\]/s";
/**
* The string to replace it with.
* @var string
*/
public static $replace = "<iframe width='560' height='315' src='https://www.youtube-nocookie.com/embed/$1'"
. " frameborder='0' allowfullscreen></iframe>";
}

View file

@ -1,175 +0,0 @@
<?php
/**
* Holds the BBcode handler.
* @package Sakura
*/
namespace Sakura;
/**
* BBcode handler.
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class BBcode
{
/**
* Holds the bbcode parsers.
* @var array
*/
public static $parsers = [
'bold' => [
'pattern' => '/\[b\](.*?)\[\/b\]/s',
'replace' => '<b>$1</b>',
'content' => '$1',
],
'italic' => [
'pattern' => '/\[i\](.*?)\[\/i\]/s',
'replace' => '<i>$1</i>',
'content' => '$1',
],
'underline' => [
'pattern' => '/\[u\](.*?)\[\/u\]/s',
'replace' => '<u>$1</u>',
'content' => '$1',
],
'linethrough' => [
'pattern' => '/\[s\](.*?)\[\/s\]/s',
'replace' => '<del>$1</del>',
'content' => '$1',
],
'header' => [
'pattern' => '/\[header\](.*?)\[\/header\]/s',
'replace' => '<h1>$1</h1>',
'content' => '$1',
],
'size' => [
'pattern' => '/\[size\=([1-7])\](.*?)\[\/size\]/s',
'replace' => '<font size="$1">$2</font>',
'content' => '$2',
],
'color' => [
'pattern' => '/\[color\=(#[A-f0-9]{6}|#[A-f0-9]{3})\](.*?)\[\/color\]/s',
'replace' => '<span style="color: $1">$2</span>',
'content' => '$2',
],
'center' => [
'pattern' => '/\[center\](.*?)\[\/center\]/s',
'replace' => '<div style="text-align: center;">$1</div>',
'content' => '$1',
],
'left' => [
'pattern' => '/\[left\](.*?)\[\/left\]/s',
'replace' => '<div style="text-align: left;">$1</div>',
'content' => '$1',
],
'right' => [
'pattern' => '/\[right\](.*?)\[\/right\]/s',
'replace' => '<div style="text-align: right;">$1</div>',
'content' => '$1',
],
'align' => [
'pattern' => '/\[align\=(left|center|right)\](.*?)\[\/align\]/s',
'replace' => '<div style="text-align: $1;">$2</div>',
'content' => '$2',
],
'quote' => [
'pattern' => '/\[quote\](.*?)\[\/quote\]/s',
'replace' => '<blockquote>$1</blockquote>',
'content' => '$1',
],
'namedquote' => [
'pattern' => '/\[quote\=(.*?)\](.*)\[\/quote\]/s',
'replace' => '<blockquote><small>$1</small>$2</blockquote>',
'content' => '$2',
],
'link' => [
'pattern' => '/\[url\](.*?)\[\/url\]/s',
'replace' => '<a href="$1">$1</a>',
'content' => '$1',
],
'namedlink' => [
'pattern' => '/\[url\=(.*?)\](.*?)\[\/url\]/s',
'replace' => '<a href="$1">$2</a>',
'content' => '$2',
],
'image' => [
'pattern' => '/\[img\](.*?)\[\/img\]/s',
'replace' => '<img src="$1" alt="$1">',
'content' => '$1',
],
'orderedlistnumerical' => [
'pattern' => '/\[list=1\](.*?)\[\/list\]/s',
'replace' => '<ol>$1</ol>',
'content' => '$1',
],
'orderedlistalpha' => [
'pattern' => '/\[list=a\](.*?)\[\/list\]/s',
'replace' => '<ol type="a">$1</ol>',
'content' => '$1',
],
'unorderedlist' => [
'pattern' => '/\[list\](.*?)\[\/list\]/s',
'replace' => '<ul>$1</ul>',
'content' => '$1',
],
'listitem' => [
'pattern' => '/\[\*\](.*)/',
'replace' => '<li>$1</li>',
'content' => '$1',
],
'code' => [
'pattern' => '/\[code\](.*?)\[\/code\]/s',
'replace' => '<pre><code>$1</code></pre>',
'content' => '$1',
],
'youtube' => [
'pattern' => '/\[youtube\](.*?)\[\/youtube\]/s',
'replace' => '<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/$1" frameborder="0" allowfullscreen></iframe>',
'content' => '$1',
],
'linebreak' => [
'pattern' => '/\r\n|\r|\n/',
'replace' => '<br>',
'content' => '',
],
];
/**
* Parse the emoticons.
* @param string $text
* @return string
*/
public static function parseEmoticons($text)
{
// Get emoticons from the database
$emotes = DB::table('emoticons')
->get();
// Parse all emoticons
foreach ($emotes as $emote) {
$image = "<img src='{$emote->emote_path}' alt='{$emote->emote_string}' class='emoticon'>";
$icon = preg_quote($emote->emote_string, '#');
$text = preg_replace("#{$icon}#", $image, $text);
}
// Return the parsed text
return $text;
}
/**
* Convert the parsed text to HTML.
* @param string $text
* @return string
*/
public static function toHTML($text)
{
$text = self::parseEmoticons($text);
foreach (self::$parsers as $parser) {
$text = preg_replace($parser['pattern'], $parser['replace'], $text);
}
return $text;
}
}

View file

@ -166,7 +166,7 @@ class Comment
public function parsed()
{
if (!$this->parsedCache) {
$this->parsedCache = BBcode::parseEmoticons(clean_string($this->text));
$this->parsedCache = BBCode\Parser::parseEmoticons(clean_string($this->text));
}
return $this->parsedCache;

View file

@ -6,7 +6,7 @@
namespace Sakura\Controllers;
use Sakura\BBcode;
use Sakura\BBCode\Parser as BBParser;
/**
* Helper controller.
@ -21,6 +21,6 @@ class HelperController extends Controller
*/
public function bbcodeParse()
{
return BBcode::toHTML(htmlentities($_POST['text'] ?? ''));
return BBParser::toHTML(htmlentities($_POST['text'] ?? ''));
}
}

View file

@ -125,7 +125,7 @@ class Post
}
// Parse the markup
$this->parsed = BBcode::toHTML(htmlentities($this->text));
$this->parsed = BBCode\Parser::toHTML(htmlentities($this->text));
}
/**

View file

@ -902,7 +902,7 @@ class User
*/
public function userPage()
{
return BBcode::toHTML(htmlentities($this->page));
return BBCode\Parser::toHTML(htmlentities($this->page));
}
/**
@ -911,7 +911,7 @@ class User
*/
public function signature()
{
return BBcode::toHTML(htmlentities($this->signature));
return BBCode\Parser::toHTML(htmlentities($this->signature));
}
/**