diff --git a/database.sql b/database.sql index ac3eaaf1d..e05a420fa 100644 --- a/database.sql +++ b/database.sql @@ -1,6 +1,6 @@ -- ------------------------------------------ -- Friendica 2023.03-dev (Giant Rhubarb) --- DB_UPDATE_VERSION 1516 +-- DB_UPDATE_VERSION 1517 -- ------------------------------------------ @@ -1570,7 +1570,7 @@ CREATE TABLE IF NOT EXISTS `post-user-notification` ( -- CREATE TABLE IF NOT EXISTS `process` ( `pid` int unsigned NOT NULL COMMENT 'The ID of the process', - `hostname` varchar(32) NOT NULL COMMENT 'The name of the host the process is ran on', + `hostname` varchar(255) NOT NULL COMMENT 'The name of the host the process is ran on', `command` varbinary(32) NOT NULL DEFAULT '' COMMENT '', `created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', PRIMARY KEY(`pid`,`hostname`), diff --git a/doc/database/db_process.md b/doc/database/db_process.md index c0eb75f29..b1a52e01b 100644 --- a/doc/database/db_process.md +++ b/doc/database/db_process.md @@ -9,7 +9,7 @@ Fields | Field | Description | Type | Null | Key | Default | Extra | | -------- | ------------------------------------------ | ------------- | ---- | --- | ------------------- | ----- | | pid | The ID of the process | int unsigned | NO | PRI | NULL | | -| hostname | The name of the host the process is ran on | varchar(32) | NO | PRI | NULL | | +| hostname | The name of the host the process is ran on | varchar(255) | NO | PRI | NULL | | | command | | varbinary(32) | NO | | | | | created | | datetime | NO | | 0001-01-01 00:00:00 | | diff --git a/src/Content/Item.php b/src/Content/Item.php index 2e86fb82d..25d27b26d 100644 --- a/src/Content/Item.php +++ b/src/Content/Item.php @@ -223,15 +223,13 @@ class Item // select someone by nick in the current network if (!DBA::isResult($contact) && ($network != '')) { - $condition = ["`nick` = ? AND `network` = ? AND `uid` = ?", - $name, $network, $profile_uid]; + $condition = ['nick' => $name, 'network' => $network, 'uid' => $profile_uid]; $contact = DBA::selectFirst('contact', $fields, $condition); } // select someone by attag in the current network if (!DBA::isResult($contact) && ($network != '')) { - $condition = ["`attag` = ? AND `network` = ? AND `uid` = ?", - $name, $network, $profile_uid]; + $condition = ['attag' => $name, 'network' => $network, 'uid' => $profile_uid]; $contact = DBA::selectFirst('contact', $fields, $condition); } @@ -243,13 +241,13 @@ class Item // select someone by nick in any network if (!DBA::isResult($contact)) { - $condition = ["`nick` = ? AND `uid` = ?", $name, $profile_uid]; + $condition = ['nick' => $name, 'uid' => $profile_uid]; $contact = DBA::selectFirst('contact', $fields, $condition); } // select someone by attag in any network if (!DBA::isResult($contact)) { - $condition = ["`attag` = ? AND `uid` = ?", $name, $profile_uid]; + $condition = ['attag' => $name, 'uid' => $profile_uid]; $contact = DBA::selectFirst('contact', $fields, $condition); } @@ -271,7 +269,7 @@ class Item $replaced = true; // create profile link $profile = str_replace(',', '%2c', $profile); - $newtag = $tag_type.'[url=' . $profile . ']' . $newname . '[/url]'; + $newtag = $tag_type . '[url=' . $profile . ']' . $newname . '[/url]'; $body = str_replace($tag_type . $name, $newtag, $body); } } @@ -295,8 +293,10 @@ class Item $xmlhead = ''; if ($this->activity->match($item['verb'], Activity::TAG)) { - $fields = ['author-id', 'author-link', 'author-name', 'author-network', - 'verb', 'object-type', 'resource-id', 'body', 'plink']; + $fields = [ + 'author-id', 'author-link', 'author-name', 'author-network', + 'verb', 'object-type', 'resource-id', 'body', 'plink' + ]; $obj = Post::selectFirst($fields, ['uri' => $item['parent-uri']]); if (!DBA::isResult($obj)) { $this->profiler->stopRecording(); @@ -333,8 +333,8 @@ class Item default: if ($obj['resource-id']) { $post_type = $this->l10n->t('photo'); - $m=[]; preg_match("/\[url=([^]]*)\]/", $obj['body'], $m); - $rr['plink'] = $m[1]; + preg_match("/\[url=([^]]*)\]/", $obj['body'], $matches); + $rr['plink'] = $matches[1]; } else { $post_type = $this->l10n->t('status'); } @@ -433,7 +433,8 @@ class Item } if ((($cid == 0) || ($rel == Contact::FOLLOWER)) && - in_array($item['network'], Protocol::FEDERATED)) { + in_array($item['network'], Protocol::FEDERATED) + ) { $menu[$this->l10n->t('Connect/Follow')] = 'contact/follow?url=' . urlencode($item['author-link']) . '&auto=1'; } } else { @@ -891,7 +892,7 @@ class Item public function moveAttachmentsFromBodyToAttach(array $post): array { - if (!preg_match_all('/(\[attachment\]([0-9]+)\[\/attachment\])/',$post['body'], $match)) { + if (!preg_match_all('/(\[attachment\]([0-9]+)\[\/attachment\])/', $post['body'], $match)) { return $post; } @@ -903,11 +904,17 @@ class Item if ($post['attach']) { $post['attach'] .= ','; } - $post['attach'] .= Post\Media::getAttachElement($this->baseURL . '/attach/' . $attachment['id'], - $attachment['filesize'], $attachment['filetype'], $attachment['filename'] ?? ''); + $post['attach'] .= Post\Media::getAttachElement( + $this->baseURL . '/attach/' . $attachment['id'], + $attachment['filesize'], + $attachment['filetype'], + $attachment['filename'] ?? '' + ); - $fields = ['allow_cid' => $post['allow_cid'], 'allow_gid' => $post['allow_gid'], - 'deny_cid' => $post['deny_cid'], 'deny_gid' => $post['deny_gid']]; + $fields = [ + 'allow_cid' => $post['allow_cid'], 'allow_gid' => $post['allow_gid'], + 'deny_cid' => $post['deny_cid'], 'deny_gid' => $post['deny_gid'] + ]; $condition = ['id' => $attachment_id]; Attach::update($fields, $condition); } @@ -925,8 +932,9 @@ class Item // embedded bookmark or attachment in post? set bookmark flag $data = BBCode::getAttachmentData($post['body']); - if ((preg_match_all("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/ism", $post['body'], $match, PREG_SET_ORDER) || isset($data['type'])) - && ($post['post-type'] != ItemModel::PT_PERSONAL_NOTE)) { + if ((preg_match_all("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/ism", $post['body'], $match, PREG_SET_ORDER) || !empty($data['type'])) + && ($post['post-type'] != ItemModel::PT_PERSONAL_NOTE) + ) { $post['post-type'] = ItemModel::PT_PAGE; $post['object-type'] = Activity\ObjectType::BOOKMARK; } @@ -934,16 +942,6 @@ class Item // Setting the object type if not defined before if (empty($post['object-type'])) { $post['object-type'] = ($post['gravity'] == ItemModel::GRAVITY_PARENT) ? Activity\ObjectType::NOTE : Activity\ObjectType::COMMENT; - - $objectdata = BBCode::getAttachedData($post['body']); - - if ($objectdata['type'] == 'link') { - $post['object-type'] = Activity\ObjectType::BOOKMARK; - } elseif ($objectdata['type'] == 'video') { - $post['object-type'] = Activity\ObjectType::VIDEO; - } elseif ($objectdata['type'] == 'photo') { - $post['object-type'] = Activity\ObjectType::IMAGE; - } } return $post; } @@ -1039,8 +1037,14 @@ class Item continue; } - $this->emailer->send(new ItemCCEMail($this->app, $this->l10n, $this->baseURL, - $post, $address, $author['thumb'] ?? '')); + $this->emailer->send(new ItemCCEMail( + $this->app, + $this->l10n, + $this->baseURL, + $post, + $address, + $author['thumb'] ?? '' + )); } } } diff --git a/src/Content/Text/BBCode.php b/src/Content/Text/BBCode.php index 949f9d9cf..12fef305b 100644 --- a/src/Content/Text/BBCode.php +++ b/src/Content/Text/BBCode.php @@ -74,77 +74,6 @@ class BBCode const PREVIEW_LARGE = 2; const PREVIEW_SMALL = 3; - /** - * Fetches attachment data that were generated the old way - * - * @param string $body Message body - * @return array - * 'type' -> Message type ('link', 'video', 'photo') - * 'text' -> Text before the shared message - * 'after' -> Text after the shared message - * 'image' -> Preview image of the message - * 'url' -> Url to the attached message - * 'title' -> Title of the attachment - * 'description' -> Description of the attachment - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - */ - private static function getOldAttachmentData(string $body): array - { - $post = []; - - // Simplify image codes - $body = preg_replace("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism", '[img]$3[/img]', $body); - - if (preg_match_all("(\[class=(.*?)\](.*?)\[\/class\])ism", $body, $attached, PREG_SET_ORDER)) { - foreach ($attached as $data) { - if (!in_array($data[1], ['type-link', 'type-video', 'type-photo'])) { - continue; - } - - $post['type'] = substr($data[1], 5); - - $pos = strpos($body, $data[0]); - if ($pos > 0) { - $post['text'] = trim(substr($body, 0, $pos)); - $post['after'] = trim(substr($body, $pos + strlen($data[0]))); - } else { - $post['text'] = trim(str_replace($data[0], '', $body)); - $post['after'] = ''; - } - - $attacheddata = $data[2]; - - if (preg_match("/\[img\](.*?)\[\/img\]/ism", $attacheddata, $matches)) { - - $picturedata = Images::getInfoFromURLCached($matches[1]); - - if ($picturedata) { - if (($picturedata[0] >= 500) && ($picturedata[0] >= $picturedata[1])) { - $post['image'] = $matches[1]; - } else { - $post['preview'] = $matches[1]; - } - } - } - - if (preg_match("/\[bookmark\=(.*?)\](.*?)\[\/bookmark\]/ism", $attacheddata, $matches)) { - $post['url'] = $matches[1]; - $post['title'] = $matches[2]; - } - if (!empty($post['url']) && (in_array($post['type'], ['link', 'video'])) - && preg_match("/\[url\=(.*?)\](.*?)\[\/url\]/ism", $attacheddata, $matches)) { - $post['url'] = $matches[1]; - } - - // Search for description - if (preg_match("/\[quote\](.*?)\[\/quote\]/ism", $attacheddata, $matches)) { - $post['description'] = $matches[1]; - } - } - } - return $post; - } - /** * Fetches attachment data that were generated with the "attachment" element * @@ -178,7 +107,7 @@ class BBCode if (!preg_match("/(.*)\[attachment(.*?)\](.*?)\[\/attachment\](.*)/ism", $body, $match)) { DI::profiler()->stopRecording(); - return self::getOldAttachmentData($body); + return []; } $attributes = $match[2]; @@ -253,6 +182,33 @@ class BBCode return $data; } + /** + * Remove [attachment] BBCode and replaces it with a regular [url] + * + * @param string $body + * @param boolean $no_link_desc No link description + * @return string with replaced body + */ + public static function replaceAttachment(string $body, bool $no_link_desc = false): string + { + return preg_replace_callback( + "/\s*\[attachment (.*?)\](.*?)\[\/attachment\]\s*/ism", + 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"; + } else { + return " \n[url=" . $attach_data['url'] . ']' . $attach_data['title'] . "[/url]\n"; + } + }, + $body + ); + } + public static function getAttachedData(string $body, array $item = []): array { /* @@ -305,7 +261,7 @@ class BBCode // 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)) { - if ((count($pictures) == 1) && !$has_title && !Photo::isLocal($pictures[0][2])) { + if ((count($pictures) == 1) && !$has_title) { if (!empty($item['object-type']) && ($item['object-type'] == Activity\ObjectType::IMAGE)) { // Replace the preview picture with the real picture $url = str_replace('-1.', '-0.', $pictures[0][2]); @@ -430,30 +386,6 @@ class BBCode return $post; } - /** - * Remove [attachment] BBCode and replaces it with a regular [url] - * - * @param string $body - * @param boolean $no_link_desc No link description - * @return string with replaced body - */ - public static function replaceAttachment(string $body, bool $no_link_desc = false): string - { - return preg_replace_callback("/\s*\[attachment (.*?)\](.*?)\[\/attachment\]\s*/ism", - 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"; - } else { - return " \n[url=" . $attach_data['url'] . ']' . $attach_data['title'] . "[/url]\n"; - } - }, $body); - } - /** * Remove [attachment] BBCode * @@ -956,8 +888,11 @@ class BBCode // We're depending on the property of 'foreach' (specified on the PHP website) that // it loops over the array starting from the first element and going sequentially // to the last element - $newbody = str_replace('[$#saved_image' . $cnt . '#$]', - '' . DI::l10n()->t('Image/photo') . '', $newbody); + $newbody = str_replace( + '[$#saved_image' . $cnt . '#$]', + '' . DI::l10n()->t('Image/photo') . '', + $newbody + ); $cnt++; } @@ -1112,8 +1047,7 @@ class BBCode $attributes[$field] = html_entity_decode($matches[2] ?? '', ENT_QUOTES, 'UTF-8'); } - $img_str = ' $value) { if (!empty($value)) { $img_str .= ' ' . $key . '="' . htmlspecialchars($value, ENT_COMPAT) . '"'; @@ -1150,13 +1084,13 @@ class BBCode switch ($simplehtml) { case self::MASTODON_API: case self::TWITTER_API: - $text = ($is_quote_share? '
' : '') . - '' . html_entity_decode('♲', ENT_QUOTES, 'UTF-8') . ' ' . $author_contact['addr'] . ":
\n" . - '
' . $content . '
'; + $text = ($is_quote_share ? '
' : '') . + '' . 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) { - $text = ($is_quote_share? '
' : '') . '

' . $attributes['link'] . '

' . "\n"; + $text = ($is_quote_share ? '
' : '') . '

' . $attributes['link'] . '

' . "\n"; } else { $headline = '

' . $attributes['author'] . ':

' . "\n"; @@ -1164,7 +1098,7 @@ class BBCode $headline = '

' . $attributes['author'] . ' - ' . $attributes['posted'] . ' GMT

' . "\n"; } - $text = ($is_quote_share? '
' : '') . $headline . '
' . trim($content) . '
' . "\n"; + $text = ($is_quote_share ? '
' : '') . $headline . '
' . trim($content) . '
' . "\n"; if (empty($attributes['posted']) && !empty($attributes['link'])) { $text .= '

[Source]

' . "\n"; @@ -1177,18 +1111,18 @@ 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: - $text = ($is_quote_share? '
' : '') . '

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

' . "\n"; + $text = ($is_quote_share ? '
' : '') . '

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

' . "\n"; break; case self::ACTIVITYPUB: $author = '@' . $author_contact['addr'] . ':'; $text = '
' . html_entity_decode('♲', ENT_QUOTES, 'UTF-8') . ' ' . $author . '
' . $content . '
' . "\n"; break; default: - $text = ($is_quote_share? "\n" : ''); + $text = ($is_quote_share ? "\n" : ''); $contact = Contact::getByURL($attributes['profile'], false, ['network']); $network = $contact['network'] ?? Protocol::PHANTOM; @@ -1500,8 +1434,7 @@ class BBCode * $match[1] = $url * $match[2] = $title or absent */ - $try_oembed_callback = function (array $match) - { + $try_oembed_callback = function (array $match) { $url = $match[1]; $title = $match[2] ?? ''; @@ -1554,18 +1487,22 @@ class BBCode } // Remove linefeeds inside of the table elements. See issue #6799 - $search = ["\n[th]", "[th]\n", " [th]", "\n[/th]", "[/th]\n", "[/th] ", + $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] ", "\n[hr]", "[hr]\n", " [hr]", "[hr] ", "\n[attachment ", " [attachment ", "\n[/attachment]", "[/attachment]\n", " [/attachment]", "[/attachment] ", - "[table]\n", "[table] ", " [table]", "\n[/table]", " [/table]", "[/table] "]; - $replace = ["[th]", "[th]", "[th]", "[/th]", "[/th]", "[/th]", + "[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]", "[hr]", "[hr]", "[hr]", "[hr]", "[attachment ", "[attachment ", "[/attachment]", "[/attachment]", "[/attachment]", "[/attachment]", - "[table]", "[table]", "[table]", "[/table]", "[/table]", "[/table]"]; + "[table]", "[table]", "[table]", "[/table]", "[/table]", "[/table]" + ]; do { $oldtext = $text; $text = str_replace($search, $replace, $text); @@ -1581,10 +1518,14 @@ class BBCode // removing multiplicated newlines if (DI::config()->get('system', 'remove_multiplicated_lines')) { - $search = ["\n\n\n", "\n ", " \n", "[/quote]\n\n", "\n[/quote]", "[/li]\n", "\n[li]", "\n[*]", "\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]"]; + $search = [ + "\n\n\n", "\n ", " \n", "[/quote]\n\n", "\n[/quote]", "[/li]\n", "\n[li]", "\n[*]", "\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; $text = str_replace($search, $replace, $text); @@ -1702,9 +1643,9 @@ class BBCode $endlessloop = 0; while ((((strpos($text, "[/list]") !== false) && (strpos($text, "[list") !== false)) || - ((strpos($text, "[/ol]") !== false) && (strpos($text, "[ol]") !== false)) || - ((strpos($text, "[/ul]") !== false) && (strpos($text, "[ul]") !== false)) || - ((strpos($text, "[/li]") !== false) && (strpos($text, "[li]") !== false))) && (++$endlessloop < 20)) { + ((strpos($text, "[/ol]") !== false) && (strpos($text, "[ol]") !== false)) || + ((strpos($text, "[/ul]") !== false) && (strpos($text, "[ul]") !== false)) || + ((strpos($text, "[/li]") !== false) && (strpos($text, "[li]") !== false))) && (++$endlessloop < 20)) { $text = preg_replace("/\[list\](.*?)\[\/list\]/ism", '', $text); $text = preg_replace("/\[list=\](.*?)\[\/list\]/ism", '', $text); $text = preg_replace("/\[list=1\](.*?)\[\/list\]/ism", '', $text); @@ -1750,10 +1691,12 @@ class BBCode // handle nested quotes $endlessloop = 0; - while ((strpos($text, "[/spoiler]")!== false) && (strpos($text, "[spoiler=") !== false) && (++$endlessloop < 20)) { - $text = preg_replace("/\[spoiler=[\"\']*(.*?)[\"\']*\](.*?)\[\/spoiler\]/ism", + while ((strpos($text, "[/spoiler]") !== false) && (strpos($text, "[spoiler=") !== false) && (++$endlessloop < 20)) { + $text = preg_replace( + "/\[spoiler=[\"\']*(.*?)[\"\']*\](.*?)\[\/spoiler\]/ism", '
$1$2
', - $text); + $text + ); } // Declare the format for [quote] layout @@ -1772,10 +1715,12 @@ class BBCode // handle nested quotes $endlessloop = 0; - while ((strpos($text, "[/quote]")!== false) && (strpos($text, "[quote=") !== false) && (++$endlessloop < 20)) { - $text = preg_replace("/\[quote=[\"\']*(.*?)[\"\']*\](.*?)\[\/quote\]/ism", - "

" . $t_wrote . "

$2
", - $text); + while ((strpos($text, "[/quote]") !== false) && (strpos($text, "[quote=") !== false) && (++$endlessloop < 20)) { + $text = preg_replace( + "/\[quote=[\"\']*(.*?)[\"\']*\](.*?)\[\/quote\]/ism", + "

" . $t_wrote . "

$2
", + $text + ); } @@ -1796,13 +1741,15 @@ class BBCode $text = preg_replace("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism", '', $text); $text = preg_replace("/\[zmg\=([0-9]*)x([0-9]*)\](.*?)\[\/zmg\]/ism", '', $text); - $text = preg_replace_callback("/\[img\=(.*?)\](.*?)\[\/img\]/ism", + $text = preg_replace_callback( + "/\[img\=(.*?)\](.*?)\[\/img\]/ism", function ($matches) use ($simple_html, $uriid) { $matches[1] = self::proxyUrl($matches[1], $simple_html, $uriid); $matches[2] = htmlspecialchars($matches[2], ENT_COMPAT); return '' . $matches[2] . ''; }, - $text); + $text + ); // Images // [img]pathtoimage[/img] @@ -1824,8 +1771,8 @@ class BBCode $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); + $text = preg_replace("/\[crypt(.*?)\](.*?)\[\/crypt\]/ism", '
' . DI::l10n()->t('Encrypted content') . '
', $text); //$text = preg_replace("/\[crypt=(.*?)\](.*?)\[\/crypt\]/ism", '
' . DI::l10n()->t('Encrypted content') . '
', $text); // Simplify "video" element @@ -1833,20 +1780,32 @@ class BBCode if ($try_oembed) { // html5 video and audio - $text = preg_replace("/\[video\](.*?\.(ogg|ogv|oga|ogm|webm|mp4).*?)\[\/video\]/ism", - '', $text); + $text = preg_replace( + "/\[video\](.*?\.(ogg|ogv|oga|ogm|webm|mp4).*?)\[\/video\]/ism", + '', + $text + ); $text = preg_replace_callback("/\[video\](.*?)\[\/video\]/ism", $try_oembed_callback, $text); $text = preg_replace_callback("/\[audio\](.*?)\[\/audio\]/ism", $try_oembed_callback, $text); - $text = preg_replace("/\[video\](.*?)\[\/video\]/ism", - '$1', $text); + $text = preg_replace( + "/\[video\](.*?)\[\/video\]/ism", + '$1', + $text + ); $text = preg_replace("/\[audio\](.*?)\[\/audio\]/ism", '', $text); } else { - $text = preg_replace("/\[video\](.*?)\[\/video\]/ism", - '$1', $text); - $text = preg_replace("/\[audio\](.*?)\[\/audio\]/ism", - '$1', $text); + $text = preg_replace( + "/\[video\](.*?)\[\/video\]/ism", + '$1', + $text + ); + $text = preg_replace( + "/\[audio\](.*?)\[\/audio\]/ism", + '$1', + $text + ); } // Backward compatibility, [iframe] support has been removed in version 2020.12 @@ -1868,8 +1827,11 @@ class BBCode if ($try_oembed) { $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); + $text = preg_replace( + "/\[youtube\]([A-Za-z0-9\-_=]+)(.*?)\[\/youtube\]/ism", + 'https://www.youtube.com/watch?v=$1', + $text + ); } if ($try_oembed) { @@ -1883,8 +1845,11 @@ class BBCode if ($try_oembed) { $text = preg_replace("/\[vimeo\]([0-9]+)(.*?)\[\/vimeo\]/ism", '', $text); } else { - $text = preg_replace("/\[vimeo\]([0-9]+)(.*?)\[\/vimeo\]/ism", - 'https://vimeo.com/$1', $text); + $text = preg_replace( + "/\[vimeo\]([0-9]+)(.*?)\[\/vimeo\]/ism", + 'https://vimeo.com/$1', + $text + ); } // oembed tag @@ -1920,27 +1885,39 @@ class BBCode // 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", + $text = preg_replace( + "/([@!])\[url\=(.*?)\](.*?)\[\/url\]/ism", '@$3', - $text); + $text + ); } elseif (in_array($simple_html, [self::OSTATUS, self::ACTIVITYPUB])) { - $text = preg_replace("/([@!])\[url\=(.*?)\](.*?)\[\/url\]/ism", + $text = preg_replace( + "/([@!])\[url\=(.*?)\](.*?)\[\/url\]/ism", '$1$3', - $text); - $text = preg_replace("/([#])\[url\=(.*?)\](.*?)\[\/url\]/ism", + $text + ); + $text = preg_replace( + "/([#])\[url\=(.*?)\](.*?)\[\/url\]/ism", '', - $text); + $text + ); } elseif (in_array($simple_html, [self::INTERNAL, self::EXTERNAL, self::TWITTER_API])) { - $text = preg_replace("/([@!])\[url\=(.*?)\](.*?)\[\/url\]/ism", + $text = preg_replace( + "/([@!])\[url\=(.*?)\](.*?)\[\/url\]/ism", '$1$3', - $text); + $text + ); } elseif ($simple_html == self::MASTODON_API) { - $text = preg_replace("/([@!])\[url\=(.*?)\](.*?)\[\/url\]/ism", + $text = preg_replace( + "/([@!])\[url\=(.*?)\](.*?)\[\/url\]/ism", '$1$3', - $text); - $text = preg_replace("/([#])\[url\=(.*?)\](.*?)\[\/url\]/ism", + $text + ); + $text = preg_replace( + "/([#])\[url\=(.*?)\](.*?)\[\/url\]/ism", '', - $text); + $text + ); } else { $text = preg_replace("/([#@!])\[url\=(.*?)\](.*?)\[\/url\]/ism", '$1$3', $text); } @@ -1958,8 +1935,11 @@ class BBCode // 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); + $text = preg_replace( + "/#\[url\=.*?\]\^\[\/url\]\[url\=(.*?)\](.*?)\[\/url\]/i", + "[bookmark=$1]$2[/bookmark]", + $text + ); if (in_array($simple_html, [self::OSTATUS, self::TWITTER])) { $text = preg_replace_callback("/([^#@!])\[url\=([^\]]*)\](.*?)\[\/url\]/ism", [self::class, 'expandLinksCallback'], $text); @@ -1979,20 +1959,22 @@ class BBCode "&\[url=/?posts/([^\[\]]*)\](.*)\[\/url\]&Usi", function ($match) { return "[url=" . DI::baseUrl() . "/display/" . $match[1] . "]" . $match[2] . "[/url]"; - }, $text + }, + $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 + }, + $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); + $text = preg_replace($expression, DI::baseUrl() . "/display/$1", $text); /* Tag conversion * Supports: @@ -2000,7 +1982,7 @@ class BBCode * - [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) { + $text = preg_replace_callback("/(?:#\[url\=[^\[\]]*\]|\[url\=[^\[\]]*\]#)(.*?)\[\/url\]/ism", function ($matches) use ($simple_html) { if ($simple_html == self::ACTIVITYPUB) { return '#' @@ -2017,8 +1999,8 @@ class BBCode // 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 $escapedBaseUrl = preg_quote(DI::baseUrl(), '/'); - $text = preg_replace("/\[url\](".$escapedBaseUrl.".*?)\[\/url\]/ism", '$1', $text); - $text = preg_replace("/\[url\=(".$escapedBaseUrl.".*?)\](.*?)\[\/url\]/ism", '$2', $text); + $text = preg_replace("/\[url\](" . $escapedBaseUrl . ".*?)\[\/url\]/ism", '$1', $text); + $text = preg_replace("/\[url\=(" . $escapedBaseUrl . ".*?)\](.*?)\[\/url\]/ism", '$2', $text); $text = preg_replace("/\[url\](.*?)\[\/url\]/ism", '$1', $text); $text = preg_replace("/\[url\=(.*?)\](.*?)\[\/url\]/ism", '$2', $text); @@ -2049,10 +2031,15 @@ class BBCode // 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://', 'contact/redir/', 'cid:']; - array_walk($allowed_src_protocols, function(&$value) { $value = preg_quote($value, '#');}); + array_walk($allowed_src_protocols, function (&$value) { + $value = preg_quote($value, '#'); + }); - $text = preg_replace('#<([^>]*?)(src)="(?!' . implode('|', $allowed_src_protocols) . ')(.*?)"(.*?)>#ism', - '<$1$2=""$4 data-original-src="$3" class="invalid-src" title="' . DI::l10n()->t('Invalid source protocol') . '">', $text); + $text = preg_replace( + '#<([^>]*?)(src)="(?!' . implode('|', $allowed_src_protocols) . ')(.*?)"(.*?)>#ism', + '<$1$2=""$4 data-original-src="$3" class="invalid-src" title="' . DI::l10n()->t('Invalid source protocol') . '">', + $text + ); // sanitize href attributes (only allowlisted protocols URLs) // default value for backward compatibility @@ -2064,7 +2051,9 @@ class BBCode $allowed_link_protocols[] = 'https://'; $allowed_link_protocols[] = 'contact/redir/'; - array_walk($allowed_link_protocols, function(&$value) { $value = preg_quote($value, '#');}); + array_walk($allowed_link_protocols, function (&$value) { + $value = preg_quote($value, '#'); + }); $regex = '#<([^>]*?)(href)="(?!' . implode('|', $allowed_link_protocols) . ')(.*?)"(.*?)>#ism'; $text = preg_replace($regex, '<$1$2="javascript:void(0)"$4 data-original-href="$3" class="invalid-href" title="' . DI::l10n()->t('Invalid link protocol') . '">', $text); @@ -2074,7 +2063,8 @@ class BBCode $text, function (array $attributes, array $author_contact, $content, $is_quote_share) use ($simple_html) { return self::convertShareCallback($attributes, $author_contact, $content, $is_quote_share, $simple_html); - }, $uriid + }, + $uriid ); $text = self::interpolateSavedImagesIntoItemBody($uriid, $text, $saved_image); @@ -2089,13 +2079,14 @@ class BBCode // Additionally, [pre] tags preserve spaces $text = preg_replace_callback("/\[pre\](.*?)\[\/pre\]/ism", function ($match) { - return str_replace([' ', "\n"], [' ', "
"], htmlentities($match[1], ENT_NOQUOTES,'UTF-8')); + return str_replace([' ', "\n"], [' ', "
"], htmlentities($match[1], ENT_NOQUOTES, 'UTF-8')); }, $text); return $text; }); // Escaped code - $text = preg_replace_callback("#\[code(?:=([^\]]*))?\](.*?)\[\/code\]#ism", + $text = preg_replace_callback( + "#\[code(?:=([^\]]*))?\](.*?)\[\/code\]#ism", function ($matches) { if (strpos($matches[2], "\n") !== false) { $return = '
' . htmlentities(trim($matches[2], "\n\r"), ENT_NOQUOTES, 'UTF-8') . '
'; @@ -2111,8 +2102,8 @@ class BBCode // Default iframe allowed domains/path $allowedIframeDomains = [ DI::baseUrl()->getHost() - . (DI::baseUrl()->getPath() ? '/' . DI::baseUrl()->getPath() : '') - . '/oembed/', # The path part has to change with the source in Content\Oembed::iframe + . (DI::baseUrl()->getPath() ? '/' . DI::baseUrl()->getPath() : '') + . '/oembed/', # The path part has to change with the source in Content\Oembed::iframe 'www.youtube.com/embed/', 'player.vimeo.com/video/', ]; @@ -2225,7 +2216,8 @@ class BBCode * Transform #tags, strip off the [url] and replace spaces with underscore */ $url_search_string = "^\[\]"; - $text = preg_replace_callback("/#\[url\=([$url_search_string]*)\](.*?)\[\/url\]/i", + $text = preg_replace_callback( + "/#\[url\=([$url_search_string]*)\](.*?)\[\/url\]/i", function ($matches) { return '#' . str_replace(' ', '_', $matches[2]); }, @@ -2360,7 +2352,8 @@ class BBCode */ public static function expandTags(string $body): string { - return preg_replace_callback("/(?<=\W|^)([!#@])([^\^ \x0D\x0A,;:?'\"]*[^\^ \x0D\x0A,;:?!'\".])/", + return preg_replace_callback( + "/(?<=\W|^)([!#@])([^\^ \x0D\x0A,;:?'\"]*[^\^ \x0D\x0A,;:?!'\".])/", function (array $match) { switch ($match[1]) { case '!': @@ -2377,7 +2370,9 @@ class BBCode default: return $match[1] . '[url=' . DI::baseUrl() . '/search?tag=' . $match[2] . ']' . $match[2] . '[/url]'; } - }, $body); + }, + $body + ); } /** diff --git a/src/Content/Text/Plaintext.php b/src/Content/Text/Plaintext.php index 9a7435f5b..c72dad2be 100644 --- a/src/Content/Text/Plaintext.php +++ b/src/Content/Text/Plaintext.php @@ -122,8 +122,11 @@ class Plaintext $body = preg_replace("/([#@])\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '$1$3', $item['body']); // Add an URL element if the text contains a raw link - $body = preg_replace('/([^\]\=\'"]|^)(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/ism', - '$1[url]$2[/url]', $body); + $body = preg_replace( + '/([^\]\=\'"]|^)(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/ism', + '$1[url]$2[/url]', + $body + ); // Remove the abstract $body = BBCode::stripAbstract($body); @@ -149,7 +152,7 @@ class Plaintext if (($limit == 0) && ($abstract == $default_abstract)) { $abstract = ''; } - } else {// Try to guess the correct target network + } else { // Try to guess the correct target network switch ($htmlmode) { case BBCode::TWITTER: $abstract = BBCode::getAbstract($item['body'], Protocol::TWITTER); diff --git a/src/Factory/Api/Mastodon/Card.php b/src/Factory/Api/Mastodon/Card.php index dc46a7780..c0bad9beb 100644 --- a/src/Factory/Api/Mastodon/Card.php +++ b/src/Factory/Api/Mastodon/Card.php @@ -39,45 +39,40 @@ class Card extends BaseFactory */ public function createFromUriId(int $uriId, array $history = []): \Friendica\Object\Api\Mastodon\Card { - $item = Post::selectFirst(['body'], ['uri-id' => $uriId]); - if (!empty($item['body'])) { - $data = BBCode::getAttachmentData($item['body']); - } else { - $data = []; + $media = Post\Media::getByURIId($uriId, [Post\Media::HTML]); + if (empty($media) || (empty($media[0]['description']) && empty($media[0]['image']) && empty($media[0]['preview']))) { + return new \Friendica\Object\Api\Mastodon\Card([], $history); } - foreach (Post\Media::getByURIId($uriId, [Post\Media::HTML]) as $attached) { - if ((empty($data['url']) || Strings::compareLink($data['url'], $attached['url'])) && - (!empty($attached['description']) || !empty($attached['image']) || !empty($attached['preview']))) { - $parts = parse_url($attached['url']); - if (!empty($parts['scheme']) && !empty($parts['host'])) { - if (empty($attached['publisher-name'])) { - $attached['publisher-name'] = $parts['host']; - } - if (empty($attached['publisher-url']) || empty(parse_url($attached['publisher-url'], PHP_URL_SCHEME))) { - $attached['publisher-url'] = $parts['scheme'] . '://' . $parts['host']; + $parts = parse_url($media[0]['url']); + if (!empty($parts['scheme']) && !empty($parts['host'])) { + if (empty($media[0]['publisher-name'])) { + $media[0]['publisher-name'] = $parts['host']; + } + if (empty($media[0]['publisher-url']) || empty(parse_url($media[0]['publisher-url'], PHP_URL_SCHEME))) { + $media[0]['publisher-url'] = $parts['scheme'] . '://' . $parts['host']; - if (!empty($parts['port'])) { - $attached['publisher-url'] .= ':' . $parts['port']; - } - } + if (!empty($parts['port'])) { + $media[0]['publisher-url'] .= ':' . $parts['port']; } - - $data['url'] = $attached['url']; - $data['title'] = $attached['name']; - $data['description'] = $attached['description']; - $data['type'] = 'link'; - $data['author_name'] = $attached['author-name']; - $data['author_url'] = $attached['author-url']; - $data['provider_name'] = $attached['publisher-name']; - $data['provider_url'] = $attached['publisher-url']; - $data['image'] = $attached['preview']; - $data['width'] = $attached['preview-width']; - $data['height'] = $attached['preview-height']; - $data['blurhash'] = $attached['blurhash']; } } + $data = []; + + $data['url'] = $media[0]['url']; + $data['title'] = $media[0]['name']; + $data['description'] = $media[0]['description']; + $data['type'] = 'link'; + $data['author_name'] = $media[0]['author-name']; + $data['author_url'] = $media[0]['author-url']; + $data['provider_name'] = $media[0]['publisher-name']; + $data['provider_url'] = $media[0]['publisher-url']; + $data['image'] = $media[0]['preview']; + $data['width'] = $media[0]['preview-width']; + $data['height'] = $media[0]['preview-height']; + $data['blurhash'] = $media[0]['blurhash']; + return new \Friendica\Object\Api\Mastodon\Card($data, $history); } } diff --git a/src/Factory/Api/Mastodon/Status.php b/src/Factory/Api/Mastodon/Status.php index d794247cb..190f1e0e2 100644 --- a/src/Factory/Api/Mastodon/Status.php +++ b/src/Factory/Api/Mastodon/Status.php @@ -259,10 +259,12 @@ class Status extends BaseFactory } } - $item['body'] = $this->contentItem->addSharedPost($item); - if (!is_null($item['raw-body'])) { $item['raw-body'] = $this->contentItem->addSharedPost($item, $item['raw-body']); + $item['raw-body'] = Post\Media::addHTMLLinkToBody($uriId, $item['raw-body']); + } else { + $item['body'] = $this->contentItem->addSharedPost($item); + $item['body'] = Post\Media::addHTMLLinkToBody($uriId, $item['body']); } } diff --git a/src/Factory/Api/Twitter/Status.php b/src/Factory/Api/Twitter/Status.php index de01ffd07..810b421ca 100644 --- a/src/Factory/Api/Twitter/Status.php +++ b/src/Factory/Api/Twitter/Status.php @@ -124,6 +124,7 @@ class Status extends BaseFactory */ private function createFromArray(array $item, int $uid, bool $include_entities): \Friendica\Object\Api\Twitter\Status { + $item = Post\Media::addHTMLAttachmentToItem($item); $author = $this->twitterUser->createFromContactId($item['author-id'], $uid, true); if (!empty($item['causer-id']) && ($item['post-reason'] == Item::PR_ANNOUNCEMENT)) { diff --git a/src/Model/Post/Media.php b/src/Model/Post/Media.php index 8545760be..1d97ac687 100644 --- a/src/Model/Post/Media.php +++ b/src/Model/Post/Media.php @@ -21,6 +21,7 @@ namespace Friendica\Model\Post; +use Friendica\Content\PageInfo; use Friendica\Content\Text\BBCode; use Friendica\Core\Logger; use Friendica\Core\Protocol; @@ -161,8 +162,10 @@ class Media */ public static function getAttachElement(string $href, int $length, string $type, string $title = ''): string { - $media = self::fetchAdditionalData(['type' => self::DOCUMENT, 'url' => $href, - 'size' => $length, 'mimetype' => $type, 'description' => $title]); + $media = self::fetchAdditionalData([ + 'type' => self::DOCUMENT, 'url' => $href, + 'size' => $length, 'mimetype' => $type, 'description' => $title + ]); return '[attach]href="' . $media['url'] . '" length="' . $media['size'] . '" type="' . $media['mimetype'] . '" title="' . $media['description'] . '"[/attach]'; @@ -262,8 +265,10 @@ class Media return $media; } - if (!empty($item['plink']) && Strings::compareLink($item['plink'], $media['url']) && - parse_url($item['plink'], PHP_URL_HOST) != parse_url($item['uri'], PHP_URL_HOST)) { + if ( + !empty($item['plink']) && Strings::compareLink($item['plink'], $media['url']) && + parse_url($item['plink'], PHP_URL_HOST) != parse_url($item['uri'], PHP_URL_HOST) + ) { Logger::debug('Not a link to an activity', ['uri-id' => $media['uri-id'], 'url' => $media['url'], 'plink' => $item['plink'], 'uri' => $item['uri']]); return $media; } @@ -482,8 +487,10 @@ class Media } $body = str_replace($picture[0], '', $body); $image = str_replace('-1.', '-0.', $picture[2]); - $attachments[$image] = ['uri-id' => $uriid, 'type' => self::IMAGE, 'url' => $image, - 'preview' => $picture[2], 'description' => $picture[3]]; + $attachments[$image] = [ + 'uri-id' => $uriid, 'type' => self::IMAGE, 'url' => $image, + 'preview' => $picture[2], 'description' => $picture[3] + ]; } } @@ -501,8 +508,10 @@ class Media } $body = str_replace($picture[0], '', $body); $image = str_replace('-1.', '-0.', $picture[2]); - $attachments[$image] = ['uri-id' => $uriid, 'type' => self::IMAGE, 'url' => $image, - 'preview' => $picture[2], 'description' => null]; + $attachments[$image] = [ + 'uri-id' => $uriid, 'type' => self::IMAGE, 'url' => $image, + 'preview' => $picture[2], 'description' => null + ]; } } @@ -630,7 +639,7 @@ class Media public static function insertFromAttachmentData(int $uriid, string $body) { $data = BBCode::getAttachmentData($body); - if (empty($data)) { + if (empty($data)) { return; } @@ -838,8 +847,10 @@ class Media continue; } - if (in_array($medium['type'], [self::AUDIO, self::IMAGE]) || - in_array($filetype, ['audio', 'image'])) { + if ( + in_array($medium['type'], [self::AUDIO, self::IMAGE]) || + in_array($filetype, ['audio', 'image']) + ) { $attachments['visual'][] = $medium; } elseif (($medium['type'] == self::VIDEO) || ($filetype == 'video')) { if (!empty($medium['height'])) { @@ -906,15 +917,15 @@ class Media if ($media['type'] == self::IMAGE) { if (!empty($media['preview'])) { if (!empty($media['description'])) { - $body .= "\n[url=" . $media['url'] . "][img=" . $media['preview'] . ']' . $media['description'] .'[/img][/url]'; + $body .= "\n[url=" . $media['url'] . "][img=" . $media['preview'] . ']' . $media['description'] . '[/img][/url]'; } else { - $body .= "\n[url=" . $media['url'] . "][img]" . $media['preview'] .'[/img][/url]'; + $body .= "\n[url=" . $media['url'] . "][img]" . $media['preview'] . '[/img][/url]'; } } else { if (!empty($media['description'])) { - $body .= "\n[img=" . $media['url'] . ']' . $media['description'] .'[/img]'; + $body .= "\n[img=" . $media['url'] . ']' . $media['description'] . '[/img]'; } else { - $body .= "\n[img]" . $media['url'] .'[/img]'; + $body .= "\n[img]" . $media['url'] . '[/img]'; } } } elseif ($media['type'] == self::AUDIO) { @@ -931,6 +942,92 @@ class Media return $body; } + /** + * Add an [attachment] element to the body for a given uri-id with a HTML media element + * + * @param integer $uriid + * @param string $body + * @return string + */ + public static function addHTMLAttachmentToBody(int $uriid, string $body): string + { + if (preg_match("/.*(\[attachment.*?\].*?\[\/attachment\]).*/ism", $body, $match)) { + return $body; + } + + $links = self::getByURIId($uriid, [self::HTML]); + if (empty($links)) { + return $body; + } + + $data = [ + 'type' => 'link', + 'url' => $links[0]['url'], + 'title' => $links[0]['name'], + 'text' => $links[0]['description'], + 'publisher_name' => $links[0]['publisher-name'], + 'publisher_url' => $links[0]['publisher-url'], + 'publisher_img' => $links[0]['publisher-image'], + 'author_name' => $links[0]['author-name'], + 'author_url' => $links[0]['author-url'], + 'author_img' => $links[0]['author-image'], + 'images' => [[ + 'src' => $links[0]['preview'], + 'height' => $links[0]['preview-height'], + 'width' => $links[0]['preview-width'], + ]] + ]; + $body .= "\n" . PageInfo::getFooterFromData($data); + + return $body; + } + + /** + * Add a link to the body for a given uri-id with a HTML media element + * + * @param integer $uriid + * @param string $body + * @return string + */ + public static function addHTMLLinkToBody(int $uriid, string $body): string + { + $links = self::getByURIId($uriid, [self::HTML]); + if (empty($links)) { + return $body; + } + + if (strpos($body, $links[0]['url'])) { + return $body; + } + + if (!empty($links[0]['name']) && ($links[0]['name'] != $links[0]['url'])) { + return $body . "\n[url=" . $links[0]['url'] . ']' . $links[0]['name'] . "[/url]"; + } else { + return $body . "\n[url]" . $links[0]['url'] . "[/url]"; + } + } + + /** + * Add an [attachment] element to the body and a link to raw-body for a given uri-id with a HTML media element + * + * @param array $item + * @return array + */ + public static function addHTMLAttachmentToItem(array $item): array + { + if (($item['gravity'] == Item::GRAVITY_ACTIVITY) || empty($item['uri-id'])) { + return $item; + } + + $item['body'] = self::addHTMLAttachmentToBody($item['uri-id'], $item['body']); + + if (!empty($item['raw-body'])) { + $item['raw-body'] = self::addHTMLLinkToBody($item['uri-id'], $item['raw-body']); + } + + return $item; + } + /** * Get preview link for given media id * diff --git a/src/Module/Api/Mastodon/Statuses.php b/src/Module/Api/Mastodon/Statuses.php index 943fc7514..65563b646 100644 --- a/src/Module/Api/Mastodon/Statuses.php +++ b/src/Module/Api/Mastodon/Statuses.php @@ -21,6 +21,7 @@ namespace Friendica\Module\Api\Mastodon; +use Friendica\Content\Text\BBCode; use Friendica\Content\Text\Markdown; use Friendica\Core\Protocol; use Friendica\Core\System; @@ -54,6 +55,7 @@ class Statuses extends BaseApi 'in_reply_to_id' => 0, // ID of the status being replied to, if status is a reply 'spoiler_text' => '', // Text to be shown as a warning or subject before the actual content. Statuses are generally collapsed behind this field. 'language' => '', // ISO 639 language code for this status. + 'friendica' => [], ], $request); $owner = User::getOwnerDataById($uid); @@ -66,7 +68,7 @@ class Statuses extends BaseApi 'origin' => true, ]; - $post = Post::selectFirst(['uri-id', 'id', 'uid', 'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid'], $condition); + $post = Post::selectFirst(['uri-id', 'id', 'gravity', 'uid', 'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid'], $condition); if (empty($post['id'])) { throw new HTTPException\NotFoundException('Item with URI ID ' . $this->parameters['id'] . ' not found for user ' . $uid . '.'); } @@ -78,11 +80,18 @@ class Statuses extends BaseApi $item['language'] = json_encode([$request['language'] => 1]); } - if (!empty($request['spoiler_text'])) { - if (($request['in_reply_to_id'] == $post['uri-id']) && DI::pConfig()->get($uid, 'system', 'api_spoiler_title', true)) { - $item['title'] = $request['spoiler_text']; + if ($post['gravity'] == Item::GRAVITY_PARENT) { + $item['title'] = $request['friendica']['title'] ?? ''; + } + + $spoiler_text = $request['spoiler_text']; + + if (!empty($spoiler_text)) { + if (!isset($request['friendica']['title']) && $post['gravity'] == Item::GRAVITY_PARENT && DI::pConfig()->get($uid, 'system', 'api_spoiler_title', true)) { + $item['title'] = $spoiler_text; } else { - $item['body'] = '[abstract=' . Protocol::ACTIVITYPUB . ']' . $request['spoiler_text'] . "[/abstract]\n" . $item['body']; + $item['body'] = '[abstract=' . Protocol::ACTIVITYPUB . ']' . $spoiler_text . "[/abstract]\n" . $item['body']; + $item['content-warning'] = BBCode::toPlaintext($spoiler_text); } } @@ -123,6 +132,9 @@ class Statuses extends BaseApi } unset($item['attachments']); } + if (!Item::isValid($item)) { + throw new \Exception('Missing parameters in definitien'); + } Item::update($item, ['id' => $post['id']]); Item::updateDisplayCache($post['uri-id']); diff --git a/src/Module/Friendica.php b/src/Module/Friendica.php index 2c669e886..90869878e 100644 --- a/src/Module/Friendica.php +++ b/src/Module/Friendica.php @@ -172,7 +172,7 @@ class Friendica extends BaseModule $data = [ 'version' => App::VERSION, - 'url' => DI::baseUrl(), + 'url' => (string)DI::baseUrl(), 'addons' => $visible_addons, 'locked_features' => $locked_features, 'explicit_content' => intval($config->get('system', 'explicit_content', 0)), diff --git a/src/Module/Post/Edit.php b/src/Module/Post/Edit.php index 2ca980381..5fb339e17 100644 --- a/src/Module/Post/Edit.php +++ b/src/Module/Post/Edit.php @@ -116,6 +116,7 @@ class Edit extends BaseModule } $item['body'] = Post\Media::addAttachmentsToBody($item['uri-id'], $item['body']); + $item = Post\Media::addHTMLAttachmentToItem($item); $jotplugins = ''; diff --git a/src/Protocol/ActivityPub/Transmitter.php b/src/Protocol/ActivityPub/Transmitter.php index 9123ae533..7da110f67 100644 --- a/src/Protocol/ActivityPub/Transmitter.php +++ b/src/Protocol/ActivityPub/Transmitter.php @@ -1639,6 +1639,8 @@ class Transmitter $real_quote = false; + $item = Post\Media::addHTMLAttachmentToItem($item); + $body = $item['body']; if ($type == 'Note') { diff --git a/src/Protocol/DFRN.php b/src/Protocol/DFRN.php index 26d5376e4..1bb7bf3ff 100644 --- a/src/Protocol/DFRN.php +++ b/src/Protocol/DFRN.php @@ -774,6 +774,7 @@ class DFRN } $body = Post\Media::addAttachmentsToBody($item['uri-id'], DI::contentItem()->addSharedPost($item)); + $body = Post\Media::addHTMLAttachmentToBody($item['uri-id'], $body); if ($item['private'] == Item::PRIVATE) { $body = Item::fixPrivatePhotos($body, $owner['uid'], $item, $cid); diff --git a/src/Protocol/Diaspora.php b/src/Protocol/Diaspora.php index 271b71b89..5554af7d8 100644 --- a/src/Protocol/Diaspora.php +++ b/src/Protocol/Diaspora.php @@ -83,14 +83,21 @@ class Diaspora return $contacts; } - $items = Post::select(['author-id', 'author-link', 'parent-author-link', 'parent-guid', 'guid'], - ['parent' => $item['parent'], 'gravity' => [Item::GRAVITY_COMMENT, Item::GRAVITY_ACTIVITY]]); + $items = Post::select( + ['author-id', 'author-link', 'parent-author-link', 'parent-guid', 'guid'], + ['parent' => $item['parent'], 'gravity' => [Item::GRAVITY_COMMENT, Item::GRAVITY_ACTIVITY]] + ); while ($item = Post::fetch($items)) { - $contact = DBA::selectFirst('contact', ['id', 'url', 'name', 'protocol', 'batch', 'network'], - ['id' => $item['author-id']]); - if (!DBA::isResult($contact) || empty($contact['batch']) || + $contact = DBA::selectFirst( + 'contact', + ['id', 'url', 'name', 'protocol', 'batch', 'network'], + ['id' => $item['author-id']] + ); + if ( + !DBA::isResult($contact) || empty($contact['batch']) || ($contact['network'] != Protocol::DIASPORA) || - Strings::compareLink($item['parent-author-link'], $item['author-link'])) { + Strings::compareLink($item['parent-author-link'], $item['author-link']) + ) { continue; } @@ -270,7 +277,7 @@ class Diaspora $basedom = XML::parseString($xml, true); if (!is_object($basedom)) { - Logger::notice('Received data does not seem to be an XML. Discarding. '.$xml); + Logger::notice('Received data does not seem to be an XML. Discarding. ' . $xml); if ($no_exit) { return false; } else { @@ -438,7 +445,7 @@ class Diaspora $alg = $base->alg; - $signed_data = $data.'.'.Strings::base64UrlEncode($type).'.'.Strings::base64UrlEncode($encoding).'.'.Strings::base64UrlEncode($alg); + $signed_data = $data . '.' . Strings::base64UrlEncode($type) . '.' . Strings::base64UrlEncode($encoding) . '.' . Strings::base64UrlEncode($alg); // decode the data @@ -719,7 +726,8 @@ class Diaspora $signed_data .= $entry; } - if (!in_array($fieldname, ['parent_author_signature', 'target_author_signature']) + if ( + !in_array($fieldname, ['parent_author_signature', 'target_author_signature']) || ($orig_type == 'relayable_retraction') ) { XML::copy($entry, $fields, $fieldname); @@ -795,7 +803,7 @@ class Diaspora Logger::info('Fetching diaspora key', ['handle' => $uri->getAddr(), 'callstack' => System::callstack(20)]); try { return DI::dsprContact()->getByAddr($uri)->pubKey; - } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) { + } catch (HTTPException\NotFoundException | \InvalidArgumentException $e) { return ''; } } @@ -1185,9 +1193,11 @@ class Diaspora */ private static function parentItem(int $uid, string $guid, WebFingerUri $author, array $contact) { - $fields = ['id', 'parent', 'body', 'wall', 'uri', 'guid', 'private', 'origin', + $fields = [ + 'id', 'parent', 'body', 'wall', 'uri', 'guid', 'private', 'origin', 'author-name', 'author-link', 'author-avatar', 'gravity', - 'owner-name', 'owner-link', 'owner-avatar']; + 'owner-name', 'owner-link', 'owner-avatar' + ]; $condition = ['uid' => $uid, 'guid' => $guid]; $item = Post::selectFirst($fields, $condition); @@ -1435,7 +1445,7 @@ class Diaspora } elseif ($person_uri) { try { return DI::dsprContact()->selectOneByAddr($person_uri)->baseurl . '/objects/' . $guid; - } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) { + } catch (HTTPException\NotFoundException | \InvalidArgumentException $e) { return ''; } } @@ -1472,7 +1482,6 @@ class Diaspora $contact = DI::dsprContact()->getByUrl(new Uri($match[3])); Tag::storeByHash($uriid, $match[1], $contact->name ?: $contact->nick, $contact->url); } catch (\Throwable $e) { - } } } @@ -1531,7 +1540,7 @@ class Diaspora try { $author_url = (string)DI::dsprContact()->getByAddr($author)->url; - } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) { + } catch (HTTPException\NotFoundException | \InvalidArgumentException $e) { Logger::notice('Unable to find author details', ['author' => $author->getAddr()]); return false; } @@ -1783,7 +1792,7 @@ class Diaspora try { $author_url = (string)DI::dsprContact()->getByAddr($author)->url; - } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) { + } catch (HTTPException\NotFoundException | \InvalidArgumentException $e) { Logger::notice('Unable to find author details', ['author' => $author->getAddr()]); return false; } @@ -1896,7 +1905,7 @@ class Diaspora try { $author = DI::dsprContact()->getByAddr($author_uri); - } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) { + } catch (HTTPException\NotFoundException | \InvalidArgumentException $e) { Logger::notice('Unable to find author details', ['author' => $author_uri->getAddr()]); return false; } @@ -1968,7 +1977,7 @@ class Diaspora try { $author_url = (string)DI::dsprContact()->getByAddr($author)->url; - } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) { + } catch (HTTPException\NotFoundException | \InvalidArgumentException $e) { Logger::notice('unable to find author details', ['author' => $author->getAddr()]); return false; } @@ -2013,8 +2022,10 @@ class Diaspora Logger::info('Participation stored', ['id' => $message_id, 'guid' => $guid, 'parent_guid' => $parent_guid, 'author' => $author]); // Send all existing comments and likes to the requesting server - $comments = Post::select(['id', 'uri-id', 'parent-author-network', 'author-network', 'verb', 'gravity'], - ['parent' => $toplevel_parent_item['id'], 'gravity' => [Item::GRAVITY_COMMENT, Item::GRAVITY_ACTIVITY]]); + $comments = Post::select( + ['id', 'uri-id', 'parent-author-network', 'author-network', 'verb', 'gravity'], + ['parent' => $toplevel_parent_item['id'], 'gravity' => [Item::GRAVITY_COMMENT, Item::GRAVITY_ACTIVITY]] + ); while ($comment = Post::fetch($comments)) { if (($comment['gravity'] == Item::GRAVITY_ACTIVITY) && !in_array($comment['verb'], [Activity::LIKE, Activity::DISLIKE])) { Logger::info('Unsupported activities are not relayed', ['item' => $comment['id'], 'verb' => $comment['verb']]); @@ -2089,7 +2100,7 @@ class Diaspora return false; } - $name = XML::unescape($data->first_name).((strlen($data->last_name)) ? ' ' . XML::unescape($data->last_name) : ''); + $name = XML::unescape($data->first_name) . ((strlen($data->last_name)) ? ' ' . XML::unescape($data->last_name) : ''); $image_url = XML::unescape($data->image_url); $birthday = XML::unescape($data->birthday); $about = Markdown::toBBCode(XML::unescape($data->bio)); @@ -2136,10 +2147,12 @@ class Diaspora $birthday = $contact['bd']; } - $fields = ['name' => $name, 'location' => $location, + $fields = [ + 'name' => $name, 'location' => $location, 'name-date' => DateTimeFormat::utcNow(), 'about' => $about, 'addr' => $author->getAddr(), 'nick' => $author->getUser(), 'keywords' => $keywords, - 'unsearchable' => !$searchable, 'sensitive' => $nsfw]; + 'unsearchable' => !$searchable, 'sensitive' => $nsfw + ]; if (!empty($birthday)) { $fields['bd'] = $birthday; @@ -2249,7 +2262,7 @@ class Diaspora try { $author_url = (string)DI::dsprContact()->getByAddr($author)->url; - } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) { + } catch (HTTPException\NotFoundException | \InvalidArgumentException $e) { Logger::notice('Cannot resolve diaspora handle for recipient', ['author' => $author->getAddr(), 'recipient' => $recipient]); return false; } @@ -2426,7 +2439,7 @@ class Diaspora try { $author = DI::dsprContact()->getByAddr($author_uri); - } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) { + } catch (HTTPException\NotFoundException | \InvalidArgumentException $e) { Logger::notice('Unable to find details for author', ['author' => $author_uri->getAddr()]); return false; } @@ -2561,7 +2574,7 @@ class Diaspora private static function storePhotoAsMedia(int $uriid, $photo) { // @TODO Need to find object type, roland@f.haeder.net - Logger::debug('photo='.get_class($photo)); + Logger::debug('photo=' . get_class($photo)); $data = []; $data['uri-id'] = $uriid; $data['type'] = Post\Media::IMAGE; @@ -2930,8 +2943,7 @@ class Diaspora try { $target = DI::dsprContact()->getByAddr(WebFingerUri::fromString($contact['addr'])); $dest_url = $public_batch ? $target->batch : $target->notify; - } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) { - + } catch (HTTPException\NotFoundException | \InvalidArgumentException $e) { } if (empty($dest_url)) { @@ -3011,8 +3023,7 @@ class Diaspora if (!empty($contact['addr'])) { try { $pubkey = DI::dsprContact()->getByAddr(WebFingerUri::fromString($contact['addr']))->pubKey; - } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) { - + } catch (HTTPException\NotFoundException | \InvalidArgumentException $e) { } } else { // The "addr" field should always be filled. @@ -3058,8 +3069,10 @@ class Diaspora // If the item belongs to a user, we take this user id. if ($item['uid'] == 0) { // @todo Possibly use an administrator account? - $condition = ['verified' => true, 'blocked' => false, - 'account_removed' => false, 'account_expired' => false, 'account-type' => User::ACCOUNT_TYPE_PERSON]; + $condition = [ + 'verified' => true, 'blocked' => false, + 'account_removed' => false, 'account_expired' => false, 'account-type' => User::ACCOUNT_TYPE_PERSON + ]; $first_user = DBA::selectFirst('user', ['uid'], $condition, ['order' => ['uid']]); $owner = User::getOwnerDataById($first_user['uid']); } else { @@ -3099,7 +3112,7 @@ class Diaspora $old_handle = DI::pConfig()->get($uid, 'system', 'previous_addr'); $profile = self::createProfileData($uid); - $signed_text = 'AccountMigration:'.$old_handle.':'.$profile['author']; + $signed_text = 'AccountMigration:' . $old_handle . ':' . $profile['author']; $signature = base64_encode(Crypto::rsaSign($signed_text, $owner['uprvkey'], 'sha256')); $message = [ @@ -3326,12 +3339,13 @@ class Diaspora $title = $item['title']; $body = Post\Media::addAttachmentsToBody($item['uri-id'], DI::contentItem()->addSharedPost($item), $attach_media); + $body = Post\Media::addHTMLLinkToBody($item['uri-id'], $body); // Fetch the title from an attached link - if there is one if (empty($item['title']) && DI::pConfig()->get($owner['uid'], 'system', 'attach_link_title')) { - $page_data = BBCode::getAttachmentData($item['body']); - if (!empty($page_data['type']) && !empty($page_data['title']) && ($page_data['type'] == 'link')) { - $title = $page_data['title']; + $media = Post\Media::getByURIId($item['uri-id'], [Post\Media::HTML]); + if (!empty($media) && !empty($media[0]['name']) && ($media[0]['name'] != $media[0]['url'])) { + $title = $media[0]['name']; } } @@ -3387,9 +3401,11 @@ class Diaspora if (count($event)) { $message['event'] = $event; - if (!empty($event['location']['address']) && + if ( + !empty($event['location']['address']) && !empty($event['location']['lat']) && - !empty($event['location']['lng'])) { + !empty($event['location']['lng']) + ) { $message['location'] = $event['location']; } @@ -3452,7 +3468,8 @@ class Diaspora private static function prependParentAuthorMention(string $body, string $profile_url): string { $profile = Contact::getByURL($profile_url, false, ['addr', 'name']); - if (!empty($profile['addr']) + if ( + !empty($profile['addr']) && !strstr($body, $profile['addr']) && !strstr($body, $profile_url) ) { @@ -3585,6 +3602,7 @@ class Diaspora } $body = Post\Media::addAttachmentsToBody($item['uri-id'], DI::contentItem()->addSharedPost($item)); + $body = Post\Media::addHTMLLinkToBody($item['uri-id'], $body); // The replied to autor mention is prepended for clarity if: // - Item replied isn't yours @@ -3906,9 +3924,9 @@ class Diaspora $kw = str_replace(' ', ' ', $kw); $arr = explode(' ', $kw); if (count($arr)) { - for ($x = 0; $x < 5; $x ++) { + for ($x = 0; $x < 5; $x++) { if (!empty($arr[$x])) { - $data['tag_string'] .= '#'. trim($arr[$x]) .' '; + $data['tag_string'] .= '#' . trim($arr[$x]) . ' '; } } } @@ -4093,7 +4111,7 @@ class Diaspora 'body' => '', 'quote-uri-id' => $UriId, 'allow_cid' => $owner['allow_cid'] ?? '', - 'allow_gid' => $owner['allow_gid']?? '', + 'allow_gid' => $owner['allow_gid'] ?? '', 'deny_cid' => $owner['deny_cid'] ?? '', 'deny_gid' => $owner['deny_gid'] ?? '', ]; diff --git a/src/Protocol/Feed.php b/src/Protocol/Feed.php index 7d778ee58..a83819742 100644 --- a/src/Protocol/Feed.php +++ b/src/Protocol/Feed.php @@ -384,8 +384,10 @@ class Feed } if (!$dryRun) { - $condition = ["`uid` = ? AND `uri` = ? AND `network` IN (?, ?)", - $importer['uid'], $item['uri'], Protocol::FEED, Protocol::DFRN]; + $condition = [ + "`uid` = ? AND `uri` = ? AND `network` IN (?, ?)", + $importer['uid'], $item['uri'], Protocol::FEED, Protocol::DFRN + ]; $previous = Post::selectFirst(['id', 'created'], $condition); if (DBA::isResult($previous)) { // Use the creation date when the post had been stored. It can happen this date changes in the feed. @@ -644,8 +646,10 @@ class Feed if (!$notify) { Post\Delayed::publish($item, $notify, $taglist, $attachments); } else { - $postings[] = ['item' => $item, 'notify' => $notify, - 'taglist' => $taglist, 'attachments' => $attachments]; + $postings[] = [ + 'item' => $item, 'notify' => $notify, + 'taglist' => $taglist, 'attachments' => $attachments + ]; } } else { Logger::info('Post already created or exists in the delayed posts queue', ['uid' => $item['uid'], 'uri' => $item['uri']]); @@ -852,7 +856,7 @@ class Feed $min_poll_interval = max(1, DI::config()->get('system', 'min_poll_interval')); - $poll_intervals = [$min_poll_interval, 15, 30, 60, 120, 180, 360, 720 ,1440, 10080, 43200]; + $poll_intervals = [$min_poll_interval, 15, 30, 60, 120, 180, 360, 720, 1440, 10080, 43200]; //$poll_intervals = [$min_poll_interval . ' minute', '15 minute', '30 minute', // '1 hour', '2 hour', '3 hour', '6 hour', '12 hour' ,'1 day', '1 week', '1 month']; @@ -942,7 +946,7 @@ class Feed $previous_created = $last_update; // Don't cache when the last item was posted less then 15 minutes ago (Cache duration) - if ((time() - strtotime($owner['last-item'])) < 15*60) { + if ((time() - strtotime($owner['last-item'])) < 15 * 60) { $result = DI::cache()->get($cachekey); if (!$nocache && !is_null($result)) { Logger::info('Cached feed duration', ['seconds' => number_format(microtime(true) - $stamp, 3), 'nick' => $owner['nickname'], 'filter' => $filter, 'created' => $previous_created]); @@ -953,11 +957,13 @@ class Feed $check_date = empty($last_update) ? '' : DateTimeFormat::utc($last_update); $authorid = Contact::getIdForURL($owner['url']); - $condition = ["`uid` = ? AND `received` > ? AND NOT `deleted` AND `gravity` IN (?, ?) + $condition = [ + "`uid` = ? AND `received` > ? AND NOT `deleted` AND `gravity` IN (?, ?) AND `private` != ? AND `visible` AND `wall` AND `parent-network` IN (?, ?, ?, ?)", $owner['uid'], $check_date, Item::GRAVITY_PARENT, Item::GRAVITY_COMMENT, Item::PRIVATE, Protocol::ACTIVITYPUB, - Protocol::OSTATUS, Protocol::DFRN, Protocol::DIASPORA]; + Protocol::OSTATUS, Protocol::DFRN, Protocol::DIASPORA + ]; if ($filter === 'comments') { $condition[0] .= " AND `gravity` = ? "; @@ -1124,13 +1130,21 @@ class Feed XML::addElement($doc, $entry, 'title', html_entity_decode($title, ENT_QUOTES, 'UTF-8')); $body = Post\Media::addAttachmentsToBody($item['uri-id'], DI::contentItem()->addSharedPost($item)); + $body = Post\Media::addHTMLLinkToBody($item['uri-id'], $body); $body = BBCode::convertForUriId($item['uri-id'], $body, BBCode::ACTIVITYPUB); XML::addElement($doc, $entry, 'content', $body, ['type' => 'html']); - XML::addElement($doc, $entry, 'link', '', ['rel' => 'alternate', 'type' => 'text/html', - 'href' => DI::baseUrl() . '/display/' . $item['guid']] + XML::addElement( + $doc, + $entry, + 'link', + '', + [ + 'rel' => 'alternate', 'type' => 'text/html', + 'href' => DI::baseUrl() . '/display/' . $item['guid'] + ] ); XML::addElement($doc, $entry, 'published', DateTimeFormat::utc($item['created'] . '+00:00', DateTimeFormat::ATOM)); @@ -1172,12 +1186,14 @@ class Feed if (isset($parent_plink)) { $attributes = [ 'ref' => $item['thr-parent'], - 'href' => $parent_plink]; + 'href' => $parent_plink + ]; XML::addElement($doc, $entry, 'thr:in-reply-to', '', $attributes); $attributes = [ 'rel' => 'related', - 'href' => $parent_plink]; + 'href' => $parent_plink + ]; XML::addElement($doc, $entry, 'link', '', $attributes); } } @@ -1210,16 +1226,16 @@ class Feed } // Fetch information about the post - $siteinfo = BBCode::getAttachedData($item['body']); - if (isset($siteinfo['title'])) { - return $siteinfo['title']; + $media = Post\Media::getByURIId($item['uri-id'], [Post\Media::HTML]); + if (!empty($media) && !empty($media[0]['name']) && ($media[0]['name'] != $media[0]['url'])) { + return $media[0]['name']; } // If no bookmark is found then take the first line // Remove the share element before fetching the first line $title = trim(preg_replace("/\[share.*?\](.*?)\[\/share\]/ism", "\n$1\n", $item['body'])); - $title = BBCode::toPlaintext($title)."\n"; + $title = BBCode::toPlaintext($title) . "\n"; $pos = strpos($title, "\n"); $trailer = ''; if (($pos == 0) || ($pos > 100)) { diff --git a/src/Protocol/OStatus.php b/src/Protocol/OStatus.php index a93a0584d..abaa298eb 100644 --- a/src/Protocol/OStatus.php +++ b/src/Protocol/OStatus.php @@ -1515,6 +1515,7 @@ class OStatus XML::addElement($doc, $entry, 'title', html_entity_decode($title, ENT_QUOTES, 'UTF-8')); $body = Post\Media::addAttachmentsToBody($item['uri-id'], DI::contentItem()->addSharedPost($item)); + $body = Post\Media::addHTMLLinkToBody($item['uri-id'], $body); if (!empty($item['title'])) { $body = '[b]' . $item['title'] . "[/b]\n\n" . $body; diff --git a/src/Security/OAuth1/OAuthUtil.php b/src/Security/OAuth1/OAuthUtil.php index bb78340ea..2547f0908 100644 --- a/src/Security/OAuth1/OAuthUtil.php +++ b/src/Security/OAuth1/OAuthUtil.php @@ -154,32 +154,9 @@ class OAuthUtil public static function build_http_query($params) { - if (!$params) return ''; - - // Urlencode both keys and values - $keys = OAuthUtil::urlencode_rfc3986(array_keys($params)); - $values = OAuthUtil::urlencode_rfc3986(array_values($params)); - $params = array_combine($keys, $values); - // Parameters are sorted by name, using lexicographical byte value ordering. // Ref: Spec: 9.1.1 (1) uksort($params, 'strcmp'); - - $pairs = []; - foreach ($params as $parameter => $value) { - if (is_array($value)) { - // If two or more parameters share the same name, they are sorted by their value - // Ref: Spec: 9.1.1 (1) - natsort($value); - foreach ($value as $duplicate_value) { - $pairs[] = $parameter . '=' . $duplicate_value; - } - } else { - $pairs[] = $parameter . '=' . $value; - } - } - // For each parameter, the name is separated from the corresponding value by an '=' character (ASCII code 61) - // Each name-value pair is separated by an '&' character (ASCII code 38) - return implode('&', $pairs); + return http_build_query($params, '', null, PHP_QUERY_RFC3986); } } diff --git a/src/Worker/Notifier.php b/src/Worker/Notifier.php index 93c6c180a..2e42c225f 100644 --- a/src/Worker/Notifier.php +++ b/src/Worker/Notifier.php @@ -114,6 +114,7 @@ class Notifier // find ancestors $condition = ['id' => $target_id, 'visible' => true]; $target_item = Post::selectFirst(Item::DELIVER_FIELDLIST, $condition); + $target_item = Post\Media::addHTMLAttachmentToItem($target_item); if (!DBA::isResult($target_item) || !intval($target_item['parent'])) { Logger::info('No target item', ['cmd' => $cmd, 'target' => $target_id]); diff --git a/static/dbstructure.config.php b/static/dbstructure.config.php index cc420a449..6eaad609e 100644 --- a/static/dbstructure.config.php +++ b/static/dbstructure.config.php @@ -55,7 +55,7 @@ use Friendica\Database\DBA; if (!defined('DB_UPDATE_VERSION')) { - define('DB_UPDATE_VERSION', 1516); + define('DB_UPDATE_VERSION', 1517); } return [ @@ -1565,7 +1565,7 @@ return [ "comment" => "Currently running system processes", "fields" => [ "pid" => ["type" => "int unsigned", "not null" => "1", "primary" => "1", "comment" => "The ID of the process"], - "hostname" => ["type" => "varchar(32)", "not null" => "1", "primary" => "1", "comment" => "The name of the host the process is ran on"], + "hostname" => ["type" => "varchar(255)", "not null" => "1", "primary" => "1", "comment" => "The name of the host the process is ran on"], "command" => ["type" => "varbinary(32)", "not null" => "1", "default" => "", "comment" => ""], "created" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => ""], ], diff --git a/view/theme/frio/css/style.css b/view/theme/frio/css/style.css index c7adc2c8a..a12c2ae6a 100644 --- a/view/theme/frio/css/style.css +++ b/view/theme/frio/css/style.css @@ -2038,7 +2038,10 @@ code > .hl-main { .wall-item-responses > div > p { margin: 0; } - +.btn-group-event { + display: flex; + flex-basis: auto; +} /* wall item hover effects */ @media (min-width: 768px) { diff --git a/view/theme/frio/templates/comment_item.tpl b/view/theme/frio/templates/comment_item.tpl index dc4cd589c..7bce14ee1 100644 --- a/view/theme/frio/templates/comment_item.tpl +++ b/view/theme/frio/templates/comment_item.tpl @@ -54,7 +54,7 @@

{{if $preview}} - + {{/if}}

diff --git a/view/theme/frio/templates/wall_thread.tpl b/view/theme/frio/templates/wall_thread.tpl index 5fda4d2bc..e4f87f940 100644 --- a/view/theme/frio/templates/wall_thread.tpl +++ b/view/theme/frio/templates/wall_thread.tpl @@ -495,7 +495,7 @@ as the value of $top_child_total (this is done at the end of this file) {{* Event attendance buttons *}} {{if $item.isevent}} -
+