зеркало из https://github.com/nextcloud/server.git
Implement guest avatar endpoint
Signed-off-by: Michael Weimann <mail@michael-weimann.eu>
This commit is contained in:
Родитель
b69b17f29f
Коммит
bf1253cb49
|
@ -42,7 +42,6 @@ use OCP\IL10N;
|
|||
use OCP\IRequest;
|
||||
use OCP\IUserManager;
|
||||
use OCP\IUserSession;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
|
||||
/**
|
||||
* Class AvatarController
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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'],
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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';
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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'
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -78,7 +78,7 @@ class AvatarControllerTest extends \Test\TestCase {
|
|||
private $request;
|
||||
/** @var TimeFactory|\PHPUnit_Framework_MockObject_MockObject */
|
||||
private $timeFactory;
|
||||
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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>
|
После Ширина: | Высота: | Размер: 352 B |
Двоичный файл не отображается.
|
@ -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'));
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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,
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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],
|
||||
|
|
Загрузка…
Ссылка в новой задаче