From 651af276744944b8833383b1c697efe451c17351 Mon Sep 17 00:00:00 2001 From: Hugo Sales Date: Fri, 14 Aug 2020 22:34:58 +0000 Subject: [PATCH] [Media] Move code from media.php to utils.php --- components/Media/Media.php | 74 ++++----------------- components/Media/Utils.php | 132 +++++++++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+), 61 deletions(-) create mode 100644 components/Media/Utils.php diff --git a/components/Media/Media.php b/components/Media/Media.php index aa9f066833..13e6bcbd26 100644 --- a/components/Media/Media.php +++ b/components/Media/Media.php @@ -19,87 +19,39 @@ namespace Component\Media; -use App\Core\DB\DB; use App\Core\Event; -use App\Core\Log; use App\Core\Module; -use App\Entity\File; use App\Util\Common; -use App\Util\Exception\ClientException; use App\Util\Nickname; +use Exception; use Symfony\Component\Asset\Package; use Symfony\Component\Asset\VersionStrategy\EmptyVersionStrategy; -use Symfony\Component\HttpFoundation\BinaryFileResponse; -use Symfony\Component\HttpFoundation\File\Exception\FileException; -use Symfony\Component\HttpFoundation\File\File as SymfonyFile; -use Symfony\Component\HttpFoundation\Response; class Media extends Module { - public static function validateAndStoreFile(SymfonyFile $sfile, string $dest_dir, ?string $title = null, bool $is_local = true): File + public static function __callStatic(string $name, array $args) { - // The following properly gets the mimetype with `file` or other - // available methods, so should be safe - $hash = hash_file(File::FILEHASH_ALGO, $sfile->getPathname()); - $file = File::create([ - 'file_hash' => $hash, - 'mimetype' => $sfile->getMimeType(), - 'size' => $sfile->getSize(), - 'title' => $title, - 'timestamp' => $sfile->getMTime(), - 'is_local' => $is_local, - ]); - $sfile->move($dest_dir, $hash); - // TODO Normalize file types - return $file; - } - - /** - * Include $filepath in the response, for viewing and downloading. - * - * @throws ServerException - */ - public static function sendFile(string $filepath, string $mimetype, string $output_filename, string $disposition = 'inline'): Response - { - if (file_exists($filepath)) { - try { - $response = new BinaryFileResponse( - $filepath, - Response::HTTP_OK, - [ - 'Content-Description' => 'File Transfer', - 'Content-Type' => $mimetype, - ], - $public = true, - $disposition, - $add_etag = true, - $add_last_modified = true - ); - if (Common::config('site', 'x_static_delivery')) { - $response->trustXSendfileTypeHeader(); - } - return $response; - } catch (FileException $e) { - // continue bellow - } - } - Log::error("Couldn't read file at {$filepath}."); - throw new ClientException('No such file', Response::HTTP_NOT_FOUND); + return Utils::$name(...$args); } public function onAddRoute($r) { $r->connect('avatar', '/{nickname<' . Nickname::DISPLAY_FMT . '>}/avatar/{size?full}', [Controller\Avatar::class, 'send']); + return Event::next; } public function onEndTwigPopulateVars(array &$vars) { - if (($user = Common::user()) !== null && ($avatar = DB::find('avatar', ['gsactor_id' => $user->getActor()->getId()])) != null) { - $vars['user_avatar'] = $avatar->getUrl(); - } else { - $package = new Package(new EmptyVersionStrategy()); - $vars['user_avatar'] = $package->getUrl('/assets/default-avatar.svg'); + try { + $user = Common::user(); + if ($user != null) { + $vars['user_avatar'] = self::getAvatar($user->getNickname())->getUrl(); + return Event::next; + } + } catch (Exception $e) { } + $package = new Package(new EmptyVersionStrategy()); + $vars['user_avatar'] = $package->getUrl(Common::config('avatar', 'default')); return Event::next; } } diff --git a/components/Media/Utils.php b/components/Media/Utils.php new file mode 100644 index 0000000000..1c85129e40 --- /dev/null +++ b/components/Media/Utils.php @@ -0,0 +1,132 @@ +. + +// }}} + +namespace Component\Media; + +use App\Core\Cache; +use App\Core\DB\DB; +use function App\Core\I18n\_m; +use App\Core\Log; +use App\Entity\Avatar; +use App\Entity\File; +use App\Util\Common; +use Exception; +use Symfony\Component\HttpFoundation\BinaryFileResponse; +use Symfony\Component\HttpFoundation\File\File as SymfonyFile; +use Symfony\Component\HttpFoundation\HeaderUtils; +use Symfony\Component\HttpFoundation\Response; + +abstract class Utils +{ + public static function validateAndStoreFile(SymfonyFile $sfile, + string $dest_dir, + ?string $title = null, + bool $is_local = true, + ?int $actor_id = null): File + { + // The following properly gets the mimetype with `file` or other + // available methods, so should be safe + $hash = hash_file(File::FILEHASH_ALGO, $sfile->getPathname()); + $file = File::create([ + 'file_hash' => $hash, + 'mimetype' => $sfile->getMimeType(), + 'size' => $sfile->getSize(), + 'title' => $title ?: _m('Untitled attachment'), + 'timestamp' => $sfile->getMTime(), + 'is_local' => $is_local, + ]); + $sfile->move($dest_dir, $hash); + // TODO Normalize file types + return $file; + } + + /** + * Include $filepath in the response, for viewing or downloading. + * + * @throws ServerException + */ + public static function sendFile(string $filepath, string $mimetype, ?string $output_filename, string $disposition = 'inline'): Response + { + $response = new BinaryFileResponse( + $filepath, + Response::HTTP_OK, + [ + 'Content-Description' => 'File Transfer', + 'Content-Type' => $mimetype, + 'Content-Disposition' => HeaderUtils::makeDisposition($disposition, $output_filename ?: _m('untitled')), + ], + $public = true, + $disposition = null, + $add_etag = true, + $add_last_modified = true + ); + if (Common::config('site', 'x_static_delivery')) { + $response->trustXSendfileTypeHeader(); + } + return $response; + } + + public static function error($res, string $nickname) + { + if (count($res) > 1) { + Log::error('Avatar query returned more than one result for nickname ' . $nickname); + throw new Exception(_m('Internal server error')); + } + return $res[0]; + } + + public static function getAvatar(string $nickname) + { + return self::error( + Cache::get('avatar-' . $nickname, + function () use ($nickname) { + return DB::dql('select a from App\\Entity\\Avatar a ' . + 'join App\Entity\GSActor p with a.gsactor_id = p.id ' . + 'where p.nickname = :nickname', + ['nickname' => $nickname]); + }), + $nickname + ); + } + + public static function getAvatarFileInfo(string $nickname) + { + try { + $res = self::error( + Cache::get('avatar-file-info-' . $nickname, + function () use ($nickname) { + return DB::dql('select f.file_hash, f.mimetype, f.title ' . + 'from App\\Entity\\File f ' . + 'join App\\Entity\\Avatar a with f.id = a.file_id ' . + 'join App\\Entity\\GSActor p with p.id = a.gsactor_id ' . + 'where p.nickname = :nickname', + ['nickname' => $nickname]); + }), + $nickname + ); + $res['file_path'] = Avatar::getFilePathStatic($res['file_hash']); + return $res; + } catch (Exception $e) { + $filepath = INSTALLDIR . '/public/assets/default-avatar.svg'; + return ['file_path' => $filepath, 'mimetype' => 'image/svg+xml', 'title' => null]; + } + } +}