Merge pull request #840 from nextcloud/theming-icon-endpoint
Add dynamic icon creation
This commit is contained in:
commit
faee255ff4
|
@ -60,5 +60,23 @@ return ['routes' => [
|
|||
'url' => '/js/theming',
|
||||
'verb' => 'GET',
|
||||
],
|
||||
[
|
||||
'name' => 'Icon#getFavicon',
|
||||
'url' => '/favicon/{app}',
|
||||
'verb' => 'GET',
|
||||
'defaults' => array('app' => 'core'),
|
||||
],
|
||||
[
|
||||
'name' => 'Icon#getTouchIcon',
|
||||
'url' => '/icon/{app}',
|
||||
'verb' => 'GET',
|
||||
'defaults' => array('app' => 'core'),
|
||||
],
|
||||
[
|
||||
'name' => 'Icon#getThemedIcon',
|
||||
'url' => '/img/{app}/{image}',
|
||||
'verb' => 'GET',
|
||||
'requirements' => array('image' => '.+')
|
||||
],
|
||||
]];
|
||||
|
||||
|
|
|
@ -0,0 +1,174 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2016 Julius Haertl <jus@bitgrid.net>
|
||||
*
|
||||
* @author Julius Haertl <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\Controller;
|
||||
|
||||
use OCA\Theming\IconBuilder;
|
||||
use OCA\Theming\ImageManager;
|
||||
use OCA\Theming\ThemingDefaults;
|
||||
use OCP\AppFramework\Controller;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\NotFoundResponse;
|
||||
use OCP\AppFramework\Http\FileDisplayResponse;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\Files\NotFoundException;
|
||||
use OCP\IRequest;
|
||||
use OCA\Theming\Util;
|
||||
use OCP\IConfig;
|
||||
|
||||
class IconController extends Controller {
|
||||
/** @var ThemingDefaults */
|
||||
private $themingDefaults;
|
||||
/** @var Util */
|
||||
private $util;
|
||||
/** @var ITimeFactory */
|
||||
private $timeFactory;
|
||||
/** @var IConfig */
|
||||
private $config;
|
||||
/** @var IconBuilder */
|
||||
private $iconBuilder;
|
||||
/** @var ImageManager */
|
||||
private $imageManager;
|
||||
|
||||
/**
|
||||
* IconController constructor.
|
||||
*
|
||||
* @param string $appName
|
||||
* @param IRequest $request
|
||||
* @param ThemingDefaults $themingDefaults
|
||||
* @param Util $util
|
||||
* @param ITimeFactory $timeFactory
|
||||
* @param IConfig $config
|
||||
* @param IconBuilder $iconBuilder
|
||||
* @param ImageManager $imageManager
|
||||
*/
|
||||
public function __construct(
|
||||
$appName,
|
||||
IRequest $request,
|
||||
ThemingDefaults $themingDefaults,
|
||||
Util $util,
|
||||
ITimeFactory $timeFactory,
|
||||
IConfig $config,
|
||||
IconBuilder $iconBuilder,
|
||||
ImageManager $imageManager
|
||||
) {
|
||||
parent::__construct($appName, $request);
|
||||
|
||||
$this->themingDefaults = $themingDefaults;
|
||||
$this->util = $util;
|
||||
$this->timeFactory = $timeFactory;
|
||||
$this->config = $config;
|
||||
$this->iconBuilder = $iconBuilder;
|
||||
$this->imageManager = $imageManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @PublicPage
|
||||
* @NoCSRFRequired
|
||||
*
|
||||
* @param $app string app name
|
||||
* @param $image string image file name (svg required)
|
||||
* @return FileDisplayResponse|NotFoundResponse
|
||||
*/
|
||||
public function getThemedIcon($app, $image) {
|
||||
try {
|
||||
$iconFile = $this->imageManager->getCachedImage("icon-" . $app . '-' . str_replace("/","_",$image));
|
||||
} catch (NotFoundException $exception) {
|
||||
$icon = $this->iconBuilder->colorSvg($app, $image);
|
||||
$iconFile = $this->imageManager->setCachedImage("icon-" . $app . '-' . str_replace("/","_",$image), $icon);
|
||||
}
|
||||
if ($iconFile !== false) {
|
||||
$response = new FileDisplayResponse($iconFile, Http::STATUS_OK, ['Content-Type' => 'image/svg+xml']);
|
||||
$response->cacheFor(86400);
|
||||
$expires = new \DateTime();
|
||||
$expires->setTimestamp($this->timeFactory->getTime());
|
||||
$expires->add(new \DateInterval('PT24H'));
|
||||
$response->addHeader('Expires', $expires->format(\DateTime::RFC2822));
|
||||
$response->addHeader('Pragma', 'cache');
|
||||
return $response;
|
||||
} else {
|
||||
return new NotFoundResponse();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a 32x32 favicon as png
|
||||
*
|
||||
* @PublicPage
|
||||
* @NoCSRFRequired
|
||||
*
|
||||
* @param $app string app name
|
||||
* @return FileDisplayResponse|NotFoundResponse
|
||||
*/
|
||||
public function getFavicon($app = "core") {
|
||||
if ($this->themingDefaults->shouldReplaceIcons()) {
|
||||
try {
|
||||
$iconFile = $this->imageManager->getCachedImage('favIcon-' . $app);
|
||||
} catch (NotFoundException $exception) {
|
||||
$icon = $this->iconBuilder->getFavicon($app);
|
||||
$iconFile = $this->imageManager->setCachedImage('favIcon-' . $app, $icon);
|
||||
}
|
||||
if ($iconFile !== false) {
|
||||
$response = new FileDisplayResponse($iconFile, Http::STATUS_OK, ['Content-Type' => 'image/x-icon']);
|
||||
$response->cacheFor(86400);
|
||||
$expires = new \DateTime();
|
||||
$expires->setTimestamp($this->timeFactory->getTime());
|
||||
$expires->add(new \DateInterval('PT24H'));
|
||||
$response->addHeader('Expires', $expires->format(\DateTime::RFC2822));
|
||||
$response->addHeader('Pragma', 'cache');
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
return new NotFoundResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a 512x512 icon for touch devices
|
||||
*
|
||||
* @PublicPage
|
||||
* @NoCSRFRequired
|
||||
*
|
||||
* @param $app string app name
|
||||
* @return FileDisplayResponse|NotFoundResponse
|
||||
*/
|
||||
public function getTouchIcon($app = "core") {
|
||||
if ($this->themingDefaults->shouldReplaceIcons()) {
|
||||
try {
|
||||
$iconFile = $this->imageManager->getCachedImage('touchIcon-' . $app);
|
||||
} catch (NotFoundException $exception) {
|
||||
$icon = $this->iconBuilder->getTouchIcon($app);
|
||||
$iconFile = $this->imageManager->setCachedImage('touchIcon-' . $app, $icon);
|
||||
}
|
||||
if ($iconFile !== false) {
|
||||
$response = new FileDisplayResponse($iconFile, Http::STATUS_OK, ['Content-Type' => 'image/png']);
|
||||
$response->cacheFor(86400);
|
||||
$expires = new \DateTime();
|
||||
$expires->setTimestamp($this->timeFactory->getTime());
|
||||
$expires->add(new \DateInterval('PT24H'));
|
||||
$response->addHeader('Expires', $expires->format(\DateTime::RFC2822));
|
||||
$response->addHeader('Pragma', 'cache');
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
return new NotFoundResponse();
|
||||
}
|
||||
}
|
|
@ -410,6 +410,15 @@ class ThemingController extends Controller {
|
|||
$responseCss .= '.nc-theming-contrast {color: #ffffff}' . "\n";
|
||||
}
|
||||
|
||||
if($logo !== '' or $color !== '') {
|
||||
$responseCss .= '.icon-file,.icon-filetype-text {' .
|
||||
'background-image: url(\'./img/core/filetypes/text.svg?v='.$cacheBusterValue.'\');' . "}\n" .
|
||||
'.icon-folder, .icon-filetype-folder {' .
|
||||
'background-image: url(\'./img/core/filetypes/folder.svg?v='.$cacheBusterValue.'\');' . "}\n" .
|
||||
'.icon-filetype-folder-drag-accept {' .
|
||||
'background-image: url(\'./img/core/filetypes/folder-drag-accept.svg?v='.$cacheBusterValue.'\')!important;' . "}\n";
|
||||
}
|
||||
|
||||
$response = new DataDownloadResponse($responseCss, 'style', 'text/css');
|
||||
$response->addHeader('Expires', date(\DateTime::RFC2822, $this->timeFactory->getTime()));
|
||||
$response->addHeader('Pragma', 'cache');
|
||||
|
@ -423,6 +432,7 @@ class ThemingController extends Controller {
|
|||
* @return DataDownloadResponse
|
||||
*/
|
||||
public function getJavascript() {
|
||||
$cacheBusterValue = $this->config->getAppValue('theming', 'cachebuster', '0');
|
||||
$responseJS = '(function() {
|
||||
OCA.Theming = {
|
||||
name: ' . json_encode($this->template->getName()) . ',
|
||||
|
@ -430,6 +440,7 @@ class ThemingController extends Controller {
|
|||
slogan: ' . json_encode($this->template->getSlogan()) . ',
|
||||
color: ' . json_encode($this->template->getMailHeaderColor()) . ',
|
||||
inverted: ' . json_encode($this->util->invertTextColor($this->template->getMailHeaderColor())) . ',
|
||||
cacheBuster: ' . json_encode($cacheBusterValue). '
|
||||
};
|
||||
})();';
|
||||
$response = new Http\DataDisplayResponse($responseJS);
|
||||
|
|
|
@ -0,0 +1,182 @@
|
|||
<?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 Imagick;
|
||||
use ImagickPixel;
|
||||
use OCP\App\AppPathNotFoundException;
|
||||
|
||||
class IconBuilder {
|
||||
/** @var ThemingDefaults */
|
||||
private $themingDefaults;
|
||||
/** @var Util */
|
||||
private $util;
|
||||
|
||||
/**
|
||||
* IconBuilder constructor.
|
||||
*
|
||||
* @param ThemingDefaults $themingDefaults
|
||||
* @param Util $util
|
||||
*/
|
||||
public function __construct(
|
||||
ThemingDefaults $themingDefaults,
|
||||
Util $util
|
||||
) {
|
||||
$this->themingDefaults = $themingDefaults;
|
||||
$this->util = $util;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $app string app name
|
||||
* @return string|false image blob
|
||||
*/
|
||||
public function getFavicon($app) {
|
||||
$icon = $this->renderAppIcon($app);
|
||||
if($icon === false) {
|
||||
return false;
|
||||
}
|
||||
$icon->resizeImage(32, 32, Imagick::FILTER_LANCZOS, 1);
|
||||
$icon->setImageFormat("png24");
|
||||
$data = $icon->getImageBlob();
|
||||
$icon->destroy();
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $app string app name
|
||||
* @return string|false image blob
|
||||
*/
|
||||
public function getTouchIcon($app) {
|
||||
$icon = $this->renderAppIcon($app);
|
||||
if($icon === false) {
|
||||
return false;
|
||||
}
|
||||
$icon->setImageFormat("png24");
|
||||
$data = $icon->getImageBlob();
|
||||
$icon->destroy();
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render app icon on themed background color
|
||||
* fallback to logo
|
||||
*
|
||||
* @param $app string app name
|
||||
* @return Imagick|false
|
||||
*/
|
||||
public function renderAppIcon($app) {
|
||||
try {
|
||||
$appIcon = $this->util->getAppIcon($app);
|
||||
$appIconContent = file_get_contents($appIcon);
|
||||
} catch (AppPathNotFoundException $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if($appIconContent === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$color = $this->themingDefaults->getMailHeaderColor();
|
||||
$mime = mime_content_type($appIcon);
|
||||
|
||||
// generate background image with rounded corners
|
||||
$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">' .
|
||||
'<rect x="0" y="0" rx="75" ry="75" width="512" height="512" style="fill:' . $color . ';" />' .
|
||||
'</svg>';
|
||||
// resize svg magic as this seems broken in Imagemagick
|
||||
if($mime === "image/svg+xml" || substr($appIconContent, 0, 4) === "<svg") {
|
||||
if(substr($appIconContent, 0, 5) !== "<?xml") {
|
||||
$svg = "<?xml version=\"1.0\"?>".$appIconContent;
|
||||
} else {
|
||||
$svg = $appIconContent;
|
||||
}
|
||||
$tmp = new Imagick();
|
||||
$tmp->readImageBlob($svg);
|
||||
$x = $tmp->getImageWidth();
|
||||
$y = $tmp->getImageHeight();
|
||||
$res = $tmp->getImageResolution();
|
||||
$tmp->destroy();
|
||||
|
||||
if($x>$y) {
|
||||
$max = $x;
|
||||
} else {
|
||||
$max = $y;
|
||||
}
|
||||
|
||||
// convert svg to resized image
|
||||
$appIconFile = new Imagick();
|
||||
$resX = (int)(512 * $res['x'] / $max * 2.53);
|
||||
$resY = (int)(512 * $res['y'] / $max * 2.53);
|
||||
$appIconFile->setResolution($resX, $resY);
|
||||
$appIconFile->setBackgroundColor(new ImagickPixel('transparent'));
|
||||
$appIconFile->readImageBlob($svg);
|
||||
$appIconFile->scaleImage(512, 512, true);
|
||||
} else {
|
||||
$appIconFile = new Imagick();
|
||||
$appIconFile->setBackgroundColor(new ImagickPixel('transparent'));
|
||||
$appIconFile->readImageBlob(file_get_contents($appIcon));
|
||||
$appIconFile->scaleImage(512, 512, true);
|
||||
}
|
||||
|
||||
// offset for icon positioning
|
||||
$border_w = (int)($appIconFile->getImageWidth() * 0.05);
|
||||
$border_h = (int)($appIconFile->getImageHeight() * 0.05);
|
||||
$innerWidth = (int)($appIconFile->getImageWidth() - $border_w * 2);
|
||||
$innerHeight = (int)($appIconFile->getImageHeight() - $border_h * 2);
|
||||
$appIconFile->adaptiveResizeImage($innerWidth, $innerHeight);
|
||||
// center icon
|
||||
$offset_w = 512 / 2 - $innerWidth / 2;
|
||||
$offset_h = 512 / 2 - $innerHeight / 2;
|
||||
|
||||
$appIconFile->setImageFormat("png24");
|
||||
|
||||
$finalIconFile = new Imagick();
|
||||
$finalIconFile->readImageBlob($background);
|
||||
$finalIconFile->setImageVirtualPixelMethod(Imagick::VIRTUALPIXELMETHOD_TRANSPARENT);
|
||||
$finalIconFile->setImageArtifact('compose:args', "1,0,-0.5,0.5");
|
||||
$finalIconFile->compositeImage($appIconFile, Imagick::COMPOSITE_ATOP, $offset_w, $offset_h);
|
||||
$finalIconFile->resizeImage(512, 512, Imagick::FILTER_LANCZOS, 1);
|
||||
|
||||
$appIconFile->destroy();
|
||||
return $finalIconFile;
|
||||
}
|
||||
|
||||
public function colorSvg($app, $image) {
|
||||
try {
|
||||
$imageFile = $this->util->getAppImage($app, $image);
|
||||
} catch (AppPathNotFoundException $e) {
|
||||
return false;
|
||||
}
|
||||
$svg = file_get_contents($imageFile);
|
||||
if ($svg !== false) {
|
||||
$color = $this->util->elementColor($this->themingDefaults->getMailHeaderColor());
|
||||
$svg = $this->util->colorizeSvg($svg, $color);
|
||||
return $svg;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
<?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
|
||||
* @throws NotFoundException
|
||||
* @return \OCP\Files\SimpleFS\ISimpleFile
|
||||
*/
|
||||
public function getCachedImage($filename) {
|
||||
$currentFolder = $this->getCacheFolder();
|
||||
return $currentFolder->getFile($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,6 +23,7 @@
|
|||
namespace OCA\Theming;
|
||||
|
||||
|
||||
use OCP\ICacheFactory;
|
||||
use OCP\IConfig;
|
||||
use OCP\IL10N;
|
||||
use OCP\IURLGenerator;
|
||||
|
@ -38,6 +39,8 @@ class ThemingDefaults extends \OC_Defaults {
|
|||
private $urlGenerator;
|
||||
/** @var IRootFolder */
|
||||
private $rootFolder;
|
||||
/** @var ICacheFactory */
|
||||
private $cacheFactory;
|
||||
/** @var string */
|
||||
private $name;
|
||||
/** @var string */
|
||||
|
@ -55,18 +58,21 @@ class ThemingDefaults extends \OC_Defaults {
|
|||
* @param IURLGenerator $urlGenerator
|
||||
* @param \OC_Defaults $defaults
|
||||
* @param IRootFolder $rootFolder
|
||||
* @param ICacheFactory $cacheFactory
|
||||
*/
|
||||
public function __construct(IConfig $config,
|
||||
IL10N $l,
|
||||
IURLGenerator $urlGenerator,
|
||||
\OC_Defaults $defaults,
|
||||
IRootFolder $rootFolder
|
||||
IRootFolder $rootFolder,
|
||||
ICacheFactory $cacheFactory
|
||||
) {
|
||||
parent::__construct();
|
||||
$this->config = $config;
|
||||
$this->l = $l;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
$this->rootFolder = $rootFolder;
|
||||
$this->cacheFactory = $cacheFactory;
|
||||
|
||||
$this->name = $defaults->getName();
|
||||
$this->url = $defaults->getBaseUrl();
|
||||
|
@ -144,6 +150,29 @@ class ThemingDefaults extends \OC_Defaults {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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->create('theming');
|
||||
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
|
||||
*/
|
||||
|
|
|
@ -23,8 +23,35 @@
|
|||
|
||||
namespace OCA\Theming;
|
||||
|
||||
use OCP\App\AppPathNotFoundException;
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\IConfig;
|
||||
use OCP\Files\IRootFolder;
|
||||
|
||||
class Util {
|
||||
|
||||
/** @var IConfig */
|
||||
private $config;
|
||||
|
||||
/** @var IRootFolder */
|
||||
private $rootFolder;
|
||||
|
||||
/** @var IAppManager */
|
||||
private $appManager;
|
||||
|
||||
/**
|
||||
* Util constructor.
|
||||
*
|
||||
* @param IConfig $config
|
||||
* @param IRootFolder $rootFolder
|
||||
* @param IAppManager $appManager
|
||||
*/
|
||||
public function __construct(IConfig $config, IRootFolder $rootFolder, IAppManager $appManager) {
|
||||
$this->config = $config;
|
||||
$this->rootFolder = $rootFolder;
|
||||
$this->appManager = $appManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $color rgb color value
|
||||
* @return bool
|
||||
|
@ -81,4 +108,86 @@ class Util {
|
|||
return base64_encode($radioButtonIcon);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $app string app name
|
||||
* @return string path to app icon / logo
|
||||
*/
|
||||
public function getAppIcon($app) {
|
||||
$app = str_replace(array('\0', '/', '\\', '..'), '', $app);
|
||||
try {
|
||||
$appPath = $this->appManager->getAppPath($app);
|
||||
$icon = $appPath . '/img/' . $app . '.svg';
|
||||
if (file_exists($icon)) {
|
||||
return $icon;
|
||||
}
|
||||
$icon = $appPath . '/img/app.svg';
|
||||
if (file_exists($icon)) {
|
||||
return $icon;
|
||||
}
|
||||
} catch (AppPathNotFoundException $e) {}
|
||||
|
||||
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 string app name
|
||||
* @param $image string relative path to image in app folder
|
||||
* @return string|false absolute path to image
|
||||
*/
|
||||
public function getAppImage($app, $image) {
|
||||
$app = str_replace(array('\0', '/', '\\', '..'), '', $app);
|
||||
$image = str_replace(array('\0', '\\', '..'), '', $image);
|
||||
if ($app === "core") {
|
||||
$icon = \OC::$SERVERROOT . '/core/img/' . $image;
|
||||
if (file_exists($icon)) {
|
||||
return $icon;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$appPath = $this->appManager->getAppPath($app);
|
||||
} catch (AppPathNotFoundException $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$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 string content of a svg file
|
||||
* @param $color string color to match
|
||||
* @return string
|
||||
*/
|
||||
public function colorizeSvg($svg, $color) {
|
||||
$svg = preg_replace('/#0082c9/i', $color, $svg);
|
||||
return $svg;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,201 @@
|
|||
<?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\Controller;
|
||||
|
||||
|
||||
use OC\Files\SimpleFS\SimpleFile;
|
||||
use OCA\Theming\ImageManager;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\DataDisplayResponse;
|
||||
use OCP\AppFramework\Http\NotFoundResponse;
|
||||
use OCP\Files\IRootFolder;
|
||||
use OCP\Files\NotFoundException;
|
||||
use OCP\IConfig;
|
||||
use OCP\IRequest;
|
||||
use Test\TestCase;
|
||||
use OCA\Theming\Util;
|
||||
use OCA\Theming\Controller\IconController;
|
||||
use OCP\AppFramework\Http\FileDisplayResponse;
|
||||
|
||||
|
||||
class IconControllerTest extends TestCase {
|
||||
/** @var IRequest|\PHPUnit_Framework_MockObject_MockObject */
|
||||
private $request;
|
||||
/** @var IConfig|\PHPUnit_Framework_MockObject_MockObject */
|
||||
private $themingDefaults;
|
||||
/** @var Util */
|
||||
private $util;
|
||||
/** @var \OCP\AppFramework\Utility\ITimeFactory */
|
||||
private $timeFactory;
|
||||
/** @var IconController|\PHPUnit_Framework_MockObject_MockObject */
|
||||
private $iconController;
|
||||
/** @var IConfig|\PHPUnit_Framework_MockObject_MockObject */
|
||||
private $config;
|
||||
/** @var IRootFolder|\PHPUnit_Framework_MockObject_MockObject */
|
||||
private $iconBuilder;
|
||||
/** @var ImageManager */
|
||||
private $imageManager;
|
||||
|
||||
public function setUp() {
|
||||
$this->request = $this->getMockBuilder('OCP\IRequest')->getMock();
|
||||
$this->themingDefaults = $this->getMockBuilder('OCA\Theming\ThemingDefaults')
|
||||
->disableOriginalConstructor()->getMock();
|
||||
$this->util = $this->getMockBuilder('\OCA\Theming\Util')->disableOriginalConstructor()
|
||||
->setMethods(['getAppImage', 'getAppIcon', 'elementColor'])->getMock();
|
||||
$this->timeFactory = $this->getMockBuilder('OCP\AppFramework\Utility\ITimeFactory')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->config = $this->getMockBuilder('OCP\IConfig')->getMock();
|
||||
$this->iconBuilder = $this->getMockBuilder('OCA\Theming\IconBuilder')
|
||||
->disableOriginalConstructor()->getMock();
|
||||
$this->imageManager = $this->getMockBuilder('OCA\Theming\ImageManager')->disableOriginalConstructor()->getMock();
|
||||
$this->timeFactory->expects($this->any())
|
||||
->method('getTime')
|
||||
->willReturn(123);
|
||||
|
||||
$this->iconController = new IconController(
|
||||
'theming',
|
||||
$this->request,
|
||||
$this->themingDefaults,
|
||||
$this->util,
|
||||
$this->timeFactory,
|
||||
$this->config,
|
||||
$this->iconBuilder,
|
||||
$this->imageManager
|
||||
);
|
||||
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
private function iconFileMock($filename, $data) {
|
||||
$icon = $this->getMockBuilder('OCP\Files\File')->getMock();
|
||||
$icon->expects($this->any())->method('getContent')->willReturn($data);
|
||||
$icon->expects($this->any())->method('getMimeType')->willReturn('image type');
|
||||
$icon->expects($this->any())->method('getEtag')->willReturn('my etag');
|
||||
$icon->method('getName')->willReturn($filename);
|
||||
return new SimpleFile($icon);
|
||||
}
|
||||
|
||||
public function testGetThemedIcon() {
|
||||
$file = $this->iconFileMock('icon-core-filetypes_folder.svg', 'filecontent');
|
||||
$this->imageManager->expects($this->once())
|
||||
->method('getCachedImage')
|
||||
->with('icon-core-filetypes_folder.svg')
|
||||
->willReturn($file);
|
||||
$expected = new FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => 'image/svg+xml']);
|
||||
$expected->cacheFor(86400);
|
||||
$expires = new \DateTime();
|
||||
$expires->setTimestamp($this->timeFactory->getTime());
|
||||
$expires->add(new \DateInterval('PT24H'));
|
||||
$expected->addHeader('Expires', $expires->format(\DateTime::RFC2822));
|
||||
$expected->addHeader('Pragma', 'cache');
|
||||
@$this->assertEquals($expected, $this->iconController->getThemedIcon('core', 'filetypes/folder.svg'));
|
||||
}
|
||||
|
||||
public function testGetFaviconDefault() {
|
||||
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->any())
|
||||
->method('shouldReplaceIcons')
|
||||
->willReturn(true);
|
||||
|
||||
$this->iconBuilder->expects($this->once())
|
||||
->method('getFavicon')
|
||||
->with('core')
|
||||
->willReturn('filecontent');
|
||||
$file = $this->iconFileMock('filename', 'filecontent');
|
||||
$this->imageManager->expects($this->once())
|
||||
->method('getCachedImage')
|
||||
->will($this->throwException(new NotFoundException()));
|
||||
$this->imageManager->expects($this->once())
|
||||
->method('setCachedImage')
|
||||
->willReturn($file);
|
||||
|
||||
$expected = new FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => 'image/x-icon']);
|
||||
$expected->cacheFor(86400);
|
||||
$expires = new \DateTime();
|
||||
$expires->setTimestamp($this->timeFactory->getTime());
|
||||
$expires->add(new \DateInterval('PT24H'));
|
||||
$expected->addHeader('Expires', $expires->format(\DateTime::RFC2822));
|
||||
$expected->addHeader('Pragma', 'cache');
|
||||
$this->assertEquals($expected, $this->iconController->getFavicon());
|
||||
}
|
||||
|
||||
public function testGetFaviconFail() {
|
||||
$this->themingDefaults->expects($this->any())
|
||||
->method('shouldReplaceIcons')
|
||||
->willReturn(false);
|
||||
$expected = new Http\Response();
|
||||
$expected->setStatus(Http::STATUS_NOT_FOUND);
|
||||
$expected->cacheFor(0);
|
||||
$expected->setLastModified(new \DateTime('now', new \DateTimeZone('GMT')));
|
||||
$this->assertInstanceOf(NotFoundResponse::class, $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->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('getCachedImage')
|
||||
->will($this->throwException(new NotFoundException()));
|
||||
$this->imageManager->expects($this->once())
|
||||
->method('setCachedImage')
|
||||
->willReturn($file);
|
||||
|
||||
$expected = new FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => 'image/png']);
|
||||
$expected->cacheFor(86400);
|
||||
$expires = new \DateTime();
|
||||
$expires->setTimestamp($this->timeFactory->getTime());
|
||||
$expires->add(new \DateInterval('PT24H'));
|
||||
$expected->addHeader('Expires', $expires->format(\DateTime::RFC2822));
|
||||
$expected->addHeader('Pragma', 'cache');
|
||||
$this->assertEquals($expected, $this->iconController->getTouchIcon());
|
||||
}
|
||||
|
||||
public function testGetTouchIconFail() {
|
||||
$this->themingDefaults->expects($this->any())
|
||||
->method('shouldReplaceIcons')
|
||||
->willReturn(false);
|
||||
$this->assertInstanceOf(NotFoundResponse::class, $this->iconController->getTouchIcon());
|
||||
}
|
||||
|
||||
}
|
|
@ -26,6 +26,7 @@ namespace OCA\Theming\Tests\Controller;
|
|||
|
||||
use OCA\Theming\Controller\ThemingController;
|
||||
use OCA\Theming\Util;
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
use OCP\Files\File;
|
||||
|
@ -57,18 +58,21 @@ class ThemingControllerTest extends TestCase {
|
|||
private $rootFolder;
|
||||
/** @var ITempManager */
|
||||
private $tempManager;
|
||||
/** @var IAppManager */
|
||||
private $appManager;
|
||||
|
||||
public function setUp() {
|
||||
$this->request = $this->getMockBuilder('OCP\IRequest')->getMock();
|
||||
$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->appManager = $this->getMockBuilder('OCP\App\IAppManager')->getMock();
|
||||
$this->util = new Util($this->config, $this->rootFolder, $this->appManager);
|
||||
$this->timeFactory->expects($this->any())
|
||||
->method('getTime')
|
||||
->willReturn(123);
|
||||
|
@ -484,6 +488,12 @@ class ThemingControllerTest extends TestCase {
|
|||
$expectedData .= sprintf('.nc-theming-main-background {background-color: %s}' . "\n", $color);
|
||||
$expectedData .= sprintf('.nc-theming-main-text {color: %s}' . "\n", $color);
|
||||
$expectedData .= '.nc-theming-contrast {color: #ffffff}' . "\n";
|
||||
$expectedData .= '.icon-file,.icon-filetype-text {' .
|
||||
'background-image: url(\'./img/core/filetypes/text.svg?v=0\');' . "}\n" .
|
||||
'.icon-folder, .icon-filetype-folder {' .
|
||||
'background-image: url(\'./img/core/filetypes/folder.svg?v=0\');' . "}\n" .
|
||||
'.icon-filetype-folder-drag-accept {' .
|
||||
'background-image: url(\'./img/core/filetypes/folder-drag-accept.svg?v=0\')!important;' . "}\n";
|
||||
|
||||
$expected = new Http\DataDownloadResponse($expectedData, 'style', 'text/css');
|
||||
|
||||
|
@ -578,6 +588,12 @@ class ThemingControllerTest extends TestCase {
|
|||
$expectedData .= '#body-login input.login { background-image: url(\'' . \OC::$WEBROOT . '/core/img/actions/confirm.svg?v=2\'); }' . "\n";
|
||||
$expectedData .= '.nc-theming-contrast {color: #000000}' . "\n";
|
||||
$expectedData .= '.ui-widget-header { color: #000000; }' . "\n";
|
||||
$expectedData .= '.icon-file,.icon-filetype-text {' .
|
||||
'background-image: url(\'./img/core/filetypes/text.svg?v=0\');' . "}\n" .
|
||||
'.icon-folder, .icon-filetype-folder {' .
|
||||
'background-image: url(\'./img/core/filetypes/folder.svg?v=0\');' . "}\n" .
|
||||
'.icon-filetype-folder-drag-accept {' .
|
||||
'background-image: url(\'./img/core/filetypes/folder-drag-accept.svg?v=0\')!important;' . "}\n";
|
||||
|
||||
|
||||
$expected = new Http\DataDownloadResponse($expectedData, 'style', 'text/css');
|
||||
|
@ -623,6 +639,12 @@ class ThemingControllerTest extends TestCase {
|
|||
'background-size: contain;' .
|
||||
'}' . "\n";
|
||||
$expectedData .= '.nc-theming-contrast {color: #ffffff}' . "\n";
|
||||
$expectedData .= '.icon-file,.icon-filetype-text {' .
|
||||
'background-image: url(\'./img/core/filetypes/text.svg?v=0\');' . "}\n" .
|
||||
'.icon-folder, .icon-filetype-folder {' .
|
||||
'background-image: url(\'./img/core/filetypes/folder.svg?v=0\');' . "}\n" .
|
||||
'.icon-filetype-folder-drag-accept {' .
|
||||
'background-image: url(\'./img/core/filetypes/folder-drag-accept.svg?v=0\')!important;' . "}\n";
|
||||
|
||||
$expected = new Http\DataDownloadResponse($expectedData, 'style', 'text/css');
|
||||
|
||||
|
@ -765,6 +787,12 @@ class ThemingControllerTest extends TestCase {
|
|||
'background-image: url(\'./loginbackground?v=0\');' .
|
||||
'}' . "\n";
|
||||
$expectedData .= '.nc-theming-contrast {color: #ffffff}' . "\n";
|
||||
$expectedData .= '.icon-file,.icon-filetype-text {' .
|
||||
'background-image: url(\'./img/core/filetypes/text.svg?v=0\');' . "}\n" .
|
||||
'.icon-folder, .icon-filetype-folder {' .
|
||||
'background-image: url(\'./img/core/filetypes/folder.svg?v=0\');' . "}\n" .
|
||||
'.icon-filetype-folder-drag-accept {' .
|
||||
'background-image: url(\'./img/core/filetypes/folder-drag-accept.svg?v=0\')!important;' . "}\n";
|
||||
$expected = new Http\DataDownloadResponse($expectedData, 'style', 'text/css');
|
||||
|
||||
$expected->cacheFor(3600);
|
||||
|
@ -876,6 +904,13 @@ class ThemingControllerTest extends TestCase {
|
|||
$expectedData .= '#body-login input.login { background-image: url(\'' . \OC::$WEBROOT . '/core/img/actions/confirm.svg?v=2\'); }' . "\n";
|
||||
$expectedData .= '.nc-theming-contrast {color: #000000}' . "\n";
|
||||
$expectedData .= '.ui-widget-header { color: #000000; }' . "\n";
|
||||
$expectedData .= '.icon-file,.icon-filetype-text {' .
|
||||
'background-image: url(\'./img/core/filetypes/text.svg?v=0\');' . "}\n" .
|
||||
'.icon-folder, .icon-filetype-folder {' .
|
||||
'background-image: url(\'./img/core/filetypes/folder.svg?v=0\');' . "}\n" .
|
||||
'.icon-filetype-folder-drag-accept {' .
|
||||
'background-image: url(\'./img/core/filetypes/folder-drag-accept.svg?v=0\')!important;' . "}\n";
|
||||
$expected = new Http\DataDownloadResponse($expectedData, 'style', 'text/css');
|
||||
|
||||
$expected = new Http\DataDownloadResponse($expectedData, 'style', 'text/css');
|
||||
$expected->cacheFor(3600);
|
||||
|
@ -910,6 +945,7 @@ class ThemingControllerTest extends TestCase {
|
|||
slogan: "",
|
||||
color: "#000",
|
||||
inverted: false,
|
||||
cacheBuster: null
|
||||
};
|
||||
})();';
|
||||
$expected = new Http\DataDisplayResponse($expectedResponse);
|
||||
|
@ -944,6 +980,7 @@ class ThemingControllerTest extends TestCase {
|
|||
slogan: "awesome",
|
||||
color: "#ffffff",
|
||||
inverted: true,
|
||||
cacheBuster: null
|
||||
};
|
||||
})();';
|
||||
$expected = new Http\DataDisplayResponse($expectedResponse);
|
||||
|
|
|
@ -0,0 +1,192 @@
|
|||
<?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 OCA\Theming\IconBuilder;
|
||||
use OCA\Theming\ThemingDefaults;
|
||||
use OCA\Theming\Util;
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\AppFramework\Http\NotFoundResponse;
|
||||
use OCP\Files\IRootFolder;
|
||||
use OCP\IConfig;
|
||||
use Test\TestCase;
|
||||
|
||||
class IconBuilderTest extends TestCase {
|
||||
|
||||
/** @var IConfig */
|
||||
protected $config;
|
||||
/** @var IRootFolder */
|
||||
protected $rootFolder;
|
||||
/** @var ThemingDefaults */
|
||||
protected $themingDefaults;
|
||||
/** @var Util */
|
||||
protected $util;
|
||||
/** @var IconBuilder */
|
||||
protected $iconBuilder;
|
||||
/** @var IAppManager */
|
||||
protected $appManager;
|
||||
|
||||
protected function 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->appManager = $this->getMockBuilder('OCP\App\IAppManager')->getMock();
|
||||
$this->util = new Util($this->config, $this->rootFolder, $this->appManager);
|
||||
$this->iconBuilder = new IconBuilder($this->themingDefaults, $this->util);
|
||||
}
|
||||
|
||||
private function checkImagick() {
|
||||
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.');
|
||||
}
|
||||
}
|
||||
|
||||
public function dataRenderAppIcon() {
|
||||
return [
|
||||
['core', '#0082c9', 'touch-original.png'],
|
||||
['core', '#FF0000', 'touch-core-red.png'],
|
||||
['testing', '#FF0000', 'touch-testing-red.png'],
|
||||
['comments', '#0082c9', 'touch-comments.png'],
|
||||
['core', '#0082c9', 'touch-original-png.png'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataRenderAppIcon
|
||||
* @param $app
|
||||
* @param $color
|
||||
* @param $file
|
||||
*/
|
||||
public function testRenderAppIcon($app, $color, $file) {
|
||||
$this->checkImagick();
|
||||
$this->themingDefaults->expects($this->once())
|
||||
->method('getMailHeaderColor')
|
||||
->willReturn($color);
|
||||
|
||||
$expectedIcon = new \Imagick(realpath(dirname(__FILE__)). "/data/" . $file);
|
||||
$icon = $this->iconBuilder->renderAppIcon($app);
|
||||
|
||||
$this->assertEquals(true, $icon->valid());
|
||||
$this->assertEquals(512, $icon->getImageWidth());
|
||||
$this->assertEquals(512, $icon->getImageHeight());
|
||||
$this->assertEquals($icon, $expectedIcon);
|
||||
$icon->destroy();
|
||||
$expectedIcon->destroy();
|
||||
// FIXME: We may need some comparison of the generated and the test images
|
||||
// cloud be something like $expectedIcon->compareImages($icon, Imagick::METRIC_MEANABSOLUTEERROR)[1])
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataRenderAppIcon
|
||||
* @param $app
|
||||
* @param $color
|
||||
* @param $file
|
||||
*/
|
||||
public function testGetTouchIcon($app, $color, $file) {
|
||||
$this->checkImagick();
|
||||
$this->themingDefaults->expects($this->once())
|
||||
->method('getMailHeaderColor')
|
||||
->willReturn($color);
|
||||
|
||||
$expectedIcon = new \Imagick(realpath(dirname(__FILE__)). "/data/" . $file);
|
||||
$icon = new \Imagick();
|
||||
$icon->readImageBlob($this->iconBuilder->getTouchIcon($app));
|
||||
|
||||
$this->assertEquals(true, $icon->valid());
|
||||
$this->assertEquals(512, $icon->getImageWidth());
|
||||
$this->assertEquals(512, $icon->getImageHeight());
|
||||
$this->assertEquals($icon, $expectedIcon);
|
||||
$icon->destroy();
|
||||
$expectedIcon->destroy();
|
||||
// FIXME: We may need some comparison of the generated and the test images
|
||||
// cloud be something like $expectedIcon->compareImages($icon, Imagick::METRIC_MEANABSOLUTEERROR)[1])
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataRenderAppIcon
|
||||
* @param $app
|
||||
* @param $color
|
||||
* @param $file
|
||||
*/
|
||||
public function testGetFavicon($app, $color, $file) {
|
||||
$this->checkImagick();
|
||||
$this->themingDefaults->expects($this->once())
|
||||
->method('getMailHeaderColor')
|
||||
->willReturn($color);
|
||||
|
||||
$expectedIcon = new \Imagick(realpath(dirname(__FILE__)). "/data/" . $file);
|
||||
$icon = new \Imagick();
|
||||
$icon->readImageBlob($this->iconBuilder->getFavicon($app));
|
||||
|
||||
$this->assertEquals(true, $icon->valid());
|
||||
$this->assertEquals(32, $icon->getImageWidth());
|
||||
$this->assertEquals(32, $icon->getImageHeight());
|
||||
$icon->destroy();
|
||||
$expectedIcon->destroy();
|
||||
// FIXME: We may need some comparison of the generated and the test images
|
||||
// cloud be something like $expectedIcon->compareImages($icon, Imagick::METRIC_MEANABSOLUTEERROR)[1])
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \PHPUnit_Framework_Error_Warning
|
||||
*/
|
||||
public function testGetFaviconNotFound() {
|
||||
$util = $this->getMockBuilder(Util::class)->disableOriginalConstructor()->getMock();
|
||||
$iconBuilder = new IconBuilder($this->themingDefaults, $util);
|
||||
$util->expects($this->once())
|
||||
->method('getAppIcon')
|
||||
->willReturn('notexistingfile');
|
||||
$this->assertFalse($iconBuilder->getFavicon('noapp'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \PHPUnit_Framework_Error_Warning
|
||||
*/
|
||||
public function testGetTouchIconNotFound() {
|
||||
$util = $this->getMockBuilder(Util::class)->disableOriginalConstructor()->getMock();
|
||||
$iconBuilder = new IconBuilder($this->themingDefaults, $util);
|
||||
$util->expects($this->once())
|
||||
->method('getAppIcon')
|
||||
->willReturn('notexistingfile');
|
||||
$this->assertFalse($iconBuilder->getTouchIcon('noapp'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \PHPUnit_Framework_Error_Warning
|
||||
*/
|
||||
public function testColorSvgNotFound() {
|
||||
$util = $this->getMockBuilder(Util::class)->disableOriginalConstructor()->getMock();
|
||||
$iconBuilder = new IconBuilder($this->themingDefaults, $util);
|
||||
$util->expects($this->once())
|
||||
->method('getAppImage')
|
||||
->willReturn('notexistingfile');
|
||||
$this->assertFalse($iconBuilder->colorSvg('noapp','noimage'));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
<?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('getFile')
|
||||
->with('filename')
|
||||
->willReturn('filecontent');
|
||||
$expected = 'filecontent';
|
||||
$this->assertEquals($expected, $this->imageManager->getCachedImage('filename'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \OCP\Files\NotFoundException
|
||||
*/
|
||||
public function testGetCachedImageNotFound() {
|
||||
$folder = $this->setupCacheFolder();
|
||||
$folder->expects($this->once())
|
||||
->method('getFile')
|
||||
->with('filename')
|
||||
->will($this->throwException(new \OCP\Files\NotFoundException()));
|
||||
$image = $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();
|
||||
}
|
||||
|
||||
}
|
|
@ -24,6 +24,7 @@
|
|||
namespace OCA\Theming\Tests;
|
||||
|
||||
use OCA\Theming\ThemingDefaults;
|
||||
use OCP\ICacheFactory;
|
||||
use OCP\IConfig;
|
||||
use OCP\IL10N;
|
||||
use OCP\IURLGenerator;
|
||||
|
@ -43,6 +44,8 @@ class ThemingDefaultsTest extends TestCase {
|
|||
private $template;
|
||||
/** @var IRootFolder */
|
||||
private $rootFolder;
|
||||
/** @var ICacheFactory */
|
||||
private $cacheFactory;
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
@ -52,6 +55,7 @@ class ThemingDefaultsTest extends TestCase {
|
|||
$this->rootFolder = $this->getMockBuilder(IRootFolder::class)
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->cacheFactory = $this->getMockBuilder(ICacheFactory::class)->getMock();
|
||||
$this->defaults = $this->getMockBuilder(\OC_Defaults::class)
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
@ -76,7 +80,8 @@ class ThemingDefaultsTest extends TestCase {
|
|||
$this->l10n,
|
||||
$this->urlGenerator,
|
||||
$this->defaults,
|
||||
$this->rootFolder
|
||||
$this->rootFolder,
|
||||
$this->cacheFactory
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -23,16 +23,28 @@
|
|||
namespace OCA\Theming\Tests;
|
||||
|
||||
use OCA\Theming\Util;
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\IConfig;
|
||||
use OCP\Files\IRootFolder;
|
||||
use Test\TestCase;
|
||||
|
||||
class UtilTest extends TestCase {
|
||||
|
||||
/** @var Util */
|
||||
protected $util;
|
||||
/** @var IConfig */
|
||||
protected $config;
|
||||
/** @var IRootFolder */
|
||||
protected $rootFolder;
|
||||
/** @var IAppManager */
|
||||
protected $appManager;
|
||||
|
||||
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->appManager = $this->getMockBuilder('OCP\App\IAppManager')->getMock();
|
||||
$this->util = new Util($this->config, $this->rootFolder, $this->appManager);
|
||||
}
|
||||
|
||||
public function testInvertTextColorLight() {
|
||||
|
@ -89,9 +101,70 @@ class UtilTest extends TestCase {
|
|||
$expected = 'PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTYiIHdpZHRoPSIxNiI+PHBhdGggZD0iTTggMWE3IDcgMCAwIDAtNyA3IDcgNyAwIDAgMCA3IDcgNyA3IDAgMCAwIDctNyA3IDcgMCAwIDAtNy03em0wIDFhNiA2IDAgMCAxIDYgNiA2IDYgMCAwIDEtNiA2IDYgNiAwIDAgMS02LTYgNiA2IDAgMCAxIDYtNnptMCAyYTQgNCAwIDEgMCAwIDggNCA0IDAgMCAwIDAtOHoiIGZpbGw9IiNmZmZmZmYiLz48L3N2Zz4=';
|
||||
$this->assertEquals($expected, $button);
|
||||
}
|
||||
|
||||
public function testGenerateRadioButtonBlack() {
|
||||
$button = $this->util->generateRadioButton('#000000');
|
||||
$expected = 'PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTYiIHdpZHRoPSIxNiI+PHBhdGggZD0iTTggMWE3IDcgMCAwIDAtNyA3IDcgNyAwIDAgMCA3IDcgNyA3IDAgMCAwIDctNyA3IDcgMCAwIDAtNy03em0wIDFhNiA2IDAgMCAxIDYgNiA2IDYgMCAwIDEtNiA2IDYgNiAwIDAgMS02LTYgNiA2IDAgMCAxIDYtNnptMCAyYTQgNCAwIDEgMCAwIDggNCA0IDAgMCAwIDAtOHoiIGZpbGw9IiMwMDAwMDAiLz48L3N2Zz4=';
|
||||
$this->assertEquals($expected, $button);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataGetAppIcon
|
||||
*/
|
||||
public function testGetAppIcon($app, $expected) {
|
||||
$this->appManager->expects($this->once())
|
||||
->method('getAppPath')
|
||||
->with($app)
|
||||
->willReturn(\OC_App::getAppPath($app));
|
||||
$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) {
|
||||
if($app !== 'core') {
|
||||
$this->appManager->expects($this->once())
|
||||
->method('getAppPath')
|
||||
->with($app)
|
||||
->willReturn(\OC_App::getAppPath($app));
|
||||
}
|
||||
$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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
Binary file not shown.
After Width: | Height: | Size: 28 KiB |
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
|
@ -91,6 +91,11 @@ OC.MimeType = {
|
|||
path += icon;
|
||||
}
|
||||
}
|
||||
if(OCA.Theming) {
|
||||
path = OC.generateUrl('/apps/theming/img/core/filetypes/');
|
||||
path += OC.MimeType._getFile(mimeType, OC.MimeTypeList.files);
|
||||
gotIcon = true;
|
||||
}
|
||||
|
||||
// If we do not yet have an icon fall back to the default
|
||||
if (gotIcon === null) {
|
||||
|
@ -100,6 +105,10 @@ OC.MimeType = {
|
|||
|
||||
path += '.svg';
|
||||
|
||||
if(OCA.Theming) {
|
||||
path += "?v=" + OCA.Theming.cacheBuster;
|
||||
}
|
||||
|
||||
// Cache the result
|
||||
OC.MimeType._mimeTypeIcons[mimeType] = path;
|
||||
return path;
|
||||
|
|
|
@ -703,7 +703,8 @@ class Server extends ServerContainer implements IServerContainer {
|
|||
$c->getL10N('theming'),
|
||||
$c->getURLGenerator(),
|
||||
new \OC_Defaults(),
|
||||
$c->getLazyRootFolder()
|
||||
$c->getLazyRootFolder(),
|
||||
$c->getMemCacheFactory()
|
||||
);
|
||||
}
|
||||
return new \OC_Defaults();
|
||||
|
|
|
@ -157,7 +157,15 @@ class URLGenerator implements IURLGenerator {
|
|||
|
||||
// Check if the app is in the app folder
|
||||
$path = '';
|
||||
if (file_exists(\OC::$SERVERROOT . "/themes/$theme/apps/$app/img/$image")) {
|
||||
if(\OCP\App::isEnabled('theming') && $image === "favicon.ico" && \OC::$server->getThemingDefaults()->shouldReplaceIcons()) {
|
||||
$cacheBusterValue = $this->config->getAppValue('theming', 'cachebuster', '0');
|
||||
if($app==="") { $app = "core"; }
|
||||
$path = $this->linkToRoute('theming.Icon.getFavicon', [ 'app' => $app ]) . '?v='. $cacheBusterValue;
|
||||
} elseif(\OCP\App::isEnabled('theming') && $image === "favicon-touch.png" && \OC::$server->getThemingDefaults()->shouldReplaceIcons()) {
|
||||
$cacheBusterValue = $this->config->getAppValue('theming', 'cachebuster', '0');
|
||||
if($app==="") { $app = "core"; }
|
||||
$path = $this->linkToRoute('theming.Icon.getTouchIcon', [ 'app' => $app ]) . '?v='. $cacheBusterValue;
|
||||
} elseif (file_exists(\OC::$SERVERROOT . "/themes/$theme/apps/$app/img/$image")) {
|
||||
$path = \OC::$WEBROOT . "/themes/$theme/apps/$app/img/$image";
|
||||
} elseif (!file_exists(\OC::$SERVERROOT . "/themes/$theme/apps/$app/img/$basename.svg")
|
||||
&& file_exists(\OC::$SERVERROOT . "/themes/$theme/apps/$app/img/$basename.png")) {
|
||||
|
|
Loading…
Reference in New Issue