X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;ds=sidebyside;f=src%2FContent%2FText%2FBBCode.php;h=e95ed0675983e48f238c6930f99c9f5db5faf1a3;hb=7e618856ab09ac74a3760e238c73ecb9515f6701;hp=0a07e18f40716b6f53acc86f8bf5adf3f6ac002d;hpb=16127156264bd8b17f3136de6ffc1001da59c479;p=friendica.git diff --git a/src/Content/Text/BBCode.php b/src/Content/Text/BBCode.php index 0a07e18f40..e95ed06759 100644 --- a/src/Content/Text/BBCode.php +++ b/src/Content/Text/BBCode.php @@ -1,6 +1,6 @@ '; const BOTTOM_ANCHOR = '
'; + + const PREVIEW_NONE = 0; + const PREVIEW_NO_IMAGE = 1; + const PREVIEW_LARGE = 2; + const PREVIEW_SMALL = 3; + /** * Fetches attachment data that were generated the old way * @@ -266,8 +273,8 @@ class BBCode // Get all linked images with alternative image description if (preg_match_all("/\[img=(http[^\[\]]*)\]([^\[\]]*)\[\/img\]/Usi", $body, $pictures, PREG_SET_ORDER)) { foreach ($pictures as $picture) { - if (Photo::isLocal($picture[1])) { - $post['images'][] = ['url' => str_replace('-1.', '-0.', $picture[1]), 'description' => $picture[2]]; + if ($id = Photo::getIdForName($picture[1])) { + $post['images'][] = ['url' => str_replace('-1.', '-0.', $picture[1]), 'description' => $picture[2], 'id' => $id]; } else { $post['remote_images'][] = ['url' => $picture[1], 'description' => $picture[2]]; } @@ -279,8 +286,8 @@ class BBCode if (preg_match_all("/\[img\]([^\[\]]*)\[\/img\]/Usi", $body, $pictures, PREG_SET_ORDER)) { foreach ($pictures as $picture) { - if (Photo::isLocal($picture[1])) { - $post['images'][] = ['url' => str_replace('-1.', '-0.', $picture[1]), 'description' => '']; + if ($id = Photo::getIdForName($picture[1])) { + $post['images'][] = ['url' => str_replace('-1.', '-0.', $picture[1]), 'description' => '', 'id' => $id]; } else { $post['remote_images'][] = ['url' => $picture[1], 'description' => '']; } @@ -294,7 +301,7 @@ class BBCode // Simplify image codes $post['text'] = preg_replace("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism", '[img]$3[/img]', $post['text']); $post['text'] = preg_replace("/\[img\=(.*?)\](.*?)\[\/img\]/ism", '[img]$1[/img]', $post['text']); - + // if nothing is found, it maybe having an image. if (!isset($post['type'])) { if (preg_match_all("#\[url=([^\]]+?)\]\s*\[img\]([^\[]+?)\[/img\]\s*\[/url\]#ism", $post['text'], $pictures, PREG_SET_ORDER)) { @@ -473,7 +480,7 @@ class BBCode private static function proxyUrl(string $image, int $simplehtml = self::INTERNAL, int $uriid = 0, string $size = ''): string { // Only send proxied pictures to API and for internal display - if (!in_array($simplehtml, [self::INTERNAL, self::API])) { + if (!in_array($simplehtml, [self::INTERNAL, self::MASTODON_API, self::TWITTER_API])) { return $image; } elseif ($uriid > 0) { return Post\Link::getByLink($uriid, $image, $size); @@ -653,7 +660,7 @@ class BBCode * @return string * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - public static function convertAttachment(string $text, int $simplehtml = self::INTERNAL, bool $tryoembed = true, array $data = [], int $uriid = 0): string + public static function convertAttachment(string $text, int $simplehtml = self::INTERNAL, bool $tryoembed = true, array $data = [], int $uriid = 0, int $preview_mode = self::PREVIEW_LARGE): string { DI::profiler()->startRecording('rendering'); $data = $data ?: self::getAttachmentData($text); @@ -688,12 +695,18 @@ class BBCode $return = sprintf('
', $data['type']); } + if ($preview_mode == self::PREVIEW_NO_IMAGE) { + unset($data['image']); + unset($data['preview']); + } + if (!empty($data['title']) && !empty($data['url'])) { + $preview_class = $preview_mode == self::PREVIEW_LARGE ? 'attachment-image' : 'attachment-preview'; if (!empty($data['image']) && empty($data['text']) && ($data['type'] == 'photo')) { - $return .= sprintf('', $data['url'], self::proxyUrl($data['image'], $simplehtml, $uriid), $data['title']); + $return .= sprintf('', $data['url'], self::proxyUrl($data['image'], $simplehtml, $uriid), $data['title']); } else { if (!empty($data['image'])) { - $return .= sprintf('
', $data['url'], self::proxyUrl($data['image'], $simplehtml, $uriid), $data['title']); + $return .= sprintf('
', $data['url'], self::proxyUrl($data['image'], $simplehtml, $uriid), $data['title']); } elseif (!empty($data['preview'])) { $return .= sprintf('
', $data['url'], self::proxyUrl($data['preview'], $simplehtml, $uriid), $data['title']); } @@ -1009,18 +1022,33 @@ class BBCode /** * @param string $text A BBCode string * @return array Empty array if no share tag is present or the following array, missing attributes end up empty strings: - * - comment: Text before the opening share tag - * - shared : Text inside the share tags - * - author : (Optional) Display name of the shared author - * - profile: (Optional) Profile page URL of the shared author - * - avatar : (Optional) Profile picture URL of the shared author - * - link : (Optional) Canonical URL of the shared post - * - posted : (Optional) Date the shared post was initially posted ("Y-m-d H:i:s" in GMT) - * - guid : (Optional) Shared post GUID if any + * - comment : Text before the opening share tag + * - shared : Text inside the share tags + * - author : (Optional) Display name of the shared author + * - profile : (Optional) Profile page URL of the shared author + * - avatar : (Optional) Profile picture URL of the shared author + * - link : (Optional) Canonical URL of the shared post + * - posted : (Optional) Date the shared post was initially posted ("Y-m-d H:i:s" in GMT) + * - message_id: (Optional) Shared post URI if any + * - guid : (Optional) Shared post GUID if any */ public static function fetchShareAttributes(string $text): array { DI::profiler()->startRecording('rendering'); + if (preg_match('~(.*?)\[share](.*)\[/share]~ism', $text, $matches)) { + DI::profiler()->stopRecording(); + return [ + 'author' => '', + 'profile' => '', + 'avatar' => '', + 'link' => '', + 'posted' => '', + 'guid' => '', + 'message_id' => trim($matches[2]), + 'comment' => trim($matches[1]), + 'shared' => '', + ]; + } // 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); @@ -1047,7 +1075,7 @@ class BBCode private static function extractShareAttributes(string $shareString): array { $attributes = []; - foreach (['author', 'profile', 'avatar', 'link', 'posted', 'guid'] as $field) { + foreach (['author', 'profile', 'avatar', 'link', 'posted', 'guid', 'message_id'] as $field) { preg_match("/$field=(['\"])(.+?)\\1/ism", $shareString, $matches); $attributes[$field] = html_entity_decode($matches[2] ?? '', ENT_QUOTES, 'UTF-8'); } @@ -1055,6 +1083,17 @@ class BBCode return $attributes; } + /** + * Remove the share block + * + * @param string $body + * @return string + */ + public static function removeSharedData(string $body): string + { + return trim(preg_replace("/\s*\[share.*?\].*?\[\/share\]\s*/ism", '', $body)); + } + /** * This function converts a [share] block to text according to a provided callback function whose signature is: * @@ -1104,7 +1143,7 @@ class BBCode ); DI::profiler()->stopRecording(); - return $return; + return trim($return); } /** @@ -1164,7 +1203,8 @@ class BBCode $mention = $attributes['author'] . ' (' . ($author_contact['addr'] ?? '') . ')'; switch ($simplehtml) { - case self::API: + case self::MASTODON_API: + case self::TWITTER_API: $text = ($is_quote_share? '
' : '') . '' . html_entity_decode('♲', ENT_QUOTES, 'UTF-8') . ' ' . $author_contact['addr'] . ":
\n" . '
' . $content . '
'; @@ -1279,7 +1319,7 @@ class BBCode /** * Callback: Expands links from given $match array * - * @param arrat $match Array with link match + * @param array $match Array with link match * @return string BBCode */ private static function expandLinksCallback(array $match): string @@ -1294,7 +1334,7 @@ class BBCode /** * Callback: Cleans picture links * - * @param arrat $match Array with link match + * @param array $match Array with link match * @return string BBCode */ private static function cleanPictureLinksCallback(array $match): string @@ -1375,8 +1415,8 @@ class BBCode public static function cleanPictureLinks(string $text): string { 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); + $return = preg_replace_callback("&\[url=([^\[\]]*)\]\[img=(.*)\](.*)\[\/img\]\[\/url\]&Usi", [self::class, 'cleanPictureLinksCallback'], $text); + $return = preg_replace_callback("&\[url=([^\[\]]*)\]\[img\](.*)\[\/img\]\[\/url\]&Usi", [self::class, 'cleanPictureLinksCallback'], $return); DI::profiler()->stopRecording(); return $return; } @@ -1410,7 +1450,7 @@ class BBCode { DI::profiler()->startRecording('rendering'); $regexp = "/([@!])\[url\=([^\[\]]*)\].*?\[\/url\]/ism"; - $body = preg_replace_callback($regexp, ['self', 'mentionCallback'], $body); + $body = preg_replace_callback($regexp, [self::class, 'mentionCallback'], $body); DI::profiler()->stopRecording(); return $body; } @@ -1560,8 +1600,8 @@ class BBCode $text = str_replace(">", ">", $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); + $text = preg_replace("/\s?\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism", "\n[share$1]$2[/share]\n", $text); + $text = preg_replace("/\s?\[quote(.*?)\]\s?(.*?)\s?\[\/quote\]\s?/ism", "\n[quote$1]$2[/quote]\n", $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) { @@ -1608,7 +1648,7 @@ class BBCode /// @todo Have a closer look at the different html modes // Handle attached links or videos - if (in_array($simple_html, [self::API, self::ACTIVITYPUB])) { + if (in_array($simple_html, [self::MASTODON_API, self::TWITTER_API, self::ACTIVITYPUB])) { $text = self::removeAttachment($text); } elseif (!in_array($simple_html, [self::INTERNAL, self::EXTERNAL, self::CONNECTORS])) { $text = self::removeAttachment($text, true); @@ -1945,22 +1985,29 @@ class BBCode $text = preg_replace("/([#])\[url\=(.*?)\](.*?)\[\/url\]/ism", '', $text); - } elseif (in_array($simple_html, [self::INTERNAL, self::EXTERNAL, self::API])) { + } 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", + '', + $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); + if (in_array($simple_html, [self::OSTATUS, self::MASTODON_API, self::TWITTER_API, self::ACTIVITYPUB])) { + $text = preg_replace_callback("/\[url\](.*?)\[\/url\]/ism", [self::class, 'convertUrlForActivityPubCallback'], $text); + $text = preg_replace_callback("/\[url\=(.*?)\](.*?)\[\/url\]/ism", [self::class, 'convertUrlForActivityPubCallback'], $text); } } else { $text = preg_replace("(\[url\](.*?)\[\/url\])ism", " $1 ", $text); - $text = preg_replace_callback("&\[url=([^\[\]]*)\]\[img\](.*)\[\/img\]\[\/url\]&Usi", 'self::removePictureLinksCallback', $text); + $text = preg_replace_callback("&\[url=([^\[\]]*)\]\[img\](.*)\[\/img\]\[\/url\]&Usi", [self::class, 'removePictureLinksCallback'], $text); } // Bookmarks in red - will be converted to bookmarks in friendica @@ -1970,7 +2017,7 @@ class BBCode "[bookmark=$1]$2[/bookmark]", $text); if (in_array($simple_html, [self::OSTATUS, self::TWITTER])) { - $text = preg_replace_callback("/([^#@!])\[url\=([^\]]*)\](.*?)\[\/url\]/ism", "self::expandLinksCallback", $text); + $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); } @@ -2055,7 +2102,7 @@ class BBCode $text = preg_replace('/\<([^>]*?)(src|href)=(.*?)\&\;(.*?)\>/ism', '<$1$2=$3&$4>', $text); // sanitizes src attributes (http and redir URLs for displaying in a web page, cid used for inline images in emails) - $allowed_src_protocols = ['//', 'http://', 'https://', 'redir/', 'cid:']; + $allowed_src_protocols = ['//', 'http://', 'https://', 'contact/redir/', 'cid:']; array_walk($allowed_src_protocols, function(&$value) { $value = preg_quote($value, '#');}); @@ -2070,7 +2117,7 @@ class BBCode $allowed_link_protocols[] = '//'; $allowed_link_protocols[] = 'http://'; $allowed_link_protocols[] = 'https://'; - $allowed_link_protocols[] = 'redir/'; + $allowed_link_protocols[] = 'contact/redir/'; array_walk($allowed_link_protocols, function(&$value) { $value = preg_quote($value, '#');}); @@ -2280,7 +2327,7 @@ class BBCode $url_search_string = "^\[\]"; $text = preg_replace_callback( "/([@!])\[(.*?)\]\(([$url_search_string]*?)\)/ism", - ['self', 'bbCodeMention2DiasporaCallback'], + [self::class, 'bbCodeMention2DiasporaCallback'], $text ); } @@ -2308,7 +2355,7 @@ class BBCode DI::profiler()->startRecording('rendering'); $ret = []; - self::performWithEscapedTags($string, ['noparse', 'pre', 'code', 'img'], function ($string) use (&$ret) { + self::performWithEscapedTags($string, ['noparse', 'pre', 'code', 'img', 'attachment'], function ($string) use (&$ret) { // Convert hashtag links to hashtags $string = preg_replace('/#\[url\=([^\[\]]*)\](.*?)\[\/url\]/ism', '#$2 ', $string); @@ -2458,10 +2505,11 @@ class BBCode * @param string $link Post source URL * @param string $posted Post created date * @param string|null $guid Post guid (if any) + * @param string|null $uri Post uri (if any) * @return string * @TODO Rewrite to handle over whole record array */ - public static function getShareOpeningTag(string $author, string $profile, string $avatar, string $link, string $posted, string $guid = null): string + public static function getShareOpeningTag(string $author, string $profile, string $avatar, string $link, string $posted, string $guid = null, string $uri = null): string { DI::profiler()->startRecording('rendering'); $header = "[share author='" . str_replace(["'", "[", "]"], ["'", "[", "]"], $author) . @@ -2474,6 +2522,10 @@ class BBCode $header .= "' guid='" . str_replace(["'", "[", "]"], ["'", "[", "]"], $guid); } + if ($uri) { + $header .= "' message_id='" . str_replace(["'", "[", "]"], ["'", "[", "]"], $uri); + } + $header .= "']"; DI::profiler()->stopRecording(); @@ -2547,7 +2599,7 @@ class BBCode // Bypass attachment if parse url for a comment if (!$tryAttachment) { DI::profiler()->stopRecording(); - return "\n" . '[url=' . $url . ']' . $siteinfo['title'] . '[/url]'; + return "\n" . '[url=' . $url . ']' . ($siteinfo['title'] ?? $url) . '[/url]'; } // Format it as BBCode attachment