';
}
}
@@ -709,7 +726,6 @@ class BBCode extends BaseObject
/**
* Converts [url] BBCodes in a format that looks fine on Mastodon. (callback function)
*
- * @brief Converts [url] BBCodes in a format that looks fine on Mastodon. (callback function)
* @param array $match Array with the matching values
* @return string reformatted link including HTML codes
*/
@@ -730,18 +746,20 @@ class BBCode extends BaseObject
}
/**
- * @brief Converts [url] BBCodes in a format that looks fine on ActivityPub systems.
+ * Converts [url] BBCodes in a format that looks fine on ActivityPub systems.
+ *
* @param string $url URL that is about to be reformatted
* @return string reformatted link including HTML codes
*/
private static function convertUrlForActivityPub($url)
{
- $html = '%s';
+ $html = '%s';
return sprintf($html, $url, self::getStyledURL($url));
}
/**
* Converts an URL in a nicer format (without the scheme and possibly shortened)
+ *
* @param string $url URL that is about to be reformatted
* @return string reformatted link
*/
@@ -924,7 +942,7 @@ class BBCode extends BaseObject
// it loops over the array starting from the first element and going sequentially
// to the last element
$newbody = str_replace('[$#saved_image' . $cnt . '#$]',
- '', $newbody);
+ '', $newbody);
$cnt++;
}
@@ -953,12 +971,11 @@ class BBCode extends BaseObject
public static function convertShare($text, callable $callback)
{
$return = preg_replace_callback(
- "/(.*?)\[share(.*?)\](.*?)\[\/share\]/ism",
+ "/(.*?)\[share(.*?)\](.*)\[\/share\]/ism",
function ($match) use ($callback) {
$attribute_string = $match[2];
-
$attributes = [];
- foreach(['author', 'profile', 'avatar', 'link', 'posted'] as $field) {
+ foreach (['author', 'profile', 'avatar', 'link', 'posted'] as $field) {
preg_match("/$field=(['\"])(.+?)\\1/ism", $attribute_string, $matches);
$attributes[$field] = html_entity_decode($matches[2] ?? '', ENT_QUOTES, 'UTF-8');
}
@@ -979,7 +996,8 @@ class BBCode extends BaseObject
Contact::getIdForURL($attributes['profile'], 0, true, $default);
$author_contact = Contact::getDetailsByURL($attributes['profile']);
- $author_contact['addr'] = ($author_contact['addr'] ?? '') ?: Protocol::getAddrFromProfileUrl($attributes['profile']);
+ $author_contact['url'] = ($author_contact['url'] ?? $attributes['profile']);
+ $author_contact['addr'] = ($author_contact['addr'] ?? '') ?: Protocol::getAddrFromProfileUrl($attributes['profile']);
$attributes['author'] = ($author_contact['name'] ?? '') ?: $attributes['author'];
$attributes['avatar'] = ($author_contact['micro'] ?? '') ?: $attributes['avatar'];
@@ -1016,13 +1034,10 @@ class BBCode extends BaseObject
$mention = Protocol::formatMention($attributes['profile'], $attributes['author']);
switch ($simplehtml) {
- case 1:
- $text = ($is_quote_share? ' ' : '') . '
' . "\n";
+ break;
default:
- // Transforms quoted tweets in rich attachments to avoid nested tweets
- if (stripos(Strings::normaliseLink($attributes['link']), 'http://twitter.com/') === 0 && OEmbed::isAllowedURL($attributes['link'])) {
- try {
- $text = ($is_quote_share? ' ' : '') . OEmbed::getHTML($attributes['link']);
- } catch (Exception $e) {
- $text = ($is_quote_share? ' ' : '') . sprintf('[bookmark=%s]%s[/bookmark]', $attributes['link'], $content);
- }
- } else {
- $text = ($is_quote_share? "\n" : '');
-
- $tpl = Renderer::getMarkupTemplate('shared_content.tpl');
- $text .= Renderer::replaceMacros($tpl, [
- '$profile' => $attributes['profile'],
- '$avatar' => $attributes['avatar'],
- '$author' => $attributes['author'],
- '$link' => $attributes['link'],
- '$posted' => $attributes['posted'],
- '$content' => trim($content)
- ]);
- }
+ $text = ($is_quote_share? "\n" : '');
+
+ $tpl = Renderer::getMarkupTemplate('shared_content.tpl');
+ $text .= Renderer::replaceMacros($tpl, [
+ '$profile' => $attributes['profile'],
+ '$avatar' => $attributes['avatar'],
+ '$author' => $attributes['author'],
+ '$link' => $attributes['link'],
+ '$posted' => $attributes['posted'],
+ '$content' => trim($content)
+ ]);
break;
}
@@ -1085,10 +1091,10 @@ class BBCode extends BaseObject
private static function removePictureLinksCallback($match)
{
$cache_key = 'remove:' . $match[1];
- $text = Cache::get($cache_key);
+ $text = DI::cache()->get($cache_key);
if (is_null($text)) {
- $a = self::getApp();
+ $a = DI::app();
$stamp1 = microtime(true);
@@ -1099,7 +1105,7 @@ class BBCode extends BaseObject
@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]";
@@ -1127,7 +1133,7 @@ class BBCode extends BaseObject
}
}
}
- Cache::set($cache_key, $text);
+ DI::cache()->set($cache_key, $text);
}
return $text;
@@ -1144,10 +1150,10 @@ class BBCode extends BaseObject
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]';
@@ -1158,7 +1164,7 @@ class BBCode extends BaseObject
}
$cache_key = 'clean:' . $match[1];
- $text = Cache::get($cache_key);
+ $text = DI::cache()->get($cache_key);
if (!is_null($text)) {
return $text;
}
@@ -1173,7 +1179,7 @@ class BBCode extends BaseObject
@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/') {
@@ -1209,7 +1215,7 @@ class BBCode extends BaseObject
}
}
}
- Cache::set($cache_key, $text);
+ DI::cache()->set($cache_key, $text);
return $text;
}
@@ -1222,7 +1228,7 @@ class BBCode extends BaseObject
}
/**
- * @brief Converts a BBCode message to HTML message
+ * Converts a BBCode message to HTML message
*
* BBcode 2 HTML was written by WAY2WEB.net
* extended to work with Mistpark/Friendica - Mike Macgirvin
@@ -1246,619 +1252,630 @@ class BBCode extends BaseObject
* @return string
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
- public static function convert($text, $try_oembed = true, $simple_html = 0, $for_plaintext = false)
+ public static function convert($text, $try_oembed = true, $simple_html = self::INTERNAL, $for_plaintext = false)
{
- $a = self::getApp();
-
- /*
- * preg_match_callback function to replace potential Oembed tags with Oembed content
- *
- * $match[0] = [tag]$url[/tag] or [tag=$url]$title[/tag]
- * $match[1] = $url
- * $match[2] = $title or absent
- */
- $try_oembed_callback = function ($match)
- {
- $url = $match[1];
- $title = $match[2] ?? null;
-
- try {
- $return = OEmbed::getHTML($url, $title);
- } catch (Exception $ex) {
- $return = $match[0];
- }
-
- return $return;
- };
-
- // Extracting code blocks before the whitespace processing and the autolinker
- $codeblocks = [];
-
- $text = preg_replace_callback("#\[code(?:=([^\]]*))?\](.*?)\[\/code\]#ism",
- function ($matches) use (&$codeblocks) {
- $return = '#codeblock-' . count($codeblocks) . '#';
- if (strpos($matches[2], "\n") !== false) {
- $codeblocks[] = '
' . trim($matches[2], "\n\r") . '
';
- } else {
- $codeblocks[] = '' . $matches[2] . '';
- }
-
- return $return;
- },
- $text
- );
+ $a = DI::app();
+
+ $text = self::performWithEscapedTags($text, ['code'], function ($text) use ($try_oembed, $simple_html, $for_plaintext, $a) {
+ $text = self::performWithEscapedTags($text, ['noparse', 'nobb', 'pre'], function ($text) use ($try_oembed, $simple_html, $for_plaintext, $a) {
+ /*
+ * preg_match_callback function to replace potential Oembed tags with Oembed content
+ *
+ * $match[0] = [tag]$url[/tag] or [tag=$url]$title[/tag]
+ * $match[1] = $url
+ * $match[2] = $title or absent
+ */
+ $try_oembed_callback = function ($match)
+ {
+ $url = $match[1];
+ $title = $match[2] ?? null;
- // Hide all [noparse] contained bbtags by spacefying them
- // POSSIBLE BUG --> Will the 'preg' functions crash if there's an embedded image?
-
- $text = preg_replace_callback("/\[noparse\](.*?)\[\/noparse\]/ism", 'self::escapeNoparseCallback', $text);
- $text = preg_replace_callback("/\[nobb\](.*?)\[\/nobb\]/ism", 'self::escapeNoparseCallback', $text);
- $text = preg_replace_callback("/\[pre\](.*?)\[\/pre\]/ism", 'self::escapeNoparseCallback', $text);
+ try {
+ $return = OEmbed::getHTML($url, $title);
+ } catch (Exception $ex) {
+ $return = $match[0];
+ }
- // Remove the abstract element. It is a non visible element.
- $text = self::stripAbstract($text);
+ return $return;
+ };
- // Move all spaces out of the tags
- $text = preg_replace("/\[(\w*)\](\s*)/ism", '$2[$1]', $text);
- $text = preg_replace("/(\s*)\[\/(\w*)\]/ism", '[/$2]$1', $text);
- // Extract the private images which use data urls since preg has issues with
- // large data sizes. Stash them away while we do bbcode conversion, and then put them back
- // in after we've done all the regex matching. We cannot use any preg functions to do this.
- $extracted = self::extractImagesFromItemBody($text);
- $text = $extracted['body'];
- $saved_image = $extracted['images'];
+ // Remove the abstract element. It is a non visible element.
+ $text = self::stripAbstract($text);
- // If we find any event code, turn it into an event.
- // After we're finished processing the bbcode we'll
- // replace all of the event code with a reformatted version.
+ // Move all spaces out of the tags
+ $text = preg_replace("/\[(\w*)\](\s*)/ism", '$2[$1]', $text);
+ $text = preg_replace("/(\s*)\[\/(\w*)\]/ism", '[/$2]$1', $text);
- $ev = Event::fromBBCode($text);
+ // Extract the private images which use data urls since preg has issues with
+ // large data sizes. Stash them away while we do bbcode conversion, and then put them back
+ // in after we've done all the regex matching. We cannot use any preg functions to do this.
- // Replace any html brackets with HTML Entities to prevent executing HTML or script
- // Don't use strip_tags here because it breaks [url] search by replacing & with amp
+ $extracted = self::extractImagesFromItemBody($text);
+ $text = $extracted['body'];
+ $saved_image = $extracted['images'];
- $text = str_replace("<", "<", $text);
- $text = str_replace(">", ">", $text);
+ // If we find any event code, turn it into an event.
+ // After we're finished processing the bbcode we'll
+ // replace all of the event code with a reformatted version.
- // remove some newlines before the general conversion
- $text = preg_replace("/\s?\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism", "[share$1]$2[/share]", $text);
- $text = preg_replace("/\s?\[quote(.*?)\]\s?(.*?)\s?\[\/quote\]\s?/ism", "[quote$1]$2[/quote]", $text);
+ $ev = Event::fromBBCode($text);
- // when the content is meant exporting to other systems then remove the avatar picture since this doesn't really look good on these systems
- if (!$try_oembed) {
- $text = preg_replace("/\[share(.*?)avatar\s?=\s?'.*?'\s?(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism", "\n[share$1$2]$3[/share]", $text);
- }
+ // Replace any html brackets with HTML Entities to prevent executing HTML or script
+ // Don't use strip_tags here because it breaks [url] search by replacing & with amp
- // Convert new line chars to html tags
+ $text = str_replace("<", "<", $text);
+ $text = str_replace(">", ">", $text);
- // nlbr seems to be hopelessly messed up
- // $Text = nl2br($Text);
+ // remove some newlines before the general conversion
+ $text = preg_replace("/\s?\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism", "[share$1]$2[/share]", $text);
+ $text = preg_replace("/\s?\[quote(.*?)\]\s?(.*?)\s?\[\/quote\]\s?/ism", "[quote$1]$2[/quote]", $text);
- // We'll emulate it.
+ // when the content is meant exporting to other systems then remove the avatar picture since this doesn't really look good on these systems
+ if (!$try_oembed) {
+ $text = preg_replace("/\[share(.*?)avatar\s?=\s?'.*?'\s?(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism", "\n[share$1$2]$3[/share]", $text);
+ }
- $text = trim($text);
- $text = str_replace("\r\n", "\n", $text);
-
- // Remove linefeeds inside of the table elements. See issue #6799
- $search = ["\n[th]", "[th]\n", " [th]", "\n[/th]", "[/th]\n", "[/th] ",
- "\n[td]", "[td]\n", " [td]", "\n[/td]", "[/td]\n", "[/td] ",
- "\n[tr]", "[tr]\n", " [tr]", "[tr] ", "\n[/tr]", "[/tr]\n", " [/tr]", "[/tr] ",
- "[table]\n", "[table] ", " [table]", "\n[/table]", " [/table]", "[/table] "];
- $replace = ["[th]", "[th]", "[th]", "[/th]", "[/th]", "[/th]",
- "[td]", "[td]", "[td]", "[/td]", "[/td]", "[/td]",
- "[tr]", "[tr]", "[tr]", "[tr]", "[/tr]", "[/tr]", "[/tr]", "[/tr]",
- "[table]", "[table]", "[table]", "[/table]", "[/table]", "[/table]"];
- do {
- $oldtext = $text;
- $text = str_replace($search, $replace, $text);
- } while ($oldtext != $text);
-
- // Replace these here only once
- $search = ["\n[table]", "[/table]\n"];
- $replace = ["[table]", "[/table]"];
- $text = str_replace($search, $replace, $text);
-
- // removing multiplicated newlines
- if (Config::get('system', 'remove_multiplicated_lines')) {
- $search = ["\n\n\n", "\n ", " \n", "[/quote]\n\n", "\n[/quote]", "[/li]\n", "\n[li]", "\n[ul]", "[/ul]\n", "\n\n[share ", "[/attachment]\n",
- "\n[h1]", "[/h1]\n", "\n[h2]", "[/h2]\n", "\n[h3]", "[/h3]\n", "\n[h4]", "[/h4]\n", "\n[h5]", "[/h5]\n", "\n[h6]", "[/h6]\n"];
- $replace = ["\n\n", "\n", "\n", "[/quote]\n", "[/quote]", "[/li]", "[li]", "[ul]", "[/ul]", "\n[share ", "[/attachment]",
- "[h1]", "[/h1]", "[h2]", "[/h2]", "[h3]", "[/h3]", "[h4]", "[/h4]", "[h5]", "[/h5]", "[h6]", "[/h6]"];
- do {
- $oldtext = $text;
+ // Convert new line chars to html tags
+
+ // nlbr seems to be hopelessly messed up
+ // $Text = nl2br($Text);
+
+ // We'll emulate it.
+
+ $text = trim($text);
+ $text = str_replace("\r\n", "\n", $text);
+
+ // Remove linefeeds inside of the table elements. See issue #6799
+ $search = ["\n[th]", "[th]\n", " [th]", "\n[/th]", "[/th]\n", "[/th] ",
+ "\n[td]", "[td]\n", " [td]", "\n[/td]", "[/td]\n", "[/td] ",
+ "\n[tr]", "[tr]\n", " [tr]", "[tr] ", "\n[/tr]", "[/tr]\n", " [/tr]", "[/tr] ",
+ "[table]\n", "[table] ", " [table]", "\n[/table]", " [/table]", "[/table] "];
+ $replace = ["[th]", "[th]", "[th]", "[/th]", "[/th]", "[/th]",
+ "[td]", "[td]", "[td]", "[/td]", "[/td]", "[/td]",
+ "[tr]", "[tr]", "[tr]", "[tr]", "[/tr]", "[/tr]", "[/tr]", "[/tr]",
+ "[table]", "[table]", "[table]", "[/table]", "[/table]", "[/table]"];
+ do {
+ $oldtext = $text;
+ $text = str_replace($search, $replace, $text);
+ } while ($oldtext != $text);
+
+ // Replace these here only once
+ $search = ["\n[table]", "[/table]\n"];
+ $replace = ["[table]", "[/table]"];
$text = str_replace($search, $replace, $text);
- } while ($oldtext != $text);
- }
-
- /// @todo Have a closer look at the different html modes
- // Handle attached links or videos
- if (in_array($simple_html, [9])) {
- $text = self::removeAttachment($text);
- } elseif (!in_array($simple_html, [0, 4])) {
- $text = self::removeAttachment($text, true);
- } else {
- $text = self::convertAttachment($text, $simple_html, $try_oembed);
- }
-
- // leave open the posibility of [map=something]
- // this is replaced in Item::prepareBody() which has knowledge of the item location
- if (strpos($text, '[/map]') !== false) {
- $text = preg_replace_callback(
- "/\[map\](.*?)\[\/map\]/ism",
- function ($match) use ($simple_html) {
- return str_replace($match[0], '
', $text);
+ /// @todo Have a closer look at the different html modes
+ // Handle attached links or videos
+ if ($simple_html == self::ACTIVITYPUB) {
+ $text = self::removeAttachment($text);
+ } elseif (!in_array($simple_html, [self::INTERNAL, self::CONNECTORS])) {
+ $text = self::removeAttachment($text, true);
+ } else {
+ $text = self::convertAttachment($text, $simple_html, $try_oembed);
+ }
- // Check for bold text
- $text = preg_replace("(\[b\](.*?)\[\/b\])ism", '$1', $text);
+ // leave open the posibility of [map=something]
+ // this is replaced in Item::prepareBody() which has knowledge of the item location
+ if (strpos($text, '[/map]') !== false) {
+ $text = preg_replace_callback(
+ "/\[map\](.*?)\[\/map\]/ism",
+ function ($match) use ($simple_html) {
+ return str_replace($match[0], '
' . Map::byLocation($match[1], $simple_html) . '
', $match[0]);
+ },
+ $text
+ );
+ }
- // Check for Italics text
- $text = preg_replace("(\[i\](.*?)\[\/i\])ism", '$1', $text);
+ if (strpos($text, '[map=') !== false) {
+ $text = preg_replace_callback(
+ "/\[map=(.*?)\]/ism",
+ function ($match) use ($simple_html) {
+ return str_replace($match[0], '
', $text);
- // Check for over-line text
- $text = preg_replace("(\[o\](.*?)\[\/o\])ism", '$1', $text);
+ // Check for paragraph
+ $text = preg_replace("(\[p\](.*?)\[\/p\])ism", '
$1
', $text);
- // Check for colored text
- $text = preg_replace("(\[color=(.*?)\](.*?)\[\/color\])ism", "$2", $text);
+ // Check for bold text
+ $text = preg_replace("(\[b\](.*?)\[\/b\])ism", '$1', $text);
- // Check for sized text
- // [size=50] --> font-size: 50px (with the unit).
- if ($simple_html != 3) {
- $text = preg_replace("(\[size=(\d*?)\](.*?)\[\/size\])ism", "$2", $text);
- $text = preg_replace("(\[size=(.*?)\](.*?)\[\/size\])ism", "$2", $text);
- } else {
- // Issue 2199: Diaspora doesn't interpret the construct above, nor the or element
- $text = preg_replace("(\[size=(.*?)\](.*?)\[\/size\])ism", "$2", $text);
- }
+ // Check for Italics text
+ $text = preg_replace("(\[i\](.*?)\[\/i\])ism", '$1', $text);
+ // Check for Underline text
+ $text = preg_replace("(\[u\](.*?)\[\/u\])ism", '$1', $text);
- // Check for centered text
- $text = preg_replace("(\[center\](.*?)\[\/center\])ism", "
$1
", $text);
+ // Check for strike-through text
+ $text = preg_replace("(\[s\](.*?)\[\/s\])ism", '$1', $text);
- // Check for list text
- $text = str_replace("[*]", "
';
+ } else {
+ $return = '' . htmlspecialchars($matches[2], ENT_NOQUOTES, 'UTF-8') . '';
}
+
return $return;
},
$text
@@ -1898,7 +1915,7 @@ class BBCode extends BaseObject
}
/**
- * @brief Strips the "abstract" tag from the provided text
+ * Strips the "abstract" tag from the provided text
*
* @param string $text The text with BBCode
* @return string The same text - but without "abstract" element
@@ -1912,7 +1929,7 @@ class BBCode extends BaseObject
}
/**
- * @brief Returns the value of the "abstract" element
+ * Returns the value of the "abstract" element
*
* @param string $text The text that maybe contains the element
* @param string $addon The addon for which the abstract is meant for
@@ -1942,7 +1959,7 @@ class BBCode extends BaseObject
}
/**
- * @brief Callback function to replace a Friendica style mention in a mention for Diaspora
+ * Callback function to replace a Friendica style mention in a mention for Diaspora
*
* @param array $match Matching values for the callback
* [1] = Mention type (! or @)
@@ -1969,7 +1986,7 @@ class BBCode extends BaseObject
}
/**
- * @brief Converts a BBCode text into Markdown
+ * Converts a BBCode text into Markdown
*
* This function converts a BBCode item body to be sent to Markdown-enabled
* systems like Diaspora and Libertree
@@ -1981,8 +1998,6 @@ class BBCode extends BaseObject
*/
public static function toMarkdown($text, $for_diaspora = true)
{
- $a = self::getApp();
-
$original_text = $text;
// Since Diaspora is creating a summary for links, this function removes them before posting
@@ -2006,7 +2021,7 @@ class BBCode extends BaseObject
// Convert it to HTML - don't try oembed
if ($for_diaspora) {
- $text = self::convert($text, false, 3);
+ $text = self::convert($text, false, self::DIASPORA);
// Add all tags that maybe were removed
if (preg_match_all("/#\[url\=([$url_search_string]*)\](.*?)\[\/url\]/ism", $original_text, $tags)) {
@@ -2020,12 +2035,9 @@ class BBCode extends BaseObject
$text = $text . " " . $tagline;
}
} else {
- $text = self::convert($text, false, 4);
+ $text = self::convert($text, false, self::CONNECTORS);
}
- // 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(['
'], ['
'], $text);
@@ -2035,10 +2047,7 @@ class BBCode extends BaseObject
// 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);
@@ -2062,76 +2071,97 @@ class BBCode extends BaseObject
}
/**
- * @brief Pull out all #hashtags and @person tags from $string.
- *
- * We also get @person@domain.com - which would make
- * the regex quite complicated as tags can also
- * end a sentence. So we'll run through our results
- * and strip the period from any tags which end with one.
- * Returns array of tags found, or empty array.
- *
- * @param string $string Post content
- *
- * @return array List of tag and person names
- */
- public static function getTags($string)
- {
- $ret = [];
-
- // Convert hashtag links to hashtags
- $string = preg_replace('/#\[url\=([^\[\]]*)\](.*?)\[\/url\]/ism', '#$2', $string);
-
- // ignore anything in a code block
- $string = preg_replace('/\[code.*?\].*?\[\/code\]/sm', '', $string);
-
- // Force line feeds at bbtags
- $string = str_replace(['[', ']'], ["\n[", "]\n"], $string);
-
- // ignore anything in a bbtag
- $string = preg_replace('/\[(.*?)\]/sm', '', $string);
-
- // Match full names against @tags including the space between first and last
- // We will look these up afterward to see if they are full names or not recognisable.
-
- if (preg_match_all('/(@[^ \x0D\x0A,:?]+ [^ \x0D\x0A@,:?]+)([ \x0D\x0A@,:?]|$)/', $string, $matches)) {
- foreach ($matches[1] as $match) {
- if (strstr($match, ']')) {
- // we might be inside a bbcode color tag - leave it alone
- continue;
- }
-
- if (substr($match, -1, 1) === '.') {
- $ret[] = substr($match, 0, -1);
- } else {
- $ret[] = $match;
- }
- }
- }
-
- // Otherwise pull out single word tags. These can be @nickname, @first_last
- // and #hash tags.
-
- if (preg_match_all('/([!#@][^\^ \x0D\x0A,;:?]+)([ \x0D\x0A,;:?]|$)/', $string, $matches)) {
- foreach ($matches[1] as $match) {
- if (strstr($match, ']')) {
- // we might be inside a bbcode color tag - leave it alone
- continue;
- }
- if (substr($match, -1, 1) === '.') {
- $match = substr($match,0,-1);
- }
- // ignore strictly numeric tags like #1
- if ((strpos($match, '#') === 0) && ctype_digit(substr($match, 1))) {
- continue;
- }
- // try not to catch url fragments
- if (strpos($string, $match) && preg_match('/[a-zA-z0-9\/]/', substr($string, strpos($string, $match) - 1, 1))) {
- continue;
- }
- $ret[] = $match;
- }
- }
-
- return $ret;
- }
+ * Pull out all #hashtags and @person tags from $string.
+ *
+ * We also get @person@domain.com - which would make
+ * the regex quite complicated as tags can also
+ * end a sentence. So we'll run through our results
+ * and strip the period from any tags which end with one.
+ * Returns array of tags found, or empty array.
+ *
+ * @param string $string Post content
+ *
+ * @return array List of tag and person names
+ */
+ public static function getTags($string)
+ {
+ $ret = [];
+
+ // Convert hashtag links to hashtags
+ $string = preg_replace('/#\[url\=([^\[\]]*)\](.*?)\[\/url\]/ism', '#$2 ', $string);
+
+ // ignore anything in a code block
+ $string = preg_replace('/\[code.*?\].*?\[\/code\]/sm', '', $string);
+
+ // Force line feeds at bbtags
+ $string = str_replace(['[', ']'], ["\n[", "]\n"], $string);
+
+ // ignore anything in a bbtag
+ $string = preg_replace('/\[(.*?)\]/sm', '', $string);
+
+ // Match full names against @tags including the space between first and last
+ // We will look these up afterward to see if they are full names or not recognisable.
+
+ if (preg_match_all('/(@[^ \x0D\x0A,:?]+ [^ \x0D\x0A@,:?]+)([ \x0D\x0A@,:?]|$)/', $string, $matches)) {
+ foreach ($matches[1] as $match) {
+ if (strstr($match, ']')) {
+ // we might be inside a bbcode color tag - leave it alone
+ continue;
+ }
+
+ if (substr($match, -1, 1) === '.') {
+ $ret[] = substr($match, 0, -1);
+ } else {
+ $ret[] = $match;
+ }
+ }
+ }
+
+ // Otherwise pull out single word tags. These can be @nickname, @first_last
+ // and #hash tags.
+
+ if (preg_match_all('/([!#@][^\^ \x0D\x0A,;:?]+)([ \x0D\x0A,;:?]|$)/', $string, $matches)) {
+ foreach ($matches[1] as $match) {
+ if (strstr($match, ']')) {
+ // we might be inside a bbcode color tag - leave it alone
+ continue;
+ }
+
+ if (substr($match, -1, 1) === '.') {
+ $match = substr($match,0,-1);
+ }
+
+ // ignore strictly numeric tags like #1
+ if ((strpos($match, '#') === 0) && ctype_digit(substr($match, 1))) {
+ continue;
+ }
+
+ // try not to catch url fragments
+ if (strpos($string, $match) && preg_match('/[a-zA-z0-9\/]/', substr($string, strpos($string, $match) - 1, 1))) {
+ continue;
+ }
+ $ret[] = $match;
+ }
+ }
+
+ return $ret;
+ }
+
+ /**
+ * Perform a custom function on a text after having escaped blocks enclosed in the provided tag list.
+ *
+ * @param string $text
+ * @param array $tagList A list of tag names, e.g ['noparse', 'nobb', 'pre']
+ * @param callable $callback
+ * @return string
+ * @throws Exception
+ *@see Strings::performWithEscapedBlocks
+ *
+ */
+ public static function performWithEscapedTags(string $text, array $tagList, callable $callback)
+ {
+ $tagList = array_map('preg_quote', $tagList);
+
+ return Strings::performWithEscapedBlocks($text, '#\[(?:' . implode('|', $tagList) . ').*?\[/(?:' . implode('|', $tagList) . ')]#ism', $callback);
+ }
}