X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FContent%2FText%2FBBCode.php;h=c9a1bfe0521ec58fe56352c149989fce3895094e;hb=3842f02b021f2f32dbe6707c22af5760c3353dfa;hp=5f9a3ee78a02512939999e1aa6d0687eca24af03;hpb=219534e88c0931a92c56727c40dddfe0470e0a79;p=friendica.git diff --git a/src/Content/Text/BBCode.php b/src/Content/Text/BBCode.php index 5f9a3ee78a..c9a1bfe052 100644 --- a/src/Content/Text/BBCode.php +++ b/src/Content/Text/BBCode.php @@ -1,6 +1,6 @@ startRecording('rendering'); $data = [ 'type' => '', 'text' => '', @@ -167,6 +170,7 @@ class BBCode ]; if (!preg_match("/(.*)\[attachment(.*?)\](.*?)\[\/attachment\](.*)/ism", $body, $match)) { + DI::profiler()->stopRecording(); return self::getOldAttachmentData($body); } @@ -211,6 +215,7 @@ class BBCode } if (!in_array($data['type'], ['link', 'audio', 'photo', 'video'])) { + DI::profiler()->stopRecording(); return []; } @@ -232,6 +237,7 @@ class BBCode } } + DI::profiler()->stopRecording(); return $data; } @@ -247,6 +253,7 @@ class BBCode - (thumbnail) */ + DI::profiler()->startRecording('rendering'); $has_title = !empty($item['title']); $plink = $item['plink'] ?? ''; $post = self::getAttachmentData($body); @@ -321,8 +328,13 @@ class BBCode } } } elseif (count($pictures) > 0) { - $post['type'] = 'link'; - $post['url'] = $plink; + if (count($pictures) > 4) { + $post['type'] = 'link'; + $post['url'] = $plink; + } else { + $post['type'] = 'photo'; + } + $post['image'] = $pictures[0][2]; $post['text'] = $body; @@ -398,6 +410,7 @@ class BBCode } } + DI::profiler()->stopRecording(); return $post; } @@ -412,14 +425,16 @@ class BBCode public static function removeAttachment($body, $no_link_desc = false) { return preg_replace_callback("/\s*\[attachment (.*?)\](.*?)\[\/attachment\]\s*/ism", - function ($match) use ($no_link_desc) { + function ($match) use ($body, $no_link_desc) { $attach_data = self::getAttachmentData($match[0]); if (empty($attach_data['url'])) { return $match[0]; + } elseif (strpos(str_replace($match[0], '', $body), $attach_data['url']) !== false) { + return ''; } elseif (empty($attach_data['title']) || $no_link_desc) { - return "\n[url]" . $attach_data['url'] . "[/url]\n"; + return " \n[url]" . $attach_data['url'] . "[/url]\n"; } else { - return "\n[url=" . $attach_data['url'] . ']' . $attach_data['title'] . "[/url]\n"; + return " \n[url=" . $attach_data['url'] . ']' . $attach_data['title'] . "[/url]\n"; } }, $body); } @@ -434,6 +449,7 @@ class BBCode */ public static function toPlaintext($text, $keep_urls = true) { + DI::profiler()->startRecording('rendering'); // Remove pictures in advance to avoid unneeded proxy calls $text = preg_replace("/\[img\=(.*?)\](.*?)\[\/img\]/ism", ' $2 ', $text); $text = preg_replace("/\[img.*?\[\/img\]/ism", ' ', $text); @@ -443,6 +459,7 @@ class BBCode $naked_text = HTML::toPlaintext(self::convert($text, false, 0, true), 0, !$keep_urls); + DI::profiler()->stopRecording(); return $naked_text; } @@ -451,10 +468,10 @@ class BBCode // Only send proxied pictures to API and for internal display if (!in_array($simplehtml, [self::INTERNAL, self::API])) { return $image; - } elseif ($uriid) { + } elseif ($uriid > 0) { return Post\Link::getByLink($uriid, $image, $size); } else { - return ProxyUtils::proxifyUrl($image, $size); + return Proxy::proxifyUrl($image, $size); } } @@ -468,6 +485,7 @@ class BBCode */ public static function scaleExternalImages(string $srctext) { + DI::profiler()->startRecording('rendering'); $s = $srctext; // Simplify image links @@ -484,11 +502,13 @@ class BBCode continue; } - $curlResult = DI::httpRequest()->get($mtch[1]); + $curlResult = DI::httpClient()->get($mtch[1], HttpClientAccept::IMAGE); if (!$curlResult->isSuccess()) { continue; } + Logger::debug('Got picture', ['Content-Type' => $curlResult->getHeader('Content-Type'), 'url' => $mtch[1]]); + $i = $curlResult->getBody(); $type = $curlResult->getContentType(); $type = Images::getMimeTypeByData($i, $mtch[1], $type); @@ -517,6 +537,7 @@ class BBCode } } + DI::profiler()->stopRecording(); return $s; } @@ -532,6 +553,7 @@ class BBCode */ public static function limitBodySize($body) { + DI::profiler()->startRecording('rendering'); $maxlen = DI::config()->get('config', 'max_import_size', 0); // If the length of the body, including the embedded images, is smaller @@ -603,8 +625,10 @@ class BBCode $new_body = $new_body . $orig_body; } + DI::profiler()->stopRecording(); return $new_body; } else { + DI::profiler()->stopRecording(); return $body; } } @@ -624,13 +648,13 @@ class BBCode */ public static function convertAttachment($text, $simplehtml = self::INTERNAL, $tryoembed = true, array $data = [], $uriid = 0) { + DI::profiler()->startRecording('rendering'); $data = $data ?: self::getAttachmentData($text); if (empty($data) || empty($data['url'])) { + DI::profiler()->stopRecording(); return $text; } - $stamp1 = microtime(true); - if (isset($data['title'])) { $data['title'] = strip_tags($data['title']); $data['title'] = str_replace(['http://', 'https://'], '', $data['title']); @@ -666,7 +690,7 @@ class BBCode } elseif (!empty($data['preview'])) { $return .= sprintf('
', $data['url'], self::proxyUrl($data['preview'], $simplehtml, $uriid), $data['title']); } - $return .= sprintf('

%s

', $data['url'], $data['title']); + $return .= sprintf('

%s

', $data['url'], $data['title']); } } @@ -677,9 +701,9 @@ class BBCode if (!empty($data['provider_url']) && !empty($data['provider_name'])) { if (!empty($data['author_name'])) { - $return .= sprintf('%s (%s)', $data['provider_url'], $data['author_name'], $data['provider_name']); + $return .= sprintf('%s (%s)', $data['provider_url'], $data['author_name'], $data['provider_name']); } else { - $return .= sprintf('%s', $data['provider_url'], $data['provider_name']); + $return .= sprintf('%s', $data['provider_url'], $data['provider_name']); } } @@ -688,17 +712,20 @@ class BBCode } } - DI::profiler()->saveTimestamp($stamp1, 'rendering'); + DI::profiler()->stopRecording(); return trim(($data['text'] ?? '') . ' ' . $return . ' ' . ($data['after'] ?? '')); } public static function removeShareInformation($Text, $plaintext = false, $nolink = false) { + DI::profiler()->startRecording('rendering'); $data = self::getAttachmentData($Text); if (!$data) { + DI::profiler()->stopRecording(); return $Text; } elseif ($nolink) { + DI::profiler()->stopRecording(); return $data['text'] . ($data['after'] ?? ''); } @@ -712,11 +739,13 @@ class BBCode } if (empty($data['text']) && !empty($data['title']) && empty($data['url'])) { + DI::profiler()->stopRecording(); return $data['title'] . $data['after']; } // If the link already is included in the post, don't add it again if (!empty($data['url']) && strpos($data['text'], $data['url'])) { + DI::profiler()->stopRecording(); return $data['text'] . $data['after']; } @@ -728,6 +757,7 @@ class BBCode $text .= "\n[url]" . $data['url'] . '[/url]'; } + DI::profiler()->stopRecording(); return $text . "\n" . $data['after']; } @@ -821,6 +851,7 @@ class BBCode */ public static function getTagPosition($text, $name, $occurrences = 0) { + DI::profiler()->startRecording('rendering'); if ($occurrences < 0) { $occurrences = 0; } @@ -833,6 +864,7 @@ class BBCode } if ($start_open === false) { + DI::profiler()->stopRecording(); return false; } @@ -840,6 +872,7 @@ class BBCode $start_close = strpos($text, ']', $start_open); if ($start_close === false) { + DI::profiler()->stopRecording(); return false; } @@ -848,6 +881,7 @@ class BBCode $end_open = strpos($text, '[/' . $name . ']', $start_close); if ($end_open === false) { + DI::profiler()->stopRecording(); return false; } @@ -866,6 +900,7 @@ class BBCode $res['start']['equal'] = $start_equal + 1; } + DI::profiler()->stopRecording(); return $res; } @@ -880,6 +915,7 @@ class BBCode */ public static function pregReplaceInTag($pattern, $replace, $name, $text) { + DI::profiler()->startRecording('rendering'); $occurrences = 0; $pos = self::getTagPosition($text, $name, $occurrences); while ($pos !== false && $occurrences++ < 1000) { @@ -896,6 +932,7 @@ class BBCode $pos = self::getTagPosition($text, $name, $occurrences); } + DI::profiler()->stopRecording(); return $text; } @@ -964,12 +1001,14 @@ class BBCode */ public static function fetchShareAttributes($text) { + DI::profiler()->startRecording('rendering'); // See Issue https://github.com/friendica/friendica/issues/10454 // Hashtags in usernames are expanded to links. This here is a quick fix. $text = preg_replace('/([@!#])\[url\=.*?\](.*?)\[\/url\]/ism', '$1$2', $text); $attributes = []; if (!preg_match("/(.*?)\[share(.*?)\](.*)\[\/share\]/ism", $text, $matches)) { + DI::profiler()->stopRecording(); return $attributes; } @@ -978,6 +1017,7 @@ class BBCode preg_match("/$field=(['\"])(.+?)\\1/ism", $attribute_string, $matches); $attributes[$field] = html_entity_decode($matches[2] ?? '', ENT_QUOTES, 'UTF-8'); } + DI::profiler()->stopRecording(); return $attributes; } @@ -1002,6 +1042,7 @@ class BBCode */ public static function convertShare($text, callable $callback, int $uriid = 0) { + DI::profiler()->startRecording('rendering'); $return = preg_replace_callback( "/(.*?)\[share(.*?)\](.*)\[\/share\]/ism", function ($match) use ($callback, $uriid) { @@ -1014,16 +1055,16 @@ class BBCode $author_contact = Contact::getByURL($attributes['profile'], false, ['id', 'url', 'addr', 'name', 'micro']); $author_contact['url'] = ($author_contact['url'] ?? $attributes['profile']); - $author_contact['addr'] = ($author_contact['addr'] ?? '') ?: Protocol::getAddrFromProfileUrl($attributes['profile']); + $author_contact['addr'] = ($author_contact['addr'] ?? ''); $attributes['author'] = ($author_contact['name'] ?? '') ?: $attributes['author']; $attributes['avatar'] = ($author_contact['micro'] ?? '') ?: $attributes['avatar']; $attributes['profile'] = ($author_contact['url'] ?? '') ?: $attributes['profile']; if (!empty($author_contact['id'])) { - $attributes['avatar'] = Contact::getAvatarUrlForId($author_contact['id'], ProxyUtils::SIZE_THUMB); + $attributes['avatar'] = Contact::getAvatarUrlForId($author_contact['id'], Proxy::SIZE_THUMB); } elseif ($attributes['avatar']) { - $attributes['avatar'] = self::proxyUrl($attributes['avatar'], self::INTERNAL, $uriid, ProxyUtils::SIZE_THUMB); + $attributes['avatar'] = self::proxyUrl($attributes['avatar'], self::INTERNAL, $uriid, Proxy::SIZE_THUMB); } $content = preg_replace(Strings::autoLinkRegEx(), '$1', $match[3]); @@ -1033,6 +1074,44 @@ class BBCode $text ); + DI::profiler()->stopRecording(); + return $return; + } + + /** + * Convert complex IMG and ZMG elements + * + * @param [type] $text + * @param integer $simplehtml + * @param integer $uriid + * @return string + */ + private static function convertImages(string $text, int $simplehtml, int $uriid = 0):string + { + DI::profiler()->startRecording('rendering'); + $return = preg_replace_callback( + "/\[[zi]mg(.*?)\]([^\[\]]*)\[\/[zi]mg\]/ism", + function ($match) use ($simplehtml, $uriid) { + $attribute_string = $match[1]; + $attributes = []; + foreach (['alt', 'width', 'height'] as $field) { + preg_match("/$field=(['\"])(.+?)\\1/ism", $attribute_string, $matches); + $attributes[$field] = html_entity_decode($matches[2] ?? '', ENT_QUOTES, 'UTF-8'); + } + + $img_str = ' $value) { + if (!empty($value)) { + $img_str .= ' ' . $key . '="' . htmlspecialchars($value, ENT_COMPAT) . '"'; + } + } + return $img_str . '>'; + }, + $text + ); + + DI::profiler()->stopRecording(); return $return; } @@ -1052,13 +1131,14 @@ class BBCode */ private static function convertShareCallback(array $attributes, array $author_contact, $content, $is_quote_share, $simplehtml) { - $mention = Protocol::formatMention($attributes['profile'], $attributes['author']); + DI::profiler()->startRecording('rendering'); + $mention = $attributes['author'] . ' (' . ($author_contact['addr'] ?? '') . ')'; switch ($simplehtml) { case self::API: $text = ($is_quote_share? '
' : '') . - '

' . html_entity_decode('♲ ', ENT_QUOTES, 'UTF-8') . ' ' . $author_contact['addr'] . ":

\n" . - '
' . $content . '
'; + '' . html_entity_decode('♲', ENT_QUOTES, 'UTF-8') . ' ' . $author_contact['addr'] . ":
\n" . + '
' . $content . '
'; break; case self::DIASPORA: if (stripos(Strings::normaliseLink($attributes['link']), 'http://twitter.com/') === 0) { @@ -1083,7 +1163,7 @@ class BBCode $headline .= DI::l10n()->t('%2$s %3$s', $attributes['link'], $mention, $attributes['posted']); $headline .= ':

' . "\n"; - $text = ($is_quote_share? '
' : '') . $headline . '
' . trim($content) . '
' . "\n"; + $text = ($is_quote_share? '
' : '') . $headline . '
' . trim($content) . '
' . "\n"; break; case self::OSTATUS: @@ -1124,9 +1204,9 @@ class BBCode $text = DI::cache()->get($cache_key); if (is_null($text)) { - $curlResult = DI::httpRequest()->head($match[1], ['timeout' => DI::config()->get('system', 'xrd_timeout')]); + $curlResult = DI::httpClient()->head($match[1], [HttpClientOptions::TIMEOUT => DI::config()->get('system', 'xrd_timeout')]); if ($curlResult->isSuccess()) { - $mimetype = $curlResult->getHeader('Content-Type'); + $mimetype = $curlResult->getHeader('Content-Type')[0] ?? ''; } else { $mimetype = ''; } @@ -1137,7 +1217,7 @@ class BBCode $text = "[url=" . $match[2] . ']' . $match[2] . "[/url]"; // if its not a picture then look if its a page that contains a picture link - $body = DI::httpRequest()->fetch($match[1]); + $body = DI::httpClient()->fetch($match[1], HttpClientAccept::HTML, 0); if (empty($body)) { DI::cache()->set($cache_key, $text); return $text; @@ -1195,9 +1275,9 @@ class BBCode return $text; } - $curlResult = DI::httpRequest()->head($match[1], ['timeout' => DI::config()->get('system', 'xrd_timeout')]); + $curlResult = DI::httpClient()->head($match[1], [HttpClientOptions::TIMEOUT => DI::config()->get('system', 'xrd_timeout')]); if ($curlResult->isSuccess()) { - $mimetype = $curlResult->getHeader('Content-Type'); + $mimetype = $curlResult->getHeader('Content-Type')[0] ?? ''; } else { $mimetype = ''; } @@ -1213,7 +1293,7 @@ class BBCode } // if its not a picture then look if its a page that contains a picture link - $body = DI::httpRequest()->fetch($match[1]); + $body = DI::httpClient()->fetch($match[1], HttpClientAccept::HTML, 0); if (empty($body)) { DI::cache()->set($cache_key, $text); return $text; @@ -1247,22 +1327,62 @@ class BBCode public static function cleanPictureLinks($text) { + DI::profiler()->startRecording('rendering'); $return = preg_replace_callback("&\[url=([^\[\]]*)\]\[img=(.*)\](.*)\[\/img\]\[\/url\]&Usi", 'self::cleanPictureLinksCallback', $text); $return = preg_replace_callback("&\[url=([^\[\]]*)\]\[img\](.*)\[\/img\]\[\/url\]&Usi", 'self::cleanPictureLinksCallback', $return); + DI::profiler()->stopRecording(); return $return; } public static function removeLinks(string $bbcode) { + DI::profiler()->startRecording('rendering'); $bbcode = preg_replace("/\[img\=(.*?)\](.*?)\[\/img\]/ism", ' $1 ', $bbcode); $bbcode = preg_replace("/\[img.*?\[\/img\]/ism", ' ', $bbcode); $bbcode = preg_replace('/[@!#]\[url\=.*?\].*?\[\/url\]/ism', '', $bbcode); $bbcode = preg_replace("/\[url=[^\[\]]*\](.*)\[\/url\]/Usi", ' $1 ', $bbcode); $bbcode = preg_replace('/[@!#]?\[url.*?\[\/url\]/ism', '', $bbcode); + DI::profiler()->stopRecording(); return $bbcode; } + /** + * Replace names in mentions with nicknames + * + * @param string $body + * @return string Body with replaced mentions + */ + public static function setMentionsToNicknames(string $body):string + { + DI::profiler()->startRecording('rendering'); + $regexp = "/([@!])\[url\=([^\[\]]*)\].*?\[\/url\]/ism"; + $body = preg_replace_callback($regexp, ['self', 'mentionCallback'], $body); + DI::profiler()->stopRecording(); + return $body; + } + + /** + * Callback function to replace a Friendica style mention in a mention with the nickname + * + * @param array $match Matching values for the callback + * @return string Replaced mention + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ + private static function mentionCallback($match) + { + if (empty($match[2])) { + return ''; + } + + $data = Contact::getByURL($match[2], false, ['url', 'nick']); + if (empty($data['nick'])) { + return $match[0]; + } + + return $match[1] . '[url=' . $data['url'] . ']' . $data['nick'] . '[/url]'; + } + /** * Converts a BBCode message for a given URI-ID to a HTML message * @@ -1327,6 +1447,8 @@ class BBCode return ''; } + DI::profiler()->startRecording('rendering'); + Hook::callAll('bbcode', $text); $a = DI::app(); @@ -1431,12 +1553,9 @@ class BBCode } while ($oldtext != $text); } - // Add HTML new lines - $text = str_replace("\n", '
', $text); - /// @todo Have a closer look at the different html modes // Handle attached links or videos - if ($simple_html == self::ACTIVITYPUB) { + if (in_array($simple_html, [self::API, self::ACTIVITYPUB])) { $text = self::removeAttachment($text); } elseif (!in_array($simple_html, [self::INTERNAL, self::EXTERNAL, self::CONNECTORS])) { $text = self::removeAttachment($text, true); @@ -1526,11 +1645,20 @@ class BBCode // Check for list text $text = str_replace("[*]", "
  • ", $text); - // Check for style sheet commands + // Check for block-level custom CSS + $text = preg_replace('#(?<=^|\n)\[style=(.*?)](.*?)\[/style](?:\n|$)#ism', '
    $2
    ', $text); + + // Check for inline custom CSS $text = preg_replace("(\[style=(.*?)\](.*?)\[\/style\])ism", '$2', $text); + // Mastodon Emoji (internal tag, do not document for users) + $text = preg_replace("(\[emoji=(.*?)](.*?)\[/emoji])ism", '$2', $text); + // Check for CSS classes + // @deprecated since 2021.12, left for backward-compatibility reasons $text = preg_replace("(\[class=(.*?)\](.*?)\[\/class\])ism", '$2', $text); + // Add HTML new lines + $text = str_replace("\n", '
    ', $text); // handle nested lists $endlessloop = 0; @@ -1656,6 +1784,8 @@ class BBCode $text = preg_replace("/\[img\](.*?)\[\/img\]/ism", '' . DI::l10n()->t('Image/photo') . '', $text); $text = preg_replace("/\[zmg\](.*?)\[\/zmg\]/ism", '' . DI::l10n()->t('Image/photo') . '', $text); + $text = self::convertImages($text, $simple_html, $uriid); + $text = preg_replace("/\[crypt\](.*?)\[\/crypt\]/ism", '
    ' . DI::l10n()->t('Encrypted content') . '
    ', $text); $text = preg_replace("/\[crypt(.*?)\](.*?)\[\/crypt\]/ism", '
    ' . DI::l10n()->t('Encrypted content') . '
    ', $text); //$Text = preg_replace("/\[crypt=(.*?)\](.*?)\[\/crypt\]/ism", '
    ' . DI::l10n()->t('Encrypted content') . '
    ', $Text); @@ -1696,7 +1826,7 @@ class BBCode $text = preg_replace("/\[youtube\]https?:\/\/youtu.be\/(.*?)\[\/youtube\]/ism", '[youtube]$1[/youtube]', $text); if ($try_oembed) { - $text = preg_replace("/\[youtube\]([A-Za-z0-9\-_=]+)(.*?)\[\/youtube\]/ism", '', $text); + $text = preg_replace("/\[youtube\]([A-Za-z0-9\-_=]+)(.*?)\[\/youtube\]/ism", '', $text); } else { $text = preg_replace("/\[youtube\]([A-Za-z0-9\-_=]+)(.*?)\[\/youtube\]/ism", 'https://www.youtube.com/watch?v=$1', $text); @@ -1711,7 +1841,7 @@ class BBCode $text = preg_replace("/\[vimeo\]https?:\/\/vimeo.com\/([0-9]+)(.*?)\[\/vimeo\]/ism", '[vimeo]$1[/vimeo]', $text); if ($try_oembed) { - $text = preg_replace("/\[vimeo\]([0-9]+)(.*?)\[\/vimeo\]/ism", '', $text); + $text = preg_replace("/\[vimeo\]([0-9]+)(.*?)\[\/vimeo\]/ism", '', $text); } else { $text = preg_replace("/\[vimeo\]([0-9]+)(.*?)\[\/vimeo\]/ism", 'https://vimeo.com/$1', $text); @@ -1736,7 +1866,6 @@ class BBCode $text = preg_replace("/\[event\-start\](.*?)\[\/event\-start\]/ism", $sub, $text); $text = preg_replace("/\[event\-finish\](.*?)\[\/event\-finish\]/ism", '', $text); $text = preg_replace("/\[event\-location\](.*?)\[\/event\-location\]/ism", '', $text); - $text = preg_replace("/\[event\-adjust\](.*?)\[\/event\-adjust\]/ism", '', $text); $text = preg_replace("/\[event\-id\](.*?)\[\/event\-id\]/ism", '', $text); } @@ -1748,32 +1877,35 @@ class BBCode } } - if (!$for_plaintext) { - if (in_array($simple_html, [self::OSTATUS, self::ACTIVITYPUB])) { - $text = preg_replace_callback("/\[url\](.*?)\[\/url\]/ism", 'self::convertUrlForActivityPubCallback', $text); - $text = preg_replace_callback("/\[url\=(.*?)\](.*?)\[\/url\]/ism", 'self::convertUrlForActivityPubCallback', $text); - } - } else { - $text = preg_replace("(\[url\](.*?)\[\/url\])ism", " $1 ", $text); - $text = preg_replace_callback("&\[url=([^\[\]]*)\]\[img\](.*)\[\/img\]\[\/url\]&Usi", 'self::removePictureLinksCallback', $text); - } - - // Remove all hashtag addresses - if ($simple_html && !in_array($simple_html, [self::DIASPORA, self::OSTATUS, self::ACTIVITYPUB])) { - $text = preg_replace("/([#@!])\[url\=(.*?)\](.*?)\[\/url\]/ism", '$1$3', $text); - } elseif ($simple_html == self::DIASPORA) { + // 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', + '$1$3', + $text); + $text = preg_replace("/([#])\[url\=(.*?)\](.*?)\[\/url\]/ism", + '', $text); - } elseif (!$simple_html) { + } elseif (in_array($simple_html, [self::INTERNAL, self::EXTERNAL, self::API])) { $text = preg_replace("/([@!])\[url\=(.*?)\](.*?)\[\/url\]/ism", '$1$3', $text); + } else { + $text = preg_replace("/([#@!])\[url\=(.*?)\](.*?)\[\/url\]/ism", '$1$3', $text); + } + + if (!$for_plaintext) { + if (in_array($simple_html, [self::OSTATUS, self::API, self::ACTIVITYPUB])) { + $text = preg_replace_callback("/\[url\](.*?)\[\/url\]/ism", 'self::convertUrlForActivityPubCallback', $text); + $text = preg_replace_callback("/\[url\=(.*?)\](.*?)\[\/url\]/ism", 'self::convertUrlForActivityPubCallback', $text); + } + } else { + $text = preg_replace("(\[url\](.*?)\[\/url\])ism", " $1 ", $text); + $text = preg_replace_callback("&\[url=([^\[\]]*)\]\[img\](.*)\[\/img\]\[\/url\]&Usi", 'self::removePictureLinksCallback', $text); } // Bookmarks in red - will be converted to bookmarks in friendica @@ -1782,7 +1914,7 @@ class BBCode $text = preg_replace("/#\[url\=.*?\]\^\[\/url\]\[url\=(.*?)\](.*?)\[\/url\]/i", "[bookmark=$1]$2[/bookmark]", $text); - if (in_array($simple_html, [self::API, self::OSTATUS, self::TWITTER])) { + if (in_array($simple_html, [self::OSTATUS, self::TWITTER])) { $text = preg_replace_callback("/([^#@!])\[url\=([^\]]*)\](.*?)\[\/url\]/ism", "self::expandLinksCallback", $text); //$Text = preg_replace("/[^#@!]\[url\=([^\]]*)\](.*?)\[\/url\]/ism", ' $2 [url]$1[/url]', $Text); $text = preg_replace("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/ism", ' $2 [url]$1[/url]',$text); @@ -1821,7 +1953,7 @@ class BBCode * - [url=]#[/url] */ $text = preg_replace_callback("/(?:#\[url\=[^\[\]]*\]|\[url\=[^\[\]]*\]#)(.*?)\[\/url\]/ism", function($matches) use ($simple_html) { - if ($simple_html == BBCode::ACTIVITYPUB) { + if ($simple_html == self::ACTIVITYPUB) { return '#' . XML::escape($matches[1]) . ''; @@ -1943,6 +2075,7 @@ class BBCode ); $text = HTML::purify($text, $allowedIframeDomains); + DI::profiler()->stopRecording(); return trim($text); } @@ -1955,9 +2088,11 @@ class BBCode */ public static function stripAbstract($text) { - $text = preg_replace("/[\s|\n]*\[abstract\].*?\[\/abstract\][\s|\n]*/ism", '', $text); - $text = preg_replace("/[\s|\n]*\[abstract=.*?\].*?\[\/abstract][\s|\n]*/ism", '', $text); + DI::profiler()->startRecording('rendering'); + $text = preg_replace("/[\s|\n]*\[abstract\].*?\[\/abstract\][\s|\n]*/ism", ' ', $text); + $text = preg_replace("/[\s|\n]*\[abstract=.*?\].*?\[\/abstract][\s|\n]*/ism", ' ', $text); + DI::profiler()->stopRecording(); return $text; } @@ -1970,12 +2105,13 @@ class BBCode */ public static function getAbstract($text, $addon = '') { + DI::profiler()->startRecording('rendering'); $abstract = ''; $abstracts = []; $addon = strtolower($addon); if (preg_match_all("/\[abstract=(.*?)\](.*?)\[\/abstract\]/ism", $text, $results, PREG_SET_ORDER)) { - foreach ($results AS $result) { + foreach ($results as $result) { $abstracts[strtolower($result[1])] = $result[2]; } } @@ -1988,6 +2124,7 @@ class BBCode $abstract = $result[1]; } + DI::profiler()->stopRecording(); return $abstract; } @@ -2026,6 +2163,7 @@ class BBCode */ public static function toMarkdown($text, $for_diaspora = true) { + DI::profiler()->startRecording('rendering'); $original_text = $text; // Since Diaspora is creating a summary for links, this function removes them before posting @@ -2070,13 +2208,9 @@ class BBCode // Maybe we should make this newline at every time before a quote. $text = str_replace(['
    '], ['
    '], $text); - $stamp1 = microtime(true); - // Now convert HTML to Markdown $text = HTML::toMarkdown($text); - DI::profiler()->saveTimestamp($stamp1, "parser"); - // Libertree has a problem with escaped hashtags. $text = str_replace(['\#'], ['#'], $text); @@ -2095,6 +2229,7 @@ class BBCode Hook::callAll('bb2diaspora', $text); + DI::profiler()->stopRecording(); return $text; } @@ -2113,9 +2248,10 @@ class BBCode */ public static function getTags($string) { + DI::profiler()->startRecording('rendering'); $ret = []; - BBCode::performWithEscapedTags($string, ['noparse', 'pre', 'code', 'img'], function ($string) use (&$ret) { + self::performWithEscapedTags($string, ['noparse', 'pre', 'code', 'img'], function ($string) use (&$ret) { // Convert hashtag links to hashtags $string = preg_replace('/#\[url\=([^\[\]]*)\](.*?)\[\/url\]/ism', '#$2 ', $string); @@ -2163,18 +2299,19 @@ class BBCode } }); + DI::profiler()->stopRecording(); return array_unique($ret); } /** - * Expand tags to URLs + * Expand tags to URLs, checks the tag is at the start of a line or preceded by a non-word character * * @param string $body * @return string body with expanded tags */ public static function expandTags(string $body) { - return preg_replace_callback("/([!#@])([^\^ \x0D\x0A,;:?\']*[^\^ \x0D\x0A,;:?!\'.])/", + return preg_replace_callback("/(?<=\W|^)([!#@])([^\^ \x0D\x0A,;:?'\"]*[^\^ \x0D\x0A,;:?!'\".])/", function ($match) { switch ($match[1]) { case '!': @@ -2187,7 +2324,8 @@ class BBCode } break; case '#': - return $match[1] . '[url=' . 'https://' . DI::baseUrl() . '/search?tag=' . $match[2] . ']' . $match[2] . '[/url]'; + default: + return $match[1] . '[url=' . DI::baseUrl() . '/search?tag=' . $match[2] . ']' . $match[2] . '[/url]'; } }, $body); } @@ -2211,7 +2349,7 @@ class BBCode } /** - * Replaces mentions in the provided message body for the provided user and network if any + * Replaces mentions in the provided message body in BBCode links for the provided user and network if any * * @param $body * @param $profile_uid @@ -2222,11 +2360,11 @@ class BBCode */ public static function setMentions($body, $profile_uid = 0, $network = '') { - BBCode::performWithEscapedTags($body, ['noparse', 'pre', 'code', 'img'], function ($body) use ($profile_uid, $network) { - $tags = BBCode::getTags($body); + DI::profiler()->startRecording('rendering'); + $body = self::performWithEscapedTags($body, ['noparse', 'pre', 'code', 'img'], function ($body) use ($profile_uid, $network) { + $tags = self::getTags($body); $tagged = []; - $inform = ''; foreach ($tags as $tag) { $tag_type = substr($tag, 0, 1); @@ -2245,7 +2383,7 @@ class BBCode } } - if (($success = Item::replaceTag($body, $inform, $profile_uid, $tag, $network)) && $success['replaced']) { + if (($success = Item::replaceTag($body, $profile_uid, $tag, $network)) && $success['replaced']) { $tagged[] = $tag; } } @@ -2253,6 +2391,7 @@ class BBCode return $body; }); + DI::profiler()->stopRecording(); return $body; } @@ -2268,6 +2407,7 @@ class BBCode */ public static function getShareOpeningTag(string $author, string $profile, string $avatar, string $link, string $posted, string $guid = null) { + DI::profiler()->startRecording('rendering'); $header = "[share author='" . str_replace(["'", "[", "]"], ["'", "[", "]"], $author) . "' profile='" . str_replace(["'", "[", "]"], ["'", "[", "]"], $profile) . "' avatar='" . str_replace(["'", "[", "]"], ["'", "[", "]"], $avatar) . @@ -2280,6 +2420,7 @@ class BBCode $header .= "']"; + DI::profiler()->stopRecording(); return $header; } @@ -2301,6 +2442,7 @@ class BBCode */ public static function embedURL(string $url, bool $tryAttachment = true, string $title = null, string $description = null, string $tags = null): string { + DI::profiler()->startRecording('rendering'); DI::logger()->info($url); // If there is already some content information submitted we don't @@ -2322,6 +2464,7 @@ class BBCode DI::logger()->info('(unparsed): returns: ' . $result); + DI::profiler()->stopRecording(); return $result; } @@ -2340,6 +2483,7 @@ class BBCode break; } + DI::profiler()->stopRecording(); return $bbcode; } @@ -2347,10 +2491,13 @@ class BBCode // Bypass attachment if parse url for a comment if (!$tryAttachment) { + DI::profiler()->stopRecording(); return "\n" . '[url=' . $url . ']' . $siteinfo['title'] . '[/url]'; } // Format it as BBCode attachment - return "\n" . PageInfo::getFooterFromData($siteinfo); + $bbcode = "\n" . PageInfo::getFooterFromData($siteinfo); + DI::profiler()->stopRecording(); + return $bbcode; } }