| @@ -35,210 +35,210 @@ | ||
| 35 | 35 | |
| 36 | 36 |  class IconsCacher { | 
| 37 | 37 | |
| 38 | - /** @var ILogger */ | |
| 39 | - protected $logger; | |
| 40 | - | |
| 41 | - /** @var IAppData */ | |
| 42 | - protected $appData; | |
| 43 | - | |
| 44 | - /** @var ISimpleFolder */ | |
| 45 | - private $folder; | |
| 46 | - | |
| 47 | - /** @var IURLGenerator */ | |
| 48 | - protected $urlGenerator; | |
| 49 | - | |
| 50 | - /** @var ITimeFactory */ | |
| 51 | - protected $timeFactory; | |
| 52 | - | |
| 53 | - /** @var string */ | |
| 54 | - private $iconVarRE = '/--(icon-[a-zA-Z0-9-]+):\s?url\(["\']?([a-zA-Z0-9-_\~\/\.\?\&\=\:\;\+\,]+)[^;]+;/m'; | |
| 55 | - | |
| 56 | - /** @var string */ | |
| 57 | - private $fileName = 'icons-vars.css'; | |
| 58 | - | |
| 59 | - private $iconList = 'icons-list.template'; | |
| 60 | - | |
| 61 | - /** | |
| 62 | - * @param ILogger $logger | |
| 63 | - * @param Factory $appDataFactory | |
| 64 | - * @param IURLGenerator $urlGenerator | |
| 65 | - * @param ITimeFactory $timeFactory | |
| 66 | - * @throws \OCP\Files\NotPermittedException | |
| 67 | - */ | |
| 68 | - public function __construct(ILogger $logger, | |
| 69 | - Factory $appDataFactory, | |
| 70 | - IURLGenerator $urlGenerator, | |
| 71 | -								ITimeFactory $timeFactory) { | |
| 72 | - $this->logger = $logger; | |
| 73 | -		$this->appData      = $appDataFactory->get('css'); | |
| 74 | - $this->urlGenerator = $urlGenerator; | |
| 75 | - $this->timeFactory = $timeFactory; | |
| 76 | - | |
| 77 | -		try { | |
| 78 | -			$this->folder = $this->appData->getFolder('icons'); | |
| 79 | -		} catch (NotFoundException $e) { | |
| 80 | -			$this->folder = $this->appData->newFolder('icons'); | |
| 81 | - } | |
| 82 | - } | |
| 83 | - | |
| 84 | -	private function getIconsFromCss(string $css): array { | |
| 85 | - preg_match_all($this->iconVarRE, $css, $matches, PREG_SET_ORDER); | |
| 86 | - $icons = []; | |
| 87 | -		foreach ($matches as $icon) { | |
| 88 | - $icons[$icon[1]] = $icon[2]; | |
| 89 | - } | |
| 90 | - | |
| 91 | - return $icons; | |
| 92 | - } | |
| 93 | - | |
| 94 | - /** | |
| 95 | - * @param string $css | |
| 96 | - * @return string | |
| 97 | - * @throws NotFoundException | |
| 98 | - * @throws \OCP\Files\NotPermittedException | |
| 99 | - */ | |
| 100 | -	public function setIconsCss(string $css): string { | |
| 101 | - | |
| 102 | - $cachedFile = $this->getCachedList(); | |
| 103 | -		if (!$cachedFile) { | |
| 104 | - $currentData = ''; | |
| 105 | - $cachedFile = $this->folder->newFile($this->iconList); | |
| 106 | -		} else { | |
| 107 | - $currentData = $cachedFile->getContent(); | |
| 108 | - } | |
| 109 | - | |
| 110 | - $cachedVarsCssFile = $this->getCachedCSS(); | |
| 111 | -		if (!$cachedVarsCssFile) { | |
| 112 | - $cachedVarsCssFile = $this->folder->newFile($this->fileName); | |
| 113 | - } | |
| 114 | - | |
| 115 | - $icons = $this->getIconsFromCss($currentData . $css); | |
| 116 | - | |
| 117 | - $data = ''; | |
| 118 | - $list = ''; | |
| 119 | -		foreach ($icons as $icon => $url) { | |
| 120 | -			$list .= "--$icon: url('$url');"; | |
| 121 | - list($location,$color) = $this->parseUrl($url); | |
| 122 | - $svg = false; | |
| 123 | -			if ($location !== '') { | |
| 124 | - $svg = file_get_contents($location); | |
| 125 | - } | |
| 126 | -			if ($svg === false) { | |
| 127 | -				$this->logger->debug('Failed to get icon file ' . $location); | |
| 128 | -				$data .= "--$icon: url('$url');"; | |
| 129 | - continue; | |
| 130 | - } | |
| 131 | - $encode = base64_encode($this->colorizeSvg($svg, $color)); | |
| 132 | - $data .= '--' . $icon . ': url(data:image/svg+xml;base64,' . $encode . ');'; | |
| 133 | - } | |
| 134 | - | |
| 135 | -		if (\strlen($data) > 0 && \strlen($list) > 0) { | |
| 136 | -			$data = ":root {\n$data\n}"; | |
| 137 | - $cachedVarsCssFile->putContent($data); | |
| 138 | -			$list = ":root {\n$list\n}"; | |
| 139 | - $cachedFile->putContent($list); | |
| 140 | - } | |
| 141 | - | |
| 142 | - return preg_replace($this->iconVarRE, '', $css); | |
| 143 | - } | |
| 144 | - | |
| 145 | - /** | |
| 146 | - * @param $url | |
| 147 | - * @return array | |
| 148 | - */ | |
| 149 | -	private function parseUrl($url): array { | |
| 150 | - $location = ''; | |
| 151 | - $color = ''; | |
| 152 | - $base = $this->getRoutePrefix() . '/svg/'; | |
| 153 | - $cleanUrl = \substr($url, \strlen($base)); | |
| 154 | -		if (\strpos($url, $base . 'core') === 0) { | |
| 155 | -			$cleanUrl = \substr($cleanUrl, \strlen('core')); | |
| 156 | -			if (\preg_match('/\/([a-zA-Z0-9-_\~\/\.\=\:\;\+\,]+)\?color=([0-9a-fA-F]{3,6})/', $cleanUrl, $matches)) { | |
| 157 | - list(,$cleanUrl,$color) = $matches; | |
| 158 | - $location = \OC::$SERVERROOT . '/core/img/' . $cleanUrl . '.svg'; | |
| 159 | - } | |
| 160 | -		} elseif (\strpos($url, $base) === 0) { | |
| 161 | -			if(\preg_match('/([A-z0-9\_\-]+)\/([a-zA-Z0-9-_\~\/\.\=\:\;\+\,]+)\?color=([0-9a-fA-F]{3,6})/', $cleanUrl, $matches)) { | |
| 162 | - list(,$app,$cleanUrl, $color) = $matches; | |
| 163 | - $location = \OC_App::getAppPath($app) . '/img/' . $cleanUrl . '.svg'; | |
| 164 | -				if ($app === 'settings') { | |
| 165 | - $location = \OC::$SERVERROOT . '/settings/img/' . $cleanUrl . '.svg'; | |
| 166 | - } | |
| 167 | - } | |
| 168 | - | |
| 169 | - } | |
| 170 | - return [ | |
| 171 | - $location, | |
| 172 | - $color | |
| 173 | - ]; | |
| 174 | - } | |
| 175 | - | |
| 176 | - /** | |
| 177 | - * @param $svg | |
| 178 | - * @param $color | |
| 179 | - * @return string | |
| 180 | - */ | |
| 181 | -	public function colorizeSvg($svg, $color): string { | |
| 182 | - // add fill (fill is not present on black elements) | |
| 183 | - $fillRe = '/<((circle|rect|path)((?!fill)[a-z0-9 =".\-#():;])+)\/>/mi'; | |
| 184 | - $svg = preg_replace($fillRe, '<$1 fill="#' . $color . '"/>', $svg); | |
| 185 | - | |
| 186 | - // replace any fill or stroke colors | |
| 187 | -		$svg = preg_replace('/stroke="#([a-z0-9]{3,6})"/mi', 'stroke="#' . $color . '"', $svg); | |
| 188 | -		$svg = preg_replace('/fill="#([a-z0-9]{3,6})"/mi', 'fill="#' . $color . '"', $svg); | |
| 189 | - return $svg; | |
| 190 | - } | |
| 191 | - | |
| 192 | -	private function getRoutePrefix() { | |
| 193 | -		$frontControllerActive = (\OC::$server->getConfig()->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true'); | |
| 194 | - $prefix = \OC::$WEBROOT . '/index.php'; | |
| 195 | -		if ($frontControllerActive) { | |
| 196 | - $prefix = \OC::$WEBROOT; | |
| 197 | - } | |
| 198 | - return $prefix; | |
| 199 | - } | |
| 200 | - | |
| 201 | - /** | |
| 202 | - * Get icons css file | |
| 203 | - * @return ISimpleFile|boolean | |
| 204 | - */ | |
| 205 | -	public function getCachedCSS() { | |
| 206 | -		try { | |
| 207 | - return $this->folder->getFile($this->fileName); | |
| 208 | -		} catch (NotFoundException $e) { | |
| 209 | - return false; | |
| 210 | - } | |
| 211 | - } | |
| 212 | - | |
| 213 | - /** | |
| 214 | - * Get icon-vars list template | |
| 215 | - * @return ISimpleFile|boolean | |
| 216 | - */ | |
| 217 | -	public function getCachedList() { | |
| 218 | -		try { | |
| 219 | - return $this->folder->getFile($this->iconList); | |
| 220 | -		} catch (NotFoundException $e) { | |
| 221 | - return false; | |
| 222 | - } | |
| 223 | - } | |
| 224 | - | |
| 225 | -	public function injectCss() { | |
| 226 | - $mtime = $this->timeFactory->getTime(); | |
| 227 | - $file = $this->getCachedList(); | |
| 228 | -		if ($file) { | |
| 229 | - $mtime = $file->getMTime(); | |
| 230 | - } | |
| 231 | - // Only inject once | |
| 232 | -		foreach (\OC_Util::$headers as $header) { | |
| 233 | - if ( | |
| 234 | -				array_key_exists('attributes', $header) && | |
| 235 | -				array_key_exists('href', $header['attributes']) && | |
| 236 | -				strpos($header['attributes']['href'], $this->fileName) !== false) { | |
| 237 | - return; | |
| 238 | - } | |
| 239 | - } | |
| 240 | -		$linkToCSS = $this->urlGenerator->linkToRoute('core.Css.getCss', ['appName' => 'icons', 'fileName' => $this->fileName, 'v' => $mtime]); | |
| 241 | -		\OC_Util::addHeader('link', ['rel' => 'stylesheet', 'href' => $linkToCSS], null, true); | |
| 242 | - } | |
| 38 | + /** @var ILogger */ | |
| 39 | + protected $logger; | |
| 40 | + | |
| 41 | + /** @var IAppData */ | |
| 42 | + protected $appData; | |
| 43 | + | |
| 44 | + /** @var ISimpleFolder */ | |
| 45 | + private $folder; | |
| 46 | + | |
| 47 | + /** @var IURLGenerator */ | |
| 48 | + protected $urlGenerator; | |
| 49 | + | |
| 50 | + /** @var ITimeFactory */ | |
| 51 | + protected $timeFactory; | |
| 52 | + | |
| 53 | + /** @var string */ | |
| 54 | + private $iconVarRE = '/--(icon-[a-zA-Z0-9-]+):\s?url\(["\']?([a-zA-Z0-9-_\~\/\.\?\&\=\:\;\+\,]+)[^;]+;/m'; | |
| 55 | + | |
| 56 | + /** @var string */ | |
| 57 | + private $fileName = 'icons-vars.css'; | |
| 58 | + | |
| 59 | + private $iconList = 'icons-list.template'; | |
| 60 | + | |
| 61 | + /** | |
| 62 | + * @param ILogger $logger | |
| 63 | + * @param Factory $appDataFactory | |
| 64 | + * @param IURLGenerator $urlGenerator | |
| 65 | + * @param ITimeFactory $timeFactory | |
| 66 | + * @throws \OCP\Files\NotPermittedException | |
| 67 | + */ | |
| 68 | + public function __construct(ILogger $logger, | |
| 69 | + Factory $appDataFactory, | |
| 70 | + IURLGenerator $urlGenerator, | |
| 71 | +                                ITimeFactory $timeFactory) { | |
| 72 | + $this->logger = $logger; | |
| 73 | +        $this->appData      = $appDataFactory->get('css'); | |
| 74 | + $this->urlGenerator = $urlGenerator; | |
| 75 | + $this->timeFactory = $timeFactory; | |
| 76 | + | |
| 77 | +        try { | |
| 78 | +            $this->folder = $this->appData->getFolder('icons'); | |
| 79 | +        } catch (NotFoundException $e) { | |
| 80 | +            $this->folder = $this->appData->newFolder('icons'); | |
| 81 | + } | |
| 82 | + } | |
| 83 | + | |
| 84 | +    private function getIconsFromCss(string $css): array { | |
| 85 | + preg_match_all($this->iconVarRE, $css, $matches, PREG_SET_ORDER); | |
| 86 | + $icons = []; | |
| 87 | +        foreach ($matches as $icon) { | |
| 88 | + $icons[$icon[1]] = $icon[2]; | |
| 89 | + } | |
| 90 | + | |
| 91 | + return $icons; | |
| 92 | + } | |
| 93 | + | |
| 94 | + /** | |
| 95 | + * @param string $css | |
| 96 | + * @return string | |
| 97 | + * @throws NotFoundException | |
| 98 | + * @throws \OCP\Files\NotPermittedException | |
| 99 | + */ | |
| 100 | +    public function setIconsCss(string $css): string { | |
| 101 | + | |
| 102 | + $cachedFile = $this->getCachedList(); | |
| 103 | +        if (!$cachedFile) { | |
| 104 | + $currentData = ''; | |
| 105 | + $cachedFile = $this->folder->newFile($this->iconList); | |
| 106 | +        } else { | |
| 107 | + $currentData = $cachedFile->getContent(); | |
| 108 | + } | |
| 109 | + | |
| 110 | + $cachedVarsCssFile = $this->getCachedCSS(); | |
| 111 | +        if (!$cachedVarsCssFile) { | |
| 112 | + $cachedVarsCssFile = $this->folder->newFile($this->fileName); | |
| 113 | + } | |
| 114 | + | |
| 115 | + $icons = $this->getIconsFromCss($currentData . $css); | |
| 116 | + | |
| 117 | + $data = ''; | |
| 118 | + $list = ''; | |
| 119 | +        foreach ($icons as $icon => $url) { | |
| 120 | +            $list .= "--$icon: url('$url');"; | |
| 121 | + list($location,$color) = $this->parseUrl($url); | |
| 122 | + $svg = false; | |
| 123 | +            if ($location !== '') { | |
| 124 | + $svg = file_get_contents($location); | |
| 125 | + } | |
| 126 | +            if ($svg === false) { | |
| 127 | +                $this->logger->debug('Failed to get icon file ' . $location); | |
| 128 | +                $data .= "--$icon: url('$url');"; | |
| 129 | + continue; | |
| 130 | + } | |
| 131 | + $encode = base64_encode($this->colorizeSvg($svg, $color)); | |
| 132 | + $data .= '--' . $icon . ': url(data:image/svg+xml;base64,' . $encode . ');'; | |
| 133 | + } | |
| 134 | + | |
| 135 | +        if (\strlen($data) > 0 && \strlen($list) > 0) { | |
| 136 | +            $data = ":root {\n$data\n}"; | |
| 137 | + $cachedVarsCssFile->putContent($data); | |
| 138 | +            $list = ":root {\n$list\n}"; | |
| 139 | + $cachedFile->putContent($list); | |
| 140 | + } | |
| 141 | + | |
| 142 | + return preg_replace($this->iconVarRE, '', $css); | |
| 143 | + } | |
| 144 | + | |
| 145 | + /** | |
| 146 | + * @param $url | |
| 147 | + * @return array | |
| 148 | + */ | |
| 149 | +    private function parseUrl($url): array { | |
| 150 | + $location = ''; | |
| 151 | + $color = ''; | |
| 152 | + $base = $this->getRoutePrefix() . '/svg/'; | |
| 153 | + $cleanUrl = \substr($url, \strlen($base)); | |
| 154 | +        if (\strpos($url, $base . 'core') === 0) { | |
| 155 | +            $cleanUrl = \substr($cleanUrl, \strlen('core')); | |
| 156 | +            if (\preg_match('/\/([a-zA-Z0-9-_\~\/\.\=\:\;\+\,]+)\?color=([0-9a-fA-F]{3,6})/', $cleanUrl, $matches)) { | |
| 157 | + list(,$cleanUrl,$color) = $matches; | |
| 158 | + $location = \OC::$SERVERROOT . '/core/img/' . $cleanUrl . '.svg'; | |
| 159 | + } | |
| 160 | +        } elseif (\strpos($url, $base) === 0) { | |
| 161 | +            if(\preg_match('/([A-z0-9\_\-]+)\/([a-zA-Z0-9-_\~\/\.\=\:\;\+\,]+)\?color=([0-9a-fA-F]{3,6})/', $cleanUrl, $matches)) { | |
| 162 | + list(,$app,$cleanUrl, $color) = $matches; | |
| 163 | + $location = \OC_App::getAppPath($app) . '/img/' . $cleanUrl . '.svg'; | |
| 164 | +                if ($app === 'settings') { | |
| 165 | + $location = \OC::$SERVERROOT . '/settings/img/' . $cleanUrl . '.svg'; | |
| 166 | + } | |
| 167 | + } | |
| 168 | + | |
| 169 | + } | |
| 170 | + return [ | |
| 171 | + $location, | |
| 172 | + $color | |
| 173 | + ]; | |
| 174 | + } | |
| 175 | + | |
| 176 | + /** | |
| 177 | + * @param $svg | |
| 178 | + * @param $color | |
| 179 | + * @return string | |
| 180 | + */ | |
| 181 | +    public function colorizeSvg($svg, $color): string { | |
| 182 | + // add fill (fill is not present on black elements) | |
| 183 | + $fillRe = '/<((circle|rect|path)((?!fill)[a-z0-9 =".\-#():;])+)\/>/mi'; | |
| 184 | + $svg = preg_replace($fillRe, '<$1 fill="#' . $color . '"/>', $svg); | |
| 185 | + | |
| 186 | + // replace any fill or stroke colors | |
| 187 | +        $svg = preg_replace('/stroke="#([a-z0-9]{3,6})"/mi', 'stroke="#' . $color . '"', $svg); | |
| 188 | +        $svg = preg_replace('/fill="#([a-z0-9]{3,6})"/mi', 'fill="#' . $color . '"', $svg); | |
| 189 | + return $svg; | |
| 190 | + } | |
| 191 | + | |
| 192 | +    private function getRoutePrefix() { | |
| 193 | +        $frontControllerActive = (\OC::$server->getConfig()->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true'); | |
| 194 | + $prefix = \OC::$WEBROOT . '/index.php'; | |
| 195 | +        if ($frontControllerActive) { | |
| 196 | + $prefix = \OC::$WEBROOT; | |
| 197 | + } | |
| 198 | + return $prefix; | |
| 199 | + } | |
| 200 | + | |
| 201 | + /** | |
| 202 | + * Get icons css file | |
| 203 | + * @return ISimpleFile|boolean | |
| 204 | + */ | |
| 205 | +    public function getCachedCSS() { | |
| 206 | +        try { | |
| 207 | + return $this->folder->getFile($this->fileName); | |
| 208 | +        } catch (NotFoundException $e) { | |
| 209 | + return false; | |
| 210 | + } | |
| 211 | + } | |
| 212 | + | |
| 213 | + /** | |
| 214 | + * Get icon-vars list template | |
| 215 | + * @return ISimpleFile|boolean | |
| 216 | + */ | |
| 217 | +    public function getCachedList() { | |
| 218 | +        try { | |
| 219 | + return $this->folder->getFile($this->iconList); | |
| 220 | +        } catch (NotFoundException $e) { | |
| 221 | + return false; | |
| 222 | + } | |
| 223 | + } | |
| 224 | + | |
| 225 | +    public function injectCss() { | |
| 226 | + $mtime = $this->timeFactory->getTime(); | |
| 227 | + $file = $this->getCachedList(); | |
| 228 | +        if ($file) { | |
| 229 | + $mtime = $file->getMTime(); | |
| 230 | + } | |
| 231 | + // Only inject once | |
| 232 | +        foreach (\OC_Util::$headers as $header) { | |
| 233 | + if ( | |
| 234 | +                array_key_exists('attributes', $header) && | |
| 235 | +                array_key_exists('href', $header['attributes']) && | |
| 236 | +                strpos($header['attributes']['href'], $this->fileName) !== false) { | |
| 237 | + return; | |
| 238 | + } | |
| 239 | + } | |
| 240 | +        $linkToCSS = $this->urlGenerator->linkToRoute('core.Css.getCss', ['appName' => 'icons', 'fileName' => $this->fileName, 'v' => $mtime]); | |
| 241 | +        \OC_Util::addHeader('link', ['rel' => 'stylesheet', 'href' => $linkToCSS], null, true); | |
| 242 | + } | |
| 243 | 243 | |
| 244 | 244 | } |