use DOMDocument;
use DOMXPath;
use Exception;
-use Friendica\BaseObject;
use Friendica\Content\OEmbed;
use Friendica\Content\Smilies;
use Friendica\Core\Cache;
use Friendica\Core\Protocol;
use Friendica\Core\Renderer;
use Friendica\Core\System;
+use Friendica\DI;
use Friendica\Model\Contact;
use Friendica\Model\Event;
use Friendica\Model\Photo;
use Friendica\Util\Strings;
use Friendica\Util\XML;
-class BBCode extends BaseObject
+class BBCode
{
/**
* @brief Fetches attachment data that were generated the old way
$post['url'] = $links[0][1];
}
+ // Simplify "video" element
+ $post['text'] = preg_replace('(\[video.*?\ssrc\s?=\s?([^\s\]]+).*?\].*?\[/video\])ism', '[video]$1[/video]', $post['text']);
+
// Now count the number of external media links
preg_match_all("(\[vimeo\](.*?)\[\/vimeo\])ism", $post['text'], $links1, PREG_SET_ORDER);
preg_match_all("(\[youtube\\](.*?)\[\/youtube\\])ism", $post['text'], $links2, PREG_SET_ORDER);
*/
public static function removeAttachment($body, $no_link_desc = false)
{
- return preg_replace_callback("/\[attachment (.*)\](.*?)\[\/attachment\]/ism",
+ return preg_replace_callback("/\s*\[attachment (.*)\](.*?)\[\/attachment\]\s*/ism",
function ($match) use ($no_link_desc) {
$attach_data = self::getAttachmentData($match[0]);
if (empty($attach_data['url'])) {
return $match[0];
} elseif (empty($attach_data['title']) || $no_link_desc) {
- return '[url]' . $attach_data['url'] . "[/url]\n";
+ return "\n[url]" . $attach_data['url'] . "[/url]\n";
} else {
- return '[url=' . $attach_data['url'] . ']' . $attach_data['title'] . "[/url]\n";
+ return "\n[url=" . $attach_data['url'] . ']' . $attach_data['title'] . "[/url]\n";
}
}, $body);
}
foreach ($matches as $mtch) {
Logger::log('scale_external_image: ' . $mtch[1]);
- $hostname = str_replace('www.', '', substr(System::baseUrl(), strpos(System::baseUrl(), '://') + 3));
+ $hostname = str_replace('www.', '', substr(DI::baseUrl(), strpos(DI::baseUrl(), '://') + 3));
if (stristr($mtch[1], $hostname)) {
continue;
}
$text = Cache::get($cache_key);
if (is_null($text)) {
- $a = self::getApp();
+ $a = DI::app();
$stamp1 = microtime(true);
@curl_exec($ch);
$curl_info = @curl_getinfo($ch);
- $a->getProfiler()->saveTimestamp($stamp1, "network", System::callstack());
+ DI::profiler()->saveTimestamp($stamp1, "network", System::callstack());
if (substr($curl_info['content_type'], 0, 6) == 'image/') {
$text = "[url=" . $match[1] . ']' . $match[1] . "[/url]";
private static function cleanPictureLinksCallback($match)
{
- $a = self::getApp();
+ $a = DI::app();
// When the picture link is the own photo path then we can avoid fetching the link
- $own_photo_url = preg_quote(Strings::normaliseLink($a->getBaseURL()) . '/photos/');
+ $own_photo_url = preg_quote(Strings::normaliseLink(DI::baseUrl()->get()) . '/photos/');
if (preg_match('|' . $own_photo_url . '.*?/image/|', Strings::normaliseLink($match[1]))) {
if (!empty($match[3])) {
$text = '[img=' . str_replace('-1.', '-0.', $match[2]) . ']' . $match[3] . '[/img]';
@curl_exec($ch);
$curl_info = @curl_getinfo($ch);
- $a->getProfiler()->saveTimestamp($stamp1, "network", System::callstack());
+ DI::profiler()->saveTimestamp($stamp1, "network", System::callstack());
// if its a link to a picture then embed this picture
if (substr($curl_info['content_type'], 0, 6) == 'image/') {
*/
public static function convert($text, $try_oembed = true, $simple_html = 0, $for_plaintext = false)
{
- $a = self::getApp();
+ $a = DI::app();
/*
* preg_match_callback function to replace potential Oembed tags with Oembed content
function ($matches) use (&$codeblocks) {
$return = '#codeblock-' . count($codeblocks) . '#';
if (strpos($matches[2], "\n") !== false) {
- $codeblocks[] = '<pre><code class="language-' . trim($matches[1]) . '">' . trim($matches[2], "\n\r") . '</code></pre>';
+ $codeblocks[] = '<pre><code class="language-' . trim($matches[1]) . '">' . htmlspecialchars(trim($matches[2], "\n\r"), ENT_NOQUOTES, 'UTF-8') . '</code></pre>';
} else {
- $codeblocks[] = '<code>' . $matches[2] . '</code>';
+ $codeblocks[] = '<code>' . htmlspecialchars($matches[2], ENT_NOQUOTES, 'UTF-8') . '</code>';
}
return $return;
$text = str_replace('[hr]', '<hr />', $text);
if (!$for_plaintext) {
+ $escaped = [];
+
+ // Escaping BBCodes susceptible to contain rogue URL we don'' want the autolinker to catch
+ $text = preg_replace_callback('#\[(url|img|audio|video|youtube|vimeo|share|attachment|iframe|bookmark).+?\[/\1\]#ism',
+ function ($matches) use (&$escaped) {
+ $return = '{escaped-' . count($escaped) . '}';
+ $escaped[] = $matches[0];
+
+ return $return;
+ },
+ $text
+ );
+
// Autolinker for isolated URLs
$text = preg_replace(Strings::autoLinkRegEx(), '[url]$1[/url]', $text);
+
+ // Restoring escaped blocks
+ $text = preg_replace_callback('/{escaped-([0-9]+)}/iU',
+ function ($matches) use ($escaped) {
+ return $escaped[intval($matches[1])] ?? $matches[0];
+ },
+ $text
+ );
}
// This is actually executed in Item::prepareBody()
$text = preg_replace("/\[img\](.*?)\[\/img\]/ism", '<img src="$1" alt="' . L10n::t('Image/photo') . '" />', $text);
$text = preg_replace("/\[zmg\](.*?)\[\/zmg\]/ism", '<img src="$1" alt="' . L10n::t('Image/photo') . '" />', $text);
- $text = preg_replace("/\[crypt\](.*?)\[\/crypt\]/ism", '<br/><img src="' .System::baseUrl() . '/images/lock_icon.gif" alt="' . L10n::t('Encrypted content') . '" title="' . L10n::t('Encrypted content') . '" /><br />', $text);
- $text = preg_replace("/\[crypt(.*?)\](.*?)\[\/crypt\]/ism", '<br/><img src="' .System::baseUrl() . '/images/lock_icon.gif" alt="' . L10n::t('Encrypted content') . '" title="' . '$1' . ' ' . L10n::t('Encrypted content') . '" /><br />', $text);
- //$Text = preg_replace("/\[crypt=(.*?)\](.*?)\[\/crypt\]/ism", '<br/><img src="' .System::baseUrl() . '/images/lock_icon.gif" alt="' . L10n::t('Encrypted content') . '" title="' . '$1' . ' ' . L10n::t('Encrypted content') . '" /><br />', $Text);
+ $text = preg_replace("/\[crypt\](.*?)\[\/crypt\]/ism", '<br/><img src="' .DI::baseUrl() . '/images/lock_icon.gif" alt="' . L10n::t('Encrypted content') . '" title="' . L10n::t('Encrypted content') . '" /><br />', $text);
+ $text = preg_replace("/\[crypt(.*?)\](.*?)\[\/crypt\]/ism", '<br/><img src="' .DI::baseUrl() . '/images/lock_icon.gif" alt="' . L10n::t('Encrypted content') . '" title="' . '$1' . ' ' . L10n::t('Encrypted content') . '" /><br />', $text);
+ //$Text = preg_replace("/\[crypt=(.*?)\](.*?)\[\/crypt\]/ism", '<br/><img src="' .DI::baseUrl() . '/images/lock_icon.gif" alt="' . L10n::t('Encrypted content') . '" title="' . '$1' . ' ' . L10n::t('Encrypted content') . '" /><br />', $Text);
+
+ // Simplify "video" element
+ $text = preg_replace('(\[video.*?\ssrc\s?=\s?([^\s\]]+).*?\].*?\[/video\])ism', '[video]$1[/video]', $text);
// Try to Oembed
if ($try_oembed) {
$text = preg_replace_callback(
"&\[url=/?posts/([^\[\]]*)\](.*)\[\/url\]&Usi",
function ($match) {
- return "[url=" . System::baseUrl() . "/display/" . $match[1] . "]" . $match[2] . "[/url]";
+ return "[url=" . DI::baseUrl() . "/display/" . $match[1] . "]" . $match[2] . "[/url]";
}, $text
);
$text = preg_replace_callback(
"&\[url=/people\?q\=(.*)\](.*)\[\/url\]&Usi",
function ($match) {
- return "[url=" . System::baseUrl() . "/search?search=%40" . $match[1] . "]" . $match[2] . "[/url]";
+ return "[url=" . DI::baseUrl() . "/search?search=%40" . $match[1] . "]" . $match[2] . "[/url]";
}, $text
);
// Server independent link to posts and comments
// See issue: https://github.com/diaspora/diaspora_federation/issues/75
$expression = "=diaspora://.*?/post/([0-9A-Za-z\-_@.:]{15,254}[0-9A-Za-z])=ism";
- $text = preg_replace($expression, System::baseUrl()."/display/$1", $text);
+ $text = preg_replace($expression, DI::baseUrl()."/display/$1", $text);
/* Tag conversion
* Supports:
*/
$text = preg_replace_callback("/(?:#\[url\=[^\[\]]*\]|\[url\=[^\[\]]*\]#)(.*?)\[\/url\]/ism", function($matches) {
return '#<a href="'
- . System::baseUrl() . '/search?tag=' . rawurlencode($matches[1])
+ . DI::baseUrl() . '/search?tag=' . rawurlencode($matches[1])
. '" class="tag" rel="tag" title="' . XML::escape($matches[1]) . '">'
. XML::escape($matches[1])
. '</a>';
}, $text);
// We need no target="_blank" for local links
- // convert links start with System::baseUrl() as local link without the target="_blank" attribute
- $escapedBaseUrl = preg_quote(System::baseUrl(), '/');
+ // convert links start with DI::baseUrl() as local link without the target="_blank" attribute
+ $escapedBaseUrl = preg_quote(DI::baseUrl(), '/');
$text = preg_replace("/\[url\](".$escapedBaseUrl.".*?)\[\/url\]/ism", '<a href="$1">$1</a>', $text);
$text = preg_replace("/\[url\=(".$escapedBaseUrl.".*?)\](.*?)\[\/url\]/ism", '<a href="$1">$2</a>', $text);
// we may need to restrict this further if it picks up too many strays
// link acct:user@host to a webfinger profile redirector
- $text = preg_replace('/acct:([^@]+)@((?!\-)(?:[a-zA-Z\d\-]{0,62}[a-zA-Z\d]\.){1,126}(?!\d+)[a-zA-Z\d]{1,63})/', '<a href="' . System::baseUrl() . '/acctlink?addr=$1@$2" target="extlink">acct:$1@$2</a>', $text);
+ $text = preg_replace('/acct:([^@]+)@((?!\-)(?:[a-zA-Z\d\-]{0,62}[a-zA-Z\d]\.){1,126}(?!\d+)[a-zA-Z\d]{1,63})/', '<a href="' . DI::baseUrl() . '/acctlink?addr=$1@$2" target="extlink">acct:$1@$2</a>', $text);
// Perform MAIL Search
$text = preg_replace("/\[mail\](.*?)\[\/mail\]/", '<a href="mailto:$1">$1</a>', $text);
*/
public static function toMarkdown($text, $for_diaspora = true)
{
- $a = self::getApp();
+ $a = DI::app();
$original_text = $text;
$text = self::convert($text, false, 4);
}
- // mask some special HTML chars from conversation to markdown
- $text = str_replace(['<', '>', '&'], ['&_lt_;', '&_gt_;', '&_amp_;'], $text);
-
// If a link is followed by a quote then there should be a newline before it
// Maybe we should make this newline at every time before a quote.
$text = str_replace(['</a><blockquote>'], ['</a><br><blockquote>'], $text);
// Now convert HTML to Markdown
$text = HTML::toMarkdown($text);
- // unmask the special chars back to HTML
- $text = str_replace(['&\_lt\_;', '&\_gt\_;', '&\_amp\_;'], ['<', '>', '&'], $text);
-
- $a->getProfiler()->saveTimestamp($stamp1, "parser", System::callstack());
+ DI::profiler()->saveTimestamp($stamp1, "parser", System::callstack());
// Libertree has a problem with escaped hashtags.
$text = str_replace(['\#'], ['#'], $text);