]> source.dussan.org Git - nextcloud-server.git/commitdiff
Added PlaceholderAvatar with own cached images
authorVincent Petry <vincent@nextcloud.com>
Thu, 25 Mar 2021 11:21:03 +0000 (12:21 +0100)
committerbackportbot[bot] <backportbot[bot]@users.noreply.github.com>
Mon, 29 Mar 2021 07:03:36 +0000 (07:03 +0000)
When avatar scope is private, the PlaceholderAvatar is used to deliver a
placeholder avatar based on the user's initials.

This was implemented as a separate class for now to avoid messing with
the existing UserAvatar implementation and its generated vs
non-generated logic.

Signed-off-by: Vincent Petry <vincent@nextcloud.com>
lib/private/Avatar/AvatarManager.php
lib/private/Avatar/PlaceholderAvatar.php [new file with mode: 0644]
lib/private/Avatar/UserAvatar.php
tests/lib/Avatar/AvatarManagerTest.php

index 03f3d89e5f6c64594c4286264badc84139bf8105..92cd502dacb4663bac4ebaa0ad43380ccd476239 100644 (file)
@@ -122,7 +122,11 @@ class AvatarManager implements IAvatarManager {
                        $requestingUser = $this->userSession->getUser();
                }
 
-               $canShowRealAvatar = true;
+               try {
+                       $folder = $this->appData->getFolder($userId);
+               } catch (NotFoundException $e) {
+                       $folder = $this->appData->newFolder($userId);
+               }
 
                // requesting in public page
                if ($requestingUser === null) {
@@ -132,18 +136,11 @@ class AvatarManager implements IAvatarManager {
 
                        // v2-private scope hides the avatar from public access
                        if ($avatarScope === IAccountManager::SCOPE_PRIVATE) {
-                               // FIXME: guest avatar is re-generated every time, use a cache instead
-                               // see how UserAvatar caches the generated one
-                               return $this->getGuestAvatar($userId);
+                               // use a placeholder avatar which caches the generated images
+                               return new PlaceholderAvatar($folder, $user, $this->logger);
                        }
                }
 
-               try {
-                       $folder = $this->appData->getFolder($userId);
-               } catch (NotFoundException $e) {
-                       $folder = $this->appData->newFolder($userId);
-               }
-
                return new UserAvatar($folder, $this->l, $user, $this->logger, $this->config);
        }
 
