From 2dbcfe02d85c03e2fe212e477a0d5d3bb66fd4b5 Mon Sep 17 00:00:00 2001 From: Toby Zerner Date: Sat, 7 Oct 2017 17:39:27 +1030 Subject: [PATCH] Consolidate avatar uploading, allow avatarUrl to be used when updating user --- src/Core/AvatarUploader.php | 55 ++++++++++++++++++ src/Core/Command/DeleteAvatarHandler.php | 18 +++--- src/Core/Command/EditUserHandler.php | 37 +++++++++++- src/Core/Command/RegisterUserHandler.php | 59 +++++-------------- src/Core/Command/UploadAvatarHandler.php | 72 ++++++++---------------- src/Core/CoreServiceProvider.php | 10 +--- src/Event/AvatarWillBeSaved.php | 13 +++-- 7 files changed, 143 insertions(+), 121 deletions(-) create mode 100755 src/Core/AvatarUploader.php diff --git a/src/Core/AvatarUploader.php b/src/Core/AvatarUploader.php new file mode 100755 index 000000000..3d7c018e7 --- /dev/null +++ b/src/Core/AvatarUploader.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Flarum\Core; + +use Intervention\Image\Image; +use League\Flysystem\FilesystemInterface; +use Illuminate\Support\Str; + +class AvatarUploader +{ + protected $uploadDir; + + public function __construct(FilesystemInterface $uploadDir) + { + $this->uploadDir = $uploadDir; + } + + public function upload(User $user, Image $image) + { + if (extension_loaded('exif')) { + $image->orientate(); + } + + $encodedImage = $image->fit(100, 100)->encode('png'); + + $avatarPath = Str::quickRandom().'.png'; + + $this->remove($user); + $user->changeAvatarPath($avatarPath); + + $this->uploadDir->put($avatarPath, $encodedImage); + } + + public function remove(User $user) + { + $avatarPath = $user->avatar_path; + + $user->afterSave(function () use ($avatarPath) { + if ($this->uploadDir->has($avatarPath)) { + $this->uploadDir->delete($avatarPath); + } + }); + + $user->changeAvatarPath(null); + } +} diff --git a/src/Core/Command/DeleteAvatarHandler.php b/src/Core/Command/DeleteAvatarHandler.php index 8efd972c7..2a7cb1638 100644 --- a/src/Core/Command/DeleteAvatarHandler.php +++ b/src/Core/Command/DeleteAvatarHandler.php @@ -12,6 +12,7 @@ namespace Flarum\Core\Command; use Flarum\Core\Access\AssertPermissionTrait; +use Flarum\Core\AvatarUploader; use Flarum\Core\Exception\PermissionDeniedException; use Flarum\Core\Repository\UserRepository; use Flarum\Core\Support\DispatchEventsTrait; @@ -30,20 +31,20 @@ class DeleteAvatarHandler protected $users; /** - * @var FilesystemInterface + * @var AvatarUploader */ - protected $uploadDir; + protected $uploader; /** * @param Dispatcher $events * @param UserRepository $users - * @param FilesystemInterface $uploadDir + * @param AvatarUploader $uploader */ - public function __construct(Dispatcher $events, UserRepository $users, FilesystemInterface $uploadDir) + public function __construct(Dispatcher $events, UserRepository $users, AvatarUploader $uploader) { $this->events = $events; $this->users = $users; - $this->uploadDir = $uploadDir; + $this->uploader = $uploader; } /** @@ -61,8 +62,7 @@ class DeleteAvatarHandler $this->assertCan($actor, 'edit', $user); } - $avatarPath = $user->avatar_path; - $user->changeAvatarPath(null); + $this->uploader->remove($user); $this->events->fire( new AvatarWillBeDeleted($user, $actor) @@ -70,10 +70,6 @@ class DeleteAvatarHandler $user->save(); - if ($this->uploadDir->has($avatarPath)) { - $this->uploadDir->delete($avatarPath); - } - $this->dispatchEventsFor($user, $actor); return $user; diff --git a/src/Core/Command/EditUserHandler.php b/src/Core/Command/EditUserHandler.php index d42acdfc9..d5d4bae1b 100644 --- a/src/Core/Command/EditUserHandler.php +++ b/src/Core/Command/EditUserHandler.php @@ -11,7 +11,9 @@ namespace Flarum\Core\Command; +use Exception; use Flarum\Core\Access\AssertPermissionTrait; +use Flarum\Core\AvatarUploader; use Flarum\Core\Repository\UserRepository; use Flarum\Core\Support\DispatchEventsTrait; use Flarum\Core\User; @@ -19,6 +21,9 @@ use Flarum\Core\Validator\UserValidator; use Flarum\Event\UserGroupsWereChanged; use Flarum\Event\UserWillBeSaved; use Illuminate\Contracts\Events\Dispatcher; +use Illuminate\Contracts\Validation\Factory; +use Illuminate\Contracts\Validation\ValidationException; +use Intervention\Image\ImageManager; class EditUserHandler { @@ -35,16 +40,30 @@ class EditUserHandler */ protected $validator; + /** + * @var AvatarUploader + */ + protected $avatarUploader; + + /** + * @var Factory + */ + private $validatorFactory; + /** * @param Dispatcher $events * @param UserRepository $users * @param UserValidator $validator + * @param AvatarUploader $avatarUploader + * @param Factory $validatorFactory */ - public function __construct(Dispatcher $events, UserRepository $users, UserValidator $validator) + public function __construct(Dispatcher $events, UserRepository $users, UserValidator $validator, AvatarUploader $avatarUploader, Factory $validatorFactory) { $this->events = $events; $this->users = $users; $this->validator = $validator; + $this->avatarUploader = $avatarUploader; + $this->validatorFactory = $validatorFactory; } /** @@ -135,6 +154,22 @@ class EditUserHandler }); } + if ($avatarUrl = array_get($attributes, 'avatarUrl')) { + $validation = $this->validatorFactory->make(compact('avatarUrl'), ['avatarUrl' => 'url']); + + if ($validation->fails()) { + throw new ValidationException($validation); + } + + try { + $image = (new ImageManager)->make($avatarUrl); + + $this->avatarUploader->upload($user, $image); + } catch (Exception $e) { + // + } + } + $this->events->fire( new UserWillBeSaved($user, $actor, $data) ); diff --git a/src/Core/Command/RegisterUserHandler.php b/src/Core/Command/RegisterUserHandler.php index c7daf38fa..f57492548 100644 --- a/src/Core/Command/RegisterUserHandler.php +++ b/src/Core/Command/RegisterUserHandler.php @@ -14,22 +14,17 @@ namespace Flarum\Core\Command; use Exception; use Flarum\Core\Access\AssertPermissionTrait; use Flarum\Core\AuthToken; +use Flarum\Core\AvatarUploader; use Flarum\Core\Exception\PermissionDeniedException; use Flarum\Core\Support\DispatchEventsTrait; use Flarum\Core\User; use Flarum\Core\Validator\UserValidator; use Flarum\Event\UserWillBeSaved; -use Flarum\Foundation\Application; use Flarum\Settings\SettingsRepositoryInterface; use Illuminate\Contracts\Events\Dispatcher; use Illuminate\Contracts\Validation\Factory; use Illuminate\Contracts\Validation\ValidationException; -use Illuminate\Support\Str; use Intervention\Image\ImageManager; -use League\Flysystem\Adapter\Local; -use League\Flysystem\Filesystem; -use League\Flysystem\FilesystemInterface; -use League\Flysystem\MountManager; class RegisterUserHandler { @@ -47,14 +42,9 @@ class RegisterUserHandler protected $validator; /** - * @var Application + * @var AvatarUploader */ - protected $app; - - /** - * @var FilesystemInterface - */ - protected $uploadDir; + protected $avatarUploader; /** * @var Factory @@ -65,17 +55,15 @@ class RegisterUserHandler * @param Dispatcher $events * @param SettingsRepositoryInterface $settings * @param UserValidator $validator - * @param Application $app - * @param FilesystemInterface $uploadDir + * @param AvatarUploader $avatarUploader * @param Factory $validatorFactory */ - public function __construct(Dispatcher $events, SettingsRepositoryInterface $settings, UserValidator $validator, Application $app, FilesystemInterface $uploadDir, Factory $validatorFactory) + public function __construct(Dispatcher $events, SettingsRepositoryInterface $settings, UserValidator $validator, AvatarUploader $avatarUploader, Factory $validatorFactory) { $this->events = $events; $this->settings = $settings; $this->validator = $validator; - $this->app = $app; - $this->uploadDir = $uploadDir; + $this->avatarUploader = $avatarUploader; $this->validatorFactory = $validatorFactory; } @@ -130,12 +118,6 @@ class RegisterUserHandler $user->activate(); } - $this->events->fire( - new UserWillBeSaved($user, $actor, $data) - ); - - $this->validator->assertValid(array_merge($user->getAttributes(), compact('password'))); - if ($avatarUrl = array_get($data, 'attributes.avatarUrl')) { $validation = $this->validatorFactory->make(compact('avatarUrl'), ['avatarUrl' => 'url']); @@ -144,12 +126,20 @@ class RegisterUserHandler } try { - $this->saveAvatarFromUrl($user, $avatarUrl); + $image = (new ImageManager)->make($avatarUrl); + + $this->avatarUploader->upload($user, $avatarUrl); } catch (Exception $e) { // } } + $this->events->fire( + new UserWillBeSaved($user, $actor, $data) + ); + + $this->validator->assertValid(array_merge($user->getAttributes(), compact('password'))); + $user->save(); if (isset($token)) { @@ -160,23 +150,4 @@ class RegisterUserHandler return $user; } - - private function saveAvatarFromUrl(User $user, $url) - { - $tmpFile = tempnam($this->app->storagePath().'/tmp', 'avatar'); - - $manager = new ImageManager; - $manager->make($url)->fit(100, 100)->save($tmpFile); - - $mount = new MountManager([ - 'source' => new Filesystem(new Local(pathinfo($tmpFile, PATHINFO_DIRNAME))), - 'target' => $this->uploadDir, - ]); - - $uploadName = Str::lower(Str::quickRandom()).'.png'; - - $user->changeAvatarPath($uploadName); - - $mount->move('source://'.pathinfo($tmpFile, PATHINFO_BASENAME), "target://$uploadName"); - } } diff --git a/src/Core/Command/UploadAvatarHandler.php b/src/Core/Command/UploadAvatarHandler.php index de451a074..02e655bd6 100644 --- a/src/Core/Command/UploadAvatarHandler.php +++ b/src/Core/Command/UploadAvatarHandler.php @@ -13,18 +13,14 @@ namespace Flarum\Core\Command; use Exception; use Flarum\Core\Access\AssertPermissionTrait; +use Flarum\Core\AvatarUploader; use Flarum\Core\Repository\UserRepository; use Flarum\Core\Support\DispatchEventsTrait; use Flarum\Core\Validator\AvatarValidator; use Flarum\Event\AvatarWillBeSaved; use Flarum\Foundation\Application; use Illuminate\Contracts\Events\Dispatcher; -use Illuminate\Support\Str; use Intervention\Image\ImageManager; -use League\Flysystem\Adapter\Local; -use League\Flysystem\Filesystem; -use League\Flysystem\FilesystemInterface; -use League\Flysystem\MountManager; use Symfony\Component\HttpFoundation\File\UploadedFile; class UploadAvatarHandler @@ -37,16 +33,16 @@ class UploadAvatarHandler */ protected $users; - /** - * @var FilesystemInterface - */ - protected $uploadDir; - /** * @var Application */ protected $app; + /** + * @var AvatarUploader + */ + protected $uploader; + /** * @var AvatarValidator */ @@ -55,16 +51,16 @@ class UploadAvatarHandler /** * @param Dispatcher $events * @param UserRepository $users - * @param FilesystemInterface $uploadDir * @param Application $app + * @param AvatarUploader $uploader * @param AvatarValidator $validator */ - public function __construct(Dispatcher $events, UserRepository $users, FilesystemInterface $uploadDir, Application $app, AvatarValidator $validator) + public function __construct(Dispatcher $events, UserRepository $users, Application $app, AvatarUploader $uploader, AvatarValidator $validator) { $this->events = $events; $this->users = $users; - $this->uploadDir = $uploadDir; $this->app = $app; + $this->uploader = $uploader; $this->validator = $validator; } @@ -83,60 +79,36 @@ class UploadAvatarHandler $this->assertCan($actor, 'edit', $user); } + $file = $command->file; + $tmpFile = tempnam($this->app->storagePath().'/tmp', 'avatar'); - $command->file->moveTo($tmpFile); + $file->moveTo($tmpFile); try { $file = new UploadedFile( $tmpFile, - $command->file->getClientFilename(), - $command->file->getClientMediaType(), - $command->file->getSize(), - $command->file->getError(), + $file->getClientFilename(), + $file->getClientMediaType(), + $file->getSize(), + $file->getError(), true ); $this->validator->assertValid(['avatar' => $file]); - $manager = new ImageManager; - - // Explicitly tell Intervention to encode the image as PNG (instead of having to guess from the extension) - // Read exif data to orientate avatar only if EXIF extension is enabled - if (extension_loaded('exif')) { - $encodedImage = $manager->make($tmpFile)->orientate()->fit(100, 100)->encode('png', 100); - } else { - $encodedImage = $manager->make($tmpFile)->fit(100, 100)->encode('png', 100); - } - file_put_contents($tmpFile, $encodedImage); + $image = (new ImageManager)->make($tmpFile); $this->events->fire( - new AvatarWillBeSaved($user, $actor, $tmpFile) + new AvatarWillBeSaved($user, $actor, $image) ); - $mount = new MountManager([ - 'source' => new Filesystem(new Local(pathinfo($tmpFile, PATHINFO_DIRNAME))), - 'target' => $this->uploadDir, - ]); - - if ($user->avatar_path && $mount->has($file = "target://$user->avatar_path")) { - $mount->delete($file); - } - - $uploadName = Str::lower(Str::quickRandom()).'.png'; - - $user->changeAvatarPath($uploadName); - - $mount->move('source://'.pathinfo($tmpFile, PATHINFO_BASENAME), "target://$uploadName"); + $this->uploader->upload($user, $image); $user->save(); - - $this->dispatchEventsFor($user, $actor); - - return $user; - } catch (Exception $e) { + } finally { @unlink($tmpFile); - - throw $e; } + + return $user; } } diff --git a/src/Core/CoreServiceProvider.php b/src/Core/CoreServiceProvider.php index 551cdcfda..713e5758e 100644 --- a/src/Core/CoreServiceProvider.php +++ b/src/Core/CoreServiceProvider.php @@ -49,15 +49,7 @@ class CoreServiceProvider extends AbstractServiceProvider return $app->make('Illuminate\Contracts\Filesystem\Factory')->disk('flarum-avatars')->getDriver(); }; - $this->app->when('Flarum\Core\Command\UploadAvatarHandler') - ->needs('League\Flysystem\FilesystemInterface') - ->give($avatarsFilesystem); - - $this->app->when('Flarum\Core\Command\DeleteAvatarHandler') - ->needs('League\Flysystem\FilesystemInterface') - ->give($avatarsFilesystem); - - $this->app->when('Flarum\Core\Command\RegisterUserHandler') + $this->app->when('Flarum\Core\AvatarUploader') ->needs('League\Flysystem\FilesystemInterface') ->give($avatarsFilesystem); } diff --git a/src/Event/AvatarWillBeSaved.php b/src/Event/AvatarWillBeSaved.php index f4e52bb65..0c52399cc 100644 --- a/src/Event/AvatarWillBeSaved.php +++ b/src/Event/AvatarWillBeSaved.php @@ -12,6 +12,7 @@ namespace Flarum\Event; use Flarum\Core\User; +use Intervention\Image\Image; class AvatarWillBeSaved { @@ -30,21 +31,21 @@ class AvatarWillBeSaved public $actor; /** - * The path to the avatar that will be saved. + * The image that will be saved. * - * @var string + * @var Image */ - public $path; + public $image; /** * @param User $user The user whose avatar will be saved. * @param User $actor The user performing the action. - * @param string $path The path to the avatar that will be saved. + * @param Image $image The image that will be saved. */ - public function __construct(User $user, User $actor, $path) + public function __construct(User $user, User $actor, Image $image) { $this->user = $user; $this->actor = $actor; - $this->path = $path; + $this->image = $image; } }