From 8e3382cedaecf551c3d6991feae08c57f66496eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20Molakvo=C3=A6=20=28skjnldsv=29?= Date: Mon, 16 Apr 2018 10:48:34 +0200 Subject: [PATCH 01/12] Fix avatar generator centering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Molakvoæ (skjnldsv) --- lib/private/Avatar.php | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/lib/private/Avatar.php b/lib/private/Avatar.php index 53dea5b966..9524b36f8e 100644 --- a/lib/private/Avatar.php +++ b/lib/private/Avatar.php @@ -275,12 +275,9 @@ class Avatar implements IAvatar { $font = __DIR__ . '/../../core/fonts/OpenSans-Semibold.ttf'; $fontSize = $size * 0.4; - $box = imagettfbbox($fontSize, 0, $font, $text); - $x = ($size - ($box[2] - $box[0])) / 2; - $y = ($size - ($box[1] - $box[7])) / 2; - $x += 1; - $y -= $box[7]; + list($x, $y) = $this->imageTTFCenter($im, $text, $font, $fontSize); + imagettftext($im, $fontSize, 0, $x, $y, $white, $font, $text); ob_start(); @@ -291,6 +288,35 @@ class Avatar implements IAvatar { return $data; } + /** + * Calculate real image ttf center + * + * @param resource $image + * @param string $text text string + * @param string $font font path + * @param int $size font size + * @param int $angle + * @return Array + */ + protected function imageTTFCenter($image, string $text, string $font, int $size, $angle = 0): Array { + // Image width & height + $xi = imagesx($image); + $yi = imagesy($image); + + // bounding box + $box = imagettfbbox($size, $angle, $font, $text); + + // imagettfbbox can return negative int + $xr = abs(max($box[2], $box[4])); + $yr = abs(max($box[5], $box[7])); + + // calculate bottom left placement + $x = intval(($xi - $xr) / 2); + $y = intval(($yi + $yr) / 2); + + return array($x, $y); + } + /** * Calculate steps between two Colors * @param object Color $steps start color From adf3856d35945790fed5f7934d8c154d1f01b377 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20Molakvo=C3=A6=20=28skjnldsv=29?= Date: Sat, 5 May 2018 18:17:09 +0200 Subject: [PATCH 02/12] Return Svg avatars MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Molakvoæ (skjnldsv) --- core/Controller/AvatarController.php | 46 ++++++++++++--- core/routes.php | 1 + lib/private/Avatar.php | 85 ++++++++++++++++++++-------- 3 files changed, 98 insertions(+), 34 deletions(-) diff --git a/core/Controller/AvatarController.php b/core/Controller/AvatarController.php index 11d81ab00b..6f0cf03d8e 100644 --- a/core/Controller/AvatarController.php +++ b/core/Controller/AvatarController.php @@ -41,6 +41,7 @@ use OCP\IL10N; use OCP\IRequest; use OCP\IUserManager; use OCP\IUserSession; +use OCP\AppFramework\Http\DataResponse; /** * Class AvatarController @@ -113,6 +114,20 @@ class AvatarController extends Controller { + + /** + * @NoAdminRequired + * @NoCSRFRequired + * @NoSameSiteCookieRequired + * @PublicPage + * + * Shortcut to getAvatar + */ + public function getAvatarPng($userId, $size) { + return $this->getAvatar($userId, $size, true); + } + + /** * @NoAdminRequired * @NoCSRFRequired @@ -121,24 +136,37 @@ class AvatarController extends Controller { * * @param string $userId * @param int $size + * @param bool $png return png or not * @return JSONResponse|FileDisplayResponse */ - public function getAvatar($userId, $size) { + public function getAvatar($userId, $size, bool $png = false) { if ($size > 2048) { $size = 2048; } elseif ($size <= 0) { $size = 64; } - try { - $avatar = $this->avatarManager->getAvatar($userId)->getFile($size); - $resp = new FileDisplayResponse($avatar, + if ($png === false) { + $avatar = $this->avatarManager->getAvatar($userId)->getAvatarVector($size); + $resp = new DataDisplayResponse( + $avatar, Http::STATUS_OK, - ['Content-Type' => $avatar->getMimeType()]); - } catch (\Exception $e) { - $resp = new Http\Response(); - $resp->setStatus(Http::STATUS_NOT_FOUND); - return $resp; + ['Content-Type' => 'image/svg+xml' + ]); + } else { + + try { + $avatar = $this->avatarManager->getAvatar($userId)->getFile($size); + $resp = new FileDisplayResponse( + $avatar, + Http::STATUS_OK, + ['Content-Type' => $avatar->getMimeType() + ]); + } catch (\Exception $e) { + $resp = new Http\Response(); + $resp->setStatus(Http::STATUS_NOT_FOUND); + return $resp; + } } // Cache for 30 minutes diff --git a/core/routes.php b/core/routes.php index cc1bd34d89..dd35638a7e 100644 --- a/core/routes.php +++ b/core/routes.php @@ -42,6 +42,7 @@ $application->registerRoutes($this, [ ['name' => 'lost#setPassword', 'url' => '/lostpassword/set/{token}/{userId}', 'verb' => 'POST'], ['name' => 'user#getDisplayNames', 'url' => '/displaynames', 'verb' => 'POST'], ['name' => 'avatar#getAvatar', 'url' => '/avatar/{userId}/{size}', 'verb' => 'GET'], + ['name' => 'avatar#getAvatarPng', 'url' => '/avatar/{userId}/{size}/png', 'verb' => 'GET'], ['name' => 'avatar#deleteAvatar', 'url' => '/avatar/', 'verb' => 'DELETE'], ['name' => 'avatar#postCroppedAvatar', 'url' => '/avatar/cropped', 'verb' => 'POST'], ['name' => 'avatar#getTmpAvatar', 'url' => '/avatar/tmp', 'verb' => 'GET'], diff --git a/lib/private/Avatar.php b/lib/private/Avatar.php index 9524b36f8e..07e8f2522c 100644 --- a/lib/private/Avatar.php +++ b/lib/private/Avatar.php @@ -30,7 +30,6 @@ namespace OC; -use OC\User\User; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; use OCP\Files\SimpleFS\ISimpleFile; @@ -39,8 +38,9 @@ use OCP\IAvatar; use OCP\IConfig; use OCP\IImage; use OCP\IL10N; -use OC_Image; use OCP\ILogger; +use OC\User\User; +use OC_Image; /** * This class gets and sets users avatars. @@ -57,6 +57,12 @@ class Avatar implements IAvatar { private $logger; /** @var IConfig */ private $config; + /** @var string */ + private $svgTemplate = ' + + + {letter} + '; /** * constructor @@ -68,10 +74,10 @@ class Avatar implements IAvatar { * @param IConfig $config */ public function __construct(ISimpleFolder $folder, - IL10N $l, - $user, - ILogger $logger, - IConfig $config) { + IL10N $l, + $user, + ILogger $logger, + IConfig $config) { $this->folder = $folder; $this->l = $l; $this->user = $user; @@ -82,7 +88,7 @@ class Avatar implements IAvatar { /** * @inheritdoc */ - public function get ($size = 64) { + public function get($size = 64) { try { $file = $this->getFile($size); } catch (NotFoundException $e) { @@ -111,17 +117,17 @@ class Avatar implements IAvatar { * @throws \Exception if the provided image is not valid * @throws NotSquareException if the image is not square * @return void - */ - public function set ($data) { + */ + public function set($data) { - if($data instanceOf IImage) { + 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)) { + } elseif (is_resource($data)) { $img->loadFromFileHandle($data); } else { try { @@ -154,7 +160,7 @@ class Avatar implements IAvatar { } $this->remove(); - $file = $this->folder->newFile('avatar.'.$type); + $file = $this->folder->newFile('avatar.' . $type); $file->putContent($data); try { @@ -165,17 +171,17 @@ class Avatar implements IAvatar { // } $this->user->triggerChange('avatar', $file); - } + } /** * remove the users avatar * @return void - */ - public function remove () { + */ + 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); + (int) $this->config->getUserValue($this->user->getUID(), 'avatar', 'version', 0) + 1); foreach ($avatars as $avatar) { $avatar->delete(); @@ -235,7 +241,7 @@ class Avatar implements IAvatar { } - if($this->config->getUserValue($this->user->getUID(), 'avatar', 'generated', null) === null) { + 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); } @@ -257,6 +263,35 @@ class Avatar implements IAvatar { } throw new NotFoundException; } + + /** + * https://github.com/sebdesign/cap-height -- for 500px height + * Open Sans cap-height is 0.72 and we want a 200px caps height size (0.4 letter-to-total-height ratio, 500*0.4=200). 200/0.72 = 278px. + * Since we start from the baseline (text-anchor) we need to shift the y axis by 100px (half the caps height): 500/2+100=350 --> + * {size} = 500 + * {fill} = hex color to fill + * {y} = top to bottom baseline text-anchor y position + * {font} = font size + * {letter} = Letter to display + * + * Generate SVG avatar + * @return string + * + */ + public function getAvatarVector($size) { + $userDisplayName = $this->user->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'); + $font = round($size * 0.4); + $fontSize = round($font / 0.72); + $y = round($size/2 + $font/2); + $toReplace = ['{size}', '{fill}', '{y}', '{font}', '{letter}']; + + return str_replace($toReplace, [$size, $bgHEX, $y, $fontSize, $letter], $this->svgTemplate); + } /** * @param string $userDisplayName @@ -295,10 +330,10 @@ class Avatar implements IAvatar { * @param string $text text string * @param string $font font path * @param int $size font size - * @param int $angle + * @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); @@ -330,6 +365,7 @@ class Avatar implements IAvatar { $step[2] = ($ends[1]->b - $ends[0]->b) / $steps; return $step; } + /** * Convert a string to an integer evenly * @param string $hash the text to parse @@ -344,12 +380,11 @@ class Avatar implements IAvatar { $r = intval($color1->r + ($step[0] * $i)); $g = intval($color1->g + ($step[1] * $i)); $b = intval($color1->b + ($step[2] * $i)); - $palette[] = new Color($r, $g, $b); + $palette[] = new Color($r, $g, $b); } return $palette; } - /** * Convert a string to an integer evenly * @param string $hash the text to parse @@ -361,7 +396,7 @@ class Avatar implements IAvatar { $result = array(); // Splitting evenly the string - for ($i=0; $i< strlen($hash); $i++) { + for ($i = 0; $i < strlen($hash); $i++) { // chars in md5 goes up to f, hex:16 $result[] = intval(substr($hash, $i, 1), 16) % 16; } @@ -373,12 +408,11 @@ class Avatar implements IAvatar { return intval($final % $maximum); } - /** * @param string $text * @return Color Object containting r g b int in the range [0, 255] */ - function avatarBackgroundColor($text) { + public function avatarBackgroundColor($text) { $hash = preg_replace('/[^0-9a-f]+/', '', $text); $hash = md5($hash); @@ -387,6 +421,7 @@ class Avatar implements IAvatar { $red = new Color(182, 70, 157); $yellow = new Color(221, 203, 85); $blue = new Color(0, 130, 201); // Nextcloud blue + // Number of steps to go from a color to another // 3 colors * 6 will result in 18 generated colors $steps = 6; @@ -397,7 +432,7 @@ class Avatar implements IAvatar { $finalPalette = array_merge($palette1, $palette2, $palette3); - return $finalPalette[$this->hashToInt($hash, $steps * 3 )]; + return $finalPalette[$this->hashToInt($hash, $steps * 3)]; } public function userChanged($feature, $oldValue, $newValue) { From c1766b2abc82a21b578824fe24dc38b007163424 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20Molakvo=C3=A6=20=28skjnldsv=29?= Date: Sat, 5 May 2018 18:19:42 +0200 Subject: [PATCH 03/12] Use base image size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Molakvoæ (skjnldsv) --- lib/private/Avatar.php | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/lib/private/Avatar.php b/lib/private/Avatar.php index 07e8f2522c..2b0fb3d266 100644 --- a/lib/private/Avatar.php +++ b/lib/private/Avatar.php @@ -57,11 +57,18 @@ class Avatar implements IAvatar { private $logger; /** @var IConfig */ private $config; - /** @var string */ + + /** + * https://github.com/sebdesign/cap-height -- for 500px height + * Open Sans cap-height is 0.72 and we want a 200px caps height size (0.4 letter-to-total-height ratio, 500*0.4=200). 200/0.72 = 278px. + * Since we start from the baseline (text-anchor) we need to shift the y axis by 100px (half the caps height): 500/2+100=350 + * + * @var string + */ private $svgTemplate = ' - + - {letter} + {letter} '; /** @@ -265,13 +272,8 @@ class Avatar implements IAvatar { } /** - * https://github.com/sebdesign/cap-height -- for 500px height - * Open Sans cap-height is 0.72 and we want a 200px caps height size (0.4 letter-to-total-height ratio, 500*0.4=200). 200/0.72 = 278px. - * Since we start from the baseline (text-anchor) we need to shift the y axis by 100px (half the caps height): 500/2+100=350 --> * {size} = 500 * {fill} = hex color to fill - * {y} = top to bottom baseline text-anchor y position - * {font} = font size * {letter} = Letter to display * * Generate SVG avatar @@ -283,14 +285,10 @@ class Avatar implements IAvatar { $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'); - $font = round($size * 0.4); - $fontSize = round($font / 0.72); - $y = round($size/2 + $font/2); - $toReplace = ['{size}', '{fill}', '{y}', '{font}', '{letter}']; - - return str_replace($toReplace, [$size, $bgHEX, $y, $fontSize, $letter], $this->svgTemplate); + + $toReplace = ['{size}', '{fill}', '{letter}']; + return str_replace($toReplace, [$size, $bgHEX, $letter], $this->svgTemplate); } /** From eea6f74ca47e94f6b740dbd33d10e22faf4221a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20Molakvo=C3=A6=20=28skjnldsv=29?= Date: Mon, 7 May 2018 08:48:38 +0200 Subject: [PATCH 04/12] Controller tests fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Molakvoæ (skjnldsv) --- core/Controller/AvatarController.php | 22 +++- lib/private/Avatar.php | 5 +- lib/public/IAvatar.php | 104 ++++++++++-------- .../Core/Controller/AvatarControllerTest.php | 58 ++++++++-- 4 files changed, 127 insertions(+), 62 deletions(-) diff --git a/core/Controller/AvatarController.php b/core/Controller/AvatarController.php index 6f0cf03d8e..e5a8e4fe29 100644 --- a/core/Controller/AvatarController.php +++ b/core/Controller/AvatarController.php @@ -8,6 +8,7 @@ * @author Roeland Jago Douma * @author Thomas Müller * @author Vincent Petry + * @author John Molakvoæ * * @license AGPL-3.0 * @@ -146,13 +147,22 @@ class AvatarController extends Controller { $size = 64; } + // Serve png as a fallback only if ($png === false) { - $avatar = $this->avatarManager->getAvatar($userId)->getAvatarVector($size); - $resp = new DataDisplayResponse( - $avatar, - Http::STATUS_OK, - ['Content-Type' => 'image/svg+xml' - ]); + + try { + $avatar = $this->avatarManager->getAvatar($userId)->getAvatarVector($size); + $resp = new DataDisplayResponse( + $avatar, + Http::STATUS_OK, + ['Content-Type' => 'image/svg+xml' + ]); + } catch (\Exception $e) { + $resp = new Http\Response(); + $resp->setStatus(Http::STATUS_NOT_FOUND); + return $resp; + } + } else { try { diff --git a/lib/private/Avatar.php b/lib/private/Avatar.php index 2b0fb3d266..6858346f22 100644 --- a/lib/private/Avatar.php +++ b/lib/private/Avatar.php @@ -11,6 +11,7 @@ * @author Robin Appelman * @author Roeland Jago Douma * @author Thomas Müller + * @author John Molakvoæ * * @license AGPL-3.0 * @@ -280,7 +281,7 @@ class Avatar implements IAvatar { * @return string * */ - public function getAvatarVector($size) { + public function getAvatarVector(int $size): string { $userDisplayName = $this->user->getDisplayName(); $bgRGB = $this->avatarBackgroundColor($userDisplayName); @@ -410,7 +411,7 @@ class Avatar implements IAvatar { * @param string $text * @return Color Object containting r g b int in the range [0, 255] */ - public function avatarBackgroundColor($text) { + public function avatarBackgroundColor(string $text) { $hash = preg_replace('/[^0-9a-f]+/', '', $text); $hash = md5($hash); diff --git a/lib/public/IAvatar.php b/lib/public/IAvatar.php index a6731b63be..b5aa65f837 100644 --- a/lib/public/IAvatar.php +++ b/lib/public/IAvatar.php @@ -8,6 +8,7 @@ * @author Robin Appelman * @author Roeland Jago Douma * @author Thomas Müller + * @author John Molakvoæ * * @license AGPL-3.0 * @@ -26,6 +27,7 @@ */ namespace OCP; + use OCP\Files\File; use OCP\Files\NotFoundException; @@ -35,52 +37,66 @@ use OCP\Files\NotFoundException; */ interface IAvatar { - /** - * get the users avatar - * @param int $size size in px of the avatar, avatars are square, defaults to 64, -1 can be used to not scale the image - * @return boolean|\OCP\IImage containing the avatar or false if there's no image - * @since 6.0.0 - size of -1 was added in 9.0.0 - */ - public function get($size = 64); + /** + * get the users avatar + * @param int $size size in px of the avatar, avatars are square, defaults to 64, -1 can be used to not scale the image + * @return boolean|\OCP\IImage containing the avatar or false if there's no image + * @since 6.0.0 - size of -1 was added in 9.0.0 + */ + public function get($size = 64); + + /** + * Check if an avatar exists for the user + * + * @return bool + * @since 8.1.0 + */ + public function exists(); + + /** + * sets the users avatar + * @param \OCP\IImage|resource|string $data An image object, imagedata or path to set a new avatar + * @throws \Exception if the provided file is not a jpg or png image + * @throws \Exception if the provided image is not valid + * @throws \OC\NotSquareException if the image is not square + * @return void + * @since 6.0.0 + */ + public function set($data); + + /** + * remove the users avatar + * @return void + * @since 6.0.0 + */ + public function remove(); + + /** + * Get the file of the avatar + * @param int $size -1 can be used to not scale the image + * @return File + * @throws NotFoundException + * @since 9.0.0 + */ + public function getFile($size); + + /** + * Generate SVG avatar + * @param int $size -1 can be used to not scale the image + * @return string + * @since 14.0.0 + */ + public function getAvatarVector(int $size): string; /** - * Check if an avatar exists for the user - * - * @return bool - * @since 8.1.0 + * @param string $text + * @return Color Object containting r g b int in the range [0, 255] */ - public function exists(); + public function avatarBackgroundColor(string $text); - /** - * sets the users avatar - * @param \OCP\IImage|resource|string $data An image object, imagedata or path to set a new avatar - * @throws \Exception if the provided file is not a jpg or png image - * @throws \Exception if the provided image is not valid - * @throws \OC\NotSquareException if the image is not square - * @return void - * @since 6.0.0 - */ - public function set($data); - - /** - * remove the users avatar - * @return void - * @since 6.0.0 - */ - public function remove(); - - /** - * Get the file of the avatar - * @param int $size -1 can be used to not scale the image - * @return File - * @throws NotFoundException - * @since 9.0.0 - */ - public function getFile($size); - - /** - * Handle a changed user - * @since 13.0.0 - */ - public function userChanged($feature, $oldValue, $newValue); + /** + * Handle a changed user + * @since 13.0.0 + */ + public function userChanged($feature, $oldValue, $newValue); } diff --git a/tests/Core/Controller/AvatarControllerTest.php b/tests/Core/Controller/AvatarControllerTest.php index 3194d67190..6d52a2c7eb 100644 --- a/tests/Core/Controller/AvatarControllerTest.php +++ b/tests/Core/Controller/AvatarControllerTest.php @@ -94,6 +94,7 @@ class AvatarControllerTest extends \Test\TestCase { $this->timeFactory = $this->getMockBuilder('OC\AppFramework\Utility\TimeFactory')->getMock(); $this->avatarMock = $this->getMockBuilder('OCP\IAvatar')->getMock(); + $this->color = new \OC\Color(0, 130, 201); $this->userMock = $this->getMockBuilder(IUser::class)->getMock(); $this->avatarController = new AvatarController( @@ -119,6 +120,8 @@ class AvatarControllerTest extends \Test\TestCase { $this->avatarFile->method('getContent')->willReturn('image data'); $this->avatarFile->method('getMimeType')->willReturn('image type'); $this->avatarFile->method('getEtag')->willReturn('my etag'); + + $this->avatarMock->method('avatarBackgroundColor')->willReturn($this->color); } public function tearDown() { @@ -130,10 +133,21 @@ class AvatarControllerTest extends \Test\TestCase { */ public function testGetAvatarNoAvatar() { $this->avatarManager->method('getAvatar')->willReturn($this->avatarMock); - $this->avatarMock->method('getFile')->will($this->throwException(new NotFoundException())); + $this->avatarMock->method('getAvatarVector')->willReturn(''); $response = $this->avatarController->getAvatar('userId', 32); - //Comment out until JS is fixed + $this->assertEquals(Http::STATUS_OK, $response->getStatus()); + $this->assertEquals('', $response->getData()); + } + + /** + * Fetch a png avatar if a user has no avatar + */ + public function testGetPngAvatarNoAvatar() { + $this->avatarManager->method('getAvatar')->willReturn($this->avatarMock); + $this->avatarMock->method('getFile')->will($this->throwException(new NotFoundException())); + $response = $this->avatarController->getAvatar('userId', 32, true); + $this->assertEquals(Http::STATUS_NOT_FOUND, $response->getStatus()); } @@ -141,11 +155,27 @@ class AvatarControllerTest extends \Test\TestCase { * Fetch the user's avatar */ public function testGetAvatar() { - $this->avatarMock->method('getFile')->willReturn($this->avatarFile); + $this->avatarMock->method('getAvatarVector')->willReturn(''); $this->avatarManager->method('getAvatar')->with('userId')->willReturn($this->avatarMock); $response = $this->avatarController->getAvatar('userId', 32); + $this->assertEquals(Http::STATUS_OK, $response->getStatus()); + $this->assertArrayHasKey('Content-Type', $response->getHeaders()); + $this->assertEquals('image/svg+xml', $response->getHeaders()['Content-Type']); + + $this->assertEquals('', $response->getData()); + } + + /** + * Fetch the user's avatar + */ + public function testGetPngAvatar() { + $this->avatarMock->method('getFile')->willReturn($this->avatarFile); + $this->avatarManager->method('getAvatar')->with('userId')->willReturn($this->avatarMock); + + $response = $this->avatarController->getAvatar('userId', 32, true); + $this->assertEquals(Http::STATUS_OK, $response->getStatus()); $this->assertArrayHasKey('Content-Type', $response->getHeaders()); $this->assertEquals('image type', $response->getHeaders()['Content-Type']); @@ -171,7 +201,7 @@ class AvatarControllerTest extends \Test\TestCase { /** * Make sure we get the correct size */ - public function testGetAvatarSize() { + public function testGetPngAvatarSize() { $this->avatarMock->expects($this->once()) ->method('getFile') ->with($this->equalTo(32)) @@ -179,13 +209,13 @@ class AvatarControllerTest extends \Test\TestCase { $this->avatarManager->method('getAvatar')->willReturn($this->avatarMock); - $this->avatarController->getAvatar('userId', 32); + $this->avatarController->getAvatar('userId', 32, true); } /** * We cannot get avatars that are 0 or negative */ - public function testGetAvatarSizeMin() { + public function testGetPngAvatarSizeMin() { $this->avatarMock->expects($this->once()) ->method('getFile') ->with($this->equalTo(64)) @@ -193,13 +223,13 @@ class AvatarControllerTest extends \Test\TestCase { $this->avatarManager->method('getAvatar')->willReturn($this->avatarMock); - $this->avatarController->getAvatar('userId', 0); + $this->avatarController->getAvatar('userId', 0, true); } /** * We do not support avatars larger than 2048*2048 */ - public function testGetAvatarSizeMax() { + public function testGetPngAvatarSizeMax() { $this->avatarMock->expects($this->once()) ->method('getFile') ->with($this->equalTo(2048)) @@ -207,7 +237,7 @@ class AvatarControllerTest extends \Test\TestCase { $this->avatarManager->method('getAvatar')->willReturn($this->avatarMock); - $this->avatarController->getAvatar('userId', 2049); + $this->avatarController->getAvatar('userId', 2049, true); } /** @@ -486,7 +516,6 @@ class AvatarControllerTest extends \Test\TestCase { $this->assertEquals($expectedResponse, $this->avatarController->postCroppedAvatar(['x' => 0, 'y' => 0, 'w' => 10, 'h' => 11])); } - /** * Check for proper reply on proper crop argument */ @@ -501,4 +530,13 @@ class AvatarControllerTest extends \Test\TestCase { $this->assertEquals('File is too big', $response->getData()['data']['message']); } + /** + * Test get Avatar BG colour algorithm + */ + public function testAvatarBackgroundColor() { + $bgRGB = $this->avatarMock->avatarBackgroundColor('TestBlue'); + $this->assertEquals($bgRGB, $this->color); + $this->assertEquals(sprintf("%02x%02x%02x", $bgRGB->r, $bgRGB->g, $bgRGB->b), '0082c9'); + } + } From ed4309ce41557d39576108731631ac59aa6e3120 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20Molakvo=C3=A6=20=28skjnldsv=29?= Date: Mon, 7 May 2018 10:50:33 +0200 Subject: [PATCH 05/12] Unify colour algorithm output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Molakvoæ (skjnldsv) --- core/js/placeholder.js | 9 ++++++--- lib/private/Avatar.php | 16 +++++++++++----- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/core/js/placeholder.js b/core/js/placeholder.js index a0dfe8491d..81f0b12e61 100644 --- a/core/js/placeholder.js +++ b/core/js/placeholder.js @@ -62,13 +62,16 @@ (function ($) { String.prototype.toRgb = function() { - var hash = this.toLowerCase().replace(/[^0-9a-f]+/g, ''); + // Normalize hash + var hash = this.toLowerCase(); // Already a md5 hash? - if( !hash.match(/^[0-9a-f]{32}$/g) ) { + if( hash.match(/^([0-9a-f]{4}-?){8}$/) === null ) { hash = md5(hash); } + hash = hash.replace(/[^0-9a-f]/g, ''); + function Color(r,g,b) { this.r = r; this.g = g; @@ -116,7 +119,7 @@ var result = Array(); // Splitting evenly the string - for (var i in hash) { + for (var i=0; i Date: Mon, 7 May 2018 14:09:31 +0200 Subject: [PATCH 06/12] Added new tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Molakvoæ (skjnldsv) --- tests/lib/AvatarTest.php | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests/lib/AvatarTest.php b/tests/lib/AvatarTest.php index 4914c02bd1..a9b798fe5d 100644 --- a/tests/lib/AvatarTest.php +++ b/tests/lib/AvatarTest.php @@ -48,6 +48,9 @@ class AvatarTest extends \Test\TestCase { $this->createMock(ILogger::class), $this->config ); + + // abcdefghi is a convenient name that our algorithm convert to our nextcloud blue 0082c9 + $this->user->method('getDisplayName')->willReturn('abcdefghi'); } public function testGetNoAvatar() { @@ -226,4 +229,37 @@ class AvatarTest extends \Test\TestCase { $this->avatar->set($image->data()); } + public function testGenerateSvgAvatar() { + $avatar = $this->avatar->getAvatarVector(64); + + $svg = ' + + + A + '; + $this->assertEquals($avatar, $svg); + } + + public function testHashToInt() { + $hashToInt = $this->invokePrivate($this->avatar, 'hashToInt', ['abcdef', 18]); + $this->assertTrue(gettype($hashToInt) === 'integer'); + } + + public function testMixPalette() { + $colorFrom = new \OC\Color(0,0,0); + $colorTo = new \OC\Color(6,12,18); + $steps = 6; + $palette = $this->invokePrivate($this->avatar, 'mixPalette', [$steps, $colorFrom, $colorTo]); + foreach($palette as $j => $color) { + // calc increment + $incR = $colorTo->r / $steps * $j; + $incG = $colorTo->g / $steps * $j; + $incB = $colorTo->b / $steps * $j; + // ensure everything is equal + $this->assertEquals($color, new \OC\Color($incR, $incG,$incB)); + } + $hashToInt = $this->invokePrivate($this->avatar, 'hashToInt', ['abcdef', 18]); + $this->assertTrue(gettype($hashToInt) === 'integer'); + } + } From eb3f43a0b5a96db92d63b0572104f3b4707ed664 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20Molakvo=C3=A6=20=28skjnldsv=29?= Date: Mon, 7 May 2018 14:12:47 +0200 Subject: [PATCH 07/12] Fixed since requirement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Molakvoæ (skjnldsv) --- lib/public/IAvatar.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/public/IAvatar.php b/lib/public/IAvatar.php index b5aa65f837..6b4f00d480 100644 --- a/lib/public/IAvatar.php +++ b/lib/public/IAvatar.php @@ -91,6 +91,7 @@ interface IAvatar { /** * @param string $text * @return Color Object containting r g b int in the range [0, 255] + * @since 14.0.0 */ public function avatarBackgroundColor(string $text); From 4432cfd445d2dea2792c0cc2550445506ba8a18a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20Molakvo=C3=A6=20=28skjnldsv=29?= Date: Sat, 19 May 2018 08:47:29 +0200 Subject: [PATCH 08/12] Embedded font MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Molakvoæ (skjnldsv) --- lib/private/Avatar.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/private/Avatar.php b/lib/private/Avatar.php index 0ea2b82f5e..0164d73a2e 100644 --- a/lib/private/Avatar.php +++ b/lib/private/Avatar.php @@ -68,6 +68,14 @@ class Avatar implements IAvatar { */ private $svgTemplate = ' + + + {letter} '; @@ -287,9 +295,10 @@ class Avatar implements IAvatar { $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'); + $font = \OC::$WEBROOT.'/core/fonts/OpenSans-Semibold.ttf'; - $toReplace = ['{size}', '{fill}', '{letter}']; - return str_replace($toReplace, [$size, $bgHEX, $letter], $this->svgTemplate); + $toReplace = ['{size}', '{fill}', '{letter}', '{font}']; + return str_replace($toReplace, [$size, $bgHEX, $letter, $font], $this->svgTemplate); } /** From 156da29ceade106176e2288ef391c4cb2006d800 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20Molakvo=C3=A6=20=28skjnldsv=29?= Date: Mon, 28 May 2018 09:44:10 +0200 Subject: [PATCH 09/12] Avatar imagick bump MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Molakvoæ (skjnldsv) --- core/Controller/AvatarController.php | 62 +++++-------------- lib/private/Avatar.php | 53 +++++++++++----- lib/public/IAvatar.php | 8 --- .../Core/Controller/AvatarControllerTest.php | 58 +++-------------- tests/lib/AvatarTest.php | 2 +- 5 files changed, 63 insertions(+), 120 deletions(-) diff --git a/core/Controller/AvatarController.php b/core/Controller/AvatarController.php index e5a8e4fe29..94c837888d 100644 --- a/core/Controller/AvatarController.php +++ b/core/Controller/AvatarController.php @@ -113,22 +113,6 @@ class AvatarController extends Controller { } - - - - /** - * @NoAdminRequired - * @NoCSRFRequired - * @NoSameSiteCookieRequired - * @PublicPage - * - * Shortcut to getAvatar - */ - public function getAvatarPng($userId, $size) { - return $this->getAvatar($userId, $size, true); - } - - /** * @NoAdminRequired * @NoCSRFRequired @@ -137,46 +121,28 @@ class AvatarController extends Controller { * * @param string $userId * @param int $size - * @param bool $png return png or not * @return JSONResponse|FileDisplayResponse */ - public function getAvatar($userId, $size, bool $png = false) { + public function getAvatar($userId, $size) { + // min/max size if ($size > 2048) { $size = 2048; } elseif ($size <= 0) { $size = 64; } - // Serve png as a fallback only - if ($png === false) { - - try { - $avatar = $this->avatarManager->getAvatar($userId)->getAvatarVector($size); - $resp = new DataDisplayResponse( - $avatar, - Http::STATUS_OK, - ['Content-Type' => 'image/svg+xml' - ]); - } catch (\Exception $e) { - $resp = new Http\Response(); - $resp->setStatus(Http::STATUS_NOT_FOUND); - return $resp; - } - - } else { - - try { - $avatar = $this->avatarManager->getAvatar($userId)->getFile($size); - $resp = new FileDisplayResponse( - $avatar, - Http::STATUS_OK, - ['Content-Type' => $avatar->getMimeType() - ]); - } catch (\Exception $e) { - $resp = new Http\Response(); - $resp->setStatus(Http::STATUS_NOT_FOUND); - return $resp; - } + try { + $avatar = $this->avatarManager->getAvatar($userId)->getFile($size); + $resp = new FileDisplayResponse( + $avatar, + Http::STATUS_OK, + ['Content-Type' => $avatar->getMimeType() + ]); + } catch (\Exception $e) { + var_dump($e); + $resp = new Http\Response(); + $resp->setStatus(Http::STATUS_NOT_FOUND); + return $resp; } // Cache for 30 minutes diff --git a/lib/private/Avatar.php b/lib/private/Avatar.php index 0164d73a2e..270f69a96a 100644 --- a/lib/private/Avatar.php +++ b/lib/private/Avatar.php @@ -42,6 +42,7 @@ use OCP\IL10N; use OCP\ILogger; use OC\User\User; use OC_Image; +use Imagick; /** * This class gets and sets users avatars. @@ -66,16 +67,8 @@ class Avatar implements IAvatar { * * @var string */ - private $svgTemplate = ' + private $svgTemplate = ' - - - {letter} '; @@ -213,7 +206,9 @@ class Avatar implements IAvatar { try { $ext = $this->getExtension(); } catch (NotFoundException $e) { - $data = $this->generateAvatar($this->user->getDisplayName(), 1024); + if (!$data = $this->generateAvatarFromSvg(1024)) { + $data = $this->generateAvatar($this->user->getDisplayName(), 1024); + } $avatar = $this->folder->newFile('avatar.png'); $avatar->putContent($data); $ext = 'png'; @@ -236,7 +231,9 @@ class Avatar implements IAvatar { } if ($this->folder->fileExists('generated')) { - $data = $this->generateAvatar($this->user->getDisplayName(), $size); + if (!$data = $this->generateAvatarFromSvg($size)) { + $data = $this->generateAvatar($this->user->getDisplayName(), $size); + } } else { $avatar = new OC_Image(); @@ -289,19 +286,45 @@ class Avatar implements IAvatar { * @return string * */ - public function getAvatarVector(int $size): string { + private function getAvatarVector(int $size): string { $userDisplayName = $this->user->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'); - $font = \OC::$WEBROOT.'/core/fonts/OpenSans-Semibold.ttf'; - $toReplace = ['{size}', '{fill}', '{letter}', '{font}']; - return str_replace($toReplace, [$size, $bgHEX, $letter, $font], $this->svgTemplate); + $toReplace = ['{size}', '{fill}', '{letter}']; + return str_replace($toReplace, [$size, $bgHEX, $letter], $this->svgTemplate); } /** + * Generate png avatar from svg with Imagick + * + * @param int $size + * @return string + */ + private function generateAvatarFromSvg(int $size) { + if (!extension_loaded('imagick')) { + return false; + } + try { + $font = __DIR__ . '/../../core/fonts/OpenSans-Semibold.ttf'; + $svg = $this->getAvatarVector($size); + $avatar = new Imagick(); + $avatar->setFont($font); + $avatar->readImageBlob($svg); + $avatar->setImageFormat('png'); + $image = new OC_Image(); + $image->loadFromData($avatar); + return $image->data(); + } catch (\Exception $e) { + return false; + } + } + + /** + * Generate png avatar with GD + * * @param string $userDisplayName * @param int $size * @return string diff --git a/lib/public/IAvatar.php b/lib/public/IAvatar.php index 6b4f00d480..54d703a502 100644 --- a/lib/public/IAvatar.php +++ b/lib/public/IAvatar.php @@ -80,14 +80,6 @@ interface IAvatar { */ public function getFile($size); - /** - * Generate SVG avatar - * @param int $size -1 can be used to not scale the image - * @return string - * @since 14.0.0 - */ - public function getAvatarVector(int $size): string; - /** * @param string $text * @return Color Object containting r g b int in the range [0, 255] diff --git a/tests/Core/Controller/AvatarControllerTest.php b/tests/Core/Controller/AvatarControllerTest.php index 6d52a2c7eb..3194d67190 100644 --- a/tests/Core/Controller/AvatarControllerTest.php +++ b/tests/Core/Controller/AvatarControllerTest.php @@ -94,7 +94,6 @@ class AvatarControllerTest extends \Test\TestCase { $this->timeFactory = $this->getMockBuilder('OC\AppFramework\Utility\TimeFactory')->getMock(); $this->avatarMock = $this->getMockBuilder('OCP\IAvatar')->getMock(); - $this->color = new \OC\Color(0, 130, 201); $this->userMock = $this->getMockBuilder(IUser::class)->getMock(); $this->avatarController = new AvatarController( @@ -120,8 +119,6 @@ class AvatarControllerTest extends \Test\TestCase { $this->avatarFile->method('getContent')->willReturn('image data'); $this->avatarFile->method('getMimeType')->willReturn('image type'); $this->avatarFile->method('getEtag')->willReturn('my etag'); - - $this->avatarMock->method('avatarBackgroundColor')->willReturn($this->color); } public function tearDown() { @@ -133,21 +130,10 @@ class AvatarControllerTest extends \Test\TestCase { */ public function testGetAvatarNoAvatar() { $this->avatarManager->method('getAvatar')->willReturn($this->avatarMock); - $this->avatarMock->method('getAvatarVector')->willReturn(''); + $this->avatarMock->method('getFile')->will($this->throwException(new NotFoundException())); $response = $this->avatarController->getAvatar('userId', 32); - $this->assertEquals(Http::STATUS_OK, $response->getStatus()); - $this->assertEquals('', $response->getData()); - } - - /** - * Fetch a png avatar if a user has no avatar - */ - public function testGetPngAvatarNoAvatar() { - $this->avatarManager->method('getAvatar')->willReturn($this->avatarMock); - $this->avatarMock->method('getFile')->will($this->throwException(new NotFoundException())); - $response = $this->avatarController->getAvatar('userId', 32, true); - + //Comment out until JS is fixed $this->assertEquals(Http::STATUS_NOT_FOUND, $response->getStatus()); } @@ -155,26 +141,10 @@ class AvatarControllerTest extends \Test\TestCase { * Fetch the user's avatar */ public function testGetAvatar() { - $this->avatarMock->method('getAvatarVector')->willReturn(''); - $this->avatarManager->method('getAvatar')->with('userId')->willReturn($this->avatarMock); - - $response = $this->avatarController->getAvatar('userId', 32); - - $this->assertEquals(Http::STATUS_OK, $response->getStatus()); - $this->assertArrayHasKey('Content-Type', $response->getHeaders()); - $this->assertEquals('image/svg+xml', $response->getHeaders()['Content-Type']); - - $this->assertEquals('', $response->getData()); - } - - /** - * Fetch the user's avatar - */ - public function testGetPngAvatar() { $this->avatarMock->method('getFile')->willReturn($this->avatarFile); $this->avatarManager->method('getAvatar')->with('userId')->willReturn($this->avatarMock); - $response = $this->avatarController->getAvatar('userId', 32, true); + $response = $this->avatarController->getAvatar('userId', 32); $this->assertEquals(Http::STATUS_OK, $response->getStatus()); $this->assertArrayHasKey('Content-Type', $response->getHeaders()); @@ -201,7 +171,7 @@ class AvatarControllerTest extends \Test\TestCase { /** * Make sure we get the correct size */ - public function testGetPngAvatarSize() { + public function testGetAvatarSize() { $this->avatarMock->expects($this->once()) ->method('getFile') ->with($this->equalTo(32)) @@ -209,13 +179,13 @@ class AvatarControllerTest extends \Test\TestCase { $this->avatarManager->method('getAvatar')->willReturn($this->avatarMock); - $this->avatarController->getAvatar('userId', 32, true); + $this->avatarController->getAvatar('userId', 32); } /** * We cannot get avatars that are 0 or negative */ - public function testGetPngAvatarSizeMin() { + public function testGetAvatarSizeMin() { $this->avatarMock->expects($this->once()) ->method('getFile') ->with($this->equalTo(64)) @@ -223,13 +193,13 @@ class AvatarControllerTest extends \Test\TestCase { $this->avatarManager->method('getAvatar')->willReturn($this->avatarMock); - $this->avatarController->getAvatar('userId', 0, true); + $this->avatarController->getAvatar('userId', 0); } /** * We do not support avatars larger than 2048*2048 */ - public function testGetPngAvatarSizeMax() { + public function testGetAvatarSizeMax() { $this->avatarMock->expects($this->once()) ->method('getFile') ->with($this->equalTo(2048)) @@ -237,7 +207,7 @@ class AvatarControllerTest extends \Test\TestCase { $this->avatarManager->method('getAvatar')->willReturn($this->avatarMock); - $this->avatarController->getAvatar('userId', 2049, true); + $this->avatarController->getAvatar('userId', 2049); } /** @@ -516,6 +486,7 @@ class AvatarControllerTest extends \Test\TestCase { $this->assertEquals($expectedResponse, $this->avatarController->postCroppedAvatar(['x' => 0, 'y' => 0, 'w' => 10, 'h' => 11])); } + /** * Check for proper reply on proper crop argument */ @@ -530,13 +501,4 @@ class AvatarControllerTest extends \Test\TestCase { $this->assertEquals('File is too big', $response->getData()['data']['message']); } - /** - * Test get Avatar BG colour algorithm - */ - public function testAvatarBackgroundColor() { - $bgRGB = $this->avatarMock->avatarBackgroundColor('TestBlue'); - $this->assertEquals($bgRGB, $this->color); - $this->assertEquals(sprintf("%02x%02x%02x", $bgRGB->r, $bgRGB->g, $bgRGB->b), '0082c9'); - } - } diff --git a/tests/lib/AvatarTest.php b/tests/lib/AvatarTest.php index a9b798fe5d..67fde694b8 100644 --- a/tests/lib/AvatarTest.php +++ b/tests/lib/AvatarTest.php @@ -230,7 +230,7 @@ class AvatarTest extends \Test\TestCase { } public function testGenerateSvgAvatar() { - $avatar = $this->avatar->getAvatarVector(64); + $avatar = $this->invokePrivate($this->avatar, 'getAvatarVector', [64]); $svg = ' From 16ec9d96708793cc6d7016743d6d8ba9182088bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20Molakvo=C3=A6=20=28skjnldsv=29?= Date: Mon, 28 May 2018 11:06:33 +0200 Subject: [PATCH 10/12] Removed old route, fix tests and fix var type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Molakvoæ (skjnldsv) --- core/routes.php | 1 - lib/private/Avatar.php | 2 +- tests/lib/AvatarTest.php | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/core/routes.php b/core/routes.php index dd35638a7e..cc1bd34d89 100644 --- a/core/routes.php +++ b/core/routes.php @@ -42,7 +42,6 @@ $application->registerRoutes($this, [ ['name' => 'lost#setPassword', 'url' => '/lostpassword/set/{token}/{userId}', 'verb' => 'POST'], ['name' => 'user#getDisplayNames', 'url' => '/displaynames', 'verb' => 'POST'], ['name' => 'avatar#getAvatar', 'url' => '/avatar/{userId}/{size}', 'verb' => 'GET'], - ['name' => 'avatar#getAvatarPng', 'url' => '/avatar/{userId}/{size}/png', 'verb' => 'GET'], ['name' => 'avatar#deleteAvatar', 'url' => '/avatar/', 'verb' => 'DELETE'], ['name' => 'avatar#postCroppedAvatar', 'url' => '/avatar/cropped', 'verb' => 'POST'], ['name' => 'avatar#getTmpAvatar', 'url' => '/avatar/tmp', 'verb' => 'GET'], diff --git a/lib/private/Avatar.php b/lib/private/Avatar.php index 270f69a96a..9dbeb4ac74 100644 --- a/lib/private/Avatar.php +++ b/lib/private/Avatar.php @@ -301,7 +301,7 @@ class Avatar implements IAvatar { * Generate png avatar from svg with Imagick * * @param int $size - * @return string + * @return string|boolean */ private function generateAvatarFromSvg(int $size) { if (!extension_loaded('imagick')) { diff --git a/tests/lib/AvatarTest.php b/tests/lib/AvatarTest.php index 67fde694b8..759dd38556 100644 --- a/tests/lib/AvatarTest.php +++ b/tests/lib/AvatarTest.php @@ -232,7 +232,7 @@ class AvatarTest extends \Test\TestCase { public function testGenerateSvgAvatar() { $avatar = $this->invokePrivate($this->avatar, 'getAvatarVector', [64]); - $svg = ' + $svg = ' A From fd4330c38fcb35c3a4d0cf1807a6cacea63636c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20Molakvo=C3=A6=20=28skjnldsv=29?= Date: Wed, 13 Jun 2018 22:07:40 +0200 Subject: [PATCH 11/12] Fix indent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Molakvoæ (skjnldsv) --- lib/public/IAvatar.php | 88 +++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/lib/public/IAvatar.php b/lib/public/IAvatar.php index 54d703a502..8586335706 100644 --- a/lib/public/IAvatar.php +++ b/lib/public/IAvatar.php @@ -37,59 +37,59 @@ use OCP\Files\NotFoundException; */ interface IAvatar { - /** - * get the users avatar - * @param int $size size in px of the avatar, avatars are square, defaults to 64, -1 can be used to not scale the image - * @return boolean|\OCP\IImage containing the avatar or false if there's no image - * @since 6.0.0 - size of -1 was added in 9.0.0 - */ - public function get($size = 64); + /** + * get the users avatar + * @param int $size size in px of the avatar, avatars are square, defaults to 64, -1 can be used to not scale the image + * @return boolean|\OCP\IImage containing the avatar or false if there's no image + * @since 6.0.0 - size of -1 was added in 9.0.0 + */ + public function get($size = 64); - /** - * Check if an avatar exists for the user - * - * @return bool - * @since 8.1.0 - */ - public function exists(); + /** + * Check if an avatar exists for the user + * + * @return bool + * @since 8.1.0 + */ + public function exists(); - /** - * sets the users avatar - * @param \OCP\IImage|resource|string $data An image object, imagedata or path to set a new avatar - * @throws \Exception if the provided file is not a jpg or png image - * @throws \Exception if the provided image is not valid - * @throws \OC\NotSquareException if the image is not square - * @return void - * @since 6.0.0 - */ - public function set($data); + /** + * sets the users avatar + * @param \OCP\IImage|resource|string $data An image object, imagedata or path to set a new avatar + * @throws \Exception if the provided file is not a jpg or png image + * @throws \Exception if the provided image is not valid + * @throws \OC\NotSquareException if the image is not square + * @return void + * @since 6.0.0 + */ + public function set($data); - /** - * remove the users avatar - * @return void - * @since 6.0.0 - */ - public function remove(); + /** + * remove the users avatar + * @return void + * @since 6.0.0 + */ + public function remove(); - /** - * Get the file of the avatar - * @param int $size -1 can be used to not scale the image - * @return File - * @throws NotFoundException - * @since 9.0.0 - */ - public function getFile($size); + /** + * Get the file of the avatar + * @param int $size -1 can be used to not scale the image + * @return File + * @throws NotFoundException + * @since 9.0.0 + */ + public function getFile($size); /** * @param string $text * @return Color Object containting r g b int in the range [0, 255] - * @since 14.0.0 + * @since 14.0.0 */ public function avatarBackgroundColor(string $text); - /** - * Handle a changed user - * @since 13.0.0 - */ - public function userChanged($feature, $oldValue, $newValue); + /** + * Handle a changed user + * @since 13.0.0 + */ + public function userChanged($feature, $oldValue, $newValue); } From 543188d0cb2f03e661ba937dcef5f56f739ea582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20Molakvo=C3=A6=20=28skjnldsv=29?= Date: Fri, 15 Jun 2018 15:40:10 +0200 Subject: [PATCH 12/12] Removed debug stack MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Molakvoæ (skjnldsv) --- core/Controller/AvatarController.php | 1 - 1 file changed, 1 deletion(-) diff --git a/core/Controller/AvatarController.php b/core/Controller/AvatarController.php index 94c837888d..0625265dd0 100644 --- a/core/Controller/AvatarController.php +++ b/core/Controller/AvatarController.php @@ -139,7 +139,6 @@ class AvatarController extends Controller { ['Content-Type' => $avatar->getMimeType() ]); } catch (\Exception $e) { - var_dump($e); $resp = new Http\Response(); $resp->setStatus(Http::STATUS_NOT_FOUND); return $resp;