Implement guest avatar endpoint

Signed-off-by: Michael Weimann <mail@michael-weimann.eu>
This commit is contained in:
Michael Weimann 2019-01-20 11:13:41 +01:00 committed by Morris Jobke
parent b69b17f29f
commit bf1253cb49
No known key found for this signature in database
GPG Key ID: FE03C3A163FEDE68
25 changed files with 1136 additions and 266 deletions

View File

@ -42,7 +42,6 @@ use OCP\IL10N;
use OCP\IRequest;
use OCP\IUserManager;
use OCP\IUserSession;
use OCP\AppFramework\Http\DataResponse;
/**
* Class AvatarController

View File

@ -0,0 +1,107 @@
<?php
/**
* @copyright Copyright (c) 2019, Michael Weimann <mail@michael-weimann.eu>
*
* @author Michael Weimann <mail@michael-weimann.eu>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
namespace OC\Core\Controller;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\FileDisplayResponse;
use OCP\IAvatarManager;
use OCP\ILogger;
use OCP\IRequest;
/**
* This controller handles guest avatar requests.
*/
class GuestAvatarController extends Controller {
/**
* @var ILogger
*/
private $logger;
/**
* @var IAvatarManager
*/
private $avatarManager;
/**
* GuestAvatarController constructor.
*
* @param $appName
* @param IRequest $request
* @param IAvatarManager $avatarManager
* @param ILogger $logger
*/
public function __construct(
$appName,
IRequest $request,
IAvatarManager $avatarManager,
ILogger $logger
) {
parent::__construct($appName, $request);
$this->avatarManager = $avatarManager;
$this->logger = $logger;
}
/**
* Returns a guest avatar image response.
*
* @PublicPage
* @NoCSRFRequired
*
* @param string $guestName The guest name, e.g. "Albert"
* @param string $size The desired avatar size, e.g. 64 for 64x64px
* @return FileDisplayResponse|Http\Response
*/
public function getAvatar($guestName, $size) {
$size = (int) $size;
// min/max size
if ($size > 2048) {
$size = 2048;
} elseif ($size <= 0) {
$size = 64;
}
try {
$avatar = $this->avatarManager->getGuestAvatar($guestName);
$avatarFile = $avatar->getFile($size);
$resp = new FileDisplayResponse(
$avatarFile,
$avatar->isCustomAvatar() ? Http::STATUS_OK : Http::STATUS_CREATED,
['Content-Type' => $avatarFile->getMimeType()]
);
} catch (\Exception $e) {
$this->logger->error('error while creating guest avatar', [
'err' => $e,
]);
$resp = new Http\Response();
$resp->setStatus(Http::STATUS_INTERNAL_SERVER_ERROR);
return $resp;
}
// Cache for 30 minutes
$resp->cacheFor(1800);
return $resp;
}
}

View File

@ -46,6 +46,7 @@ $application->registerRoutes($this, [
['name' => 'avatar#postCroppedAvatar', 'url' => '/avatar/cropped', 'verb' => 'POST'],
['name' => 'avatar#getTmpAvatar', 'url' => '/avatar/tmp', 'verb' => 'GET'],
['name' => 'avatar#postAvatar', 'url' => '/avatar/', 'verb' => 'POST'],
['name' => 'GuestAvatar#getAvatar', 'url' => '/avatar/guest/{guestName}/{size}', 'verb' => 'GET'],
['name' => 'CSRFToken#index', 'url' => '/csrftoken', 'verb' => 'GET'],
['name' => 'login#tryLogin', 'url' => '/login', 'verb' => 'POST'],
['name' => 'login#confirmPassword', 'url' => '/login/confirm', 'verb' => 'POST'],

View File

@ -221,6 +221,7 @@ return array(
'OCP\\Files\\SimpleFS\\ISimpleFile' => $baseDir . '/lib/public/Files/SimpleFS/ISimpleFile.php',
'OCP\\Files\\SimpleFS\\ISimpleFolder' => $baseDir . '/lib/public/Files/SimpleFS/ISimpleFolder.php',
'OCP\\Files\\SimpleFS\\ISimpleRoot' => $baseDir . '/lib/public/Files/SimpleFS/ISimpleRoot.php',
'OCP\\Files\\SimpleFS\\InMemoryFile' => $baseDir . '/lib/public/Files/SimpleFS/InMemoryFile.php',
'OCP\\Files\\Storage' => $baseDir . '/lib/public/Files/Storage.php',
'OCP\\Files\\StorageAuthException' => $baseDir . '/lib/public/Files/StorageAuthException.php',
'OCP\\Files\\StorageBadConfigException' => $baseDir . '/lib/public/Files/StorageBadConfigException.php',
@ -510,8 +511,10 @@ return array(
'OC\\Authentication\\TwoFactorAuth\\ProviderManager' => $baseDir . '/lib/private/Authentication/TwoFactorAuth/ProviderManager.php',
'OC\\Authentication\\TwoFactorAuth\\ProviderSet' => $baseDir . '/lib/private/Authentication/TwoFactorAuth/ProviderSet.php',
'OC\\Authentication\\TwoFactorAuth\\Registry' => $baseDir . '/lib/private/Authentication/TwoFactorAuth/Registry.php',
'OC\\Avatar' => $baseDir . '/lib/private/Avatar.php',
'OC\\AvatarManager' => $baseDir . '/lib/private/AvatarManager.php',
'OC\\Avatar\\Avatar' => $baseDir . '/lib/private/Avatar/Avatar.php',
'OC\\Avatar\\AvatarManager' => $baseDir . '/lib/private/Avatar/AvatarManager.php',
'OC\\Avatar\\GuestAvatar' => $baseDir . '/lib/private/Avatar/GuestAvatar.php',
'OC\\Avatar\\UserAvatar' => $baseDir . '/lib/private/Avatar/UserAvatar.php',
'OC\\BackgroundJob\\Job' => $baseDir . '/lib/private/BackgroundJob/Job.php',
'OC\\BackgroundJob\\JobList' => $baseDir . '/lib/private/BackgroundJob/JobList.php',
'OC\\BackgroundJob\\Legacy\\QueuedJob' => $baseDir . '/lib/private/BackgroundJob/Legacy/QueuedJob.php',
@ -648,6 +651,7 @@ return array(
'OC\\Core\\Controller\\ClientFlowLoginController' => $baseDir . '/core/Controller/ClientFlowLoginController.php',
'OC\\Core\\Controller\\ContactsMenuController' => $baseDir . '/core/Controller/ContactsMenuController.php',
'OC\\Core\\Controller\\CssController' => $baseDir . '/core/Controller/CssController.php',
'OC\\Core\\Controller\\GuestAvatarController' => $baseDir . '/core/Controller/GuestAvatarController.php',
'OC\\Core\\Controller\\JsController' => $baseDir . '/core/Controller/JsController.php',
'OC\\Core\\Controller\\LoginController' => $baseDir . '/core/Controller/LoginController.php',
'OC\\Core\\Controller\\LostController' => $baseDir . '/core/Controller/LostController.php',

View File

@ -251,6 +251,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OCP\\Files\\SimpleFS\\ISimpleFile' => __DIR__ . '/../../..' . '/lib/public/Files/SimpleFS/ISimpleFile.php',
'OCP\\Files\\SimpleFS\\ISimpleFolder' => __DIR__ . '/../../..' . '/lib/public/Files/SimpleFS/ISimpleFolder.php',
'OCP\\Files\\SimpleFS\\ISimpleRoot' => __DIR__ . '/../../..' . '/lib/public/Files/SimpleFS/ISimpleRoot.php',
'OCP\\Files\\SimpleFS\\InMemoryFile' => __DIR__ . '/../../..' . '/lib/public/Files/SimpleFS/InMemoryFile.php',
'OCP\\Files\\Storage' => __DIR__ . '/../../..' . '/lib/public/Files/Storage.php',
'OCP\\Files\\StorageAuthException' => __DIR__ . '/../../..' . '/lib/public/Files/StorageAuthException.php',
'OCP\\Files\\StorageBadConfigException' => __DIR__ . '/../../..' . '/lib/public/Files/StorageBadConfigException.php',
@ -540,8 +541,10 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OC\\Authentication\\TwoFactorAuth\\ProviderManager' => __DIR__ . '/../../..' . '/lib/private/Authentication/TwoFactorAuth/ProviderManager.php',
'OC\\Authentication\\TwoFactorAuth\\ProviderSet' => __DIR__ . '/../../..' . '/lib/private/Authentication/TwoFactorAuth/ProviderSet.php',
'OC\\Authentication\\TwoFactorAuth\\Registry' => __DIR__ . '/../../..' . '/lib/private/Authentication/TwoFactorAuth/Registry.php',
'OC\\Avatar' => __DIR__ . '/../../..' . '/lib/private/Avatar.php',
'OC\\AvatarManager' => __DIR__ . '/../../..' . '/lib/private/AvatarManager.php',
'OC\\Avatar\\Avatar' => __DIR__ . '/../../..' . '/lib/private/Avatar/Avatar.php',
'OC\\Avatar\\AvatarManager' => __DIR__ . '/../../..' . '/lib/private/Avatar/AvatarManager.php',
'OC\\Avatar\\GuestAvatar' => __DIR__ . '/../../..' . '/lib/private/Avatar/GuestAvatar.php',
'OC\\Avatar\\UserAvatar' => __DIR__ . '/../../..' . '/lib/private/Avatar/UserAvatar.php',
'OC\\BackgroundJob\\Job' => __DIR__ . '/../../..' . '/lib/private/BackgroundJob/Job.php',
'OC\\BackgroundJob\\JobList' => __DIR__ . '/../../..' . '/lib/private/BackgroundJob/JobList.php',
'OC\\BackgroundJob\\Legacy\\QueuedJob' => __DIR__ . '/../../..' . '/lib/private/BackgroundJob/Legacy/QueuedJob.php',
@ -678,6 +681,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OC\\Core\\Controller\\ClientFlowLoginController' => __DIR__ . '/../../..' . '/core/Controller/ClientFlowLoginController.php',
'OC\\Core\\Controller\\ContactsMenuController' => __DIR__ . '/../../..' . '/core/Controller/ContactsMenuController.php',
'OC\\Core\\Controller\\CssController' => __DIR__ . '/../../..' . '/core/Controller/CssController.php',
'OC\\Core\\Controller\\GuestAvatarController' => __DIR__ . '/../../..' . '/core/Controller/GuestAvatarController.php',
'OC\\Core\\Controller\\JsController' => __DIR__ . '/../../..' . '/core/Controller/JsController.php',
'OC\\Core\\Controller\\LoginController' => __DIR__ . '/../../..' . '/core/Controller/LoginController.php',
'OC\\Core\\Controller\\LostController' => __DIR__ . '/../../..' . '/core/Controller/LostController.php',

View File

@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
* @copyright 2018 John Molakvoæ <skjnldsv@protonmail.com>
@ -29,36 +30,22 @@
*
*/
namespace OC;
namespace OC\Avatar;
use OC\Color;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
use OCP\Files\SimpleFS\ISimpleFile;
use OCP\Files\SimpleFS\ISimpleFolder;
use OCP\IAvatar;
use OCP\IConfig;
use OCP\IImage;
use OCP\IL10N;
use OCP\ILogger;
use OC\User\User;
use OC_Image;
use Imagick;
/**
* This class gets and sets users avatars.
*/
abstract class Avatar implements IAvatar {
class Avatar implements IAvatar {
/** @var ISimpleFolder */
private $folder;
/** @var IL10N */
private $l;
/** @var User */
private $user;
/** @var ILogger */
private $logger;
/** @var IConfig */
private $config;
protected $logger;
/**
* https://github.com/sebdesign/cap-height -- for 500px height
@ -77,30 +64,41 @@ class Avatar implements IAvatar {
</svg>';
/**
* constructor
* The base avatar constructor.
*
* @param ISimpleFolder $folder The folder where the avatars are
* @param IL10N $l
* @param User $user
* @param ILogger $logger
* @param IConfig $config
* @param ILogger $logger The logger
*/
public function __construct(ISimpleFolder $folder,
IL10N $l,
$user,
ILogger $logger,
IConfig $config) {
$this->folder = $folder;
$this->l = $l;
$this->user = $user;
public function __construct(ILogger $logger) {
$this->logger = $logger;
$this->config = $config;
}
/**
* 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 getAvatarLetter(): string {
$displayName = $this->getDisplayName();
if (empty($displayName) === true) {
return '?';
} else {
return mb_strtoupper(mb_substr($displayName, 0, 1), 'UTF-8');
}
}
/**
* @inheritdoc
*/
public function get($size = 64) {
$size = (int) $size;
try {
$file = $this->getFile($size);
} catch (NotFoundException $e) {
@ -112,199 +110,22 @@ class Avatar implements IAvatar {
return $avatar;
}
/**
* Check if an avatar exists for the user
*
* @return bool
*/
public function exists() {
return $this->folder->fileExists('avatar.jpg') || $this->folder->fileExists('avatar.png');
}
/**
* 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';
}
/**
* 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) {
if ($data instanceof IImage) {
$img = $data;
$data = $img->data();
} else {
$img = new OC_Image();
if (is_resource($data) && get_resource_type($data) === "gd") {
$img->setResource($data);
} elseif (is_resource($data)) {
$img->loadFromFileHandle($data);
} else {
try {
// detect if it is a path or maybe the images as string
$result = @realpath($data);
if ($result === false || $result === null) {
$img->loadFromData($data);
} else {
$img->loadFromFile($data);
}
} catch (\Error $e) {
$img->loadFromData($data);
}
}
}
$type = substr($img->mimeType(), -3);
if ($type === 'peg') {
$type = 'jpg';
}
if ($type !== 'jpg' && $type !== 'png') {
throw new \Exception($this->l->t('Unknown filetype'));
}
if (!$img->valid()) {
throw new \Exception($this->l->t('Invalid image'));
}
if (!($img->height() === $img->width())) {
throw new NotSquareException($this->l->t('Avatar image is not square'));
}
$this->remove();
$file = $this->folder->newFile('avatar.' . $type);
$file->putContent($data);
try {
$generated = $this->folder->getFile('generated');
$this->config->setUserValue($this->user->getUID(), 'avatar', 'generated', 'false');
$generated->delete();
} catch (NotFoundException $e) {
//
}
$this->user->triggerChange('avatar', $file);
}
/**
* remove the users avatar
* @return void
*/
public function remove() {
$avatars = $this->folder->getDirectoryListing();
$this->config->setUserValue($this->user->getUID(), 'avatar', 'version',
(int) $this->config->getUserValue($this->user->getUID(), 'avatar', 'version', 0) + 1);
foreach ($avatars as $avatar) {
$avatar->delete();
}
$this->config->setUserValue($this->user->getUID(), 'avatar', 'generated', 'true');
$this->user->triggerChange('avatar', '');
}
/**
* @inheritdoc
*/
public function getFile($size) {
try {
$ext = $this->getExtension();
} catch (NotFoundException $e) {
if (!$data = $this->generateAvatarFromSvg(1024)) {
$data = $this->generateAvatar($this->user->getDisplayName(), 1024);
}
$avatar = $this->folder->newFile('avatar.png');
$avatar->putContent($data);
$ext = 'png';
$this->folder->newFile('generated');
$this->config->setUserValue($this->user->getUID(), 'avatar', 'generated', 'true');
}
if ($size === -1) {
$path = 'avatar.' . $ext;
} else {
$path = 'avatar.' . $size . '.' . $ext;
}
try {
$file = $this->folder->getFile($path);
} catch (NotFoundException $e) {
if ($size <= 0) {
throw new NotFoundException;
}
if ($this->folder->fileExists('generated')) {
if (!$data = $this->generateAvatarFromSvg($size)) {
$data = $this->generateAvatar($this->user->getDisplayName(), $size);
}
} else {
$avatar = new OC_Image();
/** @var ISimpleFile $file */
$file = $this->folder->getFile('avatar.' . $ext);
$avatar->loadFromData($file->getContent());
$avatar->resize($size);
$data = $avatar->data();
}
try {
$file = $this->folder->newFile($path);
$file->putContent($data);
} catch (NotPermittedException $e) {
$this->logger->error('Failed to save avatar for ' . $this->user->getUID());
throw new NotFoundException();
}
}
if ($this->config->getUserValue($this->user->getUID(), 'avatar', 'generated', null) === null) {
$generated = $this->folder->fileExists('generated') ? 'true' : 'false';
$this->config->setUserValue($this->user->getUID(), 'avatar', 'generated', $generated);
}
return $file;
}
/**
* Get the extension of the avatar. If there is no avatar throw Exception
*
* @return string
* @throws NotFoundException
*/
private function getExtension() {
if ($this->folder->fileExists('avatar.jpg')) {
return 'jpg';
} elseif ($this->folder->fileExists('avatar.png')) {
return 'png';
}
throw new NotFoundException;
}
/**
* {size} = 500
* {fill} = hex color to fill
* {letter} = Letter to display
*
* Generate SVG avatar
*
* @param int $size The requested image size in pixel
* @return string
*
*/
private function getAvatarVector(int $size): string {
$userDisplayName = $this->user->getDisplayName();
protected function getAvatarVector(int $size): string {
$userDisplayName = $this->getDisplayName();
$bgRGB = $this->avatarBackgroundColor($userDisplayName);
$bgHEX = sprintf("%02x%02x%02x", $bgRGB->r, $bgRGB->g, $bgRGB->b);
$letter = mb_strtoupper(mb_substr($userDisplayName, 0, 1), 'UTF-8');
$letter = $this->getAvatarLetter();
$toReplace = ['{size}', '{fill}', '{letter}'];
return str_replace($toReplace, [$size, $bgHEX, $letter], $this->svgTemplate);
}
@ -315,7 +136,7 @@ class Avatar implements IAvatar {
* @param int $size
* @return string|boolean
*/
private function generateAvatarFromSvg(int $size) {
protected function generateAvatarFromSvg(int $size) {
if (!extension_loaded('imagick')) {
return false;
}
@ -341,22 +162,28 @@ class Avatar implements IAvatar {
* @param int $size
* @return string
*/
private function generateAvatar($userDisplayName, $size) {
$text = mb_strtoupper(mb_substr($userDisplayName, 0, 1), 'UTF-8');
protected function generateAvatar($userDisplayName, $size) {
$letter = $this->getAvatarLetter();
$backgroundColor = $this->avatarBackgroundColor($userDisplayName);
$im = imagecreatetruecolor($size, $size);
$background = imagecolorallocate($im, $backgroundColor->r, $backgroundColor->g, $backgroundColor->b);
$background = imagecolorallocate(
$im,
$backgroundColor->r,
$backgroundColor->g,
$backgroundColor->b
);
$white = imagecolorallocate($im, 255, 255, 255);
imagefilledrectangle($im, 0, 0, $size, $size, $background);
$font = __DIR__ . '/../../core/fonts/Nunito-Regular.ttf';
$font = __DIR__ . '/../../../core/fonts/Nunito-Regular.ttf';
$fontSize = $size * 0.4;
list($x, $y) = $this->imageTTFCenter(
$im, $letter, $font, (int)$fontSize
);
list($x, $y) = $this->imageTTFCenter($im, $text, $font, $fontSize);
imagettftext($im, $fontSize, 0, $x, $y, $white, $font, $text);
imagettftext($im, $fontSize, 0, $x, $y, $white, $font, $letter);
ob_start();
imagepng($im);
@ -376,7 +203,13 @@ class Avatar implements IAvatar {
* @param int $angle
* @return array
*/
protected function imageTTFCenter($image, string $text, string $font, int $size, $angle = 0): array {
protected function imageTTFCenter(
$image,
string $text,
string $font,
int $size,
$angle = 0
): array {
// Image width & height
$xi = imagesx($image);
$yi = imagesy($image);
@ -413,10 +246,9 @@ 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
* @return int[] between 0 and $maximum
*/
private function mixPalette($steps, $color1, $color2) {
$count = $steps + 1;
$palette = array($color1);
$step = $this->stepCalc($steps, [$color1, $color2]);
for ($i = 1; $i < $steps; $i++) {
@ -483,19 +315,4 @@ class Avatar implements IAvatar {
return $finalPalette[$this->hashToInt($hash, $steps * 3)];
}
public function userChanged($feature, $oldValue, $newValue) {
// We only change the avatar on display name changes
if ($feature !== 'displayName') {
return;
}
// If the avatar is not generated (so an uploaded image) we skip this
if (!$this->folder->fileExists('generated')) {
return;
}
$this->remove();
}
}

View File

@ -26,7 +26,7 @@ declare(strict_types=1);
*
*/
namespace OC;
namespace OC\Avatar;
use OC\User\Manager;
use OCP\Files\IAppData;
@ -102,7 +102,7 @@ class AvatarManager implements IAvatarManager {
$folder = $this->appData->newFolder($userId);
}
return new Avatar($folder, $this->l, $user, $this->logger, $this->config);
return new UserAvatar($folder, $this->l, $user, $this->logger, $this->config);
}
/**
@ -120,4 +120,14 @@ class AvatarManager implements IAvatarManager {
$this->config->setUserValue($userId, 'avatar', 'generated', 'false');
}
}
/**
* Returns a GuestAvatar.
*
* @param string $name The guest name, e.g. "Albert".
* @return IAvatar
*/
public function getGuestAvatar(string $name): IAvatar {
return new GuestAvatar($name, $this->logger);
}
}

View File

@ -0,0 +1,119 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2018, Michael Weimann <mail@michael-weimann.eu>
*
* @author Michael Weimann <mail@michael-weimann.eu>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
namespace OC\Avatar;
use OCP\Files\SimpleFS\InMemoryFile;
use OCP\ILogger;
/**
* This class represents a guest user's avatar.
*/
class GuestAvatar extends Avatar {
/**
* Holds the guest user display name.
*
* @var string
*/
private $userDisplayName;
/**
* GuestAvatar constructor.
*
* @param string $userDisplayName The guest user display name
* @param ILogger $logger The logger
*/
public function __construct(string $userDisplayName, ILogger $logger) {
parent::__construct($logger);
$this->userDisplayName = $userDisplayName;
}
/**
* Tests if the user has an avatar.
*
* @return true Guests always have an avatar.
*/
public function exists() {
return true;
}
/**
* Returns the guest user display name.
*
* @return string
*/
public function getDisplayName(): string {
return $this->userDisplayName;
}
/**
* Setting avatars isn't implemented for guests.
*
* @param \OCP\IImage|resource|string $data
* @return void
*/
public function set($data) {
// unimplemented for guest user avatars
}
/**
* Removing avatars isn't implemented for guests.
*/
public function remove() {
// unimplemented for guest user avatars
}
/**
* Generates an avatar for the guest.
*
* @param int $size The desired image size.
* @return InMemoryFile
*/
public function getFile($size) {
$avatar = $this->getAvatarVector($size);
return new InMemoryFile('avatar.svg', $avatar);
}
/**
* Updates the display name if changed.
*
* @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) {
if ($feature === 'displayName') {
$this->userDisplayName = $newValue;
}
}
/**
* Guests don't have custom avatars.
*
* @return bool
*/
public function isCustomAvatar(): bool {
return false;
}
}

View File

@ -0,0 +1,336 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2018, Michael Weimann <mail@michael-weimann.eu>
*
* @author Michael Weimann <mail@michael-weimann.eu>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
namespace OC\Avatar;
use OC\NotSquareException;
use OC\User\User;
use OC_Image;
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 avatar.
*/
class UserAvatar extends Avatar {
/** @var IConfig */
private $config;
/** @var ISimpleFolder */
private $folder;
/** @var IL10N */
private $l;
/** @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,
IL10N $l,
$user,
ILogger $logger,
IConfig $config) {
parent::__construct($logger);
$this->folder = $folder;
$this->l = $l;
$this->user = $user;
$this->config = $config;
}
/**
* Check if an avatar exists for the user
*
* @return bool
*/
public function exists() {
return $this->folder->fileExists('avatar.jpg') || $this->folder->fileExists('avatar.png');
}
/**
* 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) {
$img = $this->getAvatarImage($data);
$data = $img->data();
$this->validateAvatar($img);
$this->remove();
$type = $this->getAvatarImageType($img);
$file = $this->folder->newFile('avatar.' . $type);
$file->putContent($data);
try {
$generated = $this->folder->getFile('generated');
$this->config->setUserValue($this->user->getUID(), 'avatar', 'generated', 'false');
$generated->delete();
} catch (NotFoundException $e) {
//
}
$this->user->triggerChange('avatar', $file);
}
/**
* Returns an image from several sources.
*
* @param IImage|resource|string $data An image object, imagedata or path to the avatar
* @return IImage
*/
private function getAvatarImage($data) {
if ($data instanceof IImage) {
return $data;
}
$img = new OC_Image();
if (is_resource($data) && get_resource_type($data) === 'gd') {
$img->setResource($data);
} elseif (is_resource($data)) {
$img->loadFromFileHandle($data);
} else {
try {
// detect if it is a path or maybe the images as string
$result = @realpath($data);
if ($result === false || $result === null) {
$img->loadFromData($data);
} else {
$img->loadFromFile($data);
}
} catch (\Error $e) {
$img->loadFromData($data);
}
}
return $img;
}
/**
* Returns the avatar image type.
*
* @param IImage $avatar
* @return string
*/
private function getAvatarImageType(IImage $avatar) {
$type = substr($avatar->mimeType(), -3);
if ($type === 'peg') {
$type = 'jpg';
}
return $type;
}
/**
* Validates an avatar image:
* - must be "png" or "jpg"
* - must be "valid"
* - must be in square format
*
* @param IImage $avatar The avatar to validate
* @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
*/
private function validateAvatar(IImage $avatar) {
$type = $this->getAvatarImageType($avatar);
if ($type !== 'jpg' && $type !== 'png') {
throw new \Exception($this->l->t('Unknown filetype'));
}
if (!$avatar->valid()) {
throw new \Exception($this->l->t('Invalid image'));
}
if (!($avatar->height() === $avatar->width())) {
throw new NotSquareException($this->l->t('Avatar image is not square'));
}
}
/**
* Removes the users avatar.
* @return void
* @throws \OCP\Files\NotPermittedException
* @throws \OCP\PreConditionNotMetException
*/
public function remove() {
$avatars = $this->folder->getDirectoryListing();
$this->config->setUserValue($this->user->getUID(), 'avatar', 'version',
(int) $this->config->getUserValue($this->user->getUID(), 'avatar', 'version', 0) + 1);
foreach ($avatars as $avatar) {
$avatar->delete();
}
$this->config->setUserValue($this->user->getUID(), 'avatar', 'generated', 'true');
$this->user->triggerChange('avatar', '');
}
/**
* Get the extension of the avatar. If there is no avatar throw Exception
*
* @return string
* @throws NotFoundException
*/
private function getExtension() {
if ($this->folder->fileExists('avatar.jpg')) {
return 'jpg';
} elseif ($this->folder->fileExists('avatar.png')) {
return 'png';
}
throw new NotFoundException;
}
/**
* 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;
try {
$ext = $this->getExtension();
} catch (NotFoundException $e) {
if (!$data = $this->generateAvatarFromSvg(1024)) {
$data = $this->generateAvatar($this->getDisplayName(), 1024);
}
$avatar = $this->folder->newFile('avatar.png');
$avatar->putContent($data);
$ext = 'png';
$this->folder->newFile('generated');
$this->config->setUserValue($this->user->getUID(), 'avatar', 'generated', 'true');
}
if ($size === -1) {
$path = 'avatar.' . $ext;
} else {
$path = 'avatar.' . $size . '.' . $ext;
}
try {
$file = $this->folder->getFile($path);
} catch (NotFoundException $e) {
if ($size <= 0) {
throw new NotFoundException;
}
if ($this->folder->fileExists('generated')) {
if (!$data = $this->generateAvatarFromSvg($size)) {
$data = $this->generateAvatar($this->getDisplayName(), $size);
}
} else {
$avatar = new OC_Image();
$file = $this->folder->getFile('avatar.' . $ext);
$avatar->loadFromData($file->getContent());
$avatar->resize($size);
$data = $avatar->data();
}
try {
$file = $this->folder->newFile($path);
$file->putContent($data);
} catch (NotPermittedException $e) {
$this->logger->error('Failed to save avatar for ' . $this->user->getUID());
throw new NotFoundException();
}
}
if ($this->config->getUserValue($this->user->getUID(), 'avatar', 'generated', null) === null) {
$generated = $this->folder->fileExists('generated') ? 'true' : 'false';
$this->config->setUserValue($this->user->getUID(), 'avatar', 'generated', $generated);
}
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) {
// We only change the avatar on display name changes
if ($feature !== 'displayName') {
return;
}
// If the avatar is not generated (so an uploaded image) we skip this
if (!$this->folder->fileExists('generated')) {
return;
}
$this->remove();
}
/**
* 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';
}
}

View File

@ -33,7 +33,7 @@ namespace OC;
use OCP\AppFramework\QueryException;
use OCP\Migration\IOutput;
use OCP\Migration\IRepairStep;
use OC\AvatarManager;
use OC\Avatar\AvatarManager;
use OC\Repair\AddCleanupUpdaterBackupsJob;
use OC\Repair\CleanTags;
use OC\Repair\ClearGeneratedAvatarCache;

View File

@ -23,11 +23,10 @@
namespace OC\Repair;
use OC\AvatarManager;
use OC\Avatar\AvatarManager;
use OCP\IConfig;
use OCP\Migration\IOutput;
use OCP\Migration\IRepairStep;
use OCP\Util;
class ClearGeneratedAvatarCache implements IRepairStep {

View File

@ -58,6 +58,7 @@ use OC\AppFramework\Utility\SimpleContainer;
use OC\AppFramework\Utility\TimeFactory;
use OC\Authentication\LoginCredentials\Store;
use OC\Authentication\Token\IProvider;
use OC\Avatar\AvatarManager;
use OC\Collaboration\Collaborators\GroupPlugin;
use OC\Collaboration\Collaborators\MailPlugin;
use OC\Collaboration\Collaborators\RemoteGroupPlugin;

View File

@ -66,4 +66,13 @@ class FileDisplayResponse extends Response implements ICallbackResponse {
$output->setOutput($this->file->getContent());
}
}
/**
* Returns the response file.
*
* @return \OCP\Files\File|\OCP\Files\SimpleFS\ISimpleFile
*/
public function getFile() {
return $this->file;
}
}

View File

@ -0,0 +1,137 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2018, Michael Weimann <mail@michael-weimann.eu>
*
* @author Michael Weimann <mail@michael-weimann.eu>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
namespace OCP\Files\SimpleFS;
use OCP\Files\NotPermittedException;
/**
* This class represents a file that is only hold in memory.
*
* @package OC\Files\SimpleFS
*/
class InMemoryFile implements ISimpleFile {
/**
* Holds the file name.
*
* @var string
*/
private $name;
/**
* Holds the file contents.
*
* @var string
*/
private $contents;
/**
* InMemoryFile constructor.
*
* @param string $name The file name
* @param string $contents The file contents
*/
public function __construct(string $name, string $contents) {
$this->name = $name;
$this->contents = $contents;
}
/**
* @inheritdoc
*/
public function getName() {
return $this->name;
}
/**
* @inheritdoc
*/
public function getSize() {
return strlen($this->contents);
}
/**
* @inheritdoc
*/
public function getETag() {
return '';
}
/**
* @inheritdoc
*/
public function getMTime() {
return time();
}
/**
* @inheritdoc
*/
public function getContent() {
return $this->contents;
}
/**
* @inheritdoc
*/
public function putContent($data) {
$this->contents = $data;
}
/**
* In memory files can't be deleted.
*/
public function delete() {
// unimplemented for in memory files
}
/**
* @inheritdoc
*/
public function getMimeType() {
$fileInfo = new \finfo(FILEINFO_MIME_TYPE);
return $fileInfo->buffer($this->contents);
}
/**
* Stream reading is unsupported for in memory files.
*
* @throws NotPermittedException
*/
public function read() {
throw new NotPermittedException(
'Stream reading is unsupported for in memory files'
);
}
/**
* Stream writing isn't available for in memory files.
*
* @throws NotPermittedException
*/
public function write() {
throw new NotPermittedException(
'Stream writing is unsupported for in memory files'
);
}
}

View File

@ -45,4 +45,13 @@ interface IAvatarManager {
*/
public function getAvatar(string $user) : IAvatar;
/**
* Returns a guest user avatar instance.
*
* @param string $name The guest name, e.g. "Albert".
* @return IAvatar
* @since 16.0.0
*/
public function getGuestAvatar(string $name): IAvatar;
}

View File

@ -53,7 +53,7 @@ use OCP\IUserManager;
* @package OC\Core\Controller
*/
class AvatarControllerTest extends \Test\TestCase {
/** @var \OC\Core\Controller\AvatarController */
/** @var AvatarController */
private $avatarController;
/** @var IAvatar|\PHPUnit_Framework_MockObject_MockObject */
private $avatarMock;

View File

@ -0,0 +1,90 @@
<?php
namespace Core\Controller;
use OC\Core\Controller\GuestAvatarController;
use OCP\AppFramework\Http\FileDisplayResponse;
use OCP\Files\SimpleFS\ISimpleFile;
use OCP\IAvatar;
use OCP\IAvatarManager;
use OCP\ILogger;
use OCP\IRequest;
/**
* This class provides tests for the guest avatar controller.
*/
class GuestAvatarControllerTest extends \Test\TestCase {
/**
* @var GuestAvatarController
*/
private $guestAvatarController;
/**
* @var IRequest|\PHPUnit_Framework_MockObject_MockObject
*/
private $request;
/**
* @var IAvatarManager|\PHPUnit_Framework_MockObject_MockObject
*/
private $avatarManager;
/**
* @var IAvatar|\PHPUnit_Framework_MockObject_MockObject
*/
private $avatar;
/**
* @var \OCP\Files\File|\PHPUnit_Framework_MockObject_MockObject
*/
private $file;
/**
* @var ILogger|\PHPUnit_Framework_MockObject_MockObject
*/
private $logger;
/**
* Sets up the test environment.
*/
protected function setUp() {
parent::setUp();
$this->logger = $this->getMockBuilder(ILogger::class)->getMock();
$this->request = $this->getMockBuilder(IRequest::class)->getMock();
$this->avatar = $this->getMockBuilder(IAvatar::class)->getMock();
$this->avatarManager = $this->getMockBuilder(IAvatarManager::class)->getMock();
$this->file = $this->getMockBuilder(ISimpleFile::class)->getMock();
$this->guestAvatarController = new GuestAvatarController(
'core',
$this->request,
$this->avatarManager,
$this->logger
);
}
/**
* Tests getAvatar returns the guest avatar.
*/
public function testGetAvatar() {
$this->avatarManager->expects($this->once())
->method('getGuestAvatar')
->with('Peter')
->willReturn($this->avatar);
$this->avatar->expects($this->once())
->method('getFile')
->with(128)
->willReturn($this->file);
$this->file->method('getMimeType')
->willReturn('image/svg+xml');
$response = $this->guestAvatarController->getAvatar('Peter', 128);
$this->assertGreaterThanOrEqual(201, $response->getStatus());
$this->assertInstanceOf(FileDisplayResponse::class, $response);
$this->assertSame($this->file, $response->getFile());
}
}

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="32" height="32" version="1.1" viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg">
<rect width="100%" height="100%" fill="#499aa2"></rect>
<text x="50%" y="350" style="font-weight:normal;font-size:279px;font-family:'Nunito';text-anchor:middle;fill:#fff">E</text>
</svg>

After

Width:  |  Height:  |  Size: 352 B

BIN
tests/data/test.pdf Normal file

Binary file not shown.

View File

@ -22,11 +22,10 @@
*
*/
namespace Test;
namespace Test\Avatar;
use OC\Avatar;
use OC\AvatarManager;
use OC\Files\AppData\AppData;
use OC\Avatar\UserAvatar;
use OC\Avatar\AvatarManager;
use OC\User\Manager;
use OCP\Files\IAppData;
use OCP\Files\SimpleFS\ISimpleFolder;
@ -34,7 +33,6 @@ use OCP\IConfig;
use OCP\IL10N;
use OCP\ILogger;
use OCP\IUser;
use OCP\IUserManager;
/**
* Class AvatarManagerTest
@ -103,7 +101,7 @@ class AvatarManagerTest extends \Test\TestCase {
->with('valid-user')
->willReturn($folder);
$expected = new Avatar($folder, $this->l10n, $user, $this->logger, $this->config);
$expected = new UserAvatar($folder, $this->l10n, $user, $this->logger, $this->config);
$this->assertEquals($expected, $this->avatarManager->getAvatar('valid-user'));
}
@ -125,7 +123,7 @@ class AvatarManagerTest extends \Test\TestCase {
->with('valid-user')
->willReturn($folder);
$expected = new Avatar($folder, $this->l10n, $user, $this->logger, $this->config);
$expected = new UserAvatar($folder, $this->l10n, $user, $this->logger, $this->config);
$this->assertEquals($expected, $this->avatarManager->getAvatar('vaLid-USER'));
}
}

View File

@ -0,0 +1,80 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2018, Michael Weimann <mail@michael-weimann.eu>
*
* @author Michael Weimann <mail@michael-weimann.eu>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
namespace Test\Avatar;
use OC\Avatar\GuestAvatar;
use OCP\Files\SimpleFS\InMemoryFile;
use OCP\ILogger;
use PHPUnit\Framework\MockObject\MockObject;
use Test\TestCase;
/**
* This class provides test cases for the GuestAvatar class.
*
* @package Test\Avatar
*/
class GuestAvatarTest extends TestCase {
/**
* @var GuestAvatar
*/
private $guestAvatar;
/**
* Setups a guest avatar instance for tests.
*
* @before
* @return void
*/
public function setupGuestAvatar() {
/* @var MockObject|ILogger $logger */
$logger = $this->getMockBuilder(ILogger::class)->getMock();
$this->guestAvatar = new GuestAvatar('einstein', $logger);
}
/**
* Asserts that testGet() returns the expected avatar.
*
* For the test a static name "einstein" is used and
* the generated image is compared with an expected one.
*
* @return void
*/
public function testGet() {
$avatar = $this->guestAvatar->getFile(32);
self::assertInstanceOf(InMemoryFile::class, $avatar);
$expectedFile = file_get_contents(
__DIR__ . '/../../data/guest_avatar_einstein_32.svg'
);
self::assertEquals(trim($expectedFile), trim($avatar->getContent()));
}
/**
* Asserts that "testIsCustomAvatar" returns false for guests.
*
* @return void
*/
public function testIsCustomAvatar() {
self::assertFalse($this->guestAvatar->isCustomAvatar());
}
}

View File

@ -6,7 +6,7 @@
* See the COPYING-README file.
*/
namespace Test;
namespace Test\Avatar;
use OC\Files\SimpleFS\SimpleFolder;
use OC\User\User;
@ -18,11 +18,11 @@ use OCP\IConfig;
use OCP\IL10N;
use OCP\ILogger;
class AvatarTest extends \Test\TestCase {
class UserAvatarTest extends \Test\TestCase {
/** @var Folder | \PHPUnit_Framework_MockObject_MockObject */
private $folder;
/** @var \OC\Avatar */
/** @var \OC\Avatar\UserAvatar */
private $avatar;
/** @var \OC\User\User | \PHPUnit_Framework_MockObject_MockObject $user */
@ -41,7 +41,7 @@ class AvatarTest extends \Test\TestCase {
$this->user = $this->createMock(User::class);
$this->config = $this->createMock(IConfig::class);
$this->avatar = new \OC\Avatar(
$this->avatar = new \OC\Avatar\UserAvatar(
$this->folder,
$l,
$this->user,

View File

@ -0,0 +1,145 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2018, Michael Weimann <mail@michael-weimann.eu>
*
* @author Michael Weimann <mail@michael-weimann.eu>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
namespace Test\File\SimpleFS;
use OCP\Files\NotPermittedException;
use OCP\Files\SimpleFS\InMemoryFile;
use Test\TestCase;
/**
* This class provide test casesf or the InMemoryFile.
*
* @package Test\File\SimpleFS
*/
class InMemoryFileTest extends TestCase {
/**
* Holds a pdf file with know attributes for tests.
*
* @var InMemoryFile
*/
private $testPdf;
/**
* Sets the test file from "./resources/test.pdf".
*
* @before
* @return void
*/
public function setupTestPdf() {
$fileContents = file_get_contents(
__DIR__ . '/../../../data/test.pdf'
);
$this->testPdf = new InMemoryFile('test.pdf', $fileContents);
}
/**
* Asserts that putContent replaces the file contents.
*
* @return void
*/
public function testPutContent() {
$this->testPdf->putContent('test');
self::assertEquals('test', $this->testPdf->getContent());
}
/**
* Asserts that delete() doesn't rise an exception.
*
* @return void
*/
public function testDelete() {
$this->testPdf->delete();
// assert true, otherwise phpunit complains about not doing any assert
self::assertTrue(true);
}
/**
* Asserts that getName returns the name passed on file creation.
*
* @return void
*/
public function testGetName() {
self::assertEquals('test.pdf', $this->testPdf->getName());
}
/**
* Asserts that the file size is the size of the test file.
*
* @return void
*/
public function testGetSize() {
self::assertEquals(7083, $this->testPdf->getSize());
}
/**
* Asserts the file contents are the same than the original file contents.
*
* @return void
*/
public function testGetContent() {
self::assertEquals(
file_get_contents(__DIR__ . '/../../../data/test.pdf'),
$this->testPdf->getContent()
);
}
/**
* Asserts the test file modification time is an integer.
*
* @return void
*/
public function testGetMTime() {
self::assertTrue(is_int($this->testPdf->getMTime()));
}
/**
* Asserts the test file mime type is "application/json".
*
* @return void
*/
public function testGetMimeType() {
self::assertEquals('application/pdf', $this->testPdf->getMimeType());
}
/**
* Asserts that read() raises an NotPermittedException.
*
* @return void
*/
public function testRead() {
self::expectException(NotPermittedException::class);
$this->testPdf->read();
}
/**
* Asserts that write() raises an NotPermittedException.
*
* @return void
*/
public function testWrite() {
self::expectException(NotPermittedException::class);
$this->testPdf->write();
}
}

View File

@ -25,7 +25,7 @@ namespace Test\Repair;
use OCP\IConfig;
use OCP\Migration\IOutput;
use OC\AvatarManager;
use OC\Avatar\AvatarManager;
use OC\Repair\ClearGeneratedAvatarCache;
class ClearGeneratedAvatarCacheTest extends \Test\TestCase {

View File

@ -57,7 +57,7 @@ class ServerTest extends \Test\TestCase {
['AppManager', '\OCP\App\IAppManager'],
['AsyncCommandBus', '\OC\Command\AsyncBus'],
['AsyncCommandBus', '\OCP\Command\IBus'],
['AvatarManager', '\OC\AvatarManager'],
['AvatarManager', '\OC\Avatar\AvatarManager'],
['AvatarManager', '\OCP\IAvatarManager'],
['CategoryFetcher', CategoryFetcher::class],