diff --git a/lib/private/Avatar/PlaceholderAvatar.php b/lib/private/Avatar/PlaceholderAvatar.php
new file mode 100644 (file)
index 0000000..5883fe5
--- /dev/null
@@ -0,0 +1,183 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright Copyright (c) 2018, Michael Weimann <mail@michael-weimann.eu>
+ *
+ * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
+ * @author Christoph Wurst <christoph@winzerhof-wurst.at>
+ * @author Joas Schilling <coding@schilljs.com>
+ * @author Michael Weimann <mail@michael-weimann.eu>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * 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\Avatar;
+
+use OC\NotSquareException;
+use OC\User\User;
+use OCP\Files\NotFoundException;
+use OCP\Files\NotPermittedException;
+use OCP\Files\SimpleFS\ISimpleFile;
+use OCP\Files\SimpleFS\ISimpleFolder;
+use OCP\IConfig;
+use OCP\IImage;
+use OCP\IL10N;
+use OCP\ILogger;
+
+/**
+ * This class represents a registered user's placeholder avatar.
+ *
+ * It generates an image based on the user's initials and caches it on storage
+ * for faster retrieval, unlike the GuestAvatar.
+ */
+class PlaceholderAvatar extends Avatar {
+       /** @var ISimpleFolder */
+       private $folder;
+
+       /** @var User */
+       private $user;
+
+       /**
+        * UserAvatar constructor.
+        *
+        * @param IConfig $config The configuration
+        * @param ISimpleFolder $folder The avatar files folder
+        * @param IL10N $l The localization helper
+        * @param User $user The user this class manages the avatar for
+        * @param ILogger $logger The logger
+        */
+       public function __construct(
+               ISimpleFolder $folder,
+               $user,
+               ILogger $logger) {
+               parent::__construct($logger);
+
+               $this->folder = $folder;
+               $this->user = $user;
+       }
+
+       /**
+        * Check if an avatar exists for the user
+        *
+        * @return bool
+        */
+       public function exists() {
+               return true;
+       }
+
+       /**
+        * Sets the users avatar.
+        *
+        * @param 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 NotSquareException if the image is not square
+        * @return void
+        */
+       public function set($data) {
+               // unimplemented for placeholder avatars
+       }
+
+       /**
+        * Removes the users avatar.
+        */
+       public function remove(bool $silent = false) {
+               $avatars = $this->folder->getDirectoryListing();
+
+               foreach ($avatars as $avatar) {
+                       $avatar->delete();
+               }
+       }
+
+       /**
+        * Returns the avatar for an user.
+        *
+        * If there is no avatar file yet, one is generated.
+        *
+        * @param int $size
+        * @return ISimpleFile
+        * @throws NotFoundException
+        * @throws \OCP\Files\NotPermittedException
+        * @throws \OCP\PreConditionNotMetException
+        */
+       public function getFile($size) {
+               $size = (int) $size;
+
+               $ext = 'png';
+
+               if ($size === -1) {
+                       $path = 'avatar-placeholder.' . $ext;
+               } else {
+                       $path = 'avatar-placeholder.' . $size . '.' . $ext;
+               }
+
+               try {
+                       $file = $this->folder->getFile($path);
+               } catch (NotFoundException $e) {
+                       if ($size <= 0) {
+                               throw new NotFoundException;
+                       }
+
+                       if (!$data = $this->generateAvatarFromSvg($size)) {
+                               $data = $this->generateAvatar($this->getDisplayName(), $size);
+                       }
+
+                       try {
+                               $file = $this->folder->newFile($path);
+                               $file->putContent($data);
+                       } catch (NotPermittedException $e) {
+                               $this->logger->error('Failed to save avatar placeholder for ' . $this->user->getUID());
+                               throw new NotFoundException();
+                       }
+               }
+
+               return $file;
+       }
+
+       /**
+        * Returns the user display name.
+        *
+        * @return string
+        */
+       public function getDisplayName(): string {
+               return $this->user->getDisplayName();
+       }
+
+       /**
+        * Handles user changes.
+        *
+        * @param string $feature The changed feature
+        * @param mixed $oldValue The previous value
+        * @param mixed $newValue The new value
+        * @throws NotPermittedException
+        * @throws \OCP\PreConditionNotMetException
+        */
+       public function userChanged($feature, $oldValue, $newValue) {
+               $this->remove();
+       }
+
+       /**
+        * Check if the avatar of a user is a custom uploaded one
+        *
+        * @return bool
+        */
+       public function isCustomAvatar(): bool {
+               return false;
+       }
+}
index f7ace429f7d98475e930b57ae3d4e6cc016c4530..f47809425ed4022db600a8ad42c26dc9c1ddc8cc 100644 (file)
@@ -270,6 +270,7 @@ class UserAvatar extends Avatar {
                                throw new NotFoundException;
                        }
 
+                       // TODO: rework to integrate with the PlaceholderAvatar in a compatible way
                        if ($this->folder->fileExists('generated')) {
                                if (!$data = $this->generateAvatarFromSvg($size)) {
                                        $data = $this->generateAvatar($this->getDisplayName(), $size);
index 0ce0e752569475b112cfa275f2f4cef3eba83bb3..1b06e786fb1c42e633a914056f4a3cac74b05963 100644 (file)
@@ -25,7 +25,7 @@
 namespace Test\Avatar;
 
 use OC\Avatar\AvatarManager;
-use OC\Avatar\GuestAvatar;
+use OC\Avatar\PlaceholderAvatar;
 use OC\Avatar\UserAvatar;
 use OC\User\Manager;
 use OCP\Accounts\IAccount;
@@ -159,10 +159,13 @@ class AvatarManagerTest extends \Test\TestCase {
                        ->method('get')
                        ->with('valid-user')
                        ->willReturn($user);
+
                $folder = $this->createMock(ISimpleFolder::class);
                $this->appData
-                       ->expects($this->never())
-                       ->method('getFolder');
+                       ->expects($this->once())
+                       ->method('getFolder')
+                       ->with('valid-user')
+                       ->willReturn($folder);
 
                $account = $this->createMock(IAccount::class);
                $this->accountManager->expects($this->once())
@@ -180,7 +183,7 @@ class AvatarManagerTest extends \Test\TestCase {
                        ->method('getScope')
                        ->willReturn(IAccountManager::SCOPE_PRIVATE);
 
-               $expected = new GuestAvatar('valid-user', $this->createMock(ILogger::class));
+               $expected = new PlaceholderAvatar($folder, $user, $this->createMock(ILogger::class));
                $this->assertEquals($expected, $this->avatarManager->getAvatar('valid-user'));
        }
 }