From e94df546c33ad62e5b404447776f4fcc113f8de1 Mon Sep 17 00:00:00 2001 From: Hugo Sales Date: Sun, 25 Apr 2021 21:14:35 +0000 Subject: [PATCH] [Posting] Extract and store URLs from note content. Introduce 'AttachmentStoreNew' event --- components/Posting/Posting.php | 29 +++++++++++++++++++++------- src/Core/GSFile.php | 35 +++++++++++++++++++++++----------- 2 files changed, 46 insertions(+), 18 deletions(-) diff --git a/components/Posting/Posting.php b/components/Posting/Posting.php index 313a18448a..e791792ef6 100644 --- a/components/Posting/Posting.php +++ b/components/Posting/Posting.php @@ -41,6 +41,13 @@ use Symfony\Component\Form\Extension\Core\Type\TextareaType; class Posting extends Component { + /** + * "Perfect URL Regex", courtesy of https://urlregex.com/ + */ + const URL_REGEX = << $actor_id, - 'content' => Security::sanitize($content), + 'content' => $content, 'is_local' => $is_local, 'reply_to' => $reply_to, 'repeat_of' => $repeat_of, ]); + $processed_attachments = []; foreach ($attachments as $f) { - $na = GSFile::validateAndStoreAttachment( + $processed_attachments[] = GSFile::validateAndStoreAttachment( $f, Common::config('attachments', 'dir'), - Security::sanitize($title = $f->getClientOriginalName()), - $is_local = true, $actor_id + Security::sanitize($f->getClientOriginalName()), + is_local: true, actor_id: $actor_id ); - $processed_attachments[] = $na; - DB::persist($na); } + + $matched_urls = []; + preg_match_all(self::URL_REGEX, $content, $matched_urls, PREG_SET_ORDER); + foreach ($matched_urls as $match) { + $processed_attachments[] = GSFile::validateAndStoreURL($url); + } + DB::persist($note); + // Need file and note ids for the next step DB::flush(); if ($processed_attachments != []) { diff --git a/src/Core/GSFile.php b/src/Core/GSFile.php index 5e8b9c3f87..60928a8908 100644 --- a/src/Core/GSFile.php +++ b/src/Core/GSFile.php @@ -57,20 +57,33 @@ class GSFile 'is_local' => $is_local, ]); $sfile->move($dest_dir, $hash); + DB::persist($attachment); + Event::handle('AttachmentStoreNew', [&$attachment]); return $attachment; } /** - * Perform file validation (checks and normalization) and store the given file + * Create an attachment for the given URL, fetching the mimetype + * + * @throws \InvalidArgumentException */ - public static function validateAndStoreAttachmentThumbnail(SymfonyFile $sfile, - string $dest_dir, - ?string $title = null, - bool $is_local = true, - int $actor_id = null): Attachment//Thumbnail + public static function validateAndStoreURL(string $url): Attachment { - $attachment = self::validateAndStoreAttachment($sfile,$dest_dir,$title,$is_local,$actor_id); - return $attachment; + if (Common::isValidHttpUrl($url)) { + HTTPClient::head($url); + $headers = $head->getHeaders(); + $headers = array_change_key_case($headers, CASE_LOWER); + $attachment = Attachment::create([ + 'remote_url' => $match[0], + 'remote_url_hash' => hash('sha256', $match[0]), + 'mimetype' => $headers['content-type'], + ]); + DB::persist($attachment); + Event::handle('AttachmentStoreNew', [&$at]); + return $attachment; + } else { + throw new \InvalidArgumentException(); + } } /** @@ -159,7 +172,7 @@ class GSFile */ public static function mimetypeMajor(string $mime) { - return explode('/', self::mimeBare($mime))[0]; + return explode('/', self::mimetypeBare($mime))[0]; } /** @@ -167,13 +180,13 @@ class GSFile */ public static function mimetypeMinor(string $mime) { - return explode('/', self::mimeBare($mime))[1]; + return explode('/', self::mimetypeBare($mime))[1]; } /** * Get only the mimetype and not additional info (separated from bare mime with semi-colon) */ - public static function mimeBare(string $mimetype) + public static function mimetypeBare(string $mimetype) { $mimetype = mb_strtolower($mimetype); if (($semicolon = mb_strpos($mimetype, ';')) !== false) {