mirror of https://github.com/nextcloud/server.git
Merge pull request #32326 from nextcloud/fix/icons-cacher
commit
5a0b28d603
@ -1,151 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright Copyright (c) 2018, John Molakvoæ (skjnldsv@protonmail.com)
|
||||
*
|
||||
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
* @author Daniel Kesselberg <mail@danielkesselberg.de>
|
||||
* @author Joas Schilling <coding@schilljs.com>
|
||||
* @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||
* @author Julius Härtl <jus@bitgrid.net>
|
||||
* @author Roeland Jago Douma <roeland@famdouma.nl>
|
||||
* @author Thomas Citharel <nextcloud@tcit.fr>
|
||||
*
|
||||
* @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 OC\Core\Controller;
|
||||
|
||||
use OC\Files\Filesystem;
|
||||
use OC\Template\IconsCacher;
|
||||
use OCP\App\AppPathNotFoundException;
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\AppFramework\Controller;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\DataDisplayResponse;
|
||||
use OCP\AppFramework\Http\NotFoundResponse;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\IRequest;
|
||||
|
||||
class SvgController extends Controller {
|
||||
|
||||
/** @var string */
|
||||
protected $serverRoot;
|
||||
|
||||
/** @var ITimeFactory */
|
||||
protected $timeFactory;
|
||||
|
||||
/** @var IAppManager */
|
||||
protected $appManager;
|
||||
|
||||
/** @var IconsCacher */
|
||||
private $iconsCacher;
|
||||
|
||||
public function __construct(string $appName,
|
||||
IRequest $request,
|
||||
ITimeFactory $timeFactory,
|
||||
IAppManager $appManager,
|
||||
IconsCacher $iconsCacher) {
|
||||
parent::__construct($appName, $request);
|
||||
|
||||
$this->serverRoot = \OC::$SERVERROOT;
|
||||
$this->timeFactory = $timeFactory;
|
||||
$this->appManager = $appManager;
|
||||
$this->iconsCacher = $iconsCacher;
|
||||
}
|
||||
|
||||
/**
|
||||
* @PublicPage
|
||||
* @NoCSRFRequired
|
||||
* @NoSameSiteCookieRequired
|
||||
*
|
||||
* Generate svg from filename with the requested color
|
||||
*
|
||||
* @param string $folder
|
||||
* @param string $fileName
|
||||
* @param string $color
|
||||
* @return DataDisplayResponse|NotFoundResponse
|
||||
*/
|
||||
public function getSvgFromCore(string $folder, string $fileName, string $color = 'ffffff') {
|
||||
$path = $this->serverRoot . "/core/img/$folder/$fileName.svg";
|
||||
return $this->getSvg($path, $color, $fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @PublicPage
|
||||
* @NoCSRFRequired
|
||||
* @NoSameSiteCookieRequired
|
||||
*
|
||||
* Generate svg from filename with the requested color
|
||||
*
|
||||
* @param string $app
|
||||
* @param string $fileName
|
||||
* @param string $color
|
||||
* @return DataDisplayResponse|NotFoundResponse
|
||||
*/
|
||||
public function getSvgFromApp(string $app, string $fileName, string $color = 'ffffff') {
|
||||
try {
|
||||
$appPath = $this->appManager->getAppPath($app);
|
||||
} catch (AppPathNotFoundException $e) {
|
||||
return new NotFoundResponse();
|
||||
}
|
||||
|
||||
$path = $appPath . "/img/$fileName.svg";
|
||||
return $this->getSvg($path, $color, $fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate svg from filename with the requested color
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $color
|
||||
* @param string $fileName
|
||||
* @return DataDisplayResponse|NotFoundResponse
|
||||
*/
|
||||
private function getSvg(string $path, string $color, string $fileName) {
|
||||
if (!Filesystem::isValidPath($path)) {
|
||||
return new NotFoundResponse();
|
||||
}
|
||||
|
||||
if (!file_exists($path)) {
|
||||
return new NotFoundResponse();
|
||||
}
|
||||
|
||||
$svg = file_get_contents($path);
|
||||
|
||||
if ($svg === null) {
|
||||
return new NotFoundResponse();
|
||||
}
|
||||
|
||||
$svg = $this->iconsCacher->colorizeSvg($svg, $color);
|
||||
|
||||
$response = new DataDisplayResponse($svg, Http::STATUS_OK, ['Content-Type' => 'image/svg+xml']);
|
||||
|
||||
// Set cache control
|
||||
$ttl = 31536000;
|
||||
$response->cacheFor($ttl);
|
||||
$response->addHeader('Content-Disposition', 'inline; filename="' . $fileName . '.svg"');
|
||||
$expires = new \DateTime();
|
||||
$expires->setTimestamp($this->timeFactory->getTime());
|
||||
$expires->add(new \DateInterval('PT' . $ttl . 'S'));
|
||||
$response->addHeader('Expires', $expires->format(\DateTime::RFC1123));
|
||||
$response->addHeader('Pragma', 'cache');
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" version="1.1" height="16"><path d="m8 2c-2.142 0-4.125 1.145-5.196 3l1.948 1.125c0.671-1.162 1.906-1.875 3.2476-1.875 1.1906 0 2.297 0.56157 3 1.5l-1.5 1.5h4.5v-4.5l-1.406 1.406c-1.129-1.348-2.802-2.1563-4.594-2.1563z"/><path d="m2 8.75v4.5l1.408-1.41c1.116 1.334 2.817 2.145 4.592 2.16 2.16 0.01827 4.116-1.132 5.196-3.002l-1.948-1.125c-0.677 1.171-1.9005 1.886-3.248 1.875-1.18-0.01-2.3047-0.572-3-1.5l1.5-1.5z"/></svg>
|
After Width: | Height: | Size: 493 B |
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 16 16" height="16" width="16" version="1.1"><circle stroke-width="2" stroke="#000" cy="8" cx="8" r="7" fill="none"/><path stroke-linejoin="round" d="m8 3.5-1 5 3.5 2-2-2z" stroke="#000" stroke-linecap="round" stroke-width="1.5"/></svg>
|
After Width: | Height: | Size: 289 B |
@ -0,0 +1 @@
|
||||
<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m12.5 1a2.5 2.5 0 0 0-2.5 2.5 2.5 2.5 0 0 0 0.003906 0.12891l-4.9023 2.4512a2.5 2.5 0 0 0-1.6016-0.58008 2.5 2.5 0 0 0-2.5 2.5 2.5 2.5 0 0 0 2.5 2.5 2.5 2.5 0 0 0 0.30469-0.021484l3.4395-1.7246-1.25-0.625a2.5 2.5 0 0 0 0.0058594-0.12891 2.5 2.5 0 0 0-0.0039062-0.12891l4.9023-2.4512a2.5 2.5 0 0 0 1.6016 0.58008 2.5 2.5 0 0 0 0.26562-0.013672l1.5625-0.7832a2.5 2.5 0 0 0 0.67188-1.7031 2.5 2.5 0 0 0-2.5-2.5zm0.25391 9.0156-3.7246 1.8672 0.97656 0.48828a2.5 2.5 0 0 0-0.005859 0.12891 2.5 2.5 0 0 0 2.5 2.5 2.5 2.5 0 0 0 2.5-2.5 2.5 2.5 0 0 0-2.2461-2.4844z"/><rect transform="rotate(-26.63)" x="-1.0586" y="11.891" width="11.687" height="2.0029" ry="0" style="paint-order:normal"/></svg>
|
After Width: | Height: | Size: 795 B |
@ -0,0 +1 @@
|
||||
<svg height="16" viewBox="0 0 21.33 21.33" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M10.67 1.33a9.34 9.34 0 100 18.68 9.34 9.34 0 000-18.68zM6.93 15.8a2.33 2.33 0 110-4.67 2.33 2.33 0 010 4.67zm1.4-8.87a2.33 2.33 0 114.67 0 2.33 2.33 0 01-4.67 0zm6.07 8.87a2.33 2.33 0 110-4.67 2.33 2.33 0 010 4.67z"/></svg>
|
After Width: | Height: | Size: 322 B |
@ -0,0 +1,334 @@
|
||||
/* eslint-disable quote-props */
|
||||
/* eslint-disable node/no-unpublished-import */
|
||||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
import sass from 'sass'
|
||||
|
||||
const colors = {
|
||||
dark: '000',
|
||||
white: 'fff',
|
||||
yellow: 'FC0',
|
||||
red: 'e9322d',
|
||||
orange: 'eca700',
|
||||
green: '46ba61',
|
||||
grey: '969696',
|
||||
}
|
||||
|
||||
const variables = {}
|
||||
const icons = {
|
||||
'add': path.join(__dirname, '../img', 'actions', 'add.svg'),
|
||||
'address': path.join(__dirname, '../img', 'actions', 'address.svg'),
|
||||
'alert-outline': path.join(__dirname, '../img', 'actions', 'alert-outline.svg'),
|
||||
'audio-off': path.join(__dirname, '../img', 'actions', 'audio-off.svg'),
|
||||
'audio': path.join(__dirname, '../img', 'actions', 'audio.svg'),
|
||||
'calendar': path.join(__dirname, '../img', 'places', 'calendar.svg'),
|
||||
'caret': path.join(__dirname, '../img', 'actions', 'caret.svg'),
|
||||
'category-app-bundles': path.join(__dirname, '../img', 'categories', 'bundles.svg'),
|
||||
'category-auth': path.join(__dirname, '../img', 'categories', 'auth.svg'),
|
||||
'category-customization': path.join(__dirname, '../img', 'categories', 'customization.svg'),
|
||||
'category-dashboard': path.join(__dirname, '../img', 'categories', 'dashboard.svg'),
|
||||
'category-files': path.join(__dirname, '../img', 'categories', 'files.svg'),
|
||||
'category-games': path.join(__dirname, '../img', 'categories', 'games.svg'),
|
||||
'category-integration': path.join(__dirname, '../img', 'categories', 'integration.svg'),
|
||||
'category-monitoring': path.join(__dirname, '../img', 'categories', 'monitoring.svg'),
|
||||
'category-multimedia': path.join(__dirname, '../img', 'categories', 'multimedia.svg'),
|
||||
'category-office': path.join(__dirname, '../img', 'categories', 'office.svg'),
|
||||
'category-organization': path.join(__dirname, '../img', 'categories', 'organization.svg'),
|
||||
'category-social': path.join(__dirname, '../img', 'categories', 'social.svg'),
|
||||
'category-workflow': path.join(__dirname, '../img', 'categories', 'workflow.svg'),
|
||||
'change': path.join(__dirname, '../img', 'actions', 'change.svg'),
|
||||
'checkmark': path.join(__dirname, '../img', 'actions', 'checkmark.svg'),
|
||||
'circles': path.join(__dirname, '../img', 'apps', 'circles.svg'),
|
||||
'clippy': path.join(__dirname, '../img', 'actions', 'clippy.svg'),
|
||||
'close': path.join(__dirname, '../img', 'actions', 'close.svg'),
|
||||
'comment': path.join(__dirname, '../img', 'actions', 'comment.svg'),
|
||||
'confirm-fade': path.join(__dirname, '../img', 'actions', 'confirm-fade.svg'),
|
||||
'confirm': path.join(__dirname, '../img', 'actions', 'confirm.svg'),
|
||||
'contacts': path.join(__dirname, '../img', 'places', 'contacts.svg'),
|
||||
'delete': path.join(__dirname, '../img', 'actions', 'delete.svg'),
|
||||
'desktop': path.join(__dirname, '../img', 'clients', 'desktop.svg'),
|
||||
'details': path.join(__dirname, '../img', 'actions', 'details.svg'),
|
||||
'disabled-user': path.join(__dirname, '../img', 'actions', 'disabled-user.svg'),
|
||||
'disabled-users': path.join(__dirname, '../img', 'actions', 'disabled-users.svg'),
|
||||
'download': path.join(__dirname, '../img', 'actions', 'download.svg'),
|
||||
'edit': path.join(__dirname, '../img', 'actions', 'edit.svg'),
|
||||
'encryption': path.join(__dirname, '../../', 'apps/files_external/img', 'app.svg'),
|
||||
'error': path.join(__dirname, '../img', 'actions', 'error.svg'),
|
||||
'external': path.join(__dirname, '../img', 'actions', 'external.svg'),
|
||||
'favorite': path.join(__dirname, '../img', 'actions', 'star-dark.svg'),
|
||||
'files': path.join(__dirname, '../img', 'places', 'files.svg'),
|
||||
'filter': path.join(__dirname, '../img', 'actions', 'filter.svg'),
|
||||
'folder': path.join(__dirname, '../img', 'filetypes', 'folder.svg'),
|
||||
'fullscreen': path.join(__dirname, '../img', 'actions', 'fullscreen.svg'),
|
||||
'group': path.join(__dirname, '../img', 'actions', 'group.svg'),
|
||||
'history': path.join(__dirname, '../img', 'actions', 'history.svg'),
|
||||
'home': path.join(__dirname, '../img', 'places', 'home.svg'),
|
||||
'info': path.join(__dirname, '../img', 'actions', 'info.svg'),
|
||||
'link': path.join(__dirname, '../img', 'places', 'link.svg'),
|
||||
'logout': path.join(__dirname, '../img', 'actions', 'logout.svg'),
|
||||
'mail': path.join(__dirname, '../img', 'actions', 'mail.svg'),
|
||||
'menu-sidebar': path.join(__dirname, '../img', 'actions', 'menu-sidebar.svg'),
|
||||
'menu': path.join(__dirname, '../img', 'actions', 'menu.svg'),
|
||||
'more': path.join(__dirname, '../img', 'actions', 'more.svg'),
|
||||
'music': path.join(__dirname, '../img', 'places', 'music.svg'),
|
||||
'password': path.join(__dirname, '../img', 'actions', 'password.svg'),
|
||||
'pause': path.join(__dirname, '../img', 'actions', 'pause.svg'),
|
||||
'phone': path.join(__dirname, '../img', 'clients', 'phone.svg'),
|
||||
'picture': path.join(__dirname, '../img', 'places', 'picture.svg'),
|
||||
'play-add': path.join(__dirname, '../img', 'actions', 'play-add.svg'),
|
||||
'play-next': path.join(__dirname, '../img', 'actions', 'play-next.svg'),
|
||||
'play-previous': path.join(__dirname, '../img', 'actions', 'play-previous.svg'),
|
||||
'play': path.join(__dirname, '../img', 'actions', 'play.svg'),
|
||||
'projects': path.join(__dirname, '../img', 'actions', 'projects.svg'),
|
||||
'public': path.join(__dirname, '../img', 'actions', 'public.svg'),
|
||||
'quota': path.join(__dirname, '../img', 'actions', 'quota.svg'),
|
||||
'recent': path.join(__dirname, '../img', 'actions', 'recent.svg'),
|
||||
'rename': path.join(__dirname, '../img', 'actions', 'rename.svg'),
|
||||
'screen-off': path.join(__dirname, '../img', 'actions', 'screen-off.svg'),
|
||||
'screen': path.join(__dirname, '../img', 'actions', 'screen.svg'),
|
||||
'search': path.join(__dirname, '../img', 'actions', 'search.svg'),
|
||||
'settings': path.join(__dirname, '../img', 'actions', 'settings-dark.svg'),
|
||||
'share': path.join(__dirname, '../img', 'actions', 'share.svg'),
|
||||
'shared': path.join(__dirname, '../img', 'actions', 'share.svg'),
|
||||
'sound-off': path.join(__dirname, '../img', 'actions', 'sound-off.svg'),
|
||||
'sound': path.join(__dirname, '../img', 'actions', 'sound.svg'),
|
||||
'star': path.join(__dirname, '../img', 'actions', 'star.svg'),
|
||||
'starred': path.join(__dirname, '../img', 'actions', 'star-dark.svg'),
|
||||
'tablet': path.join(__dirname, '../img', 'clients', 'tablet.svg'),
|
||||
'tag': path.join(__dirname, '../img', 'actions', 'tag.svg'),
|
||||
'talk': path.join(__dirname, '../img', 'apps', 'spreed.svg'),
|
||||
'template-add': path.join(__dirname, '../img', 'actions', 'template-add.svg'),
|
||||
'timezone': path.join(__dirname, '../img', 'actions', 'timezone.svg'),
|
||||
'toggle-background': path.join(__dirname, '../img', 'actions', 'toggle-background.svg'),
|
||||
'toggle-filelist': path.join(__dirname, '../img', 'actions', 'toggle-filelist.svg'),
|
||||
'toggle-pictures': path.join(__dirname, '../img', 'actions', 'toggle-pictures.svg'),
|
||||
'toggle': path.join(__dirname, '../img', 'actions', 'toggle.svg'),
|
||||
'triangle-e': path.join(__dirname, '../img', 'actions', 'triangle-e.svg'),
|
||||
'triangle-n': path.join(__dirname, '../img', 'actions', 'triangle-n.svg'),
|
||||
'triangle-s': path.join(__dirname, '../img', 'actions', 'triangle-s.svg'),
|
||||
'unshare': path.join(__dirname, '../img', 'actions', 'unshare.svg'),
|
||||
'upload': path.join(__dirname, '../img', 'actions', 'upload.svg'),
|
||||
'user-admin': path.join(__dirname, '../img', 'actions', 'user-admin.svg'),
|
||||
'user': path.join(__dirname, '../img', 'actions', 'user.svg'),
|
||||
'video-off': path.join(__dirname, '../img', 'actions', 'video-off.svg'),
|
||||
'video-switch': path.join(__dirname, '../img', 'actions', 'video-switch.svg'),
|
||||
'video': path.join(__dirname, '../img', 'actions', 'video.svg'),
|
||||
'view-close': path.join(__dirname, '../img', 'actions', 'view-close.svg'),
|
||||
'view-download': path.join(__dirname, '../img', 'actions', 'view-download.svg'),
|
||||
'view-next': path.join(__dirname, '../img', 'actions', 'arrow-right.svg'),
|
||||
'view-pause': path.join(__dirname, '../img', 'actions', 'view-pause.svg'),
|
||||
'view-play': path.join(__dirname, '../img', 'actions', 'view-play.svg'),
|
||||
'view-previous': path.join(__dirname, '../img', 'actions', 'arrow-left.svg'),
|
||||
}
|
||||
|
||||
const iconsColor = {
|
||||
'settings': {
|
||||
path: path.join(__dirname, '../img', 'actions', 'settings.svg'),
|
||||
color: 'black',
|
||||
},
|
||||
'error-color': {
|
||||
path: path.join(__dirname, '../img', 'actions', 'error.svg'),
|
||||
color: 'red',
|
||||
},
|
||||
'checkmark-color': {
|
||||
path: path.join(__dirname, '../img', 'actions', 'checkmark.svg'),
|
||||
color: 'green',
|
||||
},
|
||||
'starred': {
|
||||
path: path.join(__dirname, '../img', 'actions', 'star-dark.svg'),
|
||||
color: 'yellow',
|
||||
},
|
||||
'delete-color': {
|
||||
path: path.join(__dirname, '../img', 'actions', 'delete.svg'),
|
||||
color: 'red',
|
||||
},
|
||||
'file': {
|
||||
path: path.join(__dirname, '../img', 'filetypes', 'text.svg'),
|
||||
color: 'grey',
|
||||
},
|
||||
'filetype-file': {
|
||||
path: path.join(__dirname, '../img', 'filetypes', 'file.svg'),
|
||||
color: 'grey',
|
||||
},
|
||||
'filetype-folder': {
|
||||
path: path.join(__dirname, '../img', 'filetypes', 'folder.svg'),
|
||||
// TODO: replace primary ?
|
||||
color: 'primary',
|
||||
},
|
||||
'filetype-folder-drag-accept': {
|
||||
path: path.join(__dirname, '../img', 'filetypes', 'folder-drag-accept.svg'),
|
||||
// TODO: replace primary ?
|
||||
color: 'primary',
|
||||
},
|
||||
}
|
||||
|
||||
// use this to define aliases to existing icons
|
||||
// key is the css selector, value is the variable
|
||||
const iconsAliases = {
|
||||
'icon-caret': 'icon-caret-white',
|
||||
// starring action
|
||||
'icon-star:hover': 'icon-starred',
|
||||
'icon-star:focus': 'icon-starred',
|
||||
// Un-starring action
|
||||
'icon-starred:hover': 'icon-star',
|
||||
'icon-starred:focus': 'icon-star',
|
||||
// Delete normal
|
||||
'icon-delete.no-permission:hover': 'icon-delete-dark',
|
||||
'icon-delete.no-permission:focus': 'icon-delete-dark',
|
||||
'icon-delete.no-hover:hover': 'icon-delete-dark',
|
||||
'icon-delete.no-hover:focus': 'icon-delete-dark',
|
||||
'icon-delete:hover': 'icon-delete-color-red',
|
||||
'icon-delete:focus': 'icon-delete-color-red',
|
||||
// Delete white
|
||||
'icon-delete-white.no-permission:hover': 'icon-delete-white',
|
||||
'icon-delete-white.no-permission:focus': 'icon-delete-white',
|
||||
'icon-delete-white.no-hover:hover': 'icon-delete-white',
|
||||
'icon-delete-white.no-hover:focus': 'icon-delete-white',
|
||||
'icon-delete-white:hover': 'icon-delete-color-red',
|
||||
'icon-delete-white:focus': 'icon-delete-color-red',
|
||||
// Default to white
|
||||
'icon-view-close': 'icon-view-close-white',
|
||||
'icon-view-download': 'icon-view-download-white',
|
||||
'icon-view-pause': 'icon-view-pause-white',
|
||||
'icon-view-play': 'icon-view-play-white',
|
||||
// Default app place to white
|
||||
'icon-calendar': 'icon-calendar-white',
|
||||
'icon-contacts': 'icon-contacts-white',
|
||||
'icon-files': 'icon-files-white',
|
||||
// Re-using existing icons
|
||||
'icon-category-installed': 'icon-user-dark',
|
||||
'icon-category-enabled': 'icon-checkmark-dark',
|
||||
'icon-category-disabled': 'icon-close-dark',
|
||||
'icon-category-updates': 'icon-download-dark',
|
||||
'icon-category-security': 'icon-password-dark',
|
||||
'icon-category-search': 'icon-search-dark',
|
||||
'icon-category-tools': 'icon-settings-dark',
|
||||
'icon-filetype-text': 'icon-file-grey',
|
||||
}
|
||||
|
||||
const colorSvg = function(svg = '', color = '000') {
|
||||
if (!color.match(/^[0-9a-f]{3,6}$/i)) {
|
||||
// Prevent not-sane colors from being written into the SVG
|
||||
console.warn(color, 'does not match the required format')
|
||||
color = '000'
|
||||
}
|
||||
|
||||
// add fill (fill is not present on black elements)
|
||||
const fillRe = /<((circle|rect|path)((?!fill)[a-z0-9 =".\-#():;,])+)\/>/gmi
|
||||
svg = svg.replace(fillRe, '<$1 fill="#' + color + '"/>')
|
||||
|
||||
// replace any fill or stroke colors
|
||||
svg = svg.replace(/stroke="#([a-z0-9]{3,6})"/gmi, 'stroke="#' + color + '"')
|
||||
svg = svg.replace(/fill="#([a-z0-9]{3,6})"/gmi, 'fill="#' + color + '"')
|
||||
|
||||
return svg
|
||||
}
|
||||
|
||||
const generateVariablesAliases = function(invert = false) {
|
||||
let css = ''
|
||||
Object.keys(variables).forEach(variable => {
|
||||
if (variable.indexOf('original-') !== -1) {
|
||||
let finalVariable = variable.replace('original-', '')
|
||||
if (invert) {
|
||||
finalVariable = finalVariable.replace('white', 'tempwhite')
|
||||
.replace('dark', 'white')
|
||||
.replace('tempwhite', 'dark')
|
||||
}
|
||||
css += `${finalVariable}: var(${variable});`
|
||||
}
|
||||
})
|
||||
return css
|
||||
}
|
||||
|
||||
const formatIcon = function(icon, invert = false) {
|
||||
const color1 = invert ? 'white' : 'dark'
|
||||
const color2 = invert ? 'dark' : 'white'
|
||||
return `
|
||||
.icon-${icon},
|
||||
.icon-${icon}-dark {
|
||||
background-image: var(--icon-${icon}-${color1});
|
||||
}
|
||||
.icon-${icon}-white,
|
||||
.icon-${icon}.icon-white {
|
||||
background-image: var(--icon-${icon}-${color2});
|
||||
}`
|
||||
}
|
||||
const formatIconColor = function(icon) {
|
||||
const { color } = iconsColor[icon]
|
||||
return `
|
||||
.icon-${icon} {
|
||||
background-image: var(--icon-${icon}-${color});
|
||||
}`
|
||||
}
|
||||
const formatAlias = function(alias, invert = false) {
|
||||
let icon = iconsAliases[alias]
|
||||
if (invert) {
|
||||
icon = icon.replace('white', 'tempwhite')
|
||||
.replace('dark', 'white')
|
||||
.replace('tempwhite', 'dark')
|
||||
}
|
||||
return `
|
||||
.${alias} {
|
||||
background-image: var(--${icon})
|
||||
}`
|
||||
}
|
||||
|
||||
let css = ''
|
||||
Object.keys(icons).forEach(icon => {
|
||||
const path = icons[icon]
|
||||
|
||||
const svg = fs.readFileSync(path, 'utf8')
|
||||
const darkSvg = colorSvg(svg, '000000')
|
||||
const whiteSvg = colorSvg(svg, 'ffffff')
|
||||
|
||||
variables[`--original-icon-${icon}-dark`] = Buffer.from(darkSvg, 'utf-8').toString('base64')
|
||||
variables[`--original-icon-${icon}-white`] = Buffer.from(whiteSvg, 'utf-8').toString('base64')
|
||||
})
|
||||
|
||||
Object.keys(iconsColor).forEach(icon => {
|
||||
const { path, color } = iconsColor[icon]
|
||||
|
||||
const svg = fs.readFileSync(path, 'utf8')
|
||||
const coloredSvg = colorSvg(svg, colors[color])
|
||||
variables[`--icon-${icon}-${color}`] = Buffer.from(coloredSvg, 'utf-8').toString('base64')
|
||||
})
|
||||
|
||||
// ICONS VARIABLES LIST
|
||||
css += ':root {'
|
||||
Object.keys(variables).forEach(variable => {
|
||||
const data = variables[variable]
|
||||
css += `${variable}: url(data:image/svg+xml;base64,${data});`
|
||||
})
|
||||
css += '}'
|
||||
|
||||
// DEFAULT THEME
|
||||
css += 'body {'
|
||||
css += generateVariablesAliases()
|
||||
Object.keys(icons).forEach(icon => {
|
||||
css += formatIcon(icon)
|
||||
})
|
||||
Object.keys(iconsColor).forEach(icon => {
|
||||
css += formatIconColor(icon)
|
||||
})
|
||||
Object.keys(iconsAliases).forEach(alias => {
|
||||
css += formatAlias(alias)
|
||||
})
|
||||
css += '}'
|
||||
|
||||
// DARK THEME MEDIA QUERY
|
||||
css += '@media (prefers-color-scheme: dark) { body {'
|
||||
css += generateVariablesAliases(true)
|
||||
css += '}}'
|
||||
|
||||
// DARK THEME
|
||||
css += 'body[data-themes*=light] {'
|
||||
css += generateVariablesAliases()
|
||||
css += '}'
|
||||
|
||||
// DARK THEME
|
||||
css += 'body[data-themes*=dark] {'
|
||||
css += generateVariablesAliases(true)
|
||||
css += '}'
|
||||
|
||||
// WRITE CSS
|
||||
fs.writeFileSync(path.join(__dirname, '../../dist', 'icons.css'), sass.compileString(css).css)
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,263 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright Copyright (c) 2018, John Molakvoæ (skjnldsv@protonmail.com)
|
||||
*
|
||||
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
* @author Joas Schilling <coding@schilljs.com>
|
||||
* @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||
* @author Julius Härtl <jus@bitgrid.net>
|
||||
* @author Robin Appelman <robin@icewind.nl>
|
||||
* @author Roeland Jago Douma <roeland@famdouma.nl>
|
||||
*
|
||||
* @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 OC\Template;
|
||||
|
||||
use OC\Files\AppData\Factory;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\Files\IAppData;
|
||||
use OCP\Files\NotFoundException;
|
||||
use OCP\Files\SimpleFS\ISimpleFile;
|
||||
use OCP\Files\SimpleFS\ISimpleFolder;
|
||||
use OCP\IURLGenerator;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class IconsCacher {
|
||||
protected LoggerInterface $logger;
|
||||
|
||||
/** @var IAppData */
|
||||
protected $appData;
|
||||
|
||||
/** @var ISimpleFolder */
|
||||
private $folder;
|
||||
|
||||
/** @var IURLGenerator */
|
||||
protected $urlGenerator;
|
||||
|
||||
/** @var ITimeFactory */
|
||||
protected $timeFactory;
|
||||
|
||||
/** @var string */
|
||||
private $iconVarRE = '/--(icon-[a-zA-Z0-9-]+):\s?url\(["\']?([a-zA-Z0-9-_\~\/\.\?\&\=\:\;\+\,]+)[^;]+;/m';
|
||||
|
||||
/** @var string */
|
||||
private $fileName = 'icons-vars.css';
|
||||
|
||||
private $iconList = 'icons-list.template';
|
||||
|
||||
private $cachedCss;
|
||||
private $cachedList;
|
||||
|
||||
/**
|
||||
* @throws \OCP\Files\NotPermittedException
|
||||
*/
|
||||
public function __construct(LoggerInterface $logger,
|
||||
Factory $appDataFactory,
|
||||
IURLGenerator $urlGenerator,
|
||||
ITimeFactory $timeFactory) {
|
||||
$this->logger = $logger;
|
||||
$this->appData = $appDataFactory->get('css');
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
$this->timeFactory = $timeFactory;
|
||||
|
||||
try {
|
||||
$this->folder = $this->appData->getFolder('icons');
|
||||
} catch (NotFoundException $e) {
|
||||
$this->folder = $this->appData->newFolder('icons');
|
||||
}
|
||||
}
|
||||
|
||||
private function getIconsFromCss(string $css): array {
|
||||
preg_match_all($this->iconVarRE, $css, $matches, PREG_SET_ORDER);
|
||||
$icons = [];
|
||||
foreach ($matches as $icon) {
|
||||
$icons[$icon[1]] = $icon[2];
|
||||
}
|
||||
|
||||
return $icons;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $css
|
||||
* @return string
|
||||
* @throws NotFoundException
|
||||
* @throws \OCP\Files\NotPermittedException
|
||||
*/
|
||||
public function setIconsCss(string $css): string {
|
||||
$cachedFile = $this->getCachedList();
|
||||
if (!$cachedFile) {
|
||||
$currentData = '';
|
||||
$cachedFile = $this->folder->newFile($this->iconList);
|
||||
} else {
|
||||
$currentData = $cachedFile->getContent();
|
||||
}
|
||||
|
||||
$cachedVarsCssFile = $this->getCachedCSS();
|
||||
if (!$cachedVarsCssFile) {
|
||||
$cachedVarsCssFile = $this->folder->newFile($this->fileName);
|
||||
}
|
||||
|
||||
$icons = $this->getIconsFromCss($currentData . $css);
|
||||
|
||||
$data = '';
|
||||
$list = '';
|
||||
foreach ($icons as $icon => $url) {
|
||||
$list .= "--$icon: url('$url');";
|
||||
[$location,$color] = $this->parseUrl($url);
|
||||
$svg = false;
|
||||
if ($location !== '' && \file_exists($location)) {
|
||||
$svg = \file_get_contents($location);
|
||||
}
|
||||
if ($svg === false) {
|
||||
$this->logger->debug('Failed to get icon file ' . $location);
|
||||
$data .= "--$icon: url('$url');";
|
||||
continue;
|
||||
}
|
||||
$encode = base64_encode($this->colorizeSvg($svg, $color));
|
||||
$data .= '--' . $icon . ': url(data:image/svg+xml;base64,' . $encode . ');';
|
||||
}
|
||||
|
||||
if (\strlen($data) > 0 && \strlen($list) > 0) {
|
||||
$data = ":root {\n$data\n}";
|
||||
$cachedVarsCssFile->putContent($data);
|
||||
$list = ":root {\n$list\n}";
|
||||
$cachedFile->putContent($list);
|
||||
$this->cachedList = null;
|
||||
$this->cachedCss = null;
|
||||
}
|
||||
|
||||
return preg_replace($this->iconVarRE, '', $css);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $url
|
||||
* @return array
|
||||
*/
|
||||
private function parseUrl($url): array {
|
||||
$location = '';
|
||||
$color = '';
|
||||
$base = $this->getRoutePrefix() . '/svg/';
|
||||
$cleanUrl = \substr($url, \strlen($base));
|
||||
if (\strpos($url, $base . 'core') === 0) {
|
||||
$cleanUrl = \substr($cleanUrl, \strlen('core'));
|
||||
if (\preg_match('/\/([a-zA-Z0-9-_\~\/\.\=\:\;\+\,]+)\?color=([0-9a-fA-F]{3,6})/', $cleanUrl, $matches)) {
|
||||
[,$cleanUrl,$color] = $matches;
|
||||
$location = \OC::$SERVERROOT . '/core/img/' . $cleanUrl . '.svg';
|
||||
}
|
||||
} elseif (\strpos($url, $base) === 0) {
|
||||
if (\preg_match('/([A-z0-9\_\-]+)\/([a-zA-Z0-9-_\~\/\.\=\:\;\+\,]+)\?color=([0-9a-fA-F]{3,6})/', $cleanUrl, $matches)) {
|
||||
[,$app,$cleanUrl, $color] = $matches;
|
||||
$appPath = \OC_App::getAppPath($app);
|
||||
if ($appPath !== false) {
|
||||
$location = $appPath . '/img/' . $cleanUrl . '.svg';
|
||||
}
|
||||
if ($app === 'settings') {
|
||||
$location = \OC::$SERVERROOT . '/settings/img/' . $cleanUrl . '.svg';
|
||||
}
|
||||
}
|
||||
}
|
||||
return [
|
||||
$location,
|
||||
$color
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $svg
|
||||
* @param $color
|
||||
* @return string
|
||||
*/
|
||||
public function colorizeSvg($svg, $color): string {
|
||||
if (!preg_match('/^[0-9a-f]{3,6}$/i', $color)) {
|
||||
// Prevent not-sane colors from being written into the SVG
|
||||
$color = '000';
|
||||
}
|
||||
|
||||
// add fill (fill is not present on black elements)
|
||||
$fillRe = '/<((circle|rect|path)((?!fill)[a-z0-9 =".\-#():;,])+)\/>/mi';
|
||||
$svg = preg_replace($fillRe, '<$1 fill="#' . $color . '"/>', $svg);
|
||||
|
||||
// replace any fill or stroke colors
|
||||
$svg = preg_replace('/stroke="#([a-z0-9]{3,6})"/mi', 'stroke="#' . $color . '"', $svg);
|
||||
$svg = preg_replace('/fill="#([a-z0-9]{3,6})"/mi', 'fill="#' . $color . '"', $svg);
|
||||
return $svg;
|
||||
}
|
||||
|
||||
private function getRoutePrefix() {
|
||||
$frontControllerActive = (\OC::$server->getConfig()->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true');
|
||||
$prefix = \OC::$WEBROOT . '/index.php';
|
||||
if ($frontControllerActive) {
|
||||
$prefix = \OC::$WEBROOT;
|
||||
}
|
||||
return $prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get icons css file
|
||||
* @return ISimpleFile|boolean
|
||||
*/
|
||||
public function getCachedCSS() {
|
||||
try {
|
||||
if (!$this->cachedCss) {
|
||||
$this->cachedCss = $this->folder->getFile($this->fileName);
|
||||
}
|
||||
return $this->cachedCss;
|
||||
} catch (NotFoundException $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get icon-vars list template
|
||||
* @return ISimpleFile|boolean
|
||||
*/
|
||||
public function getCachedList() {
|
||||
try {
|
||||
if (!$this->cachedList) {
|
||||
$this->cachedList = $this->folder->getFile($this->iconList);
|
||||
}
|
||||
return $this->cachedList;
|
||||
} catch (NotFoundException $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the icons cache css into the header
|
||||
*/
|
||||
public function injectCss() {
|
||||
$mtime = $this->timeFactory->getTime();
|
||||
$file = $this->getCachedList();
|
||||
if ($file) {
|
||||
$mtime = $file->getMTime();
|
||||
}
|
||||
// Only inject once
|
||||
foreach (\OC_Util::$headers as $header) {
|
||||
if (
|
||||
array_key_exists('attributes', $header) &&
|
||||
array_key_exists('href', $header['attributes']) &&
|
||||
strpos($header['attributes']['href'], $this->fileName) !== false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
$linkToCSS = $this->urlGenerator->linkToRoute('core.Css.getCss', ['appName' => 'icons', 'fileName' => $this->fileName, 'v' => $mtime]);
|
||||
\OC_Util::addHeader('link', ['rel' => 'stylesheet', 'href' => $linkToCSS], null, true);
|
||||
}
|
||||
}
|
@ -1,198 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2018 Michael Weimann <mail@michael-weimann.eu>
|
||||
*
|
||||
* @author Michael Weimann <mail@michael-weimann.eu>
|
||||
*
|
||||
* @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 Tests\Core\Controller;
|
||||
|
||||
use OC\AppFramework\Http;
|
||||
use OC\Core\Controller\SvgController;
|
||||
use OC\Template\IconsCacher;
|
||||
use OCP\App\AppPathNotFoundException;
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\IRequest;
|
||||
use Test\TestCase;
|
||||
|
||||
/**
|
||||
* This class provides test cases for the svg controller
|
||||
*/
|
||||
class SvgControllerTest extends TestCase {
|
||||
public const TEST_IMAGES_SOURCE_PATH = __DIR__ . '/../../data/svg';
|
||||
public const TEST_IMAGES_PATH = __DIR__ . '/../../../core/img/testImages';
|
||||
public const TEST_IMAGE_MIXED = 'mixed-source.svg';
|
||||
public const TEST_IMAGE_RECT = 'rect-black.svg';
|
||||
public const TEST_IMAGES = [
|
||||
self::TEST_IMAGE_MIXED,
|
||||
self::TEST_IMAGE_RECT,
|
||||
];
|
||||
|
||||
/** @var IAppManager|\PHPUnit\Framework\MockObject\MockObject */
|
||||
private $appManager;
|
||||
|
||||
/**
|
||||
* @var SvgController
|
||||
*/
|
||||
private $svgController;
|
||||
|
||||
/**
|
||||
* Copy test svgs into the core img "test" directory.
|
||||
*
|
||||
* @beforeClass
|
||||
* @return void
|
||||
*/
|
||||
public static function copyTestImagesIntoPlace() {
|
||||
mkdir(self::TEST_IMAGES_PATH);
|
||||
foreach (self::TEST_IMAGES as $testImage) {
|
||||
copy(
|
||||
self::TEST_IMAGES_SOURCE_PATH .'/' . $testImage,
|
||||
self::TEST_IMAGES_PATH . '/' . $testImage
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the test svgs from the core img "test" directory.
|
||||
*
|
||||
* @afterClass
|
||||
* @return void
|
||||
*/
|
||||
public static function removeTestImages() {
|
||||
foreach (self::TEST_IMAGES as $testImage) {
|
||||
unlink(self::TEST_IMAGES_PATH . '/' . $testImage);
|
||||
}
|
||||
rmdir(self::TEST_IMAGES_PATH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setups a SVG controller instance for tests.
|
||||
*
|
||||
* @before
|
||||
* @return void
|
||||
*/
|
||||
public function setupSvgController() {
|
||||
/** @var IRequest */
|
||||
$request = $this->getMockBuilder(IRequest::class)->getMock();
|
||||
/** @var ITimeFactory $timeFactory */
|
||||
$timeFactory = $this->getMockBuilder(ITimeFactory::class)->getMock();
|
||||
/** @var IAppManager */
|
||||
$this->appManager = $this->getMockBuilder(IAppManager::class)->getMock();
|
||||
/** @var IconsCacher $iconsCacher */
|
||||
$iconsCacher = $this->getMockBuilder(IconsCacher::class)->disableOriginalConstructor()->setMethods(['__construct'])->getMock();
|
||||
$this->svgController = new SvgController('core', $request, $timeFactory, $this->appManager, $iconsCacher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that requesting an unknown image results in a 404.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testGetSvgFromCoreNotFound() {
|
||||
$response = $this->svgController->getSvgFromCore('huhuu', '2342', '#ff0000');
|
||||
self::assertEquals(Http::STATUS_NOT_FOUND, $response->getStatus());
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides svg coloring test data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function provideGetSvgFromCoreTestData(): array {
|
||||
return [
|
||||
'mixed' => ['mixed-source', 'f00', file_get_contents(self::TEST_IMAGES_SOURCE_PATH . '/mixed-red.svg')],
|
||||
'black rect' => ['rect-black', 'f00', file_get_contents(self::TEST_IMAGES_SOURCE_PATH . '/rect-red.svg')],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that retrieving a colored SVG works.
|
||||
*
|
||||
* @dataProvider provideGetSvgFromCoreTestData
|
||||
* @param string $name The requested svg name
|
||||
* @param string $color The requested color
|
||||
* @param string $expected The expected svg
|
||||
* @return void
|
||||
*/
|
||||
public function testGetSvgFromCore(string $name, string $color, string $expected) {
|
||||
$response = $this->svgController->getSvgFromCore('testImages', $name, $color);
|
||||
|
||||
self::assertEquals(Http::STATUS_OK, $response->getStatus());
|
||||
|
||||
$headers = $response->getHeaders();
|
||||
self::assertArrayHasKey('Content-Type', $headers);
|
||||
self::assertEquals($headers['Content-Type'], 'image/svg+xml');
|
||||
|
||||
self::assertEquals($expected, $response->getData());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that requesting an unknown image results in a 404.
|
||||
*/
|
||||
public function testGetSvgFromAppNotFound(): void {
|
||||
$this->appManager->expects($this->once())
|
||||
->method('getAppPath')
|
||||
->with('invalid_app')
|
||||
->willThrowException(new AppPathNotFoundException('Could not find path for invalid_app'));
|
||||
|
||||
$response = $this->svgController->getSvgFromApp('invalid_app', 'some-icon', '#ff0000');
|
||||
self::assertEquals(Http::STATUS_NOT_FOUND, $response->getStatus());
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides svg coloring test data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function provideGetSvgFromAppTestData(): array {
|
||||
return [
|
||||
'settings admin' => ['settings', 'admin', 'f00', file_get_contents(self::TEST_IMAGES_SOURCE_PATH . '/settings-admin-red.svg')],
|
||||
'files app' => ['files', 'app', 'f00', file_get_contents(self::TEST_IMAGES_SOURCE_PATH . '/files-app-red.svg')],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that retrieving a colored SVG works.
|
||||
*
|
||||
* @dataProvider provideGetSvgFromAppTestData
|
||||
* @param string $appName
|
||||
* @param string $name The requested svg name
|
||||
* @param string $color The requested color
|
||||
* @param string $expected
|
||||
*/
|
||||
public function testGetSvgFromApp(string $appName, string $name, string $color, string $expected): void {
|
||||
$this->appManager->expects($this->once())
|
||||
->method('getAppPath')
|
||||
->with($appName)
|
||||
->willReturn(realpath(__DIR__ . '/../../../apps/') . '/' . $appName);
|
||||
|
||||
$response = $this->svgController->getSvgFromApp($appName, $name, $color);
|
||||
|
||||
self::assertEquals(Http::STATUS_OK, $response->getStatus());
|
||||
|
||||
$headers = $response->getHeaders();
|
||||
self::assertArrayHasKey('Content-Type', $headers);
|
||||
self::assertEquals($headers['Content-Type'], 'image/svg+xml');
|
||||
|
||||
self::assertEquals($expected, $response->getData());
|
||||
}
|
||||
}
|
@ -1,149 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2018, John Molakvoæ (skjnldsv@protonmail.com)
|
||||
*
|
||||
* @author John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @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 Test\Template;
|
||||
|
||||
use OC\Files\AppData\AppData;
|
||||
use OC\Files\AppData\Factory;
|
||||
use OC\Template\IconsCacher;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\Files\IAppData;
|
||||
use OCP\Files\SimpleFS\ISimpleFile;
|
||||
use OCP\Files\SimpleFS\ISimpleFolder;
|
||||
use OCP\IURLGenerator;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class IconsCacherTest extends \Test\TestCase {
|
||||
/** @var LoggerInterface|\PHPUnit\Framework\MockObject\MockObject */
|
||||
protected $logger;
|
||||
/** @var IAppData|\PHPUnit\Framework\MockObject\MockObject */
|
||||
protected $appData;
|
||||
/** @var IURLGenerator|\PHPUnit\Framework\MockObject\MockObject */
|
||||
protected $urlGenerator;
|
||||
/** @var ITimeFactory|\PHPUnit\Framework\MockObject\MockObject */
|
||||
private $timeFactory;
|
||||
|
||||
protected function setUp(): void {
|
||||
$this->logger = $this->createMock(LoggerInterface::class);
|
||||
$this->appData = $this->createMock(AppData::class);
|
||||
$this->timeFactory = $this->createMock(ITimeFactory::class);
|
||||
|
||||
/** @var Factory|\PHPUnit\Framework\MockObject\MockObject $factory */
|
||||
$factory = $this->createMock(Factory::class);
|
||||
$factory->method('get')->with('css')->willReturn($this->appData);
|
||||
|
||||
$this->folder = $this->createMock(ISimpleFolder::class);
|
||||
$this->appData->method('getFolder')->willReturn($this->folder);
|
||||
|
||||
$this->urlGenerator = $this->createMock(IURLGenerator::class);
|
||||
|
||||
$this->iconsCacher = new IconsCacher(
|
||||
$this->logger,
|
||||
$factory,
|
||||
$this->urlGenerator,
|
||||
$this->timeFactory
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetIconsFromEmptyCss() {
|
||||
$css = "
|
||||
icon.test {
|
||||
color: #aaa;
|
||||
}
|
||||
";
|
||||
$icons = self::invokePrivate($this->iconsCacher, 'getIconsFromCss', [$css]);
|
||||
$this->assertTrue(empty($icons));
|
||||
}
|
||||
|
||||
public function testGetIconsFromValidCss() {
|
||||
$css = "
|
||||
icon.test {
|
||||
--icon-test: url('/svg/core/actions/add/000?v=1');
|
||||
background-image: var(--icon-test);
|
||||
}
|
||||
";
|
||||
$actual = self::invokePrivate($this->iconsCacher, 'getIconsFromCss', [$css]);
|
||||
$expected = [
|
||||
'icon-test' => '/svg/core/actions/add/000?v=1'
|
||||
];
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
|
||||
public function testSetIconsFromEmptyCss() {
|
||||
$expected = "
|
||||
icon.test {
|
||||
color: #aaa;
|
||||
}
|
||||
";
|
||||
$actual = $this->iconsCacher->setIconsCss($expected);
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
|
||||
public function testSetIconsFromValidCss() {
|
||||
$css = "
|
||||
icon.test {
|
||||
--icon-test: url('/index.php/svg/core/actions/add?color=000&v=1');
|
||||
background-image: var(--icon-test);
|
||||
}
|
||||
";
|
||||
$expected = "
|
||||
icon.test {
|
||||
\n background-image: var(--icon-test);
|
||||
}
|
||||
";
|
||||
|
||||
$iconsFile = $this->createMock(ISimpleFile::class);
|
||||
$this->folder->expects($this->exactly(2))
|
||||
->method('getFile')
|
||||
->willReturn($iconsFile);
|
||||
|
||||
$actual = $this->iconsCacher->setIconsCss($css);
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
|
||||
public function testSetIconsFromValidCssMultipleTimes() {
|
||||
$css = "
|
||||
icon.test {
|
||||
--icon-test: url('/index.php/svg/core/actions/add?color=000&v=1');
|
||||
background-image: var(--icon-test);
|
||||
}
|
||||
";
|
||||
$expected = "
|
||||
icon.test {
|
||||
\n background-image: var(--icon-test);
|
||||
}
|
||||
";
|
||||
|
||||
$iconsFile = $this->createMock(ISimpleFile::class);
|
||||
$this->folder->expects($this->exactly(4))
|
||||
->method('getFile')
|
||||
->willReturn($iconsFile);
|
||||
|
||||
$actual = $this->iconsCacher->setIconsCss($css);
|
||||
$actual = $this->iconsCacher->setIconsCss($actual);
|
||||
$actual = $this->iconsCacher->setIconsCss($actual);
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue