Merge pull request #9258 from nextcloud/theming-logo-png

Convert theming app logo to PNG to show it properly in emails
This commit is contained in:
Morris Jobke 2018-06-15 17:02:14 +02:00 committed by GitHub
commit 8f6acbff62
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 182 additions and 78 deletions

View File

@ -119,7 +119,7 @@ class IconController extends Controller {
$response = new FileDisplayResponse($iconFile, Http::STATUS_OK, ['Content-Type' => 'image/x-icon']); $response = new FileDisplayResponse($iconFile, Http::STATUS_OK, ['Content-Type' => 'image/x-icon']);
} catch (NotFoundException $e) { } catch (NotFoundException $e) {
} }
if ($iconFile === null && $this->themingDefaults->shouldReplaceIcons()) { if ($iconFile === null && $this->imageManager->shouldReplaceIcons()) {
try { try {
$iconFile = $this->imageManager->getCachedImage('favIcon-' . $app); $iconFile = $this->imageManager->getCachedImage('favIcon-' . $app);
} catch (NotFoundException $exception) { } catch (NotFoundException $exception) {
@ -155,7 +155,7 @@ class IconController extends Controller {
$response = new FileDisplayResponse($iconFile, Http::STATUS_OK, ['Content-Type' => 'image/x-icon']); $response = new FileDisplayResponse($iconFile, Http::STATUS_OK, ['Content-Type' => 'image/x-icon']);
} catch (NotFoundException $e) { } catch (NotFoundException $e) {
} }
if ($this->themingDefaults->shouldReplaceIcons()) { if ($this->imageManager->shouldReplaceIcons()) {
try { try {
$iconFile = $this->imageManager->getCachedImage('touchIcon-' . $app); $iconFile = $this->imageManager->getCachedImage('touchIcon-' . $app);
} catch (NotFoundException $exception) { } catch (NotFoundException $exception) {

View File

@ -262,6 +262,8 @@ class ThemingController extends Controller {
$folder = $this->appData->newFolder('images'); $folder = $this->appData->newFolder('images');
} }
$this->imageManager->delete($key);
$target = $folder->newFile($key); $target = $folder->newFile($key);
$supportedFormats = ['image/jpeg', 'image/png', 'image/gif', 'image/svg+xml', 'image/svg']; $supportedFormats = ['image/jpeg', 'image/png', 'image/gif', 'image/svg+xml', 'image/svg'];
$detectedMimeType = mime_content_type($image['tmp_name']); $detectedMimeType = mime_content_type($image['tmp_name']);
@ -351,12 +353,13 @@ class ThemingController extends Controller {
* @NoCSRFRequired * @NoCSRFRequired
* *
* @param string $key * @param string $key
* @param bool $useSvg
* @return FileDisplayResponse|NotFoundResponse * @return FileDisplayResponse|NotFoundResponse
* @throws \Exception * @throws NotPermittedException
*/ */
public function getImage(string $key) { public function getImage(string $key, bool $useSvg = true) {
try { try {
$file = $this->imageManager->getImage($key); $file = $this->imageManager->getImage($key, $useSvg);
} catch (NotFoundException $e) { } catch (NotFoundException $e) {
return new NotFoundResponse(); return new NotFoundResponse();
} }
@ -365,6 +368,11 @@ class ThemingController extends Controller {
$response->cacheFor(3600); $response->cacheFor(3600);
$response->addHeader('Content-Type', $this->config->getAppValue($this->appName, $key . 'Mime', '')); $response->addHeader('Content-Type', $this->config->getAppValue($this->appName, $key . 'Mime', ''));
$response->addHeader('Content-Disposition', 'attachment; filename="' . $key . '"'); $response->addHeader('Content-Disposition', 'attachment; filename="' . $key . '"');
if (!$useSvg) {
$response->addHeader('Content-Type', 'image/png');
} else {
$response->addHeader('Content-Type', $this->config->getAppValue($this->appName, $key . 'Mime', ''));
}
return $response; return $response;
} }

View File

@ -35,19 +35,24 @@ class IconBuilder {
private $themingDefaults; private $themingDefaults;
/** @var Util */ /** @var Util */
private $util; private $util;
/** @var ImageManager */
private $imageManager;
/** /**
* IconBuilder constructor. * IconBuilder constructor.
* *
* @param ThemingDefaults $themingDefaults * @param ThemingDefaults $themingDefaults
* @param Util $util * @param Util $util
* @param ImageManager $imageManager
*/ */
public function __construct( public function __construct(
ThemingDefaults $themingDefaults, ThemingDefaults $themingDefaults,
Util $util Util $util,
ImageManager $imageManager
) { ) {
$this->themingDefaults = $themingDefaults; $this->themingDefaults = $themingDefaults;
$this->util = $util; $this->util = $util;
$this->imageManager = $imageManager;
} }
/** /**
@ -55,7 +60,7 @@ class IconBuilder {
* @return string|false image blob * @return string|false image blob
*/ */
public function getFavicon($app) { public function getFavicon($app) {
if (!$this->themingDefaults->shouldReplaceIcons()) { if (!$this->imageManager->shouldReplaceIcons()) {
return false; return false;
} }
try { try {

View File

@ -26,24 +26,28 @@ namespace OCA\Theming;
use OCP\Files\SimpleFS\ISimpleFile; use OCP\Files\SimpleFS\ISimpleFile;
use OCP\Files\SimpleFS\ISimpleFolder; use OCP\Files\SimpleFS\ISimpleFolder;
use OCP\ICacheFactory;
use OCP\IConfig; use OCP\IConfig;
use OCP\Files\IAppData; use OCP\Files\IAppData;
use OCP\Files\NotFoundException; use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException; use OCP\Files\NotPermittedException;
use OCP\ILogger;
use OCP\IURLGenerator; use OCP\IURLGenerator;
/**
* @property IURLGenerator urlGenerator
*/
class ImageManager { class ImageManager {
/** @var IConfig */ /** @var IConfig */
private $config; private $config;
/** @var IAppData */ /** @var IAppData */
private $appData; private $appData;
/** @var IURLGenerator */
private $urlGenerator;
/** @var array */ /** @var array */
private $supportedImageKeys = ['background', 'logo', 'logoheader', 'favicon']; private $supportedImageKeys = ['background', 'logo', 'logoheader', 'favicon'];
/** @var ICacheFactory */
private $cacheFactory;
/** @var ILogger */
private $logger;
/** /**
* ImageManager constructor. * ImageManager constructor.
@ -51,20 +55,26 @@ class ImageManager {
* @param IConfig $config * @param IConfig $config
* @param IAppData $appData * @param IAppData $appData
* @param IURLGenerator $urlGenerator * @param IURLGenerator $urlGenerator
* @param ICacheFactory $cacheFactory
* @param ILogger $logger
*/ */
public function __construct(IConfig $config, public function __construct(IConfig $config,
IAppData $appData, IAppData $appData,
IURLGenerator $urlGenerator IURLGenerator $urlGenerator,
ICacheFactory $cacheFactory,
ILogger $logger
) { ) {
$this->config = $config; $this->config = $config;
$this->appData = $appData; $this->appData = $appData;
$this->urlGenerator = $urlGenerator; $this->urlGenerator = $urlGenerator;
$this->cacheFactory = $cacheFactory;
$this->logger = $logger;
} }
public function getImageUrl(string $key): string { public function getImageUrl(string $key, bool $useSvg = true): string {
$cacheBusterCounter = $this->config->getAppValue('theming', 'cachebuster', '0'); $cacheBusterCounter = $this->config->getAppValue('theming', 'cachebuster', '0');
try { try {
$this->getImage($key); $image = $this->getImage($key, $useSvg);
return $this->urlGenerator->linkToRoute('theming.Theming.getImage', [ 'key' => $key ]) . '?v=' . $cacheBusterCounter; return $this->urlGenerator->linkToRoute('theming.Theming.getImage', [ 'key' => $key ]) . '?v=' . $cacheBusterCounter;
} catch (NotFoundException $e) { } catch (NotFoundException $e) {
} }
@ -79,21 +89,44 @@ class ImageManager {
} }
} }
public function getImageUrlAbsolute(string $key): string { public function getImageUrlAbsolute(string $key, bool $useSvg = true): string {
return $this->urlGenerator->getAbsoluteURL($this->getImageUrl($key)); return $this->urlGenerator->getAbsoluteURL($this->getImageUrl($key, $useSvg));
} }
/** /**
* @param $key * @param string $key
* @param bool $useSvg
* @return ISimpleFile * @return ISimpleFile
* @throws NotFoundException * @throws NotFoundException
* @throws NotPermittedException
*/ */
public function getImage(string $key): ISimpleFile { public function getImage(string $key, bool $useSvg = true): ISimpleFile {
$pngFile = null;
$logo = $this->config->getAppValue('theming', $key . 'Mime', false); $logo = $this->config->getAppValue('theming', $key . 'Mime', false);
if ($logo === false) { $folder = $this->appData->getFolder('images');
if ($logo === false || !$folder->fileExists($key)) {
throw new NotFoundException(); throw new NotFoundException();
} }
$folder = $this->appData->getFolder('images'); if (!$useSvg && $this->shouldReplaceIcons()) {
if (!$folder->fileExists($key . '.png')) {
try {
$finalIconFile = new \Imagick();
$finalIconFile->setBackgroundColor('none');
$finalIconFile->readImageBlob($folder->getFile($key)->getContent());
$finalIconFile->setImageFormat('png32');
$pngFile = $folder->newFile($key . '.png');
$pngFile->putContent($finalIconFile->getImageBlob());
} catch (\ImagickException $e) {
$this->logger->info('The image was requested to be no SVG file, but converting it to PNG failed.', $e->getMessage());
$pngFile = null;
}
} else {
$pngFile = $folder->getFile($key . '.png');
}
}
if ($pngFile !== null) {
return $pngFile;
}
return $folder->getFile($key); return $folder->getFile($key);
} }
@ -159,12 +192,19 @@ class ImageManager {
} }
public function delete(string $key) { public function delete(string $key) {
/* ignore exceptions, since we don't want to fail hard if something goes wrong during cleanup */
try { try {
$file = $this->appData->getFolder('images')->getFile($key); $file = $this->appData->getFolder('images')->getFile($key);
$file->delete(); $file->delete();
} catch (NotFoundException $e) { } catch (NotFoundException $e) {
} catch (NotPermittedException $e) { } catch (NotPermittedException $e) {
} }
try {
$file = $this->appData->getFolder('images')->getFile($key . '.png');
$file->delete();
} catch (NotFoundException $e) {
} catch (NotPermittedException $e) {
}
} }
/** /**
@ -182,4 +222,25 @@ class ImageManager {
} }
} }
} }
/**
* Check if Imagemagick is enabled and if SVG is supported
* otherwise we can't render custom icons
*
* @return bool
*/
public function shouldReplaceIcons() {
$cache = $this->cacheFactory->createDistributed('theming-' . $this->urlGenerator->getBaseUrl());
if($value = $cache->get('shouldReplaceIcons')) {
return (bool)$value;
}
$value = false;
if(extension_loaded('imagick')) {
if (count(\Imagick::queryFormats('SVG')) >= 1) {
$value = true;
}
}
$cache->set('shouldReplaceIcons', $value);
return $value;
}
} }

View File

@ -81,7 +81,7 @@ class Admin implements ISettings {
'slogan' => $this->themingDefaults->getSlogan(), 'slogan' => $this->themingDefaults->getSlogan(),
'color' => $this->themingDefaults->getColorPrimary(), 'color' => $this->themingDefaults->getColorPrimary(),
'uploadLogoRoute' => $this->urlGenerator->linkToRoute('theming.Theming.uploadImage'), 'uploadLogoRoute' => $this->urlGenerator->linkToRoute('theming.Theming.uploadImage'),
'canThemeIcons' => $this->themingDefaults->shouldReplaceIcons(), 'canThemeIcons' => $this->imageManager->shouldReplaceIcons(),
'iconDocs' => $this->urlGenerator->linkToDocs('admin-theming-icons'), 'iconDocs' => $this->urlGenerator->linkToDocs('admin-theming-icons'),
'images' => $this->imageManager->getCustomImages(), 'images' => $this->imageManager->getCustomImages(),
'imprintUrl' => $this->themingDefaults->getImprintUrl(), 'imprintUrl' => $this->themingDefaults->getImprintUrl(),

View File

@ -205,7 +205,7 @@ class ThemingDefaults extends \OC_Defaults {
$logoExists = true; $logoExists = true;
try { try {
$this->imageManager->getImage('logo'); $this->imageManager->getImage('logo', $useSvg);
} catch (\Exception $e) { } catch (\Exception $e) {
$logoExists = false; $logoExists = false;
} }
@ -221,7 +221,7 @@ class ThemingDefaults extends \OC_Defaults {
return $logo . '?v=' . $cacheBusterCounter; return $logo . '?v=' . $cacheBusterCounter;
} }
return $this->urlGenerator->linkToRoute('theming.Theming.getImage', [ 'key' => 'logo' ]) . '?v=' . $cacheBusterCounter; return $this->urlGenerator->linkToRoute('theming.Theming.getImage', [ 'key' => 'logo', 'useSvg' => $useSvg, 'v' => $cacheBusterCounter ]);
} }
/** /**
@ -317,10 +317,10 @@ class ThemingDefaults extends \OC_Defaults {
$customFavicon = null; $customFavicon = null;
} }
if ($image === 'favicon.ico' && ($customFavicon !== null || $this->shouldReplaceIcons())) { if ($image === 'favicon.ico' && ($customFavicon !== null || $this->imageManager->shouldReplaceIcons())) {
return $this->urlGenerator->linkToRoute('theming.Icon.getFavicon', ['app' => $app]) . '?v=' . $cacheBusterValue; return $this->urlGenerator->linkToRoute('theming.Icon.getFavicon', ['app' => $app]) . '?v=' . $cacheBusterValue;
} }
if ($image === 'favicon-touch.png' && ($customFavicon !== null || $this->shouldReplaceIcons())) { if ($image === 'favicon-touch.png' && ($customFavicon !== null || $this->imageManager->shouldReplaceIcons())) {
return $this->urlGenerator->linkToRoute('theming.Icon.getTouchIcon', ['app' => $app]) . '?v=' . $cacheBusterValue; return $this->urlGenerator->linkToRoute('theming.Icon.getTouchIcon', ['app' => $app]) . '?v=' . $cacheBusterValue;
} }
if ($image === 'manifest.json') { if ($image === 'manifest.json') {
@ -335,29 +335,6 @@ class ThemingDefaults extends \OC_Defaults {
return false; return false;
} }
/**
* Check if Imagemagick is enabled and if SVG is supported
* otherwise we can't render custom icons
*
* @return bool
*/
public function shouldReplaceIcons() {
$cache = $this->cacheFactory->createDistributed('theming-' . $this->urlGenerator->getBaseUrl());
if($value = $cache->get('shouldReplaceIcons')) {
return (bool)$value;
}
$value = false;
if(extension_loaded('imagick')) {
$checkImagick = new \Imagick();
if (count($checkImagick->queryFormats('SVG')) >= 1) {
$value = true;
}
$checkImagick->clear();
}
$cache->set('shouldReplaceIcons', $value);
return $value;
}
/** /**
* Increases the cache buster key * Increases the cache buster key
*/ */

View File

@ -120,7 +120,7 @@ class IconControllerTest extends TestCase {
->method('getImage') ->method('getImage')
->with('favicon') ->with('favicon')
->will($this->throwException(new NotFoundException())); ->will($this->throwException(new NotFoundException()));
$this->themingDefaults->expects($this->any()) $this->imageManager->expects($this->any())
->method('shouldReplaceIcons') ->method('shouldReplaceIcons')
->willReturn(true); ->willReturn(true);
$this->imageManager->expects($this->once()) $this->imageManager->expects($this->once())
@ -144,7 +144,7 @@ class IconControllerTest extends TestCase {
->method('getImage') ->method('getImage')
->with('favicon') ->with('favicon')
->will($this->throwException(new NotFoundException())); ->will($this->throwException(new NotFoundException()));
$this->themingDefaults->expects($this->any()) $this->imageManager->expects($this->any())
->method('shouldReplaceIcons') ->method('shouldReplaceIcons')
->willReturn(false); ->willReturn(false);
$fallbackLogo = \OC::$SERVERROOT . '/core/img/favicon.png'; $fallbackLogo = \OC::$SERVERROOT . '/core/img/favicon.png';
@ -165,10 +165,13 @@ class IconControllerTest extends TestCase {
if (count($checkImagick->queryFormats('SVG')) < 1) { if (count($checkImagick->queryFormats('SVG')) < 1) {
$this->markTestSkipped('No SVG provider present.'); $this->markTestSkipped('No SVG provider present.');
} }
$this->themingDefaults->expects($this->any())
$this->imageManager->expects($this->once())
->method('getImage')
->will($this->throwException(new NotFoundException()));
$this->imageManager->expects($this->any())
->method('shouldReplaceIcons') ->method('shouldReplaceIcons')
->willReturn(true); ->willReturn(true);
$this->iconBuilder->expects($this->once()) $this->iconBuilder->expects($this->once())
->method('getTouchIcon') ->method('getTouchIcon')
->with('core') ->with('core')
@ -191,7 +194,7 @@ class IconControllerTest extends TestCase {
->method('getImage') ->method('getImage')
->with('favicon') ->with('favicon')
->will($this->throwException(new NotFoundException())); ->will($this->throwException(new NotFoundException()));
$this->themingDefaults->expects($this->any()) $this->imageManager->expects($this->any())
->method('shouldReplaceIcons') ->method('shouldReplaceIcons')
->willReturn(false); ->willReturn(false);
$fallbackLogo = \OC::$SERVERROOT . '/core/img/favicon-touch.png'; $fallbackLogo = \OC::$SERVERROOT . '/core/img/favicon-touch.png';

View File

@ -690,7 +690,7 @@ class ThemingControllerTest extends TestCase {
->method('getImage') ->method('getImage')
->willReturn($file); ->willReturn($file);
$this->config $this->config
->expects($this->once()) ->expects($this->any())
->method('getAppValue') ->method('getAppValue')
->with('theming', 'logoMime', '') ->with('theming', 'logoMime', '')
->willReturn('text/svg'); ->willReturn('text/svg');
@ -718,7 +718,7 @@ class ThemingControllerTest extends TestCase {
->willReturn($file); ->willReturn($file);
$this->config $this->config
->expects($this->once()) ->expects($this->any())
->method('getAppValue') ->method('getAppValue')
->with('theming', 'backgroundMime', '') ->with('theming', 'backgroundMime', '')
->willReturn('image/png'); ->willReturn('image/png');

View File

@ -27,6 +27,7 @@ namespace OCA\Theming\Tests;
use OC\Files\AppData\AppData; use OC\Files\AppData\AppData;
use OCA\Theming\IconBuilder; use OCA\Theming\IconBuilder;
use OCA\Theming\ImageManager;
use OCA\Theming\ThemingDefaults; use OCA\Theming\ThemingDefaults;
use OCA\Theming\Util; use OCA\Theming\Util;
use OCP\App\IAppManager; use OCP\App\IAppManager;
@ -45,6 +46,8 @@ class IconBuilderTest extends TestCase {
protected $themingDefaults; protected $themingDefaults;
/** @var Util */ /** @var Util */
protected $util; protected $util;
/** @var ImageManager */
protected $imageManager;
/** @var IconBuilder */ /** @var IconBuilder */
protected $iconBuilder; protected $iconBuilder;
/** @var IAppManager */ /** @var IAppManager */
@ -53,13 +56,13 @@ class IconBuilderTest extends TestCase {
protected function setUp() { protected function setUp() {
parent::setUp(); parent::setUp();
$this->config = $this->getMockBuilder(IConfig::class)->getMock(); $this->config = $this->createMock(IConfig::class);
$this->appData = $this->createMock(AppData::class); $this->appData = $this->createMock(AppData::class);
$this->themingDefaults = $this->getMockBuilder('OCA\Theming\ThemingDefaults') $this->themingDefaults = $this->createMock(ThemingDefaults::class);
->disableOriginalConstructor()->getMock(); $this->appManager = $this->createMock(IAppManager::class);
$this->appManager = $this->getMockBuilder('OCP\App\IAppManager')->getMock(); $this->imageManager = $this->createMock(ImageManager::class);
$this->util = new Util($this->config, $this->appManager, $this->appData); $this->util = new Util($this->config, $this->appManager, $this->appData);
$this->iconBuilder = new IconBuilder($this->themingDefaults, $this->util); $this->iconBuilder = new IconBuilder($this->themingDefaults, $this->util, $this->imageManager);
} }
private function checkImagick() { private function checkImagick() {
@ -152,7 +155,7 @@ class IconBuilderTest extends TestCase {
*/ */
public function testGetFavicon($app, $color, $file) { public function testGetFavicon($app, $color, $file) {
$this->checkImagick(); $this->checkImagick();
$this->themingDefaults->expects($this->once()) $this->imageManager->expects($this->once())
->method('shouldReplaceIcons') ->method('shouldReplaceIcons')
->willReturn(true); ->willReturn(true);
$this->themingDefaults->expects($this->once()) $this->themingDefaults->expects($this->once())
@ -183,8 +186,8 @@ class IconBuilderTest extends TestCase {
$this->checkImagick(); $this->checkImagick();
$this->expectException(Warning::class); $this->expectException(Warning::class);
$util = $this->getMockBuilder(Util::class)->disableOriginalConstructor()->getMock(); $util = $this->getMockBuilder(Util::class)->disableOriginalConstructor()->getMock();
$iconBuilder = new IconBuilder($this->themingDefaults, $util); $iconBuilder = new IconBuilder($this->themingDefaults, $util, $this->imageManager);
$this->themingDefaults->expects($this->once()) $this->imageManager->expects($this->once())
->method('shouldReplaceIcons') ->method('shouldReplaceIcons')
->willReturn(true); ->willReturn(true);
$util->expects($this->once()) $util->expects($this->once())
@ -197,7 +200,7 @@ class IconBuilderTest extends TestCase {
$this->checkImagick(); $this->checkImagick();
$this->expectException(Warning::class); $this->expectException(Warning::class);
$util = $this->getMockBuilder(Util::class)->disableOriginalConstructor()->getMock(); $util = $this->getMockBuilder(Util::class)->disableOriginalConstructor()->getMock();
$iconBuilder = new IconBuilder($this->themingDefaults, $util); $iconBuilder = new IconBuilder($this->themingDefaults, $util, $this->imageManager);
$util->expects($this->once()) $util->expects($this->once())
->method('getAppIcon') ->method('getAppIcon')
->willReturn('notexistingfile'); ->willReturn('notexistingfile');
@ -208,7 +211,7 @@ class IconBuilderTest extends TestCase {
$this->checkImagick(); $this->checkImagick();
$this->expectException(Warning::class); $this->expectException(Warning::class);
$util = $this->getMockBuilder(Util::class)->disableOriginalConstructor()->getMock(); $util = $this->getMockBuilder(Util::class)->disableOriginalConstructor()->getMock();
$iconBuilder = new IconBuilder($this->themingDefaults, $util); $iconBuilder = new IconBuilder($this->themingDefaults, $util, $this->imageManager);
$util->expects($this->once()) $util->expects($this->once())
->method('getAppImage') ->method('getAppImage')
->willReturn('notexistingfile'); ->willReturn('notexistingfile');

View File

@ -24,8 +24,11 @@
namespace OCA\Theming\Tests; namespace OCA\Theming\Tests;
use OCA\Theming\ImageManager; use OCA\Theming\ImageManager;
use OCA\Theming\ThemingDefaults;
use OCP\Files\SimpleFS\ISimpleFile; use OCP\Files\SimpleFS\ISimpleFile;
use OCP\ICacheFactory;
use OCP\IConfig; use OCP\IConfig;
use OCP\ILogger;
use OCP\IURLGenerator; use OCP\IURLGenerator;
use Test\TestCase; use Test\TestCase;
use OCP\Files\SimpleFS\ISimpleFolder; use OCP\Files\SimpleFS\ISimpleFolder;
@ -42,19 +45,40 @@ class ImageManagerTest extends TestCase {
protected $imageManager; protected $imageManager;
/** @var IURLGenerator|\PHPUnit_Framework_MockObject_MockObject */ /** @var IURLGenerator|\PHPUnit_Framework_MockObject_MockObject */
private $urlGenerator; private $urlGenerator;
/** @var ICacheFactory|\PHPUnit_Framework_MockObject_MockObject */
private $cacheFactory;
/** @var ILogger|\PHPUnit_Framework_MockObject_MockObject */
private $logger;
protected function setUp() { protected function setUp() {
parent::setUp(); parent::setUp();
$this->config = $this->createMock(IConfig::class); $this->config = $this->createMock(IConfig::class);
$this->appData = $this->createMock(IAppData::class); $this->appData = $this->createMock(IAppData::class);
$this->urlGenerator = $this->createMock(IURLGenerator::class); $this->urlGenerator = $this->createMock(IURLGenerator::class);
$this->cacheFactory = $this->createMock(ICacheFactory::class);
$this->logger = $this->createMock(ILogger::class);
$this->imageManager = new ImageManager( $this->imageManager = new ImageManager(
$this->config, $this->config,
$this->appData, $this->appData,
$this->urlGenerator $this->urlGenerator,
$this->cacheFactory,
$this->logger
); );
} }
private function checkImagick() {
if(!extension_loaded('imagick')) {
$this->markTestSkipped('Imagemagick is required for dynamic icon generation.');
}
$checkImagick = new \Imagick();
if (empty($checkImagick->queryFormats('SVG'))) {
$this->markTestSkipped('No SVG provider present.');
}
if (empty($checkImagick->queryFormats('PNG'))) {
$this->markTestSkipped('No PNG provider present.');
}
}
public function mockGetImage($key, $file) { public function mockGetImage($key, $file) {
/** @var \PHPUnit_Framework_MockObject_MockObject $folder */ /** @var \PHPUnit_Framework_MockObject_MockObject $folder */
$folder = $this->createMock(ISimpleFolder::class); $folder = $this->createMock(ISimpleFolder::class);
@ -64,10 +88,28 @@ class ImageManagerTest extends TestCase {
->with('logo') ->with('logo')
->willThrowException(new NotFoundException()); ->willThrowException(new NotFoundException());
} else { } else {
$folder->expects($this->once()) $file->expects($this->once())
->method('getContent')
->willReturn(file_get_contents(__DIR__ . '/../../../tests/data/testimage.png'));
$folder->expects($this->at(0))
->method('fileExists')
->with('logo')
->willReturn(true);
$folder->expects($this->at(1))
->method('fileExists')
->with('logo.png')
->willReturn(false);
$folder->expects($this->at(2))
->method('getFile') ->method('getFile')
->with('logo') ->with('logo')
->willReturn($file); ->willReturn($file);
$newFile = $this->createMock(ISimpleFile::class);
$folder->expects($this->at(3))
->method('newFile')
->with('logo.png')
->willReturn($newFile);
$newFile->expects($this->once())
->method('putContent');
$this->appData->expects($this->once()) $this->appData->expects($this->once())
->method('getFolder') ->method('getFolder')
->with('images') ->with('images')
@ -76,19 +118,20 @@ class ImageManagerTest extends TestCase {
} }
public function testGetImageUrl() { public function testGetImageUrl() {
$this->checkImagick();
$file = $this->createMock(ISimpleFile::class); $file = $this->createMock(ISimpleFile::class);
$this->config->expects($this->exactly(2)) $this->config->expects($this->exactly(2))
->method('getAppValue') ->method('getAppValue')
->withConsecutive( ->withConsecutive(
['theming', 'cachebuster', '0'], ['theming', 'cachebuster', '0'],
['theming', 'logoMime', false] ['theming', 'logoMime', '']
) )
->willReturn(0); ->willReturn(0);
$this->mockGetImage('logo', $file); $this->mockGetImage('logo', $file);
$this->urlGenerator->expects($this->once()) $this->urlGenerator->expects($this->once())
->method('linkToRoute') ->method('linkToRoute')
->willReturn('url-to-image'); ->willReturn('url-to-image');
$this->assertEquals('url-to-image?v=0', $this->imageManager->getImageUrl('logo')); $this->assertEquals('url-to-image?v=0', $this->imageManager->getImageUrl('logo', false));
} }
public function testGetImageUrlDefault() { public function testGetImageUrlDefault() {
@ -107,33 +150,37 @@ class ImageManagerTest extends TestCase {
} }
public function testGetImageUrlAbsolute() { public function testGetImageUrlAbsolute() {
$this->checkImagick();
$file = $this->createMock(ISimpleFile::class); $file = $this->createMock(ISimpleFile::class);
$this->config->expects($this->exactly(2)) $this->config->expects($this->exactly(2))
->method('getAppValue') ->method('getAppValue')
->withConsecutive( ->withConsecutive(
['theming', 'cachebuster', '0'], ['theming', 'cachebuster', '0'],
['theming', 'logoMime', false] ['theming', 'logoMime', '']
) )
->willReturn(0); ->willReturn(0);
$this->mockGetImage('logo', $file); $this->mockGetImage('logo', $file);
$this->urlGenerator->expects($this->at(0)) $this->urlGenerator->expects($this->at(0))
->method('linkToRoute') ->method('getBaseUrl')
->willReturn('url-to-image'); ->willReturn('baseurl');
$this->urlGenerator->expects($this->at(1)) $this->urlGenerator->expects($this->at(1))
->method('getAbsoluteUrl') ->method('getAbsoluteUrl')
->with('url-to-image?v=0')
->willReturn('url-to-image-absolute?v=0'); ->willReturn('url-to-image-absolute?v=0');
$this->assertEquals('url-to-image-absolute?v=0', $this->imageManager->getImageUrlAbsolute('logo')); $this->urlGenerator->expects($this->at(2))
->method('getAbsoluteUrl')
->willReturn('url-to-image-absolute?v=0');
$this->assertEquals('url-to-image-absolute?v=0', $this->imageManager->getImageUrlAbsolute('logo', false));
} }
public function testGetImage() { public function testGetImage() {
$this->checkImagick();
$this->config->expects($this->once()) $this->config->expects($this->once())
->method('getAppValue')->with('theming', 'logoMime', false) ->method('getAppValue')->with('theming', 'logoMime', false)
->willReturn('png'); ->willReturn('png');
$file = $this->createMock(ISimpleFile::class); $file = $this->createMock(ISimpleFile::class);
$this->mockGetImage('logo', $file); $this->mockGetImage('logo', $file);
$this->assertEquals($file, $this->imageManager->getImage('logo')); $this->assertEquals($file, $this->imageManager->getImage('logo', false));
} }
/** /**

View File

@ -604,7 +604,7 @@ class ThemingDefaultsTest extends TestCase {
$this->urlGenerator->expects($this->once()) $this->urlGenerator->expects($this->once())
->method('linkToRoute') ->method('linkToRoute')
->with('theming.Theming.getImage') ->with('theming.Theming.getImage')
->willReturn('custom-logo'); ->willReturn('custom-logo?v=0');
$this->assertEquals('custom-logo' . '?v=0', $this->template->getLogo()); $this->assertEquals('custom-logo' . '?v=0', $this->template->getLogo());
} }

View File

@ -954,7 +954,7 @@ class Server extends ServerContainer implements IServerContainer {
$c->getURLGenerator(), $c->getURLGenerator(),
$c->getMemCacheFactory(), $c->getMemCacheFactory(),
new Util($c->getConfig(), $this->getAppManager(), $c->getAppDataDir('theming')), new Util($c->getConfig(), $this->getAppManager(), $c->getAppDataDir('theming')),
new ImageManager($c->getConfig(), $c->getAppDataDir('theming'), $c->getURLGenerator()), new ImageManager($c->getConfig(), $c->getAppDataDir('theming'), $c->getURLGenerator(), $this->getMemCacheFactory(), $this->getLogger()),
$c->getAppManager() $c->getAppManager()
); );
} }