diff --git a/src/Content/Text/BBCode.php b/src/Content/Text/BBCode.php
index d10eed697a..bacfe27bb1 100644
--- a/src/Content/Text/BBCode.php
+++ b/src/Content/Text/BBCode.php
@@ -1805,132 +1805,7 @@ class BBCode
$text = '' . $text . '';
}
- $text = preg_replace_callback("/\[(url)\](.*?)\[\/url\]/ism", [self::class, 'sanitizeLinksCallback'], $text);
- $text = preg_replace_callback("/\[(url)\=(.*?)\](.*?)\[\/url\]/ism", [self::class, 'sanitizeLinksCallback'], $text);
-
- // Handle mentions and hashtag links
- if ($simple_html == self::DIASPORA) {
- // The ! is converted to @ since Diaspora only understands the @
- $text = preg_replace(
- "/([@!])\[url\=(.*?)\](.*?)\[\/url\]/ism",
- '@$3',
- $text
- );
- } elseif (in_array($simple_html, [self::OSTATUS, self::ACTIVITYPUB])) {
- $text = preg_replace(
- "/([@!])\[url\=(.*?)\](.*?)\[\/url\]/ism",
- '$1$3',
- $text
- );
- $text = preg_replace(
- "/([#])\[url\=(.*?)\](.*?)\[\/url\]/ism",
- '$1$3',
- $text
- );
- } elseif (in_array($simple_html, [self::INTERNAL, self::EXTERNAL, self::TWITTER_API])) {
- $text = preg_replace(
- "/([@!])\[url\=(.*?)\](.*?)\[\/url\]/ism",
- '$1$3',
- $text
- );
- } elseif ($simple_html == self::MASTODON_API) {
- $text = preg_replace(
- "/([@!])\[url\=(.*?)\](.*?)\[\/url\]/ism",
- '$1$3',
- $text
- );
- $text = preg_replace(
- "/([#])\[url\=(.*?)\](.*?)\[\/url\]/ism",
- '$1$3',
- $text
- );
- } else {
- $text = preg_replace("/([#@!])\[url\=(.*?)\](.*?)\[\/url\]/ism", '$1$3', $text);
- }
-
- // Bookmarks in red - will be converted to bookmarks in friendica
- $text = preg_replace("/#\^\[url\](.*?)\[\/url\]/ism", '[bookmark=$1]$1[/bookmark]', $text);
- $text = preg_replace("/#\^\[url\=(.*?)\](.*?)\[\/url\]/ism", '[bookmark=$1]$2[/bookmark]', $text);
- $text = preg_replace(
- "/#\[url\=.*?\]\^\[\/url\]\[url\=(.*?)\](.*?)\[\/url\]/i",
- "[bookmark=$1]$2[/bookmark]",
- $text
- );
-
- if (in_array($simple_html, [self::OSTATUS, self::TWITTER, self::BLUESKY])) {
- $text = preg_replace_callback("/([^#@!])\[url\=([^\]]*)\](.*?)\[\/url\]/ism", [self::class, 'expandLinksCallback'], $text);
- //$text = preg_replace("/[^#@!]\[url\=([^\]]*)\](.*?)\[\/url\]/ism", ' $2 [url]$1[/url]', $text);
- $text = preg_replace("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/ism", ' $2 [url]$1[/url]', $text);
- }
-
- // Perform URL Search
- if (!$for_plaintext && $try_oembed) {
- $text = preg_replace_callback("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/ism", $try_oembed_callback, $text);
- }
-
- $text = preg_replace("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/ism", '[url=$1]$2[/url]', $text);
-
- // Handle Diaspora posts
- $text = preg_replace_callback(
- "&\[url=/?posts/([^\[\]]*)\](.*)\[\/url\]&Usi",
- function ($match) {
- return "[url=" . DI::baseUrl() . "/display/" . $match[1] . "]" . $match[2] . "[/url]";
- },
- $text
- );
-
- $text = preg_replace_callback(
- "&\[url=/people\?q\=(.*)\](.*)\[\/url\]&Usi",
- function ($match) {
- 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, DI::baseUrl() . "/display/$1", $text);
-
- // Red compatibility, though the link can't be authenticated on Friendica
- $text = preg_replace("/\[zrl\=(.*?)\](.*?)\[\/zrl\]/ism", '[url=$1]$2[/url]', $text);
-
- if ($for_plaintext) {
- $text = preg_replace("(\[url\](.*?)\[\/url\])ism", " $1 ", $text);
- $text = preg_replace_callback("&\[url=([^\[\]]*)\]\[img\](.*)\[\/img\]\[\/url\]&Usi", [self::class, 'removePictureLinksCallback'], $text);
- }
-
- /* Tag conversion
- * Supports:
- * - #[url=][/url]
- * - [url=]#[/url]
- */
- self::performWithEscapedTags($text, ['url', 'share'], function ($text) use ($simple_html) {
- $text = preg_replace_callback("/(?:#\[url\=[^\[\]]*\]|\[url\=[^\[\]]*\]#)(.*?)\[\/url\]/ism", function ($matches) use ($simple_html) {
- if ($simple_html == self::ACTIVITYPUB) {
- return '#'
- . XML::escape($matches[1]) . '';
- } else {
- return '#'
- . XML::escape($matches[1]) . '';
- }
- }, $text);
- return $text;
- });
-
- if (in_array($simple_html, [self::INTERNAL, self::EXTERNAL, self::DIASPORA, self::OSTATUS, self::MASTODON_API, self::TWITTER_API, self::ACTIVITYPUB])) {
- $text = self::shortenLinkDescription($text);
- } else {
- $text = self::unifyLinks($text);
- }
-
- // We need no target="_blank" rel="noopener noreferrer" for local links
- // convert links start with DI::baseUrl() as local link without the target="_blank" rel="noopener noreferrer" attribute
- $text = preg_replace("/\[url\=(" . preg_quote(DI::baseUrl(), '/') . ".*?)\](.*?)\[\/url\]/ism", '$2', $text);
-
- $text = preg_replace("/\[url\=(.*?)\](.*?)\[\/url\]/ism", '$2', $text);
+ $text = self::convertUrlToHtml($text, $simple_html, $for_plaintext, $try_oembed, $try_oembed_callback);
// we may need to restrict this further if it picks up too many strays
// link acct:user@host to a webfinger profile redirector
@@ -2043,6 +1918,137 @@ class BBCode
return trim($text);
}
+ private static function convertUrlToHtml(string $text, int $simple_html, bool $for_plaintext, bool $try_oembed, \Closure $try_oembed_callback): string
+ {
+ $text = preg_replace_callback("/\[(url)\](.*?)\[\/url\]/ism", [self::class, 'sanitizeLinksCallback'], $text);
+ $text = preg_replace_callback("/\[(url)\=(.*?)\](.*?)\[\/url\]/ism", [self::class, 'sanitizeLinksCallback'], $text);
+
+ // Handle mentions and hashtag links
+ if ($simple_html == self::DIASPORA) {
+ // The ! is converted to @ since Diaspora only understands the @
+ $text = preg_replace(
+ "/([@!])\[url\=(.*?)\](.*?)\[\/url\]/ism",
+ '@$3',
+ $text
+ );
+ } elseif (in_array($simple_html, [self::OSTATUS, self::ACTIVITYPUB])) {
+ $text = preg_replace(
+ "/([@!])\[url\=(.*?)\](.*?)\[\/url\]/ism",
+ '$1$3',
+ $text
+ );
+ $text = preg_replace(
+ "/([#])\[url\=(.*?)\](.*?)\[\/url\]/ism",
+ '$1$3',
+ $text
+ );
+ } elseif (in_array($simple_html, [self::INTERNAL, self::EXTERNAL, self::TWITTER_API])) {
+ $text = preg_replace(
+ "/([@!])\[url\=(.*?)\](.*?)\[\/url\]/ism",
+ '$1$3',
+ $text
+ );
+ } elseif ($simple_html == self::MASTODON_API) {
+ $text = preg_replace(
+ "/([@!])\[url\=(.*?)\](.*?)\[\/url\]/ism",
+ '$1$3',
+ $text
+ );
+ $text = preg_replace(
+ "/([#])\[url\=(.*?)\](.*?)\[\/url\]/ism",
+ '$1$3',
+ $text
+ );
+ } else {
+ $text = preg_replace("/([#@!])\[url\=(.*?)\](.*?)\[\/url\]/ism", '$1$3', $text);
+ }
+
+ if ($for_plaintext) {
+ $text = preg_replace("(\[url\](.*?)\[\/url\])ism", " $1 ", $text);
+ $text = preg_replace_callback("&\[url=([^\[\]]*)\]\[img\](.*)\[\/img\]\[\/url\]&Usi", [self::class, 'removePictureLinksCallback'], $text);
+ }
+
+ // Bookmarks in red - will be converted to bookmarks in friendica
+ $text = preg_replace("/#\^\[url\](.*?)\[\/url\]/ism", '[bookmark=$1]$1[/bookmark]', $text);
+ $text = preg_replace("/#\^\[url\=(.*?)\](.*?)\[\/url\]/ism", '[bookmark=$1]$2[/bookmark]', $text);
+ $text = preg_replace(
+ "/#\[url\=.*?\]\^\[\/url\]\[url\=(.*?)\](.*?)\[\/url\]/i",
+ "[bookmark=$1]$2[/bookmark]",
+ $text
+ );
+
+ if (in_array($simple_html, [self::OSTATUS, self::TWITTER, self::BLUESKY])) {
+ $text = preg_replace_callback("/([^#@!])\[url\=([^\]]*)\](.*?)\[\/url\]/ism", [self::class, 'expandLinksCallback'], $text);
+ //$text = preg_replace("/[^#@!]\[url\=([^\]]*)\](.*?)\[\/url\]/ism", ' $2 [url]$1[/url]', $text);
+ $text = preg_replace("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/ism", ' $2 [url]$1[/url]', $text);
+ }
+
+ // Perform URL Search
+ if ($try_oembed) {
+ $text = preg_replace_callback("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/ism", $try_oembed_callback, $text);
+ }
+
+ $text = preg_replace("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/ism", '[url=$1]$2[/url]', $text);
+
+ // Handle Diaspora posts
+ $text = preg_replace_callback(
+ "&\[url=/?posts/([^\[\]]*)\](.*)\[\/url\]&Usi",
+ function ($match) {
+ return "[url=" . DI::baseUrl() . "/display/" . $match[1] . "]" . $match[2] . "[/url]";
+ },
+ $text
+ );
+
+ $text = preg_replace_callback(
+ "&\[url=/people\?q\=(.*)\](.*)\[\/url\]&Usi",
+ function ($match) {
+ 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, DI::baseUrl() . "/display/$1", $text);
+
+ /* Tag conversion
+ * Supports:
+ * - #[url=][/url]
+ * - [url=]#[/url]
+ */
+ self::performWithEscapedTags($text, ['url', 'share'], function ($text) use ($simple_html) {
+ $text = preg_replace_callback("/(?:#\[url\=[^\[\]]*\]|\[url\=[^\[\]]*\]#)(.*?)\[\/url\]/ism", function ($matches) use ($simple_html) {
+ if ($simple_html == self::ACTIVITYPUB) {
+ return '#'
+ . XML::escape($matches[1]) . '';
+ } else {
+ return '#'
+ . XML::escape($matches[1]) . '';
+ }
+ }, $text);
+ return $text;
+ });
+
+ // Red compatibility, though the link can't be authenticated on Friendica
+ $text = preg_replace("/\[zrl\=(.*?)\](.*?)\[\/zrl\]/ism", '[url=$1]$2[/url]', $text);
+
+ if (in_array($simple_html, [self::INTERNAL, self::EXTERNAL, self::DIASPORA, self::OSTATUS, self::MASTODON_API, self::TWITTER_API, self::ACTIVITYPUB])) {
+ $text = self::shortenLinkDescription($text);
+ } else {
+ $text = self::unifyLinks($text);
+ }
+
+ // We need no target="_blank" rel="noopener noreferrer" for local links
+ // convert links start with DI::baseUrl() as local link without the target="_blank" rel="noopener noreferrer" attribute
+ $text = preg_replace("/\[url\=(" . preg_quote(DI::baseUrl(), '/') . ".*?)\](.*?)\[\/url\]/ism", '$2', $text);
+
+ $text = preg_replace("/\[url\=(.*?)\](.*?)\[\/url\]/ism", '$2', $text);
+ return $text;
+ }
+
private static function escapeUrl(string $url): string
{
return str_replace(['[', ']'], ['[', ']'], $url);