Files
Gw0bm/app/Services/Markdown.php
2025-09-18 11:13:45 +02:00

343 lines
11 KiB
PHP

<?php
namespace App\Services;
use App\Models\User;
class Markdown extends \Parsedown {
function __construct() {
$this->setMarkupEscaped(true);
$this->setBreaksEnabled(true);
$this->setUrlsLinked(true);
$this->InlineTypes['@'][] = 'UserMention';
#$this->InlineTypes['%'][] = 'ColoredText';
$this->InlineTypes['['][] = 'KrebsText';
$this->InlineTypes['['][] = 'ReichText';
$this->InlineTypes['['][] = 'RainbowText';
$this->InlineTypes['['][] = 'SpoilerText';
$this->InlineTypes[':'][] = 'emojimatcherpng';
//$this->InlineTypes[':'][] = 'emojimatchermp4';
$this->InlineTypes[':'][] = 'ClickableTimestamp';
$this->inlineMarkerList .= '@';
}
protected function paragraph($Line) {
$Block = array(
'element' => array(
'name' => 'p',
'text' => $Line['text'],
'handler' => 'line',
'attributes' => [
'class' => 'comment'
]
),
);
return $Block;
}
// Matches the emojis in png format
protected function inlineemojimatcherpng($Excerpt) {
if (preg_match('/\:(\w+)\:/mUs', $Excerpt['text'], $matches)) {
$path = "images/comments/" . $matches[1];
$file_ext = "";
if(file_exists($path . ".webp"))
$file_ext = ".webp";
else if(file_exists($path . ".webp"))
$file_ext = ".webp";
if($file_ext === "")
return;
return [
'extent' => strlen($matches[0]),
'element' => [
'name' => 'img',
'handler' => 'line',
'attributes' => [
'class' => 'comment_emoji',
'src' => '/images/comments/' . $matches[1] . $file_ext,
'alt' => ':' . $matches[1] . ':',
'title' => ':' . $matches[1] . ':'
],
]
];
}
}
// Matches MP4 Emojis (Currently not possible due to Chromes shitty and malfunctioning autplay policy. Thanks idiots :*)
//protected function inlineemojimatchermp4($Excerpt) {
// if (preg_match('/\:(uwe)\:/mUs', $Excerpt['text'], $matches)) {
// return [
// 'extent' => strlen($matches[0]),
// 'element' => [
// 'name' => 'video',
// 'handler' => 'line',
// 'attributes' => [
// 'loop' => 'true',
// 'autoplay' => 'true',
// 'muted' => '',
// 'class' => 'comment_video',
// 'src' => '/images/comments/' . $matches[1] . '.mp4'
// ],
// ]
// ];
// }
//}
protected function inlineUserMention($Excerpt) {
if (preg_match('/\B@([\wÄÖÜäöü]+)/i', $Excerpt['context'], $matches)) {
if(User::whereUsername($matches[1])->count() > 0) {
return [
'extent' => strlen($matches[0]),
'element' => [
'name' => 'a',
'text' => $matches[0],
'attributes' => [
'href' => '/user/' . $matches[1], //link to username profile
'class' => 'user-mention', //style class of url
],
],
];
} else {
return [
'markup' => $matches[0],
'extent' => strlen($matches[0]),
];
}
}
}
// Matches [rb][/rb]
protected function inlineRainbowText($Excerpt) {
if (preg_match('/\[rb\](.+)\[\/rb]/mUs', $Excerpt['text'], $matches)) {
return [
'extent' => strlen($matches[0]),
'element' => [
'name' => 'span',
'handler' => 'line',
'text' => $matches[1],
'attributes' => [
'class' => 'rainbow'
],
]
];
}
}
// Matches [spoiler][/spoiler]
protected function inlineSpoilerText($Excerpt) {
if (preg_match('/\[spoiler\](.+)\[\/spoiler]/mUs', $Excerpt['text'], $matches)) {
return [
'extent' => strlen($matches[0]),
'element' => [
'name' => 'span',
'handler' => 'line',
'text' => $matches[1],
'attributes' => [
'class' => 'spoiler'
],
]
];
}
}
// Matches [krebs][/krebs]
protected function inlineKrebsText($Excerpt) {
if (preg_match('/\[krebs\](.+)\[\/krebs]/mUs', $Excerpt['text'], $matches)) {
return [
'extent' => strlen($matches[0]),
'element' => [
'name' => 'span',
'handler' => 'line',
'text' => $matches[1],
'attributes' => [
'class' => 'anim'
],
]
];
}
}
// Matches Reichtext
protected function inlineReichText($Excerpt) {
if (preg_match('/\[reich\](.+)\[\/reich]/mUs', $Excerpt['text'], $matches)) {
return [
'extent' => strlen($matches[0]),
'element' => [
'name' => 'span',
'handler' => 'line',
'text' => $matches[1],
'attributes' => [
'class' => 'reich'
],
]
];
}
}
// Matches %text% <- literally wtf error
/*protected function inlineColoredText($Excerpt) {
if (preg_match('/%(.+)%/', $Excerpt['text'], $matches)) {
return [
'extent' => strlen($matches[0]),
'element' => [
'name' => 'span',
'text' => $matches[1],
'attributes' => [
'class' => ''
],
]
];
}
}*/
//Greentext
protected function blockQuote($Excerpt) {
if (preg_match('/^>[ ]?(.*)/', $Excerpt['text'], $matches)) {
$Block = [
'element' => [
'name' => 'blockquote',
'handler' => 'lines',
'text' => (array) ('&gt;' . $matches[1]),
],
];
return $Block;
}
}
protected function blockQuoteContinue($Excerpt, array $Block) {
if ($Excerpt['text'][0] === '>' && preg_match('/^>[ ]?(.*)/', $Excerpt['text'], $matches)) {
if (isset($Block['interrupted'])) {
$Block['element']['text'][] = '';
unset($Block['interrupted']);
}
$Block['element']['text'][] = '&gt;' . $matches[1];
return $Block;
}
if (!isset($Block['interrupted'])) {
$Block['element']['text'][] = '&gt;' . $Excerpt['text'];
return $Block;
}
}
// Disable Lists
protected function blockList($Excerpt) {
return;
}
protected function blockListContinue($Excerpt, array $block) {
return;
}
// Disable headers
protected function blockHeader($Excerpt) {
return;
}
protected function blockSetextHeader($Excerpt, array $block = null) {
return;
}
protected function blockTable($Excerpt, array $block = null) {
return;
}
// Disable markdown links
protected function inlineLink($Excerpt) {
return;
}
// Disable markdown images
protected function inlineImage($Excerpt) {
return;
}
// Differentiate between internal and external urls and images
protected function inlineUrl($Excerpt) {
$e = parent::inlineUrl($Excerpt);
if (is_null($e)) return;
$e['element']['attributes']['href'] = htmlspecialchars($e['element']['attributes']['href']);
if (static::isImage($e['element']['attributes']['href'])) {
$e['element']['name'] = 'img';
$e['element']['attributes']['src'] = $e['element']['attributes']['href'];
$e['element']['attributes']['alt'] = 'Image';
$e['element']['attributes']['class'] = 'comment_image';
$e['element']['attributes']['loading'] = 'lazy';
unset($e['element']['attributes']['href']);
unset($e['element']['text']);
return $e;
}
if (!static::isInternal($e['element']['attributes']['href'])) {
$e['element']['attributes']['target'] = '_blank';
$e['element']['attributes']['rel'] = 'extern';
} else {
$url = parse_url($e['element']['text']);
$text = $url['path'];
if (isset($url['query'])) {
$text .= '?' . $url['query'];
}
if (isset($url['fragment'])) {
$text .= '#' . $url['fragment'];
}
$e['element']['text'] = $text;
}
return $e;
}
protected function inlineClickableTimestamp($Excerpt) {
if (preg_match('/(?<=\s|^)([0-5]?\d:[0-5]\d)(?=\s|$)/', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE)) {
return [
'extent' => strlen($matches[0][0]),
'position' => $matches[0][1],
'element' => [
'name' => 'a',
'text' => $matches[0][0],
'attributes' => [
'href' => '#',
'class' => 'comment_clickable_timestamp'
]
]
];
}
}
private static function isInternal($url) {
$host = parse_url($url, PHP_URL_HOST);
$currhost = $_SERVER['SERVER_NAME'];
if (0 === strpos($host, 'www')) {
$host = substr($host, 4);
}
if (0 === strpos($currhost, 'www')) {
$currhost = substr($currhost, 4);
}
return $host === $currhost;
}
private static function isImage($url) {
$cfg = config('comments');
$allowedHosters = $cfg['allowedHosters'];
$allowedExtensions = $cfg['allowedImageFileExtensions'];
$url = parse_url($url);
if (isset($url['path'])) {
$ext = pathinfo($url['path'], PATHINFO_EXTENSION);
if (in_array($ext, $allowedExtensions)) {
if (isset($url['scheme']) && $url['scheme'] === 'https') {
foreach($allowedHosters as $hoster) {
foreach($hoster as $regex) {
if(preg_match($regex, $url['host'])) {
return true;
}
}
}
return false;
}
}
}
return false;
}
}