diff options
-rw-r--r-- | build/psalm-baseline.xml | 6 | ||||
-rw-r--r-- | lib/composer/composer/autoload_classmap.php | 2 | ||||
-rw-r--r-- | lib/composer/composer/autoload_static.php | 2 | ||||
-rw-r--r-- | lib/private/Avatar/Avatar.php | 46 | ||||
-rw-r--r-- | lib/private/Avatar/GuestAvatar.php | 28 | ||||
-rw-r--r-- | lib/private/Avatar/PlaceholderAvatar.php | 25 | ||||
-rw-r--r-- | lib/private/Avatar/UserAvatar.php | 48 | ||||
-rw-r--r-- | lib/private/Server.php | 58 | ||||
-rw-r--r-- | lib/private/User/Listeners/UserChangedListener.php | 62 | ||||
-rw-r--r-- | lib/private/User/Listeners/UserDeletedListener.php | 63 | ||||
-rw-r--r-- | lib/public/IAvatar.php | 39 | ||||
-rw-r--r-- | lib/public/User/Events/UserChangedEvent.php | 10 | ||||
-rw-r--r-- | tests/lib/Avatar/AvatarManagerTest.php | 6 | ||||
-rw-r--r-- | tests/lib/Avatar/UserAvatarTest.php | 10 |
14 files changed, 215 insertions, 190 deletions
diff --git a/build/psalm-baseline.xml b/build/psalm-baseline.xml index 937c2fe1687..c70ef72574c 100644 --- a/build/psalm-baseline.xml +++ b/build/psalm-baseline.xml @@ -2497,12 +2497,6 @@ <ImplementedReturnTypeMismatch occurrences="1"> <code>Color</code> </ImplementedReturnTypeMismatch> - <InvalidReturnStatement occurrences="1"> - <code>$finalPalette[$this->hashToInt($hash, $steps * 3)]</code> - </InvalidReturnStatement> - <InvalidReturnType occurrences="1"> - <code>Color</code> - </InvalidReturnType> <ParamNameMismatch occurrences="1"> <code>$hash</code> </ParamNameMismatch> diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 1edc39c52f2..646bd3acb5e 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -1548,6 +1548,8 @@ return array( 'OC\\User\\Database' => $baseDir . '/lib/private/User/Database.php', 'OC\\User\\DisplayNameCache' => $baseDir . '/lib/private/User/DisplayNameCache.php', 'OC\\User\\LazyUser' => $baseDir . '/lib/private/User/LazyUser.php', + 'OC\\User\\Listeners\\UserChangedListener' => $baseDir . '/lib/private/User/Listeners/UserChangedListener.php', + 'OC\\User\\Listeners\\UserDeletedListener' => $baseDir . '/lib/private/User/Listeners/UserDeletedListener.php', 'OC\\User\\LoginException' => $baseDir . '/lib/private/User/LoginException.php', 'OC\\User\\Manager' => $baseDir . '/lib/private/User/Manager.php', 'OC\\User\\NoUserException' => $baseDir . '/lib/private/User/NoUserException.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 2efd6effc91..75d00eb32c7 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -1581,6 +1581,8 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\User\\Database' => __DIR__ . '/../../..' . '/lib/private/User/Database.php', 'OC\\User\\DisplayNameCache' => __DIR__ . '/../../..' . '/lib/private/User/DisplayNameCache.php', 'OC\\User\\LazyUser' => __DIR__ . '/../../..' . '/lib/private/User/LazyUser.php', + 'OC\\User\\Listeners\\UserChangedListener' => __DIR__ . '/../../..' . '/lib/private/User/Listeners/UserChangedListener.php', + 'OC\\User\\Listeners\\UserDeletedListener' => __DIR__ . '/../../..' . '/lib/private/User/Listeners/UserDeletedListener.php', 'OC\\User\\LoginException' => __DIR__ . '/../../..' . '/lib/private/User/LoginException.php', 'OC\\User\\Manager' => __DIR__ . '/../../..' . '/lib/private/User/Manager.php', 'OC\\User\\NoUserException' => __DIR__ . '/../../..' . '/lib/private/User/NoUserException.php', diff --git a/lib/private/Avatar/Avatar.php b/lib/private/Avatar/Avatar.php index f0e14ad8b2f..188e7378aff 100644 --- a/lib/private/Avatar/Avatar.php +++ b/lib/private/Avatar/Avatar.php @@ -46,9 +46,7 @@ use Psr\Log\LoggerInterface; * This class gets and sets users avatars. */ abstract class Avatar implements IAvatar { - - /** @var LoggerInterface */ - protected $logger; + protected LoggerInterface $logger; /** * https://github.com/sebdesign/cap-height -- for 500px height @@ -57,10 +55,8 @@ abstract class Avatar implements IAvatar { * (0.4 letter-to-total-height ratio, 500*0.4=200), so: 200/0.715 = 280px. * Since we start from the baseline (text-anchor) we need to * shift the y axis by 100px (half the caps height): 500/2+100=350 - * - * @var string */ - private $svgTemplate = '<?xml version="1.0" encoding="UTF-8" standalone="no"?> + private string $svgTemplate = '<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg width="{size}" height="{size}" version="1.1" viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg"> <rect width="100%" height="100%" fill="#{fill}"></rect> <text x="50%" y="350" style="font-weight:normal;font-size:280px;font-family:\'Noto Sans\';text-anchor:middle;fill:#fff">{letter}</text> @@ -72,15 +68,11 @@ abstract class Avatar implements IAvatar { /** * Returns the user display name. - * - * @return string */ abstract public function getDisplayName(): string; /** * Returns the first letter of the display name, or "?" if no name given. - * - * @return string */ private function getAvatarText(): string { $displayName = $this->getDisplayName(); @@ -96,9 +88,7 @@ abstract class Avatar implements IAvatar { /** * @inheritdoc */ - public function get($size = 64) { - $size = (int) $size; - + public function get(int $size = 64) { try { $file = $this->getFile($size); } catch (NotFoundException $e) { @@ -158,12 +148,8 @@ abstract class Avatar implements IAvatar { /** * Generate png avatar with GD - * - * @param string $userDisplayName - * @param int $size - * @return string */ - protected function generateAvatar($userDisplayName, $size) { + protected function generateAvatar(string $userDisplayName, int $size): string { $text = $this->getAvatarText(); $backgroundColor = $this->avatarBackgroundColor($userDisplayName); @@ -209,7 +195,7 @@ abstract class Avatar implements IAvatar { string $text, string $font, int $size, - $angle = 0 + int $angle = 0 ): array { // Image width & height $xi = imagesx($image); @@ -231,11 +217,11 @@ abstract class Avatar implements IAvatar { /** * Calculate steps between two Colors - * @param object Color $steps start color - * @param object Color $ends end color - * @return array [r,g,b] steps for each color to go from $steps to $ends + * @param int $steps start color + * @param Color[] $ends end color + * @return array{0: int, 1: int, 2: int} [r,g,b] steps for each color to go from $steps to $ends */ - private function stepCalc($steps, $ends) { + private function stepCalc(int $steps, array $ends): array { $step = []; $step[0] = ($ends[1]->r - $ends[0]->r) / $steps; $step[1] = ($ends[1]->g - $ends[0]->g) / $steps; @@ -244,12 +230,11 @@ abstract class Avatar implements IAvatar { } /** - * Convert a string to an integer evenly - * @param string $hash the text to parse - * @param int $maximum the maximum range - * @return int[] between 0 and $maximum + * Mix two colors + * + * @return Color[] */ - private function mixPalette($steps, $color1, $color2) { + private function mixPalette($steps, Color $color1, Color $color2) { $palette = [$color1]; $step = $this->stepCalc($steps, [$color1, $color2]); for ($i = 1; $i < $steps; $i++) { @@ -267,7 +252,7 @@ abstract class Avatar implements IAvatar { * @param int $maximum the maximum range * @return int between 0 and $maximum */ - private function hashToInt($hash, $maximum) { + private function hashToInt(string $hash, int $maximum): int { $final = 0; $result = []; @@ -285,10 +270,9 @@ abstract class Avatar implements IAvatar { } /** - * @param string $hash * @return Color Object containting r g b int in the range [0, 255] */ - public function avatarBackgroundColor(string $hash) { + public function avatarBackgroundColor(string $hash): Color { // Normalize hash $hash = strtolower($hash); diff --git a/lib/private/Avatar/GuestAvatar.php b/lib/private/Avatar/GuestAvatar.php index f0297b3a93c..f3dde0840e9 100644 --- a/lib/private/Avatar/GuestAvatar.php +++ b/lib/private/Avatar/GuestAvatar.php @@ -26,6 +26,7 @@ declare(strict_types=1); */ namespace OC\Avatar; +use OCP\Files\SimpleFS\ISimpleFile; use OCP\Files\SimpleFS\InMemoryFile; use Psr\Log\LoggerInterface; @@ -35,16 +36,13 @@ use Psr\Log\LoggerInterface; class GuestAvatar extends Avatar { /** * Holds the guest user display name. - * - * @var string */ - private $userDisplayName; + private string $userDisplayName; /** * GuestAvatar constructor. * * @param string $userDisplayName The guest user display name - * @param LoggerInterface $logger The logger */ public function __construct(string $userDisplayName, LoggerInterface $logger) { parent::__construct($logger); @@ -53,17 +51,13 @@ class GuestAvatar extends Avatar { /** * Tests if the user has an avatar. - * - * @return true Guests always have an avatar. */ - public function exists() { - return true; + public function exists(): bool { + return true; // Guests always have an avatar. } /** * Returns the guest user display name. - * - * @return string */ public function getDisplayName(): string { return $this->userDisplayName; @@ -75,24 +69,21 @@ class GuestAvatar extends Avatar { * @param \OCP\IImage|resource|string $data * @return void */ - public function set($data) { + public function set($data): void { // unimplemented for guest user avatars } /** * Removing avatars isn't implemented for guests. */ - public function remove() { + public function remove(bool $silent = false): void { // unimplemented for guest user avatars } /** * Generates an avatar for the guest. - * - * @param int $size The desired image size. - * @return InMemoryFile */ - public function getFile($size) { + public function getFile(int $size): ISimpleFile { $avatar = $this->generateAvatar($this->userDisplayName, $size); return new InMemoryFile('avatar.png', $avatar); } @@ -103,9 +94,8 @@ class GuestAvatar extends Avatar { * @param string $feature The changed feature * @param mixed $oldValue The previous value * @param mixed $newValue The new value - * @return void */ - public function userChanged($feature, $oldValue, $newValue) { + public function userChanged(string $feature, $oldValue, $newValue): void { if ($feature === 'displayName') { $this->userDisplayName = $newValue; } @@ -113,8 +103,6 @@ class GuestAvatar extends Avatar { /** * Guests don't have custom avatars. - * - * @return bool */ public function isCustomAvatar(): bool { return false; diff --git a/lib/private/Avatar/PlaceholderAvatar.php b/lib/private/Avatar/PlaceholderAvatar.php index df7a490cbe4..504e5c1457d 100644 --- a/lib/private/Avatar/PlaceholderAvatar.php +++ b/lib/private/Avatar/PlaceholderAvatar.php @@ -44,11 +44,8 @@ use Psr\Log\LoggerInterface; * for faster retrieval, unlike the GuestAvatar. */ class PlaceholderAvatar extends Avatar { - /** @var ISimpleFolder */ - private $folder; - - /** @var User */ - private $user; + private ISimpleFolder $folder; + private User $user; /** * UserAvatar constructor. @@ -71,10 +68,8 @@ class PlaceholderAvatar extends Avatar { /** * Check if an avatar exists for the user - * - * @return bool */ - public function exists() { + public function exists(): bool { return true; } @@ -87,14 +82,14 @@ class PlaceholderAvatar extends Avatar { * @throws NotSquareException if the image is not square * @return void */ - public function set($data) { + public function set($data): void { // unimplemented for placeholder avatars } /** * Removes the users avatar. */ - public function remove(bool $silent = false) { + public function remove(bool $silent = false): void { $avatars = $this->folder->getDirectoryListing(); foreach ($avatars as $avatar) { @@ -113,9 +108,7 @@ class PlaceholderAvatar extends Avatar { * @throws \OCP\Files\NotPermittedException * @throws \OCP\PreConditionNotMetException */ - public function getFile($size) { - $size = (int) $size; - + public function getFile(int $size): ISimpleFile { $ext = 'png'; if ($size === -1) { @@ -149,8 +142,6 @@ class PlaceholderAvatar extends Avatar { /** * Returns the user display name. - * - * @return string */ public function getDisplayName(): string { return $this->user->getDisplayName(); @@ -165,14 +156,12 @@ class PlaceholderAvatar extends Avatar { * @throws NotPermittedException * @throws \OCP\PreConditionNotMetException */ - public function userChanged($feature, $oldValue, $newValue) { + public function userChanged(string $feature, $oldValue, $newValue): void { $this->remove(); } /** * Check if the avatar of a user is a custom uploaded one - * - * @return bool */ public function isCustomAvatar(): bool { return false; diff --git a/lib/private/Avatar/UserAvatar.php b/lib/private/Avatar/UserAvatar.php index 6cca5a1c355..191a6ebfff4 100644 --- a/lib/private/Avatar/UserAvatar.php +++ b/lib/private/Avatar/UserAvatar.php @@ -44,17 +44,10 @@ use Psr\Log\LoggerInterface; * This class represents a registered user's avatar. */ class UserAvatar extends Avatar { - /** @var IConfig */ - private $config; - - /** @var ISimpleFolder */ - private $folder; - - /** @var IL10N */ - private $l; - - /** @var User */ - private $user; + private IConfig $config; + private ISimpleFolder $folder; + private IL10N $l; + private User $user; /** * UserAvatar constructor. @@ -68,7 +61,7 @@ class UserAvatar extends Avatar { public function __construct( ISimpleFolder $folder, IL10N $l, - $user, + User $user, LoggerInterface $logger, IConfig $config) { parent::__construct($logger); @@ -80,10 +73,8 @@ class UserAvatar extends Avatar { /** * Check if an avatar exists for the user - * - * @return bool */ - public function exists() { + public function exists(): bool { return $this->folder->fileExists('avatar.jpg') || $this->folder->fileExists('avatar.png'); } @@ -96,7 +87,7 @@ class UserAvatar extends Avatar { * @throws NotSquareException if the image is not square * @return void */ - public function set($data) { + public function set($data): void { $img = $this->getAvatarImage($data); $data = $img->data(); @@ -124,7 +115,7 @@ class UserAvatar extends Avatar { * @param IImage|resource|string|\GdImage $data An image object, imagedata or path to the avatar * @return IImage */ - private function getAvatarImage($data) { + private function getAvatarImage($data): IImage { if ($data instanceof IImage) { return $data; } @@ -156,11 +147,8 @@ class UserAvatar extends Avatar { /** * Returns the avatar image type. - * - * @param IImage $avatar - * @return string */ - private function getAvatarImageType(IImage $avatar) { + private function getAvatarImageType(IImage $avatar): string { $type = substr($avatar->mimeType(), -3); if ($type === 'peg') { $type = 'jpg'; @@ -179,7 +167,7 @@ class UserAvatar extends Avatar { * @throws \Exception if the provided image is not valid * @throws NotSquareException if the image is not square */ - private function validateAvatar(IImage $avatar) { + private function validateAvatar(IImage $avatar): void { $type = $this->getAvatarImageType($avatar); if ($type !== 'jpg' && $type !== 'png') { @@ -197,11 +185,10 @@ class UserAvatar extends Avatar { /** * Removes the users avatar. - * @return void * @throws \OCP\Files\NotPermittedException * @throws \OCP\PreConditionNotMetException */ - public function remove(bool $silent = false) { + public function remove(bool $silent = false): void { $avatars = $this->folder->getDirectoryListing(); $this->config->setUserValue($this->user->getUID(), 'avatar', 'version', @@ -219,10 +206,9 @@ class UserAvatar extends Avatar { /** * Get the extension of the avatar. If there is no avatar throw Exception * - * @return string * @throws NotFoundException */ - private function getExtension() { + private function getExtension(): string { if ($this->folder->fileExists('avatar.jpg')) { return 'jpg'; } elseif ($this->folder->fileExists('avatar.png')) { @@ -242,9 +228,7 @@ class UserAvatar extends Avatar { * @throws \OCP\Files\NotPermittedException * @throws \OCP\PreConditionNotMetException */ - public function getFile($size) { - $size = (int) $size; - + public function getFile(int $size): ISimpleFile { try { $ext = $this->getExtension(); } catch (NotFoundException $e) { @@ -304,8 +288,6 @@ class UserAvatar extends Avatar { /** * Returns the user display name. - * - * @return string */ public function getDisplayName(): string { return $this->user->getDisplayName(); @@ -320,7 +302,7 @@ class UserAvatar extends Avatar { * @throws NotPermittedException * @throws \OCP\PreConditionNotMetException */ - public function userChanged($feature, $oldValue, $newValue) { + public function userChanged(string $feature, $oldValue, $newValue): void { // If the avatar is not generated (so an uploaded image) we skip this if (!$this->folder->fileExists('generated')) { return; @@ -331,8 +313,6 @@ class UserAvatar extends Avatar { /** * Check if the avatar of a user is a custom uploaded one - * - * @return bool */ public function isCustomAvatar(): bool { return $this->config->getUserValue($this->user->getUID(), 'avatar', 'generated', 'false') !== 'true'; diff --git a/lib/private/Server.php b/lib/private/Server.php index 6e6fa430489..bcbb6ef6b00 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -151,6 +151,8 @@ use OC\SystemTag\ManagerFactory as SystemTagManagerFactory; use OC\Tagging\TagMapper; use OC\Talk\Broker; use OC\Template\JSCombiner; +use OC\User\Listeners\UserChangedListener; +use OC\User\Listeners\UserDeletedListener; use OCA\Theming\ImageManager; use OCA\Theming\ThemingDefaults; use OCA\Theming\Util; @@ -180,7 +182,6 @@ use OCP\Files\IMimeTypeLoader; use OCP\Files\IRootFolder; use OCP\Files\Lock\ILockManager; use OCP\Files\Mount\IMountManager; -use OCP\Files\NotFoundException; use OCP\Files\Storage\IStorageFactory; use OCP\Files\Template\ITemplateManager; use OCP\FullTextSearch\IFullTextSearchManager; @@ -217,7 +218,6 @@ use OCP\ISession; use OCP\ITagManager; use OCP\ITempManager; use OCP\IURLGenerator; -use OCP\IUser; use OCP\IUserManager; use OCP\IUserSession; use OCP\L10N\IFactory; @@ -249,6 +249,7 @@ use OCP\User\Events\BeforeUserLoggedOutEvent; use OCP\User\Events\PasswordUpdatedEvent; use OCP\User\Events\PostLoginEvent; use OCP\User\Events\UserChangedEvent; +use OCP\User\Events\UserDeletedEvent; use OCP\User\Events\UserLoggedInEvent; use OCP\User\Events\UserLoggedInWithCookieEvent; use OCP\User\Events\UserLoggedOutEvent; @@ -1473,52 +1474,13 @@ class Server extends ServerContainer implements IServerContainer { return $this->get(\OC\Calendar\Room\Manager::class); } - private function connectDispatcher() { - $dispatcher = $this->get(SymfonyAdapter::class); - - // Delete avatar on user deletion - $dispatcher->addListener('OCP\IUser::preDelete', function (GenericEvent $e) { - $logger = $this->get(LoggerInterface::class); - $manager = $this->getAvatarManager(); - /** @var IUser $user */ - $user = $e->getSubject(); - - try { - $avatar = $manager->getAvatar($user->getUID()); - $avatar->remove(); - } catch (NotFoundException $e) { - // no avatar to remove - } catch (\Exception $e) { - // Ignore exceptions - $logger->info('Could not cleanup avatar of ' . $user->getUID()); - } - }); - - $dispatcher->addListener('OCP\IUser::changeUser', function (GenericEvent $e) { - $manager = $this->getAvatarManager(); - /** @var IUser $user */ - $user = $e->getSubject(); - $feature = $e->getArgument('feature'); - $oldValue = $e->getArgument('oldValue'); - $value = $e->getArgument('value'); - - // We only change the avatar on display name changes - if ($feature !== 'displayName') { - return; - } - - try { - $avatar = $manager->getAvatar($user->getUID()); - $avatar->userChanged($feature, $oldValue, $value); - } catch (NotFoundException $e) { - // no avatar to remove - } - }); - - /** @var IEventDispatcher $eventDispatched */ - $eventDispatched = $this->get(IEventDispatcher::class); - $eventDispatched->addServiceListener(LoginFailed::class, LoginFailedListener::class); - $eventDispatched->addServiceListener(PostLoginEvent::class, UserLoggedInListener::class); + private function connectDispatcher(): void { + /** @var IEventDispatcher $eventDispatcher */ + $eventDispatcher = $this->get(IEventDispatcher::class); + $eventDispatcher->addServiceListener(LoginFailed::class, LoginFailedListener::class); + $eventDispatcher->addServiceListener(PostLoginEvent::class, UserLoggedInListener::class); + $eventDispatcher->addServiceListener(UserChangedEvent::class, UserChangedListener::class); + $eventDispatcher->addServiceListener(UserDeletedEvent::class, UserDeletedListener::class); } /** diff --git a/lib/private/User/Listeners/UserChangedListener.php b/lib/private/User/Listeners/UserChangedListener.php new file mode 100644 index 00000000000..f3c7468460a --- /dev/null +++ b/lib/private/User/Listeners/UserChangedListener.php @@ -0,0 +1,62 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2022 Carl Schwan <carl@carlschwan.eu> + * + * @license AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +namespace OC\User\Listeners; + +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use OCP\User\Events\UserChangedEvent; +use OCP\Files\NotFoundException; +use OCP\IAvatarManager; + +/** + * @template-implements IEventListener<\OCP\User\Events\UserChangedEvent> + */ +class UserChangedListener implements IEventListener { + private IAvatarManager $avatarManager; + + public function __construct(IAvatarManager $avatarManager) { + $this->avatarManager = $avatarManager; + } + + public function handle(Event $event): void { + if (!($event instanceof UserChangedEvent)) { + return; + } + + $user = $event->getUser(); + $feature = $event->getFeature(); + $oldValue = $event->getOldValue(); + $value = $event->getValue(); + + // We only change the avatar on display name changes + if ($feature === 'displayName') { + try { + $avatar = $this->avatarManager->getAvatar($user->getUID()); + $avatar->userChanged($feature, $oldValue, $value); + } catch (NotFoundException $e) { + // no avatar to remove + } + } + } +} diff --git a/lib/private/User/Listeners/UserDeletedListener.php b/lib/private/User/Listeners/UserDeletedListener.php new file mode 100644 index 00000000000..a8ad56d97ab --- /dev/null +++ b/lib/private/User/Listeners/UserDeletedListener.php @@ -0,0 +1,63 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2022 Carl Schwan <carl@carlschwan.eu> + * + * @license AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +namespace OC\User\Listeners; + +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use OCP\User\Events\UserDeletedEvent; +use OCP\Files\NotFoundException; +use OCP\IAvatarManager; +use Psr\Log\LoggerInterface; + +/** + * @template-implements IEventListener<\OCP\User\Events\UserDeletedEvent> + */ +class UserDeletedListener implements IEventListener { + private IAvatarManager $avatarManager; + private LoggerInterface $logger; + + public function __construct(LoggerInterface $logger, IAvatarManager $avatarManager) { + $this->avatarManager = $avatarManager; + $this->logger = $logger; + } + + public function handle(Event $event): void { + if (!($event instanceof UserDeletedEvent)) { + return; + } + + $user = $event->getUser(); + + // Delete avatar on user deletion + try { + $avatar = $this->avatarManager->getAvatar($user->getUID()); + $avatar->remove(); + } catch (NotFoundException $e) { + // no avatar to remove + } catch (\Exception $e) { + // Ignore exceptions + $this->logger->info('Could not cleanup avatar of ' . $user->getUID()); + } + } +} diff --git a/lib/public/IAvatar.php b/lib/public/IAvatar.php index 218ef258250..e4a9ce5dcc1 100644 --- a/lib/public/IAvatar.php +++ b/lib/public/IAvatar.php @@ -36,66 +36,71 @@ use OCP\Files\SimpleFS\ISimpleFile; interface IAvatar { /** - * get the users avatar + * Get the users avatar + * * @param int $size size in px of the avatar, avatars are square, defaults to 64, -1 can be used to not scale the image * @return false|\OCP\IImage containing the avatar or false if there's no image * @since 6.0.0 - size of -1 was added in 9.0.0 */ - public function get($size = 64); + public function get(int $size = 64); /** * Check if an avatar exists for the user * - * @return bool * @since 8.1.0 */ - public function exists(); + public function exists(): bool; /** * Check if the avatar of a user is a custom uploaded one * - * @return bool * @since 14.0.0 */ public function isCustomAvatar(): bool; /** - * sets the users avatar + * Sets the users avatar + * * @param \OCP\IImage|resource|string $data An image object, imagedata or path to set a new avatar * @throws \Exception if the provided file is not a jpg or png image * @throws \Exception if the provided image is not valid * @throws \OC\NotSquareException if the image is not square - * @return void * @since 6.0.0 */ - public function set($data); + public function set($data): void; /** - * remove the users avatar - * @return void + * Remove the user's avatar + * + * @param bool $silent Whether removing the avatar should trigger a change * @since 6.0.0 */ - public function remove(); + public function remove(bool $silent = false): void; /** * Get the file of the avatar - * @param int $size -1 can be used to not scale the image - * @return ISimpleFile + * + * @param int $size The desired image size. -1 can be used to not scale the image * @throws NotFoundException * @since 9.0.0 */ - public function getFile($size); + public function getFile(int $size): ISimpleFile; /** - * @param string $text + * Get the avatar background color + * * @return Color Object containting r g b int in the range [0, 255] * @since 14.0.0 */ public function avatarBackgroundColor(string $text); /** - * Handle a changed user + * Updates the display name if changed. + * + * @param string $feature The changed feature + * @param mixed $oldValue The previous value + * @param mixed $newValue The new value * @since 13.0.0 */ - public function userChanged($feature, $oldValue, $newValue); + public function userChanged(string $feature, $oldValue, $newValue): void; } diff --git a/lib/public/User/Events/UserChangedEvent.php b/lib/public/User/Events/UserChangedEvent.php index 3a40f8c3f11..f48dd3914e6 100644 --- a/lib/public/User/Events/UserChangedEvent.php +++ b/lib/public/User/Events/UserChangedEvent.php @@ -32,16 +32,10 @@ use OCP\IUser; * @since 18.0.0 */ class UserChangedEvent extends Event { - - /** @var IUser */ - private $user; - - /** @var string */ - private $feature; - + private IUser $user; + private string $feature; /** @var mixed */ private $value; - /** @var mixed */ private $oldValue; diff --git a/tests/lib/Avatar/AvatarManagerTest.php b/tests/lib/Avatar/AvatarManagerTest.php index ae9c0e1671f..ce9475a4600 100644 --- a/tests/lib/Avatar/AvatarManagerTest.php +++ b/tests/lib/Avatar/AvatarManagerTest.php @@ -101,7 +101,7 @@ class AvatarManagerTest extends \Test\TestCase { } public function testGetAvatarForSelf() { - $user = $this->createMock(IUser::class); + $user = $this->createMock(\OC\User\User::class); $user ->expects($this->any()) ->method('getUID') @@ -151,7 +151,7 @@ class AvatarManagerTest extends \Test\TestCase { } public function testGetAvatarValidUserDifferentCasing() { - $user = $this->createMock(IUser::class); + $user = $this->createMock(\OC\User\User::class); $this->userManager->expects($this->once()) ->method('get') ->with('vaLid-USER') @@ -225,7 +225,7 @@ class AvatarManagerTest extends \Test\TestCase { ->method('getUser') ->willReturn($requestingUser); - $user = $this->createMock(IUser::class); + $user = $this->createMock(\OC\User\User::class); $user ->expects($this->once()) ->method('getUID') diff --git a/tests/lib/Avatar/UserAvatarTest.php b/tests/lib/Avatar/UserAvatarTest.php index dd5f25163f2..bd45a819760 100644 --- a/tests/lib/Avatar/UserAvatarTest.php +++ b/tests/lib/Avatar/UserAvatarTest.php @@ -57,7 +57,7 @@ class UserAvatarTest extends \Test\TestCase { ->willReturn($file); $this->folder->method('getFile') - ->willReturnCallback(function ($path) { + ->willReturnCallback(function (string $path) { if ($path === 'avatar.64.png') { throw new NotFoundException(); } @@ -96,7 +96,7 @@ class UserAvatarTest extends \Test\TestCase { $expected = new \OC_Image(); $expected->loadFromFile(\OC::$SERVERROOT . '/tests/data/testavatar.png'); - $file = $this->createMock(File::class); + $file = $this->createMock(ISimpleFile::class); $file->method('getContent')->willReturn($expected->data()); $this->folder->method('getFile')->with('avatar.128.jpg')->willReturn($file); @@ -112,7 +112,7 @@ class UserAvatarTest extends \Test\TestCase { $expected = new \OC_Image(); $expected->loadFromFile(\OC::$SERVERROOT . '/tests/data/testavatar.png'); - $file = $this->createMock(File::class); + $file = $this->createMock(ISimpleFile::class); $file->method('getContent')->willReturn($expected->data()); $this->folder->method('getFile')->with('avatar.jpg')->willReturn($file); @@ -132,7 +132,7 @@ class UserAvatarTest extends \Test\TestCase { $expected2->loadFromFile(\OC::$SERVERROOT . '/tests/data/testavatar.png'); $expected2->resize(32); - $file = $this->createMock(File::class); + $file = $this->createMock(ISimpleFile::class); $file->method('getContent')->willReturn($expected->data()); $this->folder->method('getFile') @@ -146,7 +146,7 @@ class UserAvatarTest extends \Test\TestCase { } ); - $newFile = $this->createMock(File::class); + $newFile = $this->createMock(ISimpleFile::class); $newFile->expects($this->once()) ->method('putContent') ->with($expected2->data()); |