Caching for icon files using AppData
Signed-off-by: Julius Haertl <jus@bitgrid.net>
This commit is contained in:
parent
9e28a3ba12
commit
492d0b9f9b
|
@ -23,13 +23,16 @@
|
||||||
namespace OCA\Theming\Controller;
|
namespace OCA\Theming\Controller;
|
||||||
|
|
||||||
use OCA\Theming\IconBuilder;
|
use OCA\Theming\IconBuilder;
|
||||||
|
use OCA\Theming\ImageManager;
|
||||||
use OCA\Theming\ThemingDefaults;
|
use OCA\Theming\ThemingDefaults;
|
||||||
use OCP\AppFramework\Controller;
|
use OCP\AppFramework\Controller;
|
||||||
use OCP\AppFramework\Http;
|
use OCP\AppFramework\Http;
|
||||||
use OCP\AppFramework\Http\DataDisplayResponse;
|
use OCP\AppFramework\Http\DataDisplayResponse;
|
||||||
|
use OCP\AppFramework\Http\FileDisplayResponse;
|
||||||
use OCP\AppFramework\Utility\ITimeFactory;
|
use OCP\AppFramework\Utility\ITimeFactory;
|
||||||
use OCP\IRequest;
|
use OCP\IRequest;
|
||||||
use OCA\Theming\Util;
|
use OCA\Theming\Util;
|
||||||
|
use OCP\IConfig;
|
||||||
|
|
||||||
class IconController extends Controller {
|
class IconController extends Controller {
|
||||||
/** @var ThemingDefaults */
|
/** @var ThemingDefaults */
|
||||||
|
@ -38,8 +41,12 @@ class IconController extends Controller {
|
||||||
private $util;
|
private $util;
|
||||||
/** @var ITimeFactory */
|
/** @var ITimeFactory */
|
||||||
private $timeFactory;
|
private $timeFactory;
|
||||||
|
/** @var IConfig */
|
||||||
|
private $config;
|
||||||
/** @var IconBuilder */
|
/** @var IconBuilder */
|
||||||
private $iconBuilder;
|
private $iconBuilder;
|
||||||
|
/** @var ImageManager */
|
||||||
|
private $imageManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IconController constructor.
|
* IconController constructor.
|
||||||
|
@ -49,7 +56,9 @@ class IconController extends Controller {
|
||||||
* @param ThemingDefaults $themingDefaults
|
* @param ThemingDefaults $themingDefaults
|
||||||
* @param Util $util
|
* @param Util $util
|
||||||
* @param ITimeFactory $timeFactory
|
* @param ITimeFactory $timeFactory
|
||||||
|
* @param IConfig $config
|
||||||
* @param IconBuilder $iconBuilder
|
* @param IconBuilder $iconBuilder
|
||||||
|
* @param ImageManager $imageManager
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
$appName,
|
$appName,
|
||||||
|
@ -57,14 +66,18 @@ class IconController extends Controller {
|
||||||
ThemingDefaults $themingDefaults,
|
ThemingDefaults $themingDefaults,
|
||||||
Util $util,
|
Util $util,
|
||||||
ITimeFactory $timeFactory,
|
ITimeFactory $timeFactory,
|
||||||
IconBuilder $iconBuilder
|
IConfig $config,
|
||||||
|
IconBuilder $iconBuilder,
|
||||||
|
ImageManager $imageManager
|
||||||
) {
|
) {
|
||||||
parent::__construct($appName, $request);
|
parent::__construct($appName, $request);
|
||||||
|
|
||||||
$this->themingDefaults = $themingDefaults;
|
$this->themingDefaults = $themingDefaults;
|
||||||
$this->util = $util;
|
$this->util = $util;
|
||||||
$this->timeFactory = $timeFactory;
|
$this->timeFactory = $timeFactory;
|
||||||
|
$this->config = $config;
|
||||||
$this->iconBuilder = $iconBuilder;
|
$this->iconBuilder = $iconBuilder;
|
||||||
|
$this->imageManager = $imageManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -73,14 +86,15 @@ class IconController extends Controller {
|
||||||
*
|
*
|
||||||
* @param $app string app name
|
* @param $app string app name
|
||||||
* @param $image string image file name (svg required)
|
* @param $image string image file name (svg required)
|
||||||
* @return DataDisplayResponse
|
* @return FileDisplayResponse
|
||||||
*/
|
*/
|
||||||
public function getThemedIcon($app, $image) {
|
public function getThemedIcon($app, $image) {
|
||||||
$image = $this->util->getAppImage($app, $image);
|
$iconFile = $this->imageManager->getCachedImage("icon-" . $app . '-' . str_replace("/","_",$image));
|
||||||
$svg = file_get_contents($image);
|
if ($iconFile === null) {
|
||||||
$color = $this->util->elementColor($this->themingDefaults->getMailHeaderColor());
|
$icon = $this->iconBuilder->colorSvg($app, $image);
|
||||||
$svg = $this->util->colorizeSvg($svg, $color);
|
$iconFile = $this->imageManager->setCachedImage("icon-" . $app . '-' . str_replace("/","_",$image), $icon);
|
||||||
$response = new DataDisplayResponse($svg, Http::STATUS_OK, ['Content-Type' => 'image/svg+xml']);
|
}
|
||||||
|
$response = new FileDisplayResponse($iconFile, Http::STATUS_OK, ['Content-Type' => 'image/svg+xml']);
|
||||||
$response->cacheFor(86400);
|
$response->cacheFor(86400);
|
||||||
$response->addHeader('Expires', date(\DateTime::RFC2822, $this->timeFactory->getTime()));
|
$response->addHeader('Expires', date(\DateTime::RFC2822, $this->timeFactory->getTime()));
|
||||||
$response->addHeader('Pragma', 'cache');
|
$response->addHeader('Pragma', 'cache');
|
||||||
|
@ -94,12 +108,16 @@ class IconController extends Controller {
|
||||||
* @NoCSRFRequired
|
* @NoCSRFRequired
|
||||||
*
|
*
|
||||||
* @param $app string app name
|
* @param $app string app name
|
||||||
* @return DataDisplayResponse
|
* @return FileDisplayResponse|DataDisplayResponse
|
||||||
*/
|
*/
|
||||||
public function getFavicon($app="core") {
|
public function getFavicon($app = "core") {
|
||||||
if($this->themingDefaults->shouldReplaceIcons()) {
|
$iconFile = $this->imageManager->getCachedImage('favIcon-' . $app);
|
||||||
|
if($iconFile === null && $this->themingDefaults->shouldReplaceIcons()) {
|
||||||
$icon = $this->iconBuilder->getFavicon($app);
|
$icon = $this->iconBuilder->getFavicon($app);
|
||||||
$response = new DataDisplayResponse($icon, Http::STATUS_OK, ['Content-Type' => 'image/x-icon']);
|
$iconFile = $this->imageManager->setCachedImage('favIcon-' . $app, $icon);
|
||||||
|
}
|
||||||
|
if ($this->themingDefaults->shouldReplaceIcons()) {
|
||||||
|
$response = new FileDisplayResponse($iconFile, Http::STATUS_OK, ['Content-Type' => 'image/x-icon']);
|
||||||
} else {
|
} else {
|
||||||
$response = new DataDisplayResponse(null, Http::STATUS_NOT_FOUND);
|
$response = new DataDisplayResponse(null, Http::STATUS_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
@ -116,12 +134,16 @@ class IconController extends Controller {
|
||||||
* @NoCSRFRequired
|
* @NoCSRFRequired
|
||||||
*
|
*
|
||||||
* @param $app string app name
|
* @param $app string app name
|
||||||
* @return DataDisplayResponse
|
* @return FileDisplayResponse|DataDisplayResponse
|
||||||
*/
|
*/
|
||||||
public function getTouchIcon($app="core") {
|
public function getTouchIcon($app = "core") {
|
||||||
if($this->themingDefaults->shouldReplaceIcons()) {
|
$iconFile = $this->imageManager->getCachedImage('touchIcon-' . $app);
|
||||||
|
if ($iconFile === null && $this->themingDefaults->shouldReplaceIcons()) {
|
||||||
$icon = $this->iconBuilder->getTouchIcon($app);
|
$icon = $this->iconBuilder->getTouchIcon($app);
|
||||||
$response = new DataDisplayResponse($icon, Http::STATUS_OK, ['Content-Type' => 'image/png']);
|
$iconFile = $this->imageManager->setCachedImage('touchIcon-' . $app, $icon);
|
||||||
|
}
|
||||||
|
if ($this->themingDefaults->shouldReplaceIcons()) {
|
||||||
|
$response = new FileDisplayResponse($iconFile, Http::STATUS_OK, ['Content-Type' => 'image/png']);
|
||||||
} else {
|
} else {
|
||||||
$response = new DataDisplayResponse(null, Http::STATUS_NOT_FOUND);
|
$response = new DataDisplayResponse(null, Http::STATUS_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
@ -130,5 +152,4 @@ class IconController extends Controller {
|
||||||
$response->addHeader('Pragma', 'cache');
|
$response->addHeader('Pragma', 'cache');
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -80,19 +80,21 @@ class IconBuilder {
|
||||||
*/
|
*/
|
||||||
public function renderAppIcon($app) {
|
public function renderAppIcon($app) {
|
||||||
$appIcon = $this->util->getAppIcon($app);
|
$appIcon = $this->util->getAppIcon($app);
|
||||||
|
$appIconContent = file_get_contents($appIcon);
|
||||||
|
|
||||||
$color = $this->themingDefaults->getMailHeaderColor();
|
$color = $this->themingDefaults->getMailHeaderColor();
|
||||||
$mime = mime_content_type($appIcon);
|
$mime = mime_content_type($appIcon);
|
||||||
|
|
||||||
// generate background image with rounded corners
|
// generate background image with rounded corners
|
||||||
$background = '<?xml version="1.0" encoding="UTF-8"?>' .
|
$background = '<?xml version="1.0" encoding="UTF-8"?>' .
|
||||||
'<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:cc="http://creativecommons.org/ns#" width="512" height="512" xmlns:xlink="http://www.w3.org/1999/xlink">' .
|
'<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:cc="http://creativecommons.org/ns#" width="512" height="512" xmlns:xlink="http://www.w3.org/1999/xlink">' .
|
||||||
'<rect x="0" y="0" rx="75" ry="75" width="512" height="512" style="fill:' . $color . ';" />' .
|
'<rect x="0" y="0" rx="75" ry="75" width="512" height="512" style="fill:' . $color . ';" />' .
|
||||||
'</svg>';
|
'</svg>';
|
||||||
|
|
||||||
// resize svg magic as this seems broken in Imagemagick
|
// resize svg magic as this seems broken in Imagemagick
|
||||||
if($mime === "image/svg+xml") {
|
if($mime === "image/svg+xml" || substr($appIconContent, 0, 4) === "<svg") {
|
||||||
$svg = file_get_contents($appIcon);
|
if(substr($appIconContent, 0, 5) !== "<?xml") {
|
||||||
|
$svg = "<?xml version=\"1.0\"?>".$appIconContent;
|
||||||
|
}
|
||||||
$tmp = new Imagick();
|
$tmp = new Imagick();
|
||||||
$tmp->readImageBlob($svg);
|
$tmp->readImageBlob($svg);
|
||||||
$x = $tmp->getImageWidth();
|
$x = $tmp->getImageWidth();
|
||||||
|
@ -137,4 +139,12 @@ class IconBuilder {
|
||||||
return $finalIconFile;
|
return $finalIconFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function colorSvg($app, $image) {
|
||||||
|
$imageFile = $this->util->getAppImage($app, $image);
|
||||||
|
$svg = file_get_contents($imageFile);
|
||||||
|
$color = $this->util->elementColor($this->themingDefaults->getMailHeaderColor());
|
||||||
|
$svg = $this->util->colorizeSvg($svg, $color);
|
||||||
|
return $svg;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,118 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
|
||||||
|
*
|
||||||
|
* @author Julius Härtl <jus@bitgrid.net>
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
namespace OCA\Theming;
|
||||||
|
|
||||||
|
use OCP\IConfig;
|
||||||
|
use OCP\Files\IAppData;
|
||||||
|
use OCP\Files\NotFoundException;
|
||||||
|
use OCP\Files\NotPermittedException;
|
||||||
|
|
||||||
|
class ImageManager {
|
||||||
|
|
||||||
|
/** @var IConfig */
|
||||||
|
private $config;
|
||||||
|
/** @var IAppData */
|
||||||
|
private $appData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ImageManager constructor.
|
||||||
|
*
|
||||||
|
* @param IConfig $config
|
||||||
|
* @param IAppData $appData
|
||||||
|
*/
|
||||||
|
public function __construct(IConfig $config,
|
||||||
|
IAppData $appData
|
||||||
|
) {
|
||||||
|
$this->config = $config;
|
||||||
|
$this->appData = $appData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get folder for current theming files
|
||||||
|
*
|
||||||
|
* @return \OCP\Files\SimpleFS\ISimpleFolder
|
||||||
|
* @throws NotPermittedException
|
||||||
|
* @throws \RuntimeException
|
||||||
|
*/
|
||||||
|
public function getCacheFolder() {
|
||||||
|
$cacheBusterValue = $this->config->getAppValue('theming', 'cachebuster', '0');
|
||||||
|
try {
|
||||||
|
$folder = $this->appData->getFolder($cacheBusterValue);
|
||||||
|
} catch (NotFoundException $e) {
|
||||||
|
$folder = $this->appData->newFolder($cacheBusterValue);
|
||||||
|
$this->cleanup();
|
||||||
|
}
|
||||||
|
return $folder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a file from AppData
|
||||||
|
*
|
||||||
|
* @param string $filename
|
||||||
|
* @return null|\OCP\Files\SimpleFS\ISimpleFile
|
||||||
|
*/
|
||||||
|
public function getCachedImage($filename) {
|
||||||
|
$currentFolder = $this->getCacheFolder();
|
||||||
|
if($currentFolder->fileExists($filename)) {
|
||||||
|
return $currentFolder->getFile($filename);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store a file for theming in AppData
|
||||||
|
*
|
||||||
|
* @param string $filename
|
||||||
|
* @param string $data
|
||||||
|
* @return \OCP\Files\SimpleFS\ISimpleFile
|
||||||
|
*/
|
||||||
|
public function setCachedImage($filename, $data) {
|
||||||
|
$currentFolder = $this->getCacheFolder();
|
||||||
|
if ($currentFolder->fileExists($filename)) {
|
||||||
|
$file = $currentFolder->getFile($filename);
|
||||||
|
} else {
|
||||||
|
$file = $currentFolder->newFile($filename);
|
||||||
|
}
|
||||||
|
$file->putContent($data);
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* remove cached files that are not required any longer
|
||||||
|
*/
|
||||||
|
public function cleanup() {
|
||||||
|
$currentFolder = $this->getCacheFolder();
|
||||||
|
$folders = $this->appData->getDirectoryListing();
|
||||||
|
foreach ($folders as $folder) {
|
||||||
|
if ($folder->getName() !== $currentFolder->getName()) {
|
||||||
|
$folder->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -23,12 +23,18 @@
|
||||||
namespace OCA\Theming\Tests\Controller;
|
namespace OCA\Theming\Tests\Controller;
|
||||||
|
|
||||||
|
|
||||||
|
use OC\Files\SimpleFS\SimpleFile;
|
||||||
|
use OCA\Theming\ImageManager;
|
||||||
use OCP\AppFramework\Http;
|
use OCP\AppFramework\Http;
|
||||||
use OCP\AppFramework\Http\DataDisplayResponse;
|
use OCP\AppFramework\Http\DataDisplayResponse;
|
||||||
|
use OCP\Files\IRootFolder;
|
||||||
|
use OCP\IConfig;
|
||||||
|
use OCP\IL10N;
|
||||||
use OCP\IRequest;
|
use OCP\IRequest;
|
||||||
use Test\TestCase;
|
use Test\TestCase;
|
||||||
use OCA\Theming\Util;
|
use OCA\Theming\Util;
|
||||||
use OCA\Theming\Controller\IconController;
|
use OCA\Theming\Controller\IconController;
|
||||||
|
use OCP\AppFramework\Http\FileDisplayResponse;
|
||||||
|
|
||||||
|
|
||||||
class IconControllerTest extends TestCase {
|
class IconControllerTest extends TestCase {
|
||||||
|
@ -42,8 +48,12 @@ class IconControllerTest extends TestCase {
|
||||||
private $timeFactory;
|
private $timeFactory;
|
||||||
/** @var IL10N|\PHPUnit_Framework_MockObject_MockObject */
|
/** @var IL10N|\PHPUnit_Framework_MockObject_MockObject */
|
||||||
private $iconController;
|
private $iconController;
|
||||||
|
/** @var IConfig|\PHPUnit_Framework_MockObject_MockObject */
|
||||||
|
private $config;
|
||||||
/** @var IRootFolder|\PHPUnit_Framework_MockObject_MockObject */
|
/** @var IRootFolder|\PHPUnit_Framework_MockObject_MockObject */
|
||||||
private $iconBuilder;
|
private $iconBuilder;
|
||||||
|
/** @var ImageManager */
|
||||||
|
private $imageManager;
|
||||||
|
|
||||||
public function setUp() {
|
public function setUp() {
|
||||||
$this->request = $this->getMockBuilder('OCP\IRequest')->getMock();
|
$this->request = $this->getMockBuilder('OCP\IRequest')->getMock();
|
||||||
|
@ -54,9 +64,10 @@ class IconControllerTest extends TestCase {
|
||||||
$this->timeFactory = $this->getMockBuilder('OCP\AppFramework\Utility\ITimeFactory')
|
$this->timeFactory = $this->getMockBuilder('OCP\AppFramework\Utility\ITimeFactory')
|
||||||
->disableOriginalConstructor()
|
->disableOriginalConstructor()
|
||||||
->getMock();
|
->getMock();
|
||||||
|
$this->config = $this->getMockBuilder('OCP\IConfig')->getMock();
|
||||||
$this->iconBuilder = $this->getMockBuilder('OCA\Theming\IconBuilder')
|
$this->iconBuilder = $this->getMockBuilder('OCA\Theming\IconBuilder')
|
||||||
->disableOriginalConstructor()->getMock();
|
->disableOriginalConstructor()->getMock();
|
||||||
|
$this->imageManager = $this->getMockBuilder('OCA\Theming\ImageManager')->disableOriginalConstructor()->getMock();
|
||||||
$this->timeFactory->expects($this->any())
|
$this->timeFactory->expects($this->any())
|
||||||
->method('getTime')
|
->method('getTime')
|
||||||
->willReturn(123);
|
->willReturn(123);
|
||||||
|
@ -67,124 +78,114 @@ class IconControllerTest extends TestCase {
|
||||||
$this->themingDefaults,
|
$this->themingDefaults,
|
||||||
$this->util,
|
$this->util,
|
||||||
$this->timeFactory,
|
$this->timeFactory,
|
||||||
$this->iconBuilder
|
$this->config,
|
||||||
|
$this->iconBuilder,
|
||||||
|
$this->imageManager
|
||||||
);
|
);
|
||||||
|
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetThemedIcon() {
|
private function iconFileMock($filename, $data) {
|
||||||
$this->util->expects($this->once())
|
$icon = $this->getMockBuilder('OCP\Files\File')->getMock();
|
||||||
->method('getAppImage')
|
$icon->expects($this->any())->method('getContent')->willReturn($data);
|
||||||
->with('core','filetypes/folder.svg')
|
$icon->expects($this->any())->method('getMimeType')->willReturn('image type');
|
||||||
->willReturn(\OC::$SERVERROOT . "/core/img/filetypes/folder.svg");
|
$icon->expects($this->any())->method('getEtag')->willReturn('my etag');
|
||||||
$this->themingDefaults
|
$icon->method('getName')->willReturn($filename);
|
||||||
->expects($this->once())
|
return new SimpleFile($icon);
|
||||||
->method('getMailHeaderColor')
|
}
|
||||||
->willReturn('#000000');
|
|
||||||
$this->util
|
|
||||||
->expects($this->once())
|
|
||||||
->method('elementColor')
|
|
||||||
->willReturn('#000000');
|
|
||||||
|
|
||||||
$svg = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>
|
public function testGetThemedIcon() {
|
||||||
<svg xmlns=\"http://www.w3.org/2000/svg\" height=\"16\" width=\"16\" version=\"1.0\">
|
$file = $this->iconFileMock('icon-core-filetypes_folder.svg', 'filecontent');
|
||||||
<g fill-rule=\"evenodd\" transform=\"matrix(.86667 0 0 .86667 -172.05 -864.43)\" fill=\"#000000\">
|
$this->imageManager->expects($this->once())
|
||||||
<path d=\"m200.2 999.72c-0.28913 0-0.53125 0.2421-0.53125 0.53117v12.784c0 0.2985 0.23264 0.5312 0.53125 0.5312h15.091c0.2986 0 0.53124-0.2327 0.53124-0.5312l0.0004-10.474c0-0.2889-0.24211-0.5338-0.53124-0.5338l-7.5457 0.0005-2.3076-2.3078z\" fill-rule=\"evenodd\" fill=\"#000000\"/>
|
->method('getCachedImage')
|
||||||
</g>
|
->with('icon-core-filetypes_folder.svg')
|
||||||
</svg>
|
->willReturn($file);
|
||||||
";
|
$expected = new FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => 'image/svg+xml']);
|
||||||
$expected = new DataDisplayResponse($svg, Http::STATUS_OK, ['Content-Type' => 'image/svg+xml']);
|
|
||||||
$expected->cacheFor(86400);
|
$expected->cacheFor(86400);
|
||||||
$expected->addHeader('Expires', date(\DateTime::RFC2822, $this->timeFactory->getTime()));
|
$expected->addHeader('Expires', date(\DateTime::RFC2822, $this->timeFactory->getTime()));
|
||||||
$expected->addHeader('Pragma', 'cache');
|
$expected->addHeader('Pragma', 'cache');
|
||||||
@$this->assertEquals($expected, $this->iconController->getThemedIcon('core','filetypes/folder.svg'));
|
@$this->assertEquals($expected, $this->iconController->getThemedIcon('core', 'filetypes/folder.svg'));
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetFaviconDefault() {
|
public function testGetFaviconDefault() {
|
||||||
if(!extension_loaded('imagick')) {
|
if (!extension_loaded('imagick')) {
|
||||||
$this->markTestSkipped('Imagemagick is required for dynamic icon generation.');
|
$this->markTestSkipped('Imagemagick is required for dynamic icon generation.');
|
||||||
}
|
}
|
||||||
$checkImagick = new \Imagick();
|
$checkImagick = new \Imagick();
|
||||||
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->once())
|
$this->themingDefaults->expects($this->any())
|
||||||
->method('shouldReplaceIcons')
|
->method('shouldReplaceIcons')
|
||||||
->willReturn(true);
|
->willReturn(true);
|
||||||
$expectedIcon = new \Imagick(realpath(dirname(__FILE__)) . '/../data/favicon-original.ico');
|
|
||||||
$this->iconBuilder->expects($this->once())
|
$this->iconBuilder->expects($this->once())
|
||||||
->method('getFavicon')
|
->method('getFavicon')
|
||||||
->with('core')
|
->with('core')
|
||||||
->willReturn($expectedIcon);
|
->willReturn('filecontent');
|
||||||
$favicon = $this->iconController->getFavicon();
|
$file = $this->iconFileMock('filename', 'filecontent');
|
||||||
|
$this->imageManager->expects($this->once())
|
||||||
|
->method('setCachedImage')
|
||||||
|
->willReturn($file);
|
||||||
|
|
||||||
$expected = new DataDisplayResponse($expectedIcon, Http::STATUS_OK, ['Content-Type' => 'image/x-icon']);
|
$expected = new FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => 'image/x-icon']);
|
||||||
$expected->cacheFor(86400);
|
$expected->cacheFor(86400);
|
||||||
$expected->addHeader('Expires', date(\DateTime::RFC2822, $this->timeFactory->getTime()));
|
$expected->addHeader('Expires', date(\DateTime::RFC2822, $this->timeFactory->getTime()));
|
||||||
$expected->addHeader('Pragma', 'cache');
|
$expected->addHeader('Pragma', 'cache');
|
||||||
$this->assertEquals($expected, $favicon);
|
$this->assertEquals($expected, $this->iconController->getFavicon());
|
||||||
}
|
|
||||||
public function testGetTouchIconDefault() {
|
|
||||||
if(!extension_loaded('imagick')) {
|
|
||||||
$this->markTestSkipped('Imagemagick is required for dynamic icon generation.');
|
|
||||||
}
|
|
||||||
$checkImagick = new \Imagick();
|
|
||||||
if (count($checkImagick->queryFormats('SVG')) < 1) {
|
|
||||||
$this->markTestSkipped('No SVG provider present.');
|
|
||||||
}
|
|
||||||
$this->themingDefaults->expects($this->once())
|
|
||||||
->method('shouldReplaceIcons')
|
|
||||||
->willReturn(true);
|
|
||||||
$expectedIcon = new \Imagick(realpath(dirname(__FILE__)) . '/../data/touch-original.png');
|
|
||||||
$this->iconBuilder->expects($this->once())
|
|
||||||
->method('getTouchIcon')
|
|
||||||
->with('core')
|
|
||||||
->willReturn($expectedIcon);
|
|
||||||
$favicon = $this->iconController->getTouchIcon();
|
|
||||||
|
|
||||||
$expected = new DataDisplayResponse($expectedIcon, Http::STATUS_OK, ['Content-Type' => 'image/png']);
|
|
||||||
$expected->cacheFor(86400);
|
|
||||||
$expected->addHeader('Expires', date(\DateTime::RFC2822, $this->timeFactory->getTime()));
|
|
||||||
$expected->addHeader('Pragma', 'cache');
|
|
||||||
$this->assertEquals($expected, $favicon);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetFaviconFail() {
|
public function testGetFaviconFail() {
|
||||||
if(!extension_loaded('imagick')) {
|
$this->themingDefaults->expects($this->any())
|
||||||
$this->markTestSkipped('Imagemagick is required for dynamic icon generation.');
|
|
||||||
}
|
|
||||||
$checkImagick = new \Imagick();
|
|
||||||
if (count($checkImagick->queryFormats('SVG')) < 1) {
|
|
||||||
$this->markTestSkipped('No SVG provider present.');
|
|
||||||
}
|
|
||||||
$this->themingDefaults->expects($this->once())
|
|
||||||
->method('shouldReplaceIcons')
|
->method('shouldReplaceIcons')
|
||||||
->willReturn(false);
|
->willReturn(false);
|
||||||
$favicon = $this->iconController->getFavicon();
|
|
||||||
$expected = new DataDisplayResponse(null, Http::STATUS_NOT_FOUND);
|
$expected = new DataDisplayResponse(null, Http::STATUS_NOT_FOUND);
|
||||||
$expected->cacheFor(86400);
|
$expected->cacheFor(86400);
|
||||||
$expected->addHeader('Expires', date(\DateTime::RFC2822, $this->timeFactory->getTime()));
|
$expected->addHeader('Expires', date(\DateTime::RFC2822, $this->timeFactory->getTime()));
|
||||||
$expected->addHeader('Pragma', 'cache');
|
$expected->addHeader('Pragma', 'cache');
|
||||||
$this->assertEquals($expected, $favicon);
|
$this->assertEquals($expected, $this->iconController->getFavicon());
|
||||||
}
|
}
|
||||||
public function testGetTouchIconFail() {
|
|
||||||
if(!extension_loaded('imagick')) {
|
public function testGetTouchIconDefault() {
|
||||||
|
if (!extension_loaded('imagick')) {
|
||||||
$this->markTestSkipped('Imagemagick is required for dynamic icon generation.');
|
$this->markTestSkipped('Imagemagick is required for dynamic icon generation.');
|
||||||
}
|
}
|
||||||
$checkImagick = new \Imagick();
|
$checkImagick = new \Imagick();
|
||||||
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->once())
|
$this->themingDefaults->expects($this->any())
|
||||||
|
->method('shouldReplaceIcons')
|
||||||
|
->willReturn(true);
|
||||||
|
|
||||||
|
$this->iconBuilder->expects($this->once())
|
||||||
|
->method('getTouchIcon')
|
||||||
|
->with('core')
|
||||||
|
->willReturn('filecontent');
|
||||||
|
$file = $this->iconFileMock('filename', 'filecontent');
|
||||||
|
$this->imageManager->expects($this->once())
|
||||||
|
->method('setCachedImage')
|
||||||
|
->willReturn($file);
|
||||||
|
|
||||||
|
$expected = new FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => 'image/png']);
|
||||||
|
$expected->cacheFor(86400);
|
||||||
|
$expected->addHeader('Expires', date(\DateTime::RFC2822, $this->timeFactory->getTime()));
|
||||||
|
$expected->addHeader('Pragma', 'cache');
|
||||||
|
$this->assertEquals($expected, $this->iconController->getTouchIcon());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetTouchIconFail() {
|
||||||
|
$this->themingDefaults->expects($this->any())
|
||||||
->method('shouldReplaceIcons')
|
->method('shouldReplaceIcons')
|
||||||
->willReturn(false);
|
->willReturn(false);
|
||||||
$favicon = $this->iconController->getTouchIcon();
|
|
||||||
$expected = new DataDisplayResponse(null, Http::STATUS_NOT_FOUND);
|
$expected = new DataDisplayResponse(null, Http::STATUS_NOT_FOUND);
|
||||||
$expected->cacheFor(86400);
|
$expected->cacheFor(86400);
|
||||||
$expected->addHeader('Expires', date(\DateTime::RFC2822, $this->timeFactory->getTime()));
|
$expected->addHeader('Expires', date(\DateTime::RFC2822, $this->timeFactory->getTime()));
|
||||||
$expected->addHeader('Pragma', 'cache');
|
$expected->addHeader('Pragma', 'cache');
|
||||||
$this->assertEquals($expected, $favicon);
|
$this->assertEquals($expected, $this->iconController->getTouchIcon());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,15 @@ class IconBuilderTest extends TestCase {
|
||||||
protected function setUp() {
|
protected function setUp() {
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->config = $this->getMockBuilder('\OCP\IConfig')->getMock();
|
||||||
|
$this->rootFolder = $this->getMockBuilder('OCP\Files\IRootFolder')->getMock();
|
||||||
|
$this->themingDefaults = $this->getMockBuilder('OCA\Theming\ThemingDefaults')
|
||||||
|
->disableOriginalConstructor()->getMock();
|
||||||
|
$this->util = new Util($this->config, $this->rootFolder);
|
||||||
|
$this->iconBuilder = new IconBuilder($this->themingDefaults, $this->util);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function checkImagick() {
|
||||||
if(!extension_loaded('imagick')) {
|
if(!extension_loaded('imagick')) {
|
||||||
$this->markTestSkipped('Imagemagick is required for dynamic icon generation.');
|
$this->markTestSkipped('Imagemagick is required for dynamic icon generation.');
|
||||||
}
|
}
|
||||||
|
@ -52,13 +61,6 @@ class IconBuilderTest 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->config = $this->getMockBuilder('\OCP\IConfig')->getMock();
|
|
||||||
$this->rootFolder = $this->getMockBuilder('OCP\Files\IRootFolder')->getMock();
|
|
||||||
$this->themingDefaults = $this->getMockBuilder('OCA\Theming\ThemingDefaults')
|
|
||||||
->disableOriginalConstructor()->getMock();
|
|
||||||
$this->util = new Util($this->config, $this->rootFolder);
|
|
||||||
$this->iconBuilder = new IconBuilder($this->themingDefaults, $this->util);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function dataRenderAppIcon() {
|
public function dataRenderAppIcon() {
|
||||||
|
@ -78,6 +80,7 @@ class IconBuilderTest extends TestCase {
|
||||||
* @param $file
|
* @param $file
|
||||||
*/
|
*/
|
||||||
public function testRenderAppIcon($app, $color, $file) {
|
public function testRenderAppIcon($app, $color, $file) {
|
||||||
|
$this->checkImagick();
|
||||||
$this->themingDefaults->expects($this->once())
|
$this->themingDefaults->expects($this->once())
|
||||||
->method('getMailHeaderColor')
|
->method('getMailHeaderColor')
|
||||||
->willReturn($color);
|
->willReturn($color);
|
||||||
|
@ -102,6 +105,7 @@ class IconBuilderTest extends TestCase {
|
||||||
* @param $file
|
* @param $file
|
||||||
*/
|
*/
|
||||||
public function testGetTouchIcon($app, $color, $file) {
|
public function testGetTouchIcon($app, $color, $file) {
|
||||||
|
$this->checkImagick();
|
||||||
$this->themingDefaults->expects($this->once())
|
$this->themingDefaults->expects($this->once())
|
||||||
->method('getMailHeaderColor')
|
->method('getMailHeaderColor')
|
||||||
->willReturn($color);
|
->willReturn($color);
|
||||||
|
@ -127,6 +131,7 @@ class IconBuilderTest extends TestCase {
|
||||||
* @param $file
|
* @param $file
|
||||||
*/
|
*/
|
||||||
public function testGetFavicon($app, $color, $file) {
|
public function testGetFavicon($app, $color, $file) {
|
||||||
|
$this->checkImagick();
|
||||||
$this->themingDefaults->expects($this->once())
|
$this->themingDefaults->expects($this->once())
|
||||||
->method('getMailHeaderColor')
|
->method('getMailHeaderColor')
|
||||||
->willReturn($color);
|
->willReturn($color);
|
||||||
|
|
|
@ -0,0 +1,185 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
|
||||||
|
*
|
||||||
|
* @author Julius Härtl <jus@bitgrid.net>
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
namespace OCA\Theming\Tests;
|
||||||
|
|
||||||
|
use OCP\Files\SimpleFS\ISimpleFile;
|
||||||
|
use OCP\IConfig;
|
||||||
|
use Test\TestCase;
|
||||||
|
use OCP\Files\SimpleFS\ISimpleFolder;
|
||||||
|
use OCP\Files\IAppData;
|
||||||
|
use OCP\Files\NotFoundException;
|
||||||
|
|
||||||
|
class ImageManager extends TestCase {
|
||||||
|
|
||||||
|
/** @var IConfig|\PHPUnit_Framework_MockObject_MockObject */
|
||||||
|
protected $config;
|
||||||
|
/** @var IAppData|\PHPUnit_Framework_MockObject_MockObject */
|
||||||
|
protected $appData;
|
||||||
|
/** @var ImageManager */
|
||||||
|
protected $imageManager;
|
||||||
|
|
||||||
|
protected function setUp() {
|
||||||
|
parent::setUp();
|
||||||
|
$this->config = $this->getMockBuilder('\OCP\IConfig')->getMock();
|
||||||
|
$this->appData = $this->getMockBuilder('OCP\Files\IAppData')->getMock();
|
||||||
|
$this->imageManager = new \OCA\Theming\ImageManager(
|
||||||
|
$this->config,
|
||||||
|
$this->appData
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetCacheFolder() {
|
||||||
|
$folder = $this->createMock(ISimpleFolder::class);
|
||||||
|
$this->config->expects($this->once())
|
||||||
|
->method('getAppValue')
|
||||||
|
->with('theming', 'cachebuster', '0')
|
||||||
|
->willReturn('0');
|
||||||
|
$this->appData->expects($this->at(0))
|
||||||
|
->method('getFolder')
|
||||||
|
->with('0')
|
||||||
|
->willReturn($folder);
|
||||||
|
$this->assertEquals($folder, $this->imageManager->getCacheFolder());
|
||||||
|
}
|
||||||
|
public function testGetCacheFolderCreate() {
|
||||||
|
$folder = $this->createMock(ISimpleFolder::class);
|
||||||
|
$this->config->expects($this->exactly(2))
|
||||||
|
->method('getAppValue')
|
||||||
|
->with('theming', 'cachebuster', '0')
|
||||||
|
->willReturn('0');
|
||||||
|
$this->appData->expects($this->at(0))
|
||||||
|
->method('getFolder')
|
||||||
|
->willThrowException(new NotFoundException());
|
||||||
|
$this->appData->expects($this->at(1))
|
||||||
|
->method('newFolder')
|
||||||
|
->with('0')
|
||||||
|
->willReturn($folder);
|
||||||
|
$this->appData->expects($this->at(2))
|
||||||
|
->method('getFolder')
|
||||||
|
->with('0')
|
||||||
|
->willReturn($folder);
|
||||||
|
$this->appData->expects($this->once())
|
||||||
|
->method('getDirectoryListing')
|
||||||
|
->willReturn([]);
|
||||||
|
$this->assertEquals($folder, $this->imageManager->getCacheFolder());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetCachedImage() {
|
||||||
|
$folder = $this->setupCacheFolder();
|
||||||
|
$folder->expects($this->once())
|
||||||
|
->method('fileExists')
|
||||||
|
->with('filename')
|
||||||
|
->willReturn(true);
|
||||||
|
$folder->expects($this->once())
|
||||||
|
->method('getFile')
|
||||||
|
->with('filename')
|
||||||
|
->willReturn('filecontent');
|
||||||
|
$expected = 'filecontent';
|
||||||
|
$this->assertEquals($expected, $this->imageManager->getCachedImage('filename'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetCachedImageNotFound() {
|
||||||
|
$folder = $this->setupCacheFolder();
|
||||||
|
$folder->expects($this->once())
|
||||||
|
->method('fileExists')
|
||||||
|
->with('filename')
|
||||||
|
->willReturn(false);
|
||||||
|
$expected = null;
|
||||||
|
$this->assertEquals($expected, $this->imageManager->getCachedImage('filename'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetCachedImage() {
|
||||||
|
$folder = $this->setupCacheFolder();
|
||||||
|
$file = $this->createMock(ISimpleFile::class);
|
||||||
|
$folder->expects($this->once())
|
||||||
|
->method('fileExists')
|
||||||
|
->with('filename')
|
||||||
|
->willReturn(true);
|
||||||
|
$folder->expects($this->once())
|
||||||
|
->method('getFile')
|
||||||
|
->with('filename')
|
||||||
|
->willReturn($file);
|
||||||
|
$file->expects($this->once())
|
||||||
|
->method('putContent')
|
||||||
|
->with('filecontent');
|
||||||
|
$this->assertEquals($file, $this->imageManager->setCachedImage('filename', 'filecontent'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetCachedImageCreate() {
|
||||||
|
$folder = $this->setupCacheFolder();
|
||||||
|
$file = $this->createMock(ISimpleFile::class);
|
||||||
|
$folder->expects($this->once())
|
||||||
|
->method('fileExists')
|
||||||
|
->with('filename')
|
||||||
|
->willReturn(false);
|
||||||
|
$folder->expects($this->once())
|
||||||
|
->method('newFile')
|
||||||
|
->with('filename')
|
||||||
|
->willReturn($file);
|
||||||
|
$file->expects($this->once())
|
||||||
|
->method('putContent')
|
||||||
|
->with('filecontent');
|
||||||
|
$this->assertEquals($file, $this->imageManager->setCachedImage('filename', 'filecontent'));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function setupCacheFolder() {
|
||||||
|
$folder = $this->createMock(ISimpleFolder::class);
|
||||||
|
$this->config->expects($this->once())
|
||||||
|
->method('getAppValue')
|
||||||
|
->with('theming', 'cachebuster', '0')
|
||||||
|
->willReturn('0');
|
||||||
|
$this->appData->expects($this->at(0))
|
||||||
|
->method('getFolder')
|
||||||
|
->with('0')
|
||||||
|
->willReturn($folder);
|
||||||
|
return $folder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCleanup() {
|
||||||
|
$folders = [
|
||||||
|
$this->createMock(ISimpleFolder::class),
|
||||||
|
$this->createMock(ISimpleFolder::class),
|
||||||
|
$this->createMock(ISimpleFolder::class)
|
||||||
|
];
|
||||||
|
foreach ($folders as $index=>$folder) {
|
||||||
|
$folder->expects($this->any())
|
||||||
|
->method('getName')
|
||||||
|
->willReturn($index);
|
||||||
|
}
|
||||||
|
$folders[0]->expects($this->once())->method('delete');
|
||||||
|
$folders[1]->expects($this->once())->method('delete');
|
||||||
|
$folders[2]->expects($this->never())->method('delete');
|
||||||
|
$this->config->expects($this->once())
|
||||||
|
->method('getAppValue')
|
||||||
|
->with('theming','cachebuster','0')
|
||||||
|
->willReturn('2');
|
||||||
|
$this->appData->expects($this->once())
|
||||||
|
->method('getDirectoryListing')
|
||||||
|
->willReturn($folders);
|
||||||
|
$this->appData->expects($this->once())
|
||||||
|
->method('getFolder')
|
||||||
|
->with('2')
|
||||||
|
->willReturn($folders[2]);
|
||||||
|
$this->imageManager->cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue