From 7e200174d6086eb135ccd74f84c89f35bf91cd86 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 28 Jun 2021 04:53:20 +0000 Subject: [PATCH 1/5] Replace attachment links in the body --- src/Model/Item.php | 65 +++++++++++++++++++++++++++++++------------- src/Module/Photo.php | 4 +-- 2 files changed, 48 insertions(+), 21 deletions(-) diff --git a/src/Model/Item.php b/src/Model/Item.php index 78e0321b47..fb88da8a28 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -2742,6 +2742,23 @@ class Item } $body = $item['body'] ?? ''; + $shared = BBCode::fetchShareAttributes($body); + if (!empty($shared['guid'])) { + $shared_item = Post::selectFirst(['uri-id', 'plink'], ['guid' => $shared['guid']]); + $shared_uri_id = $shared_item['uri-id'] ?? 0; + $shared_links = [strtolower($shared_item['plink'] ?? '')]; + $shared_attachments = Post\Media::splitAttachments($shared_uri_id, $shared['guid']); + $shared_links = array_merge($shared_links, array_column($shared_attachments['visual'], 'url')); + $shared_links = array_merge($shared_links, array_column($shared_attachments['link'], 'url')); + $shared_links = array_merge($shared_links, array_column($shared_attachments['additional'], 'url')); + $item['body'] = self::replaceVisualAttachments($shared_attachments, $item['body']); + } else { + $shared_uri_id = 0; + $shared_links = []; + } + $attachments = Post\Media::splitAttachments($item['uri-id'], $item['guid'] ?? '', $shared_links); + $item['body'] = self::replaceVisualAttachments($attachments, $item['body'] ?? ''); + $item['body'] = preg_replace("/\s*\[attachment .*?\].*?\[\/attachment\]\s*/ism", "\n", $item['body']); self::putInCache($item); $item['body'] = $body; @@ -2764,25 +2781,13 @@ class Item return $s; } - $shared = BBCode::fetchShareAttributes($item['body']); - if (!empty($shared['guid'])) { - $shared_item = Post::selectFirst(['uri-id', 'plink'], ['guid' => $shared['guid']]); - $shared_uri_id = $shared_item['uri-id'] ?? 0; - $shared_links = [strtolower($shared_item['plink'] ?? '')]; - $attachments = Post\Media::splitAttachments($shared_uri_id, $shared['guid']); - $s = self::addVisualAttachments($attachments, $item, $s, true); - $s = self::addLinkAttachment($attachments, $body, $s, true, []); - $s = self::addNonVisualAttachments($attachments, $item, $s, true); - $shared_links = array_merge($shared_links, array_column($attachments['visual'], 'url')); - $shared_links = array_merge($shared_links, array_column($attachments['link'], 'url')); - $shared_links = array_merge($shared_links, array_column($attachments['additional'], 'url')); + if (!empty($shared_attachments)) { + $s = self::addVisualAttachments($shared_attachments, $item, $s, true); + $s = self::addLinkAttachment($shared_attachments, $body, $s, true, []); + $s = self::addNonVisualAttachments($shared_attachments, $item, $s, true); $body = preg_replace("/\s*\[share .*?\].*?\[\/share\]\s*/ism", '', $body); - } else { - $shared_uri_id = 0; - $shared_links = []; } - $attachments = Post\Media::splitAttachments($item['uri-id'], $item['guid'] ?? '', $shared_links); $s = self::addVisualAttachments($attachments, $item, $s, false); $s = self::addLinkAttachment($attachments, $body, $s, false, $shared_links); $s = self::addNonVisualAttachments($attachments, $item, $s, false); @@ -2843,6 +2848,28 @@ class Item return false; } + /** + * Replace visual attachments in the body + * + * @param array $attachments + * @param string $body + * @return string modified body + */ + private static function replaceVisualAttachments(array $attachments, string $body) + { + $stamp1 = microtime(true); + + foreach ($attachments['visual'] as $attachment) { + if (!empty($attachment['preview'])) { + $body = str_replace($attachment['preview'], Post\Media::getPreviewUrlForId($attachment['id'], Proxy::SIZE_LARGE), $body); + } elseif ($attachment['filetype'] == 'image') { + $body = str_replace($attachment['url'], Post\Media::getUrlForId($attachment['id']), $body); + } + } + DI::profiler()->saveTimestamp($stamp1, 'rendering'); + return $body; + } + /** * Add visual attachments to the content * @@ -2864,7 +2891,7 @@ class Item } if (!empty($attachment['preview'])) { - $preview_url = Post\Media::getPreviewUrlForId($attachment['id'], Proxy::SIZE_LARGE); + $preview_url = Post\Media::getPreviewUrlForId($attachment['id'], Proxy::SIZE_LARGE); } else { $preview_url = ''; } @@ -2901,7 +2928,7 @@ class Item } } elseif ($attachment['filetype'] == 'image') { $media = Renderer::replaceMacros(Renderer::getMarkupTemplate('content/image.tpl'), [ - '$image' => [ + '$image' => [ 'src' => Post\Media::getUrlForId($attachment['id']), 'preview' => Post\Media::getPreviewUrlForId($attachment['id'], ($attachment['width'] > $attachment['height']) ? Proxy::SIZE_MEDIUM : Proxy::SIZE_LARGE), 'attachment' => $attachment, @@ -2984,7 +3011,7 @@ class Item if ($preview && !empty($attachment['preview'])) { if ($attachment['preview-width'] >= 500) { - $data['image'] = Post\Media::getPreviewUrlForId($attachment['id'], Proxy::SIZE_MEDIUM); + $data['image'] = Post\Media::getPreviewUrlForId($attachment['id'], Proxy::SIZE_MEDIUM); } else { $data['preview'] = Post\Media::getPreviewUrlForId($attachment['id'], Proxy::SIZE_MEDIUM); } diff --git a/src/Module/Photo.php b/src/Module/Photo.php index 9fdda1a8d0..c1e681cd25 100644 --- a/src/Module/Photo.php +++ b/src/Module/Photo.php @@ -173,8 +173,8 @@ class Photo extends BaseModule return MPhoto::createPhotoForExternalResource($url); case "media": - $media = DBA::selectFirst('post-media', ['url'], ['id' => $uid, 'type' => Post\Media::IMAGE]); - if (empty($media['url'])) { + $media = DBA::selectFirst('post-media', ['url', 'uri-id'], ['id' => $uid, 'type' => Post\Media::IMAGE]); + if (empty($media)) { return false; } From 6f350c1e5957d6c426c2031e402e9f612b2876e5 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 28 Jun 2021 10:08:51 +0000 Subject: [PATCH 2/5] Use signed requests for fetching private images --- src/Model/Photo.php | 5 +++-- src/Model/Storage/ExternalResource.php | 21 +++++++++++++-------- src/Module/Photo.php | 10 +++++----- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/Model/Photo.php b/src/Model/Photo.php index acc6b0d197..40bdcf71e3 100644 --- a/src/Model/Photo.php +++ b/src/Model/Photo.php @@ -268,19 +268,20 @@ class Photo * Construct a photo array for an external resource image * * @param string $url Image URL + * @param int $uid User ID of the requesting person * @param string $mimetype Image mime type. Defaults to "image/jpeg" * * @return array * @throws \Exception */ - public static function createPhotoForExternalResource($url, $mimetype = "image/jpeg") + public static function createPhotoForExternalResource($url, $uid, $mimetype = "image/jpeg") { $fields = self::getFields(); $values = array_fill(0, count($fields), ""); $photo = array_combine($fields, $values); $photo['backend-class'] = ExternalResource::NAME; - $photo['backend-ref'] = $url; + $photo['backend-ref'] = json_encode(['url' => $url, 'uid' => $uid]); $photo['type'] = $mimetype; $photo['cacheable'] = false; diff --git a/src/Model/Storage/ExternalResource.php b/src/Model/Storage/ExternalResource.php index 9c57e3990b..69df7b9b20 100644 --- a/src/Model/Storage/ExternalResource.php +++ b/src/Model/Storage/ExternalResource.php @@ -22,7 +22,7 @@ namespace Friendica\Model\Storage; use BadMethodCallException; -use Friendica\DI; +use Friendica\Util\HTTPSignature; /** * External resource storage class @@ -37,16 +37,21 @@ class ExternalResource implements IStorage /** * @inheritDoc */ - public function get(string $filename) + public function get(string $reference) { - $parts = parse_url($filename); + $data = json_decode($reference); + if (empty($data->url)) { + return ""; + } + + $parts = parse_url($data->url); if (empty($parts['scheme']) || empty($parts['host'])) { return ""; } - $curlResult = DI::httpRequest()->get($filename); - if ($curlResult->isSuccess()) { - return $curlResult->getBody(); + $fetchResult = HTTPSignature::fetchRaw($data->url, $data->uid); + if ($fetchResult->isSuccess()) { + return $fetchResult->getBody(); } else { return ""; } @@ -55,12 +60,12 @@ class ExternalResource implements IStorage /** * @inheritDoc */ - public function put(string $data, string $filename = '') + public function put(string $data, string $reference = '') { throw new BadMethodCallException(); } - public function delete(string $filename) + public function delete(string $reference) { throw new BadMethodCallException(); } diff --git a/src/Module/Photo.php b/src/Module/Photo.php index c1e681cd25..8a8c304361 100644 --- a/src/Module/Photo.php +++ b/src/Module/Photo.php @@ -171,7 +171,7 @@ class Photo extends BaseModule $author = Contact::selectFirst([], ["`id` IN (SELECT `author-id` FROM `post` WHERE `uri-id` = ?)", $media['uri-id']]); $url = Contact::magicLinkByContact($author, $url); - return MPhoto::createPhotoForExternalResource($url); + return MPhoto::createPhotoForExternalResource($url, local_user()); case "media": $media = DBA::selectFirst('post-media', ['url', 'uri-id'], ['id' => $uid, 'type' => Post\Media::IMAGE]); if (empty($media)) { @@ -181,7 +181,7 @@ class Photo extends BaseModule $author = Contact::selectFirst([], ["`id` IN (SELECT `author-id` FROM `post` WHERE `uri-id` = ?)", $media['uri-id']]); $url = Contact::magicLinkByContact($author, $media['url']); - return MPhoto::createPhotoForExternalResource($url); + return MPhoto::createPhotoForExternalResource($url, local_user()); case "contact": $contact = Contact::getById($uid, ['uid', 'url', 'avatar', 'photo', 'xmpp', 'addr']); if (empty($contact)) { @@ -201,7 +201,7 @@ class Photo extends BaseModule } else { $url = Contact::getDefaultAvatar($contact, Proxy::SIZE_SMALL); } - return MPhoto::createPhotoForExternalResource($url); + return MPhoto::createPhotoForExternalResource($url, local_user()); case "header": $contact = Contact::getById($uid, ['uid', 'url', 'header']); if (empty($contact)) { @@ -215,7 +215,7 @@ class Photo extends BaseModule } else { $url = DI::baseUrl() . '/images/blank.png'; } - return MPhoto::createPhotoForExternalResource($url); + return MPhoto::createPhotoForExternalResource($url, local_user()); case "profile": case "custom": $scale = 4; @@ -247,7 +247,7 @@ class Photo extends BaseModule $parts = parse_url($default); if (!empty($parts['scheme']) || !empty($parts['host'])) { - $photo = MPhoto::createPhotoForExternalResource($default); + $photo = MPhoto::createPhotoForExternalResource($default, local_user()); } else { $photo = MPhoto::createPhotoForSystemResource($default); } From 4f95622d7355c01d4e268ca02c71c21db136620e Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 28 Jun 2021 12:40:58 +0000 Subject: [PATCH 3/5] Only user the user with attached media. --- src/Module/Photo.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Module/Photo.php b/src/Module/Photo.php index 8a8c304361..2a7fdc5fd8 100644 --- a/src/Module/Photo.php +++ b/src/Module/Photo.php @@ -171,7 +171,7 @@ class Photo extends BaseModule $author = Contact::selectFirst([], ["`id` IN (SELECT `author-id` FROM `post` WHERE `uri-id` = ?)", $media['uri-id']]); $url = Contact::magicLinkByContact($author, $url); - return MPhoto::createPhotoForExternalResource($url, local_user()); + return MPhoto::createPhotoForExternalResource($url, (int)local_user()); case "media": $media = DBA::selectFirst('post-media', ['url', 'uri-id'], ['id' => $uid, 'type' => Post\Media::IMAGE]); if (empty($media)) { @@ -181,7 +181,7 @@ class Photo extends BaseModule $author = Contact::selectFirst([], ["`id` IN (SELECT `author-id` FROM `post` WHERE `uri-id` = ?)", $media['uri-id']]); $url = Contact::magicLinkByContact($author, $media['url']); - return MPhoto::createPhotoForExternalResource($url, local_user()); + return MPhoto::createPhotoForExternalResource($url, (int)local_user()); case "contact": $contact = Contact::getById($uid, ['uid', 'url', 'avatar', 'photo', 'xmpp', 'addr']); if (empty($contact)) { @@ -201,7 +201,7 @@ class Photo extends BaseModule } else { $url = Contact::getDefaultAvatar($contact, Proxy::SIZE_SMALL); } - return MPhoto::createPhotoForExternalResource($url, local_user()); + return MPhoto::createPhotoForExternalResource($url, 0); case "header": $contact = Contact::getById($uid, ['uid', 'url', 'header']); if (empty($contact)) { @@ -215,7 +215,7 @@ class Photo extends BaseModule } else { $url = DI::baseUrl() . '/images/blank.png'; } - return MPhoto::createPhotoForExternalResource($url, local_user()); + return MPhoto::createPhotoForExternalResource($url, 0); case "profile": case "custom": $scale = 4; @@ -247,7 +247,7 @@ class Photo extends BaseModule $parts = parse_url($default); if (!empty($parts['scheme']) || !empty($parts['host'])) { - $photo = MPhoto::createPhotoForExternalResource($default, local_user()); + $photo = MPhoto::createPhotoForExternalResource($default, 0); } else { $photo = MPhoto::createPhotoForSystemResource($default); } From a1a584f444ef9552a59d05cd337351ca375fc3d5 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 28 Jun 2021 13:09:00 +0000 Subject: [PATCH 4/5] External resources are cacheable --- src/Model/Photo.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Model/Photo.php b/src/Model/Photo.php index 40bdcf71e3..7de0084e24 100644 --- a/src/Model/Photo.php +++ b/src/Model/Photo.php @@ -283,7 +283,7 @@ class Photo $photo['backend-class'] = ExternalResource::NAME; $photo['backend-ref'] = json_encode(['url' => $url, 'uid' => $uid]); $photo['type'] = $mimetype; - $photo['cacheable'] = false; + $photo['cacheable'] = true; return $photo; } From 2f06d271bb4380cacd95d9f17ecf65dfc7faffcb Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 28 Jun 2021 13:58:06 +0000 Subject: [PATCH 5/5] Use a default value for "uid" --- src/Model/Photo.php | 2 +- src/Module/Photo.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Model/Photo.php b/src/Model/Photo.php index 7de0084e24..fe416c360f 100644 --- a/src/Model/Photo.php +++ b/src/Model/Photo.php @@ -274,7 +274,7 @@ class Photo * @return array * @throws \Exception */ - public static function createPhotoForExternalResource($url, $uid, $mimetype = "image/jpeg") + public static function createPhotoForExternalResource($url, $uid = 0, $mimetype = "image/jpeg") { $fields = self::getFields(); $values = array_fill(0, count($fields), ""); diff --git a/src/Module/Photo.php b/src/Module/Photo.php index 2a7fdc5fd8..f61a4564d6 100644 --- a/src/Module/Photo.php +++ b/src/Module/Photo.php @@ -201,7 +201,7 @@ class Photo extends BaseModule } else { $url = Contact::getDefaultAvatar($contact, Proxy::SIZE_SMALL); } - return MPhoto::createPhotoForExternalResource($url, 0); + return MPhoto::createPhotoForExternalResource($url); case "header": $contact = Contact::getById($uid, ['uid', 'url', 'header']); if (empty($contact)) { @@ -215,7 +215,7 @@ class Photo extends BaseModule } else { $url = DI::baseUrl() . '/images/blank.png'; } - return MPhoto::createPhotoForExternalResource($url, 0); + return MPhoto::createPhotoForExternalResource($url); case "profile": case "custom": $scale = 4; @@ -247,7 +247,7 @@ class Photo extends BaseModule $parts = parse_url($default); if (!empty($parts['scheme']) || !empty($parts['host'])) { - $photo = MPhoto::createPhotoForExternalResource($default, 0); + $photo = MPhoto::createPhotoForExternalResource($default); } else { $photo = MPhoto::createPhotoForSystemResource($default); }