diff --git a/apps/theming/lib/Controller/IconController.php b/apps/theming/lib/Controller/IconController.php index 56782992ab..5770bd2074 100644 --- a/apps/theming/lib/Controller/IconController.php +++ b/apps/theming/lib/Controller/IconController.php @@ -53,6 +53,7 @@ class IconController extends Controller { /** @var IRootFolder */ private $rootFolder; + /** * IconController constructor. * @@ -94,10 +95,10 @@ class IconController extends Controller { * @return StreamResponse|DataResponse */ public function getThemedIcon($app, $image) { - $image = $this->getAppImage($app, $image); + $image = $this->util->getAppImage($app, $image); $svg = file_get_contents($image); $color = $this->util->elementColor($this->themingDefaults->getMailHeaderColor()); - $svg = $this->colorizeSvg($svg, $color); + $svg = $this->util->colorizeSvg($svg, $color); $response = new DataDisplayResponse($svg, Http::STATUS_OK, ['Content-Type' => 'image/svg+xml']); $response->cacheFor(86400); @@ -152,24 +153,19 @@ class IconController extends Controller { * @return Imagick */ private function renderAppIcon($app) { - $appIcon = $this->getAppIcon($app); + $appIcon = $this->util->getAppIcon($app); $color = $this->themingDefaults->getMailHeaderColor(); $mime = mime_content_type($appIcon); - // FIXME: test if we need this - if ($color === "") { - $color = '#0082c9'; - } // generate background image with rounded corners $background = '' . '' . '' . ''; - // resize svg magic as this seems broken in Imagemagick if($mime === "image/svg+xml") { $svg = file_get_contents($appIcon); - + $tmp = new Imagick(); $tmp->readImageBlob($svg); $x = $tmp->getImageWidth(); @@ -214,76 +210,6 @@ class IconController extends Controller { return $finalIconFile; } - /** - * @param $app app name - * @return string path to app icon / logo - */ - private function getAppIcon($app) { - $appPath = \OC_App::getAppPath($app); - $icon = $appPath . '/img/' . $app . '.svg'; - if(file_exists($icon)) { - return $icon; - } - $icon = $appPath . '/img/app.svg'; - if(file_exists($icon)) { - return $icon; - } - - if($this->rootFolder->nodeExists('/themedinstancelogo')) { - return $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data/') . '/themedinstancelogo'; - } - return \OC::$SERVERROOT . '/core/img/logo.svg'; - } - - /** - * @param $app app name - * @param $image relative path to image in app folder - * @return string absolute path to image - */ - private function getAppImage($app, $image) { - $appPath = \OC_App::getAppPath($app); - - if($app==="core") { - $icon = \OC::$SERVERROOT . '/core/img/' . $image; - if(file_exists($icon)) { - return $icon; - } - } - - $icon = $appPath . '/img/' . $image; - if(file_exists($icon)) { - return $icon; - } - $icon = $appPath . '/img/' . $image . '.svg'; - if(file_exists($icon)) { - return $icon; - } - $icon = $appPath . '/img/' . $image . '.png'; - if(file_exists($icon)) { - return $icon; - } - $icon = $appPath . '/img/' . $image . '.gif'; - if(file_exists($icon)) { - return $icon; - } - $icon = $appPath . '/img/' . $image . '.jpg'; - if(file_exists($icon)) { - return $icon; - } - return false; - } - - /** - * replace default color with a custom one - * - * @param $svg content of a svg file - * @param $color color to match - * @return string - */ - private function colorizeSvg($svg, $color) { - $svg = preg_replace('/#0082c9/i', $color, $svg); - return $svg; - } } \ No newline at end of file diff --git a/apps/theming/lib/Util.php b/apps/theming/lib/Util.php index 71ed0958e4..8aa5f50ede 100644 --- a/apps/theming/lib/Util.php +++ b/apps/theming/lib/Util.php @@ -23,8 +23,19 @@ namespace OCA\Theming; +use OCP\IConfig; +use OCP\Files\IRootFolder; + class Util { + private $config; + private $rootFolder; + + public function __construct(IConfig $config, IRootFolder $rootFolder) { + $this->config = $config; + $this->rootFolder = $rootFolder; + } + /** * @param string $color rgb color value * @return bool @@ -81,4 +92,77 @@ class Util { return base64_encode($radioButtonIcon); } + + /** + * @param $app app name + * @return string path to app icon / logo + */ + public function getAppIcon($app) { + $appPath = \OC_App::getAppPath($app); + + $icon = $appPath . '/img/' . $app . '.svg'; + if(file_exists($icon)) { + return $icon; + } + $icon = $appPath . '/img/app.svg'; + if(file_exists($icon)) { + return $icon; + } + + if($this->config->getAppValue('theming', 'logoMime', '') !== '' && $this->rootFolder->nodeExists('/themedinstancelogo')) { + return $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data/') . '/themedinstancelogo'; + } + return \OC::$SERVERROOT . '/core/img/logo.svg'; + } + + /** + * @param $app app name + * @param $image relative path to image in app folder + * @return string absolute path to image + */ + public function getAppImage($app, $image) { + $appPath = \OC_App::getAppPath($app); + + if($app==="core") { + $icon = \OC::$SERVERROOT . '/core/img/' . $image; + if(file_exists($icon)) { + return $icon; + } + } + + $icon = $appPath . '/img/' . $image; + if(file_exists($icon)) { + return $icon; + } + $icon = $appPath . '/img/' . $image . '.svg'; + if(file_exists($icon)) { + return $icon; + } + $icon = $appPath . '/img/' . $image . '.png'; + if(file_exists($icon)) { + return $icon; + } + $icon = $appPath . '/img/' . $image . '.gif'; + if(file_exists($icon)) { + return $icon; + } + $icon = $appPath . '/img/' . $image . '.jpg'; + if(file_exists($icon)) { + return $icon; + } + return false; + } + + /** + * replace default color with a custom one + * + * @param $svg content of a svg file + * @param $color color to match + * @return string + */ + public function colorizeSvg($svg, $color) { + $svg = preg_replace('/#0082c9/i', $color, $svg); + return $svg; + } + } diff --git a/apps/theming/tests/Controller/IconControllerTest.php b/apps/theming/tests/Controller/IconControllerTest.php index e22369aa74..22d4ae343a 100644 --- a/apps/theming/tests/Controller/IconControllerTest.php +++ b/apps/theming/tests/Controller/IconControllerTest.php @@ -35,7 +35,7 @@ use Test\TestCase; use OCA\Theming\ThemingDefaults; use \Imagick; -class ThemingControllerTest extends TestCase { +class IconControllerTest extends TestCase { /** @var IRequest|\PHPUnit_Framework_MockObject_MockObject */ private $request; /** @var IConfig|\PHPUnit_Framework_MockObject_MockObject */ @@ -54,11 +54,21 @@ class ThemingControllerTest extends TestCase { private $rootFolder; public function setUp() { + + if(!extension_loaded('imagick')) { + $this->markTestSkipped('Tests skipped as Imagemagick is required for dynamic icon generation.'); + } + $checkImagick = new \Imagick(); + if (count($checkImagick->queryFormats('SVG')) < 1) { + $this->markTestSkipped('No SVG provider present'); + } + $this->request = $this->getMockBuilder('OCP\IRequest')->getMock(); $this->config = $this->getMockBuilder('OCP\IConfig')->getMock(); $this->themingDefaults = $this->getMockBuilder('OCA\Theming\ThemingDefaults') ->disableOriginalConstructor()->getMock(); - $this->util = new Util(); + $this->util = $this->getMockBuilder('\OCA\Theming\Util')->disableOriginalConstructor() + ->setMethods(['getAppImage', 'getAppIcon', 'elementColor'])->getMock(); $this->timeFactory = $this->getMockBuilder('OCP\AppFramework\Utility\ITimeFactory') ->disableOriginalConstructor() ->getMock(); @@ -84,10 +94,19 @@ class ThemingControllerTest extends TestCase { } public function testGetThemedIcon() { + $this->util->expects($this->once()) + ->method('getAppImage') + ->with('core','filetypes/folder.svg') + ->willReturn(\OC::$SERVERROOT . "/core/img/filetypes/folder.svg"); $this->themingDefaults ->expects($this->once()) ->method('getMailHeaderColor') ->willReturn('#000000'); + $this->util + ->expects($this->once()) + ->method('elementColor') + ->willReturn('#000000'); + $svg = " @@ -102,23 +121,27 @@ class ThemingControllerTest extends TestCase { } public function testGetFaviconDefault() { + + $this->util->expects($this->once()) + ->method('getAppIcon') + ->with('core') + ->willReturn(\OC::$SERVERROOT . "/core/img/logo.svg"); + $favicon = $this->iconController->getFavicon(); - - $expectedIcon = $this->invokePrivate($this->iconController, 'renderAppIcon', ["core"]); - $expectedIcon->resizeImage(32, 32, Imagick::FILTER_LANCZOS, 1); - $expectedIcon->setImageFormat("png24"); - + $expectedIcon = new \Imagick(realpath(dirname(__FILE__)) . '/../data/favicon-original.ico'); $expected = new DataDisplayResponse($expectedIcon, Http::STATUS_OK, ['Content-Type' => 'image/x-icon']); $expected->cacheFor(86400); $expected->addHeader('Expires', date(\DateTime::RFC2822, $this->timeFactory->getTime())); $this->assertEquals($expected, $favicon); } public function testGetTouchIconDefault() { - $favicon = $this->iconController->getTouchIcon(); - $expectedIcon = $this->invokePrivate($this->iconController, 'renderAppIcon', ["core"]); - $expectedIcon->resizeImage(512, 512, Imagick::FILTER_LANCZOS, 1); - $expectedIcon->setImageFormat("png24"); + $this->util->expects($this->once()) + ->method('getAppIcon') + ->with('core') + ->willReturn(\OC::$SERVERROOT . "/core/img/logo.svg"); + $favicon = $this->iconController->getTouchIcon(); + $expectedIcon = new \Imagick(realpath(dirname(__FILE__)) . '/../data/touch-original.png'); $expected = new DataDisplayResponse($expectedIcon, Http::STATUS_OK, ['Content-Type' => 'image/png']); $expected->cacheFor(86400); @@ -126,76 +149,42 @@ class ThemingControllerTest extends TestCase { $this->assertEquals($expected, $favicon); } - public function testRenderAppIcon() { + /** + * @dataProvider dataRenderAppIcon + * @param $appicon + * @param $color + * @param $file + */ + public function testRenderAppIcon($app, $appicon, $color, $file) { + + $this->util->expects($this->once()) + ->method('getAppIcon') + ->with($app) + ->willReturn(\OC::$SERVERROOT . "/" . $appicon); $this->themingDefaults->expects($this->once()) ->method('getMailHeaderColor') - ->willReturn('#000000'); + ->willReturn($color); + + $expectedIcon = new \Imagick(realpath(dirname(__FILE__)). "/../data/" . $file); + + $icon = $this->invokePrivate($this->iconController, 'renderAppIcon', [$app]); - $icon = $this->invokePrivate($this->iconController, 'renderAppIcon', ['core']); $this->assertEquals(true, $icon->valid()); $this->assertEquals(512, $icon->getImageWidth()); $this->assertEquals(512, $icon->getImageHeight()); + $this->assertEquals($icon, $expectedIcon); + //$this->assertLessThan(0.0005, $expectedIcon->compareImages($icon, Imagick::METRIC_MEANABSOLUTEERROR)[1]); } - public function testRenderAppIconColor() { - $this->themingDefaults->expects($this->once()) - ->method('getMailHeaderColor') - ->willReturn('#0082c9'); - $icon = $this->invokePrivate($this->iconController, 'renderAppIcon', ['core']); - $this->assertEquals(true, $icon->valid()); - $this->assertEquals(512, $icon->getImageWidth()); - $this->assertEquals(512, $icon->getImageHeight()); - - } - - - /** - * @dataProvider dataGetAppIcon - */ - public function testGetAppIcon($app, $expected) { - $icon = $this->invokePrivate($this->iconController, 'getAppIcon', [$app]); - $this->assertEquals($expected, $icon); - } - - public function dataGetAppIcon() { + public function dataRenderAppIcon() { return [ - ['user_ldap', \OC_App::getAppPath('user_ldap') . '/img/app.svg'], - ['noapplikethis', \OC::$SERVERROOT . '/core/img/logo.svg'], - ['comments', \OC_App::getAppPath('comments') . '/img/comments.svg'], + ['core','core/img/logo.svg', '#0082c9', 'touch-original.png'], + ['core','core/img/logo.svg', '#FF0000', 'touch-core-red.png'], + ['testing','apps/testing/img/app.svg', '#FF0000', 'touch-testing-red.png'], + ['comments','apps/comments/img/comments.svg', '#0082c9', 'touch-comments.png'], + ['core','core/img/logo.png', '#0082c9', 'touch-original-png.png'], ]; } - public function testGetAppIconThemed() { - $this->rootFolder->expects($this->once()) - ->method('nodeExists') - ->with('/themedinstancelogo') - ->willReturn(true); - $expected = '/themedinstancelogo'; - $icon = $this->invokePrivate($this->iconController, 'getAppIcon', ['noapplikethis']); - $this->assertEquals($expected, $icon); - } - - /** - * @dataProvider dataGetAppImage - */ - public function testGetAppImage($app, $image, $expected) { - $this->assertEquals($expected, $this->invokePrivate($this->iconController, 'getAppImage', [$app, $image])); - } - public function dataGetAppImage() { - return [ - ['core', 'logo.svg', \OC::$SERVERROOT . '/core/img/logo.svg'], - ['files', 'external', \OC::$SERVERROOT . '/apps/files/img/external.svg'], - ['files', 'external.svg', \OC::$SERVERROOT . '/apps/files/img/external.svg'], - ['noapplikethis', 'foobar.svg', false], - ]; - } - - public function testColorizeSvg() { - $input = "#0082c9 #0082C9 #000000 #FFFFFF"; - $expected = "#AAAAAA #AAAAAA #000000 #FFFFFF"; - $result = $this->invokePrivate($this->iconController, 'colorizeSvg', [$input, '#AAAAAA']); - $this->assertEquals($expected, $result); - } - } diff --git a/apps/theming/tests/Controller/ThemingControllerTest.php b/apps/theming/tests/Controller/ThemingControllerTest.php index 5de16a7abd..b065046fdb 100644 --- a/apps/theming/tests/Controller/ThemingControllerTest.php +++ b/apps/theming/tests/Controller/ThemingControllerTest.php @@ -61,12 +61,12 @@ class ThemingControllerTest extends TestCase { $this->config = $this->getMockBuilder('OCP\IConfig')->getMock(); $this->template = $this->getMockBuilder('OCA\Theming\ThemingDefaults') ->disableOriginalConstructor()->getMock(); - $this->util = new Util(); $this->timeFactory = $this->getMockBuilder('OCP\AppFramework\Utility\ITimeFactory') ->disableOriginalConstructor() ->getMock(); $this->l10n = $this->getMockBuilder('OCP\IL10N')->getMock(); $this->rootFolder = $this->getMockBuilder('OCP\Files\IRootFolder')->getMock(); + $this->util = new Util($this->config, $this->rootFolder); $this->timeFactory->expects($this->any()) ->method('getTime') ->willReturn(123); diff --git a/apps/theming/tests/UtilTest.php b/apps/theming/tests/UtilTest.php index c7fc385d25..499a3ffc45 100644 --- a/apps/theming/tests/UtilTest.php +++ b/apps/theming/tests/UtilTest.php @@ -23,16 +23,22 @@ namespace OCA\Theming\Tests; use OCA\Theming\Util; +use OCP\IConfig; +use OCP\Files\IRootFolder; use Test\TestCase; class UtilTest extends TestCase { /** @var Util */ protected $util; - + /** @var IConfig */ + protected $config; + protected $rootFolder; protected function setUp() { parent::setUp(); - $this->util = new Util(); + $this->config = $this->getMockBuilder('\OCP\IConfig')->getMock(); + $this->rootFolder = $this->getMockBuilder('OCP\Files\IRootFolder')->getMock(); + $this->util = new Util($this->config, $this->rootFolder); } public function testInvertTextColorLight() { @@ -94,4 +100,55 @@ class UtilTest extends TestCase { $expected = 'PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTYiIHdpZHRoPSIxNiI+PHBhdGggZD0iTTggMWE3IDcgMCAwIDAtNyA3IDcgNyAwIDAgMCA3IDcgNyA3IDAgMCAwIDctNyA3IDcgMCAwIDAtNy03em0wIDFhNiA2IDAgMCAxIDYgNiA2IDYgMCAwIDEtNiA2IDYgNiAwIDAgMS02LTYgNiA2IDAgMCAxIDYtNnptMCAyYTQgNCAwIDEgMCAwIDggNCA0IDAgMCAwIDAtOHoiIGZpbGw9IiMwMDAwMDAiLz48L3N2Zz4='; $this->assertEquals($expected, $button); } + + + + /** + * @dataProvider dataGetAppIcon + */ + public function testGetAppIcon($app, $expected) { + $icon = $this->util->getAppIcon($app); + $this->assertEquals($expected, $icon); + } + + public function dataGetAppIcon() { + return [ + ['user_ldap', \OC_App::getAppPath('user_ldap') . '/img/app.svg'], + ['noapplikethis', \OC::$SERVERROOT . '/core/img/logo.svg'], + ['comments', \OC_App::getAppPath('comments') . '/img/comments.svg'], + ]; + } + + public function testGetAppIconThemed() { + $this->rootFolder->expects($this->once()) + ->method('nodeExists') + ->with('/themedinstancelogo') + ->willReturn(true); + $expected = '/themedinstancelogo'; + $icon = $this->util->getAppIcon('noapplikethis'); + $this->assertEquals($expected, $icon); + } + + /** + * @dataProvider dataGetAppImage + */ + public function testGetAppImage($app, $image, $expected) { + $this->assertEquals($expected, $this->util->getAppImage($app, $image)); + } + public function dataGetAppImage() { + return [ + ['core', 'logo.svg', \OC::$SERVERROOT . '/core/img/logo.svg'], + ['files', 'external', \OC::$SERVERROOT . '/apps/files/img/external.svg'], + ['files', 'external.svg', \OC::$SERVERROOT . '/apps/files/img/external.svg'], + ['noapplikethis', 'foobar.svg', false], + ]; + } + + public function testColorizeSvg() { + $input = "#0082c9 #0082C9 #000000 #FFFFFF"; + $expected = "#AAAAAA #AAAAAA #000000 #FFFFFF"; + $result = $this->util->colorizeSvg($input, '#AAAAAA'); + $this->assertEquals($expected, $result); + } + } diff --git a/apps/theming/tests/data/favicon-original.ico b/apps/theming/tests/data/favicon-original.ico new file mode 100644 index 0000000000..fab2f7f023 Binary files /dev/null and b/apps/theming/tests/data/favicon-original.ico differ diff --git a/apps/theming/tests/data/touch-comments.png b/apps/theming/tests/data/touch-comments.png new file mode 100644 index 0000000000..af0d2ca579 Binary files /dev/null and b/apps/theming/tests/data/touch-comments.png differ diff --git a/apps/theming/tests/data/touch-core-red.png b/apps/theming/tests/data/touch-core-red.png new file mode 100644 index 0000000000..7a492f10e2 Binary files /dev/null and b/apps/theming/tests/data/touch-core-red.png differ diff --git a/apps/theming/tests/data/touch-original-png.png b/apps/theming/tests/data/touch-original-png.png new file mode 100644 index 0000000000..6997dfc6b8 Binary files /dev/null and b/apps/theming/tests/data/touch-original-png.png differ diff --git a/apps/theming/tests/data/touch-original.png b/apps/theming/tests/data/touch-original.png new file mode 100644 index 0000000000..ac39872aa6 Binary files /dev/null and b/apps/theming/tests/data/touch-original.png differ diff --git a/apps/theming/tests/data/touch-testing-red.png b/apps/theming/tests/data/touch-testing-red.png new file mode 100644 index 0000000000..4cbdfbbb42 Binary files /dev/null and b/apps/theming/tests/data/touch-testing-red.png differ