Passed
Push — master ( c193c0...685f00 )
by Roeland
09:30 queued 10s
created
lib/private/Template/IconsCacher.php 1 patch
Indentation   +224 added lines, -224 removed lines patch added patch discarded remove patch
@@ -35,229 +35,229 @@
 block discarded – undo
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
-	private $cachedCss;
62
-	private $cachedList;
63
-
64
-	/**
65
-	 * @param ILogger $logger
66
-	 * @param Factory $appDataFactory
67
-	 * @param IURLGenerator $urlGenerator
68
-	 * @param ITimeFactory $timeFactory
69
-	 * @throws \OCP\Files\NotPermittedException
70
-	 */
71
-	public function __construct(ILogger $logger,
72
-								Factory $appDataFactory,
73
-								IURLGenerator $urlGenerator,
74
-								ITimeFactory $timeFactory) {
75
-		$this->logger       = $logger;
76
-		$this->appData      = $appDataFactory->get('css');
77
-		$this->urlGenerator = $urlGenerator;
78
-		$this->timeFactory  = $timeFactory;
79
-
80
-		try {
81
-			$this->folder = $this->appData->getFolder('icons');
82
-		} catch (NotFoundException $e) {
83
-			$this->folder = $this->appData->newFolder('icons');
84
-		}
85
-	}
86
-
87
-	private function getIconsFromCss(string $css): array {
88
-		preg_match_all($this->iconVarRE, $css, $matches, PREG_SET_ORDER);
89
-		$icons = [];
90
-		foreach ($matches as $icon) {
91
-			$icons[$icon[1]] = $icon[2];
92
-		}
93
-
94
-		return $icons;
95
-	}
96
-
97
-	/**
98
-	 * @param string $css
99
-	 * @return string
100
-	 * @throws NotFoundException
101
-	 * @throws \OCP\Files\NotPermittedException
102
-	 */
103
-	public function setIconsCss(string $css): string {
104
-
105
-		$cachedFile = $this->getCachedList();
106
-		if (!$cachedFile) {
107
-			$currentData = '';
108
-			$cachedFile = $this->folder->newFile($this->iconList);
109
-		} else {
110
-			$currentData = $cachedFile->getContent();
111
-		}
112
-
113
-		$cachedVarsCssFile = $this->getCachedCSS();
114
-		if (!$cachedVarsCssFile) {
115
-			$cachedVarsCssFile = $this->folder->newFile($this->fileName);
116
-		}
117
-
118
-		$icons = $this->getIconsFromCss($currentData . $css);
119
-
120
-		$data = '';
121
-		$list = '';
122
-		foreach ($icons as $icon => $url) {
123
-			$list .= "--$icon: url('$url');";
124
-			list($location,$color) = $this->parseUrl($url);
125
-			$svg = false;
126
-			if ($location !== '' && \file_exists($location)) {
127
-				$svg = \file_get_contents($location);
128
-			}
129
-			if ($svg === false) {
130
-				$this->logger->debug('Failed to get icon file ' . $location);
131
-				$data .= "--$icon: url('$url');";
132
-				continue;
133
-			}
134
-			$encode = base64_encode($this->colorizeSvg($svg, $color));
135
-			$data .= '--' . $icon . ': url(data:image/svg+xml;base64,' . $encode . ');';
136
-		}
137
-
138
-		if (\strlen($data) > 0 && \strlen($list) > 0) {
139
-			$data = ":root {\n$data\n}";
140
-			$cachedVarsCssFile->putContent($data);
141
-			$list = ":root {\n$list\n}";
142
-			$cachedFile->putContent($list);
143
-			$this->cachedList = null;
144
-			$this->cachedCss = null;
145
-		}
146
-
147
-		return preg_replace($this->iconVarRE, '', $css);
148
-	}
149
-
150
-	/**
151
-	 * @param $url
152
-	 * @return array
153
-	 */
154
-	private function parseUrl($url): array {
155
-		$location = '';
156
-		$color = '';
157
-		$base = $this->getRoutePrefix() . '/svg/';
158
-		$cleanUrl = \substr($url, \strlen($base));
159
-		if (\strpos($url, $base . 'core') === 0) {
160
-			$cleanUrl = \substr($cleanUrl, \strlen('core'));
161
-			if (\preg_match('/\/([a-zA-Z0-9-_\~\/\.\=\:\;\+\,]+)\?color=([0-9a-fA-F]{3,6})/', $cleanUrl, $matches)) {
162
-				list(,$cleanUrl,$color) = $matches;
163
-				$location = \OC::$SERVERROOT . '/core/img/' . $cleanUrl . '.svg';
164
-			}
165
-		} elseif (\strpos($url, $base) === 0) {
166
-			if(\preg_match('/([A-z0-9\_\-]+)\/([a-zA-Z0-9-_\~\/\.\=\:\;\+\,]+)\?color=([0-9a-fA-F]{3,6})/', $cleanUrl, $matches)) {
167
-				list(,$app,$cleanUrl, $color) = $matches;
168
-				$location = \OC_App::getAppPath($app) . '/img/' . $cleanUrl . '.svg';
169
-				if ($app === 'settings') {
170
-					$location = \OC::$SERVERROOT . '/settings/img/' . $cleanUrl . '.svg';
171
-				}
172
-			}
173
-
174
-		}
175
-		return [
176
-			$location,
177
-			$color
178
-		];
179
-	}
180
-
181
-	/**
182
-	 * @param $svg
183
-	 * @param $color
184
-	 * @return string
185
-	 */
186
-	public function colorizeSvg($svg, $color): string {
187
-		if (!preg_match('/^[0-9a-f]{3,6}$/i', $color)) {
188
-			// Prevent not-sane colors from being written into the SVG
189
-			$color = '000';
190
-		}
191
-
192
-		// add fill (fill is not present on black elements)
193
-		$fillRe = '/<((circle|rect|path)((?!fill)[a-z0-9 =".\-#():;,])+)\/>/mi';
194
-		$svg = preg_replace($fillRe, '<$1 fill="#' . $color . '"/>', $svg);
195
-
196
-		// replace any fill or stroke colors
197
-		$svg = preg_replace('/stroke="#([a-z0-9]{3,6})"/mi', 'stroke="#' . $color . '"', $svg);
198
-		$svg = preg_replace('/fill="#([a-z0-9]{3,6})"/mi', 'fill="#' . $color . '"', $svg);
199
-		return $svg;
200
-	}
201
-
202
-	private function getRoutePrefix() {
203
-		$frontControllerActive = (\OC::$server->getConfig()->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true');
204
-		$prefix = \OC::$WEBROOT . '/index.php';
205
-		if ($frontControllerActive) {
206
-			$prefix = \OC::$WEBROOT;
207
-		}
208
-		return $prefix;
209
-	}
210
-
211
-	/**
212
-	 * Get icons css file
213
-	 * @return ISimpleFile|boolean
214
-	 */
215
-	public function getCachedCSS() {
216
-		try {
217
-			if (!$this->cachedCss) {
218
-				$this->cachedCss = $this->folder->getFile($this->fileName);
219
-			}
220
-			return $this->cachedCss;
221
-		} catch (NotFoundException $e) {
222
-			return false;
223
-		}
224
-	}
225
-
226
-	/**
227
-	 * Get icon-vars list template
228
-	 * @return ISimpleFile|boolean
229
-	 */
230
-	public function getCachedList() {
231
-		try {
232
-			if (!$this->cachedList) {
233
-				$this->cachedList = $this->folder->getFile($this->iconList);
234
-			}
235
-			return $this->cachedList;
236
-		} catch (NotFoundException $e) {
237
-			return false;
238
-		}
239
-	}
240
-
241
-	/**
242
-	 * Add the icons cache css into the header
243
-	 */
244
-	public function injectCss() {
245
-		$mtime = $this->timeFactory->getTime();
246
-		$file = $this->getCachedList();
247
-		if ($file) {
248
-			$mtime = $file->getMTime();
249
-		}
250
-		// Only inject once
251
-		foreach (\OC_Util::$headers as $header) {
252
-			if (
253
-				array_key_exists('attributes', $header) &&
254
-				array_key_exists('href', $header['attributes']) &&
255
-				strpos($header['attributes']['href'], $this->fileName) !== false) {
256
-				return;
257
-			}
258
-		}
259
-		$linkToCSS = $this->urlGenerator->linkToRoute('core.Css.getCss', ['appName' => 'icons', 'fileName' => $this->fileName, 'v' => $mtime]);
260
-		\OC_Util::addHeader('link', ['rel' => 'stylesheet', 'href' => $linkToCSS], null, true);
261
-	}
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
+    private $cachedCss;
62
+    private $cachedList;
63
+
64
+    /**
65
+     * @param ILogger $logger
66
+     * @param Factory $appDataFactory
67
+     * @param IURLGenerator $urlGenerator
68
+     * @param ITimeFactory $timeFactory
69
+     * @throws \OCP\Files\NotPermittedException
70
+     */
71
+    public function __construct(ILogger $logger,
72
+                                Factory $appDataFactory,
73
+                                IURLGenerator $urlGenerator,
74
+                                ITimeFactory $timeFactory) {
75
+        $this->logger       = $logger;
76
+        $this->appData      = $appDataFactory->get('css');
77
+        $this->urlGenerator = $urlGenerator;
78
+        $this->timeFactory  = $timeFactory;
79
+
80
+        try {
81
+            $this->folder = $this->appData->getFolder('icons');
82
+        } catch (NotFoundException $e) {
83
+            $this->folder = $this->appData->newFolder('icons');
84
+        }
85
+    }
86
+
87
+    private function getIconsFromCss(string $css): array {
88
+        preg_match_all($this->iconVarRE, $css, $matches, PREG_SET_ORDER);
89
+        $icons = [];
90
+        foreach ($matches as $icon) {
91
+            $icons[$icon[1]] = $icon[2];
92
+        }
93
+
94
+        return $icons;
95
+    }
96
+
97
+    /**
98
+     * @param string $css
99
+     * @return string
100
+     * @throws NotFoundException
101
+     * @throws \OCP\Files\NotPermittedException
102
+     */
103
+    public function setIconsCss(string $css): string {
104
+
105
+        $cachedFile = $this->getCachedList();
106
+        if (!$cachedFile) {
107
+            $currentData = '';
108
+            $cachedFile = $this->folder->newFile($this->iconList);
109
+        } else {
110
+            $currentData = $cachedFile->getContent();
111
+        }
112
+
113
+        $cachedVarsCssFile = $this->getCachedCSS();
114
+        if (!$cachedVarsCssFile) {
115
+            $cachedVarsCssFile = $this->folder->newFile($this->fileName);
116
+        }
117
+
118
+        $icons = $this->getIconsFromCss($currentData . $css);
119
+
120
+        $data = '';
121
+        $list = '';
122
+        foreach ($icons as $icon => $url) {
123
+            $list .= "--$icon: url('$url');";
124
+            list($location,$color) = $this->parseUrl($url);
125
+            $svg = false;
126
+            if ($location !== '' && \file_exists($location)) {
127
+                $svg = \file_get_contents($location);
128
+            }
129
+            if ($svg === false) {
130
+                $this->logger->debug('Failed to get icon file ' . $location);
131
+                $data .= "--$icon: url('$url');";
132
+                continue;
133
+            }
134
+            $encode = base64_encode($this->colorizeSvg($svg, $color));
135
+            $data .= '--' . $icon . ': url(data:image/svg+xml;base64,' . $encode . ');';
136
+        }
137
+
138
+        if (\strlen($data) > 0 && \strlen($list) > 0) {
139
+            $data = ":root {\n$data\n}";
140
+            $cachedVarsCssFile->putContent($data);
141
+            $list = ":root {\n$list\n}";
142
+            $cachedFile->putContent($list);
143
+            $this->cachedList = null;
144
+            $this->cachedCss = null;
145
+        }
146
+
147
+        return preg_replace($this->iconVarRE, '', $css);
148
+    }
149
+
150
+    /**
151
+     * @param $url
152
+     * @return array
153
+     */
154
+    private function parseUrl($url): array {
155
+        $location = '';
156
+        $color = '';
157
+        $base = $this->getRoutePrefix() . '/svg/';
158
+        $cleanUrl = \substr($url, \strlen($base));
159
+        if (\strpos($url, $base . 'core') === 0) {
160
+            $cleanUrl = \substr($cleanUrl, \strlen('core'));
161
+            if (\preg_match('/\/([a-zA-Z0-9-_\~\/\.\=\:\;\+\,]+)\?color=([0-9a-fA-F]{3,6})/', $cleanUrl, $matches)) {
162
+                list(,$cleanUrl,$color) = $matches;
163
+                $location = \OC::$SERVERROOT . '/core/img/' . $cleanUrl . '.svg';
164
+            }
165
+        } elseif (\strpos($url, $base) === 0) {
166
+            if(\preg_match('/([A-z0-9\_\-]+)\/([a-zA-Z0-9-_\~\/\.\=\:\;\+\,]+)\?color=([0-9a-fA-F]{3,6})/', $cleanUrl, $matches)) {
167
+                list(,$app,$cleanUrl, $color) = $matches;
168
+                $location = \OC_App::getAppPath($app) . '/img/' . $cleanUrl . '.svg';
169
+                if ($app === 'settings') {
170
+                    $location = \OC::$SERVERROOT . '/settings/img/' . $cleanUrl . '.svg';
171
+                }
172
+            }
173
+
174
+        }
175
+        return [
176
+            $location,
177
+            $color
178
+        ];
179
+    }
180
+
181
+    /**
182
+     * @param $svg
183
+     * @param $color
184
+     * @return string
185
+     */
186
+    public function colorizeSvg($svg, $color): string {
187
+        if (!preg_match('/^[0-9a-f]{3,6}$/i', $color)) {
188
+            // Prevent not-sane colors from being written into the SVG
189
+            $color = '000';
190
+        }
191
+
192
+        // add fill (fill is not present on black elements)
193
+        $fillRe = '/<((circle|rect|path)((?!fill)[a-z0-9 =".\-#():;,])+)\/>/mi';
194
+        $svg = preg_replace($fillRe, '<$1 fill="#' . $color . '"/>', $svg);
195
+
196
+        // replace any fill or stroke colors
197
+        $svg = preg_replace('/stroke="#([a-z0-9]{3,6})"/mi', 'stroke="#' . $color . '"', $svg);
198
+        $svg = preg_replace('/fill="#([a-z0-9]{3,6})"/mi', 'fill="#' . $color . '"', $svg);
199
+        return $svg;
200
+    }
201
+
202
+    private function getRoutePrefix() {
203
+        $frontControllerActive = (\OC::$server->getConfig()->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true');
204
+        $prefix = \OC::$WEBROOT . '/index.php';
205
+        if ($frontControllerActive) {
206
+            $prefix = \OC::$WEBROOT;
207
+        }
208
+        return $prefix;
209
+    }
210
+
211
+    /**
212
+     * Get icons css file
213
+     * @return ISimpleFile|boolean
214
+     */
215
+    public function getCachedCSS() {
216
+        try {
217
+            if (!$this->cachedCss) {
218
+                $this->cachedCss = $this->folder->getFile($this->fileName);
219
+            }
220
+            return $this->cachedCss;
221
+        } catch (NotFoundException $e) {
222
+            return false;
223
+        }
224
+    }
225
+
226
+    /**
227
+     * Get icon-vars list template
228
+     * @return ISimpleFile|boolean
229
+     */
230
+    public function getCachedList() {
231
+        try {
232
+            if (!$this->cachedList) {
233
+                $this->cachedList = $this->folder->getFile($this->iconList);
234
+            }
235
+            return $this->cachedList;
236
+        } catch (NotFoundException $e) {
237
+            return false;
238
+        }
239
+    }
240
+
241
+    /**
242
+     * Add the icons cache css into the header
243
+     */
244
+    public function injectCss() {
245
+        $mtime = $this->timeFactory->getTime();
246
+        $file = $this->getCachedList();
247
+        if ($file) {
248
+            $mtime = $file->getMTime();
249
+        }
250
+        // Only inject once
251
+        foreach (\OC_Util::$headers as $header) {
252
+            if (
253
+                array_key_exists('attributes', $header) &&
254
+                array_key_exists('href', $header['attributes']) &&
255
+                strpos($header['attributes']['href'], $this->fileName) !== false) {
256
+                return;
257
+            }
258
+        }
259
+        $linkToCSS = $this->urlGenerator->linkToRoute('core.Css.getCss', ['appName' => 'icons', 'fileName' => $this->fileName, 'v' => $mtime]);
260
+        \OC_Util::addHeader('link', ['rel' => 'stylesheet', 'href' => $linkToCSS], null, true);
261
+    }
262 262
 
263 263
 }
Please login to merge, or discard this patch.
lib/private/Template/SCSSCacher.php 2 patches
Indentation   +455 added lines, -455 removed lines patch added patch discarded remove patch
@@ -50,459 +50,459 @@
 block discarded – undo
50 50
 
51 51
 class SCSSCacher {
52 52
 
53
-	/** @var ILogger */
54
-	protected $logger;
55
-
56
-	/** @var IAppData */
57
-	protected $appData;
58
-
59
-	/** @var IURLGenerator */
60
-	protected $urlGenerator;
61
-
62
-	/** @var IConfig */
63
-	protected $config;
64
-
65
-	/** @var \OC_Defaults */
66
-	private $defaults;
67
-
68
-	/** @var string */
69
-	protected $serverRoot;
70
-
71
-	/** @var ICache */
72
-	protected $depsCache;
73
-
74
-	/** @var null|string */
75
-	private $injectedVariables;
76
-
77
-	/** @var ICacheFactory */
78
-	private $cacheFactory;
79
-
80
-	/** @var IconsCacher */
81
-	private $iconsCacher;
82
-
83
-	/** @var ICache */
84
-	private $isCachedCache;
85
-
86
-	/** @var ITimeFactory */
87
-	private $timeFactory;
88
-
89
-	/** @var IMemcache */
90
-	private $lockingCache;
91
-
92
-	/**
93
-	 * @param ILogger $logger
94
-	 * @param Factory $appDataFactory
95
-	 * @param IURLGenerator $urlGenerator
96
-	 * @param IConfig $config
97
-	 * @param \OC_Defaults $defaults
98
-	 * @param string $serverRoot
99
-	 * @param ICacheFactory $cacheFactory
100
-	 * @param IconsCacher $iconsCacher
101
-	 * @param ITimeFactory $timeFactory
102
-	 */
103
-	public function __construct(ILogger $logger,
104
-								Factory $appDataFactory,
105
-								IURLGenerator $urlGenerator,
106
-								IConfig $config,
107
-								\OC_Defaults $defaults,
108
-								$serverRoot,
109
-								ICacheFactory $cacheFactory,
110
-								IconsCacher $iconsCacher,
111
-								ITimeFactory $timeFactory) {
112
-		$this->logger       = $logger;
113
-		$this->appData      = $appDataFactory->get('css');
114
-		$this->urlGenerator = $urlGenerator;
115
-		$this->config       = $config;
116
-		$this->defaults     = $defaults;
117
-		$this->serverRoot   = $serverRoot;
118
-		$this->cacheFactory = $cacheFactory;
119
-		$this->depsCache    = $cacheFactory->createDistributed('SCSS-deps-' . md5($this->urlGenerator->getBaseUrl()));
120
-		$this->isCachedCache = $cacheFactory->createLocal('SCSS-cached-' . md5($this->urlGenerator->getBaseUrl()));
121
-		$lockingCache = $cacheFactory->createDistributed('SCSS-locks-' . md5($this->urlGenerator->getBaseUrl()));
122
-		if (!($lockingCache instanceof IMemcache)) {
123
-			$lockingCache = new NullCache();
124
-		}
125
-		$this->lockingCache = $lockingCache;
126
-		$this->iconsCacher = $iconsCacher;
127
-		$this->timeFactory = $timeFactory;
128
-	}
129
-
130
-	/**
131
-	 * Process the caching process if needed
132
-	 *
133
-	 * @param string $root Root path to the nextcloud installation
134
-	 * @param string $file
135
-	 * @param string $app The app name
136
-	 * @return boolean
137
-	 * @throws NotPermittedException
138
-	 */
139
-	public function process(string $root, string $file, string $app): bool {
140
-		$path = explode('/', $root . '/' . $file);
141
-
142
-		$fileNameSCSS = array_pop($path);
143
-		$fileNameCSS  = $this->prependVersionPrefix($this->prependBaseurlPrefix(str_replace('.scss', '.css', $fileNameSCSS)), $app);
144
-
145
-		$path   = implode('/', $path);
146
-		$webDir = $this->getWebDir($path, $app, $this->serverRoot, \OC::$WEBROOT);
147
-
148
-		if (!$this->variablesChanged() && $this->isCached($fileNameCSS, $app)) {
149
-			// Inject icons vars css if any
150
-			return $this->injectCssVariablesIfAny();
151
-		}
152
-
153
-		try {
154
-			$folder = $this->appData->getFolder($app);
155
-		} catch (NotFoundException $e) {
156
-			// creating css appdata folder
157
-			$folder = $this->appData->newFolder($app);
158
-		}
159
-
160
-		$lockKey = $webDir . '/' . $fileNameSCSS;
161
-
162
-		if (!$this->lockingCache->add($lockKey, 'locked!', 120)) {
163
-			$retry = 0;
164
-			sleep(1);
165
-			while ($retry < 10) {
166
-				if (!$this->variablesChanged() && $this->isCached($fileNameCSS, $app)) {
167
-					// Inject icons vars css if any
168
-					$this->lockingCache->remove($lockKey);
169
-					$this->logger->debug('SCSSCacher: ' .$lockKey.' is now available after '.$retry.'s. Moving on...', ['app' => 'core']);
170
-					return $this->injectCssVariablesIfAny();
171
-				}
172
-				$this->logger->debug('SCSSCacher: scss cache file locked for '.$lockKey, ['app' => 'core']);
173
-				sleep($retry);
174
-				$retry++;
175
-			}
176
-			$this->logger->debug('SCSSCacher: Giving up scss caching for '.$lockKey, ['app' => 'core']);
177
-			return false;
178
-		}
179
-
180
-		try {
181
-			$cached = $this->cache($path, $fileNameCSS, $fileNameSCSS, $folder, $webDir);
182
-		} catch (\Exception $e) {
183
-			$this->lockingCache->remove($lockKey);
184
-			throw $e;
185
-		}
186
-
187
-		// Cleaning lock
188
-		$this->lockingCache->remove($lockKey);
189
-
190
-		// Inject icons vars css if any
191
-		if ($this->iconsCacher->getCachedCSS() && $this->iconsCacher->getCachedCSS()->getSize() > 0) {
192
-			$this->iconsCacher->injectCss();
193
-		}
194
-
195
-		return $cached;
196
-	}
197
-
198
-	/**
199
-	 * @param $appName
200
-	 * @param $fileName
201
-	 * @return ISimpleFile
202
-	 */
203
-	public function getCachedCSS(string $appName, string $fileName): ISimpleFile {
204
-		$folder         = $this->appData->getFolder($appName);
205
-		$cachedFileName = $this->prependVersionPrefix($this->prependBaseurlPrefix($fileName), $appName);
206
-
207
-		return $folder->getFile($cachedFileName);
208
-	}
209
-
210
-	/**
211
-	 * Check if the file is cached or not
212
-	 * @param string $fileNameCSS
213
-	 * @param string $app
214
-	 * @return boolean
215
-	 */
216
-	private function isCached(string $fileNameCSS, string $app) {
217
-		$key = $this->config->getSystemValue('version') . '/' . $app . '/' . $fileNameCSS;
218
-
219
-		// If the file mtime is more recent than our cached one,
220
-		// let's consider the file is properly cached
221
-		if ($cacheValue = $this->isCachedCache->get($key)) {
222
-			if ($cacheValue > $this->timeFactory->getTime()) {
223
-				return true;
224
-			}
225
-		}
226
-
227
-		// Creating file cache if none for further checks
228
-		try {
229
-			$folder = $this->appData->getFolder($app);
230
-		} catch (NotFoundException $e) {
231
-			return false;
232
-		}
233
-
234
-		// Checking if file size is coherent
235
-		// and if one of the css dependency changed
236
-		try {
237
-			$cachedFile = $folder->getFile($fileNameCSS);
238
-			if ($cachedFile->getSize() > 0) {
239
-				$depFileName = $fileNameCSS . '.deps';
240
-				$deps        = $this->depsCache->get($folder->getName() . '-' . $depFileName);
241
-				if ($deps === null) {
242
-					$depFile = $folder->getFile($depFileName);
243
-					$deps    = $depFile->getContent();
244
-					// Set to memcache for next run
245
-					$this->depsCache->set($folder->getName() . '-' . $depFileName, $deps);
246
-				}
247
-				$deps = json_decode($deps, true);
248
-
249
-				foreach ((array) $deps as $file => $mtime) {
250
-					if (!file_exists($file) || filemtime($file) > $mtime) {
251
-						return false;
252
-					}
253
-				}
254
-
255
-				$this->isCachedCache->set($key, $this->timeFactory->getTime() + 5 * 60);
256
-				return true;
257
-			}
258
-
259
-			return false;
260
-		} catch (NotFoundException $e) {
261
-			return false;
262
-		}
263
-	}
264
-
265
-	/**
266
-	 * Check if the variables file has changed
267
-	 * @return bool
268
-	 */
269
-	private function variablesChanged(): bool {
270
-		$injectedVariables = $this->getInjectedVariables();
271
-		if ($this->config->getAppValue('core', 'theming.variables') !== md5($injectedVariables)) {
272
-			$this->resetCache();
273
-			$this->config->setAppValue('core', 'theming.variables', md5($injectedVariables));
274
-			return true;
275
-		}
276
-		return false;
277
-	}
278
-
279
-	/**
280
-	 * Cache the file with AppData
281
-	 *
282
-	 * @param string $path
283
-	 * @param string $fileNameCSS
284
-	 * @param string $fileNameSCSS
285
-	 * @param ISimpleFolder $folder
286
-	 * @param string $webDir
287
-	 * @return boolean
288
-	 * @throws NotPermittedException
289
-	 */
290
-	private function cache(string $path, string $fileNameCSS, string $fileNameSCSS, ISimpleFolder $folder, string $webDir) {
291
-		$scss = new Compiler();
292
-		$scss->setImportPaths([
293
-			$path,
294
-			$this->serverRoot . '/core/css/'
295
-		]);
296
-
297
-		// Continue after throw
298
-		$scss->setIgnoreErrors(true);
299
-		if ($this->config->getSystemValue('debug')) {
300
-			// Debug mode
301
-			$scss->setFormatter(Expanded::class);
302
-			$scss->setLineNumberStyle(Compiler::LINE_COMMENTS);
303
-		} else {
304
-			// Compression
305
-			$scss->setFormatter(Crunched::class);
306
-		}
307
-
308
-		try {
309
-			$cachedfile = $folder->getFile($fileNameCSS);
310
-		} catch (NotFoundException $e) {
311
-			$cachedfile = $folder->newFile($fileNameCSS);
312
-		}
313
-
314
-		$depFileName = $fileNameCSS . '.deps';
315
-		try {
316
-			$depFile = $folder->getFile($depFileName);
317
-		} catch (NotFoundException $e) {
318
-			$depFile = $folder->newFile($depFileName);
319
-		}
320
-
321
-		// Compile
322
-		try {
323
-			$compiledScss = $scss->compile(
324
-				'$webroot: \'' . $this->getRoutePrefix() . '\';' .
325
-				$this->getInjectedVariables() .
326
-				'@import "variables.scss";' .
327
-				'@import "functions.scss";' .
328
-				'@import "' . $fileNameSCSS . '";');
329
-		} catch (ParserException $e) {
330
-			$this->logger->logException($e, ['app' => 'core']);
331
-
332
-			return false;
333
-		}
334
-
335
-		// Parse Icons and create related css variables
336
-		$compiledScss = $this->iconsCacher->setIconsCss($compiledScss);
337
-
338
-		// Gzip file
339
-		try {
340
-			$gzipFile = $folder->getFile($fileNameCSS . '.gzip'); # Safari doesn't like .gz
341
-		} catch (NotFoundException $e) {
342
-			$gzipFile = $folder->newFile($fileNameCSS . '.gzip'); # Safari doesn't like .gz
343
-		}
344
-
345
-		try {
346
-			$data = $this->rebaseUrls($compiledScss, $webDir);
347
-			$cachedfile->putContent($data);
348
-			$deps = json_encode($scss->getParsedFiles());
349
-			$depFile->putContent($deps);
350
-			$this->depsCache->set($folder->getName() . '-' . $depFileName, $deps);
351
-			$gzipFile->putContent(gzencode($data, 9));
352
-			$this->logger->debug('SCSSCacher: ' . $webDir . '/' . $fileNameSCSS . ' compiled and successfully cached', ['app' => 'core']);
353
-
354
-			return true;
355
-		} catch (NotPermittedException $e) {
356
-			$this->logger->error('SCSSCacher: unable to cache: ' . $fileNameSCSS);
357
-
358
-			return false;
359
-		}
360
-	}
361
-
362
-	/**
363
-	 * Reset scss cache by deleting all generated css files
364
-	 * We need to regenerate all files when variables change
365
-	 */
366
-	public function resetCache() {
367
-		$this->injectedVariables = null;
368
-
369
-		// do not clear locks
370
-		$this->cacheFactory->createDistributed('SCSS-deps-')->clear();
371
-		$this->cacheFactory->createDistributed('SCSS-cached-')->clear();
372
-
373
-		$appDirectory = $this->appData->getDirectoryListing();
374
-		foreach ($appDirectory as $folder) {
375
-			foreach ($folder->getDirectoryListing() as $file) {
376
-				try {
377
-					$file->delete();
378
-				} catch (NotPermittedException $e) {
379
-					$this->logger->logException($e, ['message' => 'SCSSCacher: unable to delete file: ' . $file->getName()]);
380
-				}
381
-			}
382
-		}
383
-		$this->logger->debug('SCSSCacher: css cache cleared!');
384
-	}
385
-
386
-	/**
387
-	 * @return string SCSS code for variables from OC_Defaults
388
-	 */
389
-	private function getInjectedVariables(): string {
390
-		if ($this->injectedVariables !== null) {
391
-			return $this->injectedVariables;
392
-		}
393
-		$variables = '';
394
-		foreach ($this->defaults->getScssVariables() as $key => $value) {
395
-			$variables .= '$' . $key . ': ' . $value . ' !default;';
396
-		}
397
-
398
-		// check for valid variables / otherwise fall back to defaults
399
-		try {
400
-			$scss = new Compiler();
401
-			$scss->compile($variables);
402
-			$this->injectedVariables = $variables;
403
-		} catch (ParserException $e) {
404
-			$this->logger->logException($e, ['app' => 'core']);
405
-		}
406
-
407
-		return $variables;
408
-	}
409
-
410
-	/**
411
-	 * Add the correct uri prefix to make uri valid again
412
-	 * @param string $css
413
-	 * @param string $webDir
414
-	 * @return string
415
-	 */
416
-	private function rebaseUrls(string $css, string $webDir): string {
417
-		$re    = '/url\([\'"]([^\/][\.\w?=\/-]*)[\'"]\)/x';
418
-		$subst = 'url(\'' . $webDir . '/$1\')';
419
-
420
-		return preg_replace($re, $subst, $css);
421
-	}
422
-
423
-	/**
424
-	 * Return the cached css file uri
425
-	 * @param string $appName the app name
426
-	 * @param string $fileName
427
-	 * @return string
428
-	 */
429
-	public function getCachedSCSS(string $appName, string $fileName): string {
430
-		$tmpfileLoc = explode('/', $fileName);
431
-		$fileName   = array_pop($tmpfileLoc);
432
-		$fileName   = $this->prependVersionPrefix($this->prependBaseurlPrefix(str_replace('.scss', '.css', $fileName)), $appName);
433
-
434
-		return substr($this->urlGenerator->linkToRoute('core.Css.getCss', [
435
-			'fileName' => $fileName,
436
-			'appName' => $appName,
437
-			'v' => $this->config->getAppValue('core', 'theming.variables', '0')
438
-		]), \strlen(\OC::$WEBROOT) + 1);
439
-	}
440
-
441
-	/**
442
-	 * Prepend hashed base url to the css file
443
-	 * @param string $cssFile
444
-	 * @return string
445
-	 */
446
-	private function prependBaseurlPrefix(string $cssFile): string {
447
-		return substr(md5($this->urlGenerator->getBaseUrl() . $this->getRoutePrefix()), 0, 4) . '-' . $cssFile;
448
-	}
449
-
450
-	private function getRoutePrefix() {
451
-		$frontControllerActive = ($this->config->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true');
452
-		$prefix = \OC::$WEBROOT . '/index.php';
453
-		if ($frontControllerActive) {
454
-			$prefix = \OC::$WEBROOT;
455
-		}
456
-		return $prefix;
457
-	}
458
-
459
-	/**
460
-	 * Prepend hashed app version hash
461
-	 * @param string $cssFile
462
-	 * @param string $appId
463
-	 * @return string
464
-	 */
465
-	private function prependVersionPrefix(string $cssFile, string $appId): string {
466
-		$appVersion = \OC_App::getAppVersion($appId);
467
-		if ($appVersion !== '0') {
468
-			return substr(md5($appVersion), 0, 4) . '-' . $cssFile;
469
-		}
470
-		$coreVersion = \OC_Util::getVersionString();
471
-
472
-		return substr(md5($coreVersion), 0, 4) . '-' . $cssFile;
473
-	}
474
-
475
-	/**
476
-	 * Get WebDir root
477
-	 * @param string $path the css file path
478
-	 * @param string $appName the app name
479
-	 * @param string $serverRoot the server root path
480
-	 * @param string $webRoot the nextcloud installation root path
481
-	 * @return string the webDir
482
-	 */
483
-	private function getWebDir(string $path, string $appName, string $serverRoot, string $webRoot): string {
484
-		// Detect if path is within server root AND if path is within an app path
485
-		if (strpos($path, $serverRoot) === false && $appWebPath = \OC_App::getAppWebPath($appName)) {
486
-			// Get the file path within the app directory
487
-			$appDirectoryPath = explode($appName, $path)[1];
488
-			// Remove the webroot
489
-
490
-			return str_replace($webRoot, '', $appWebPath . $appDirectoryPath);
491
-		}
492
-
493
-		return $webRoot . substr($path, strlen($serverRoot));
494
-	}
495
-
496
-	/**
497
-	 * Add the icons css cache in the header if needed 
498
-	 *
499
-	 * @return boolean true
500
-	 */
501
-	private function injectCssVariablesIfAny() {
502
-		// Inject icons vars css if any
503
-		if ($this->iconsCacher->getCachedCSS() && $this->iconsCacher->getCachedCSS()->getSize() > 0) {
504
-			$this->iconsCacher->injectCss();
505
-		}
506
-		return true;
507
-	}
53
+    /** @var ILogger */
54
+    protected $logger;
55
+
56
+    /** @var IAppData */
57
+    protected $appData;
58
+
59
+    /** @var IURLGenerator */
60
+    protected $urlGenerator;
61
+
62
+    /** @var IConfig */
63
+    protected $config;
64
+
65
+    /** @var \OC_Defaults */
66
+    private $defaults;
67
+
68
+    /** @var string */
69
+    protected $serverRoot;
70
+
71
+    /** @var ICache */
72
+    protected $depsCache;
73
+
74
+    /** @var null|string */
75
+    private $injectedVariables;
76
+
77
+    /** @var ICacheFactory */
78
+    private $cacheFactory;
79
+
80
+    /** @var IconsCacher */
81
+    private $iconsCacher;
82
+
83
+    /** @var ICache */
84
+    private $isCachedCache;
85
+
86
+    /** @var ITimeFactory */
87
+    private $timeFactory;
88
+
89
+    /** @var IMemcache */
90
+    private $lockingCache;
91
+
92
+    /**
93
+     * @param ILogger $logger
94
+     * @param Factory $appDataFactory
95
+     * @param IURLGenerator $urlGenerator
96
+     * @param IConfig $config
97
+     * @param \OC_Defaults $defaults
98
+     * @param string $serverRoot
99
+     * @param ICacheFactory $cacheFactory
100
+     * @param IconsCacher $iconsCacher
101
+     * @param ITimeFactory $timeFactory
102
+     */
103
+    public function __construct(ILogger $logger,
104
+                                Factory $appDataFactory,
105
+                                IURLGenerator $urlGenerator,
106
+                                IConfig $config,
107
+                                \OC_Defaults $defaults,
108
+                                $serverRoot,
109
+                                ICacheFactory $cacheFactory,
110
+                                IconsCacher $iconsCacher,
111
+                                ITimeFactory $timeFactory) {
112
+        $this->logger       = $logger;
113
+        $this->appData      = $appDataFactory->get('css');
114
+        $this->urlGenerator = $urlGenerator;
115
+        $this->config       = $config;
116
+        $this->defaults     = $defaults;
117
+        $this->serverRoot   = $serverRoot;
118
+        $this->cacheFactory = $cacheFactory;
119
+        $this->depsCache    = $cacheFactory->createDistributed('SCSS-deps-' . md5($this->urlGenerator->getBaseUrl()));
120
+        $this->isCachedCache = $cacheFactory->createLocal('SCSS-cached-' . md5($this->urlGenerator->getBaseUrl()));
121
+        $lockingCache = $cacheFactory->createDistributed('SCSS-locks-' . md5($this->urlGenerator->getBaseUrl()));
122
+        if (!($lockingCache instanceof IMemcache)) {
123
+            $lockingCache = new NullCache();
124
+        }
125
+        $this->lockingCache = $lockingCache;
126
+        $this->iconsCacher = $iconsCacher;
127
+        $this->timeFactory = $timeFactory;
128
+    }
129
+
130
+    /**
131
+     * Process the caching process if needed
132
+     *
133
+     * @param string $root Root path to the nextcloud installation
134
+     * @param string $file
135
+     * @param string $app The app name
136
+     * @return boolean
137
+     * @throws NotPermittedException
138
+     */
139
+    public function process(string $root, string $file, string $app): bool {
140
+        $path = explode('/', $root . '/' . $file);
141
+
142
+        $fileNameSCSS = array_pop($path);
143
+        $fileNameCSS  = $this->prependVersionPrefix($this->prependBaseurlPrefix(str_replace('.scss', '.css', $fileNameSCSS)), $app);
144
+
145
+        $path   = implode('/', $path);
146
+        $webDir = $this->getWebDir($path, $app, $this->serverRoot, \OC::$WEBROOT);
147
+
148
+        if (!$this->variablesChanged() && $this->isCached($fileNameCSS, $app)) {
149
+            // Inject icons vars css if any
150
+            return $this->injectCssVariablesIfAny();
151
+        }
152
+
153
+        try {
154
+            $folder = $this->appData->getFolder($app);
155
+        } catch (NotFoundException $e) {
156
+            // creating css appdata folder
157
+            $folder = $this->appData->newFolder($app);
158
+        }
159
+
160
+        $lockKey = $webDir . '/' . $fileNameSCSS;
161
+
162
+        if (!$this->lockingCache->add($lockKey, 'locked!', 120)) {
163
+            $retry = 0;
164
+            sleep(1);
165
+            while ($retry < 10) {
166
+                if (!$this->variablesChanged() && $this->isCached($fileNameCSS, $app)) {
167
+                    // Inject icons vars css if any
168
+                    $this->lockingCache->remove($lockKey);
169
+                    $this->logger->debug('SCSSCacher: ' .$lockKey.' is now available after '.$retry.'s. Moving on...', ['app' => 'core']);
170
+                    return $this->injectCssVariablesIfAny();
171
+                }
172
+                $this->logger->debug('SCSSCacher: scss cache file locked for '.$lockKey, ['app' => 'core']);
173
+                sleep($retry);
174
+                $retry++;
175
+            }
176
+            $this->logger->debug('SCSSCacher: Giving up scss caching for '.$lockKey, ['app' => 'core']);
177
+            return false;
178
+        }
179
+
180
+        try {
181
+            $cached = $this->cache($path, $fileNameCSS, $fileNameSCSS, $folder, $webDir);
182
+        } catch (\Exception $e) {
183
+            $this->lockingCache->remove($lockKey);
184
+            throw $e;
185
+        }
186
+
187
+        // Cleaning lock
188
+        $this->lockingCache->remove($lockKey);
189
+
190
+        // Inject icons vars css if any
191
+        if ($this->iconsCacher->getCachedCSS() && $this->iconsCacher->getCachedCSS()->getSize() > 0) {
192
+            $this->iconsCacher->injectCss();
193
+        }
194
+
195
+        return $cached;
196
+    }
197
+
198
+    /**
199
+     * @param $appName
200
+     * @param $fileName
201
+     * @return ISimpleFile
202
+     */
203
+    public function getCachedCSS(string $appName, string $fileName): ISimpleFile {
204
+        $folder         = $this->appData->getFolder($appName);
205
+        $cachedFileName = $this->prependVersionPrefix($this->prependBaseurlPrefix($fileName), $appName);
206
+
207
+        return $folder->getFile($cachedFileName);
208
+    }
209
+
210
+    /**
211
+     * Check if the file is cached or not
212
+     * @param string $fileNameCSS
213
+     * @param string $app
214
+     * @return boolean
215
+     */
216
+    private function isCached(string $fileNameCSS, string $app) {
217
+        $key = $this->config->getSystemValue('version') . '/' . $app . '/' . $fileNameCSS;
218
+
219
+        // If the file mtime is more recent than our cached one,
220
+        // let's consider the file is properly cached
221
+        if ($cacheValue = $this->isCachedCache->get($key)) {
222
+            if ($cacheValue > $this->timeFactory->getTime()) {
223
+                return true;
224
+            }
225
+        }
226
+
227
+        // Creating file cache if none for further checks
228
+        try {
229
+            $folder = $this->appData->getFolder($app);
230
+        } catch (NotFoundException $e) {
231
+            return false;
232
+        }
233
+
234
+        // Checking if file size is coherent
235
+        // and if one of the css dependency changed
236
+        try {
237
+            $cachedFile = $folder->getFile($fileNameCSS);
238
+            if ($cachedFile->getSize() > 0) {
239
+                $depFileName = $fileNameCSS . '.deps';
240
+                $deps        = $this->depsCache->get($folder->getName() . '-' . $depFileName);
241
+                if ($deps === null) {
242
+                    $depFile = $folder->getFile($depFileName);
243
+                    $deps    = $depFile->getContent();
244
+                    // Set to memcache for next run
245
+                    $this->depsCache->set($folder->getName() . '-' . $depFileName, $deps);
246
+                }
247
+                $deps = json_decode($deps, true);
248
+
249
+                foreach ((array) $deps as $file => $mtime) {
250
+                    if (!file_exists($file) || filemtime($file) > $mtime) {
251
+                        return false;
252
+                    }
253
+                }
254
+
255
+                $this->isCachedCache->set($key, $this->timeFactory->getTime() + 5 * 60);
256
+                return true;
257
+            }
258
+
259
+            return false;
260
+        } catch (NotFoundException $e) {
261
+            return false;
262
+        }
263
+    }
264
+
265
+    /**
266
+     * Check if the variables file has changed
267
+     * @return bool
268
+     */
269
+    private function variablesChanged(): bool {
270
+        $injectedVariables = $this->getInjectedVariables();
271
+        if ($this->config->getAppValue('core', 'theming.variables') !== md5($injectedVariables)) {
272
+            $this->resetCache();
273
+            $this->config->setAppValue('core', 'theming.variables', md5($injectedVariables));
274
+            return true;
275
+        }
276
+        return false;
277
+    }
278
+
279
+    /**
280
+     * Cache the file with AppData
281
+     *
282
+     * @param string $path
283
+     * @param string $fileNameCSS
284
+     * @param string $fileNameSCSS
285
+     * @param ISimpleFolder $folder
286
+     * @param string $webDir
287
+     * @return boolean
288
+     * @throws NotPermittedException
289
+     */
290
+    private function cache(string $path, string $fileNameCSS, string $fileNameSCSS, ISimpleFolder $folder, string $webDir) {
291
+        $scss = new Compiler();
292
+        $scss->setImportPaths([
293
+            $path,
294
+            $this->serverRoot . '/core/css/'
295
+        ]);
296
+
297
+        // Continue after throw
298
+        $scss->setIgnoreErrors(true);
299
+        if ($this->config->getSystemValue('debug')) {
300
+            // Debug mode
301
+            $scss->setFormatter(Expanded::class);
302
+            $scss->setLineNumberStyle(Compiler::LINE_COMMENTS);
303
+        } else {
304
+            // Compression
305
+            $scss->setFormatter(Crunched::class);
306
+        }
307
+
308
+        try {
309
+            $cachedfile = $folder->getFile($fileNameCSS);
310
+        } catch (NotFoundException $e) {
311
+            $cachedfile = $folder->newFile($fileNameCSS);
312
+        }
313
+
314
+        $depFileName = $fileNameCSS . '.deps';
315
+        try {
316
+            $depFile = $folder->getFile($depFileName);
317
+        } catch (NotFoundException $e) {
318
+            $depFile = $folder->newFile($depFileName);
319
+        }
320
+
321
+        // Compile
322
+        try {
323
+            $compiledScss = $scss->compile(
324
+                '$webroot: \'' . $this->getRoutePrefix() . '\';' .
325
+                $this->getInjectedVariables() .
326
+                '@import "variables.scss";' .
327
+                '@import "functions.scss";' .
328
+                '@import "' . $fileNameSCSS . '";');
329
+        } catch (ParserException $e) {
330
+            $this->logger->logException($e, ['app' => 'core']);
331
+
332
+            return false;
333
+        }
334
+
335
+        // Parse Icons and create related css variables
336
+        $compiledScss = $this->iconsCacher->setIconsCss($compiledScss);
337
+
338
+        // Gzip file
339
+        try {
340
+            $gzipFile = $folder->getFile($fileNameCSS . '.gzip'); # Safari doesn't like .gz
341
+        } catch (NotFoundException $e) {
342
+            $gzipFile = $folder->newFile($fileNameCSS . '.gzip'); # Safari doesn't like .gz
343
+        }
344
+
345
+        try {
346
+            $data = $this->rebaseUrls($compiledScss, $webDir);
347
+            $cachedfile->putContent($data);
348
+            $deps = json_encode($scss->getParsedFiles());
349
+            $depFile->putContent($deps);
350
+            $this->depsCache->set($folder->getName() . '-' . $depFileName, $deps);
351
+            $gzipFile->putContent(gzencode($data, 9));
352
+            $this->logger->debug('SCSSCacher: ' . $webDir . '/' . $fileNameSCSS . ' compiled and successfully cached', ['app' => 'core']);
353
+
354
+            return true;
355
+        } catch (NotPermittedException $e) {
356
+            $this->logger->error('SCSSCacher: unable to cache: ' . $fileNameSCSS);
357
+
358
+            return false;
359
+        }
360
+    }
361
+
362
+    /**
363
+     * Reset scss cache by deleting all generated css files
364
+     * We need to regenerate all files when variables change
365
+     */
366
+    public function resetCache() {
367
+        $this->injectedVariables = null;
368
+
369
+        // do not clear locks
370
+        $this->cacheFactory->createDistributed('SCSS-deps-')->clear();
371
+        $this->cacheFactory->createDistributed('SCSS-cached-')->clear();
372
+
373
+        $appDirectory = $this->appData->getDirectoryListing();
374
+        foreach ($appDirectory as $folder) {
375
+            foreach ($folder->getDirectoryListing() as $file) {
376
+                try {
377
+                    $file->delete();
378
+                } catch (NotPermittedException $e) {
379
+                    $this->logger->logException($e, ['message' => 'SCSSCacher: unable to delete file: ' . $file->getName()]);
380
+                }
381
+            }
382
+        }
383
+        $this->logger->debug('SCSSCacher: css cache cleared!');
384
+    }
385
+
386
+    /**
387
+     * @return string SCSS code for variables from OC_Defaults
388
+     */
389
+    private function getInjectedVariables(): string {
390
+        if ($this->injectedVariables !== null) {
391
+            return $this->injectedVariables;
392
+        }
393
+        $variables = '';
394
+        foreach ($this->defaults->getScssVariables() as $key => $value) {
395
+            $variables .= '$' . $key . ': ' . $value . ' !default;';
396
+        }
397
+
398
+        // check for valid variables / otherwise fall back to defaults
399
+        try {
400
+            $scss = new Compiler();
401
+            $scss->compile($variables);
402
+            $this->injectedVariables = $variables;
403
+        } catch (ParserException $e) {
404
+            $this->logger->logException($e, ['app' => 'core']);
405
+        }
406
+
407
+        return $variables;
408
+    }
409
+
410
+    /**
411
+     * Add the correct uri prefix to make uri valid again
412
+     * @param string $css
413
+     * @param string $webDir
414
+     * @return string
415
+     */
416
+    private function rebaseUrls(string $css, string $webDir): string {
417
+        $re    = '/url\([\'"]([^\/][\.\w?=\/-]*)[\'"]\)/x';
418
+        $subst = 'url(\'' . $webDir . '/$1\')';
419
+
420
+        return preg_replace($re, $subst, $css);
421
+    }
422
+
423
+    /**
424
+     * Return the cached css file uri
425
+     * @param string $appName the app name
426
+     * @param string $fileName
427
+     * @return string
428
+     */
429
+    public function getCachedSCSS(string $appName, string $fileName): string {
430
+        $tmpfileLoc = explode('/', $fileName);
431
+        $fileName   = array_pop($tmpfileLoc);
432
+        $fileName   = $this->prependVersionPrefix($this->prependBaseurlPrefix(str_replace('.scss', '.css', $fileName)), $appName);
433
+
434
+        return substr($this->urlGenerator->linkToRoute('core.Css.getCss', [
435
+            'fileName' => $fileName,
436
+            'appName' => $appName,
437
+            'v' => $this->config->getAppValue('core', 'theming.variables', '0')
438
+        ]), \strlen(\OC::$WEBROOT) + 1);
439
+    }
440
+
441
+    /**
442
+     * Prepend hashed base url to the css file
443
+     * @param string $cssFile
444
+     * @return string
445
+     */
446
+    private function prependBaseurlPrefix(string $cssFile): string {
447
+        return substr(md5($this->urlGenerator->getBaseUrl() . $this->getRoutePrefix()), 0, 4) . '-' . $cssFile;
448
+    }
449
+
450
+    private function getRoutePrefix() {
451
+        $frontControllerActive = ($this->config->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true');
452
+        $prefix = \OC::$WEBROOT . '/index.php';
453
+        if ($frontControllerActive) {
454
+            $prefix = \OC::$WEBROOT;
455
+        }
456
+        return $prefix;
457
+    }
458
+
459
+    /**
460
+     * Prepend hashed app version hash
461
+     * @param string $cssFile
462
+     * @param string $appId
463
+     * @return string
464
+     */
465
+    private function prependVersionPrefix(string $cssFile, string $appId): string {
466
+        $appVersion = \OC_App::getAppVersion($appId);
467
+        if ($appVersion !== '0') {
468
+            return substr(md5($appVersion), 0, 4) . '-' . $cssFile;
469
+        }
470
+        $coreVersion = \OC_Util::getVersionString();
471
+
472
+        return substr(md5($coreVersion), 0, 4) . '-' . $cssFile;
473
+    }
474
+
475
+    /**
476
+     * Get WebDir root
477
+     * @param string $path the css file path
478
+     * @param string $appName the app name
479
+     * @param string $serverRoot the server root path
480
+     * @param string $webRoot the nextcloud installation root path
481
+     * @return string the webDir
482
+     */
483
+    private function getWebDir(string $path, string $appName, string $serverRoot, string $webRoot): string {
484
+        // Detect if path is within server root AND if path is within an app path
485
+        if (strpos($path, $serverRoot) === false && $appWebPath = \OC_App::getAppWebPath($appName)) {
486
+            // Get the file path within the app directory
487
+            $appDirectoryPath = explode($appName, $path)[1];
488
+            // Remove the webroot
489
+
490
+            return str_replace($webRoot, '', $appWebPath . $appDirectoryPath);
491
+        }
492
+
493
+        return $webRoot . substr($path, strlen($serverRoot));
494
+    }
495
+
496
+    /**
497
+     * Add the icons css cache in the header if needed 
498
+     *
499
+     * @return boolean true
500
+     */
501
+    private function injectCssVariablesIfAny() {
502
+        // Inject icons vars css if any
503
+        if ($this->iconsCacher->getCachedCSS() && $this->iconsCacher->getCachedCSS()->getSize() > 0) {
504
+            $this->iconsCacher->injectCss();
505
+        }
506
+        return true;
507
+    }
508 508
 }
Please login to merge, or discard this patch.
Spacing   +31 added lines, -31 removed lines patch added patch discarded remove patch
@@ -116,9 +116,9 @@  discard block
 block discarded – undo
116 116
 		$this->defaults     = $defaults;
117 117
 		$this->serverRoot   = $serverRoot;
118 118
 		$this->cacheFactory = $cacheFactory;
119
-		$this->depsCache    = $cacheFactory->createDistributed('SCSS-deps-' . md5($this->urlGenerator->getBaseUrl()));
120
-		$this->isCachedCache = $cacheFactory->createLocal('SCSS-cached-' . md5($this->urlGenerator->getBaseUrl()));
121
-		$lockingCache = $cacheFactory->createDistributed('SCSS-locks-' . md5($this->urlGenerator->getBaseUrl()));
119
+		$this->depsCache    = $cacheFactory->createDistributed('SCSS-deps-'.md5($this->urlGenerator->getBaseUrl()));
120
+		$this->isCachedCache = $cacheFactory->createLocal('SCSS-cached-'.md5($this->urlGenerator->getBaseUrl()));
121
+		$lockingCache = $cacheFactory->createDistributed('SCSS-locks-'.md5($this->urlGenerator->getBaseUrl()));
122 122
 		if (!($lockingCache instanceof IMemcache)) {
123 123
 			$lockingCache = new NullCache();
124 124
 		}
@@ -137,7 +137,7 @@  discard block
 block discarded – undo
137 137
 	 * @throws NotPermittedException
138 138
 	 */
139 139
 	public function process(string $root, string $file, string $app): bool {
140
-		$path = explode('/', $root . '/' . $file);
140
+		$path = explode('/', $root.'/'.$file);
141 141
 
142 142
 		$fileNameSCSS = array_pop($path);
143 143
 		$fileNameCSS  = $this->prependVersionPrefix($this->prependBaseurlPrefix(str_replace('.scss', '.css', $fileNameSCSS)), $app);
@@ -157,7 +157,7 @@  discard block
 block discarded – undo
157 157
 			$folder = $this->appData->newFolder($app);
158 158
 		}
159 159
 
160
-		$lockKey = $webDir . '/' . $fileNameSCSS;
160
+		$lockKey = $webDir.'/'.$fileNameSCSS;
161 161
 
162 162
 		if (!$this->lockingCache->add($lockKey, 'locked!', 120)) {
163 163
 			$retry = 0;
@@ -166,7 +166,7 @@  discard block
 block discarded – undo
166 166
 				if (!$this->variablesChanged() && $this->isCached($fileNameCSS, $app)) {
167 167
 					// Inject icons vars css if any
168 168
 					$this->lockingCache->remove($lockKey);
169
-					$this->logger->debug('SCSSCacher: ' .$lockKey.' is now available after '.$retry.'s. Moving on...', ['app' => 'core']);
169
+					$this->logger->debug('SCSSCacher: '.$lockKey.' is now available after '.$retry.'s. Moving on...', ['app' => 'core']);
170 170
 					return $this->injectCssVariablesIfAny();
171 171
 				}
172 172
 				$this->logger->debug('SCSSCacher: scss cache file locked for '.$lockKey, ['app' => 'core']);
@@ -214,7 +214,7 @@  discard block
 block discarded – undo
214 214
 	 * @return boolean
215 215
 	 */
216 216
 	private function isCached(string $fileNameCSS, string $app) {
217
-		$key = $this->config->getSystemValue('version') . '/' . $app . '/' . $fileNameCSS;
217
+		$key = $this->config->getSystemValue('version').'/'.$app.'/'.$fileNameCSS;
218 218
 
219 219
 		// If the file mtime is more recent than our cached one,
220 220
 		// let's consider the file is properly cached
@@ -236,13 +236,13 @@  discard block
 block discarded – undo
236 236
 		try {
237 237
 			$cachedFile = $folder->getFile($fileNameCSS);
238 238
 			if ($cachedFile->getSize() > 0) {
239
-				$depFileName = $fileNameCSS . '.deps';
240
-				$deps        = $this->depsCache->get($folder->getName() . '-' . $depFileName);
239
+				$depFileName = $fileNameCSS.'.deps';
240
+				$deps        = $this->depsCache->get($folder->getName().'-'.$depFileName);
241 241
 				if ($deps === null) {
242 242
 					$depFile = $folder->getFile($depFileName);
243 243
 					$deps    = $depFile->getContent();
244 244
 					// Set to memcache for next run
245
-					$this->depsCache->set($folder->getName() . '-' . $depFileName, $deps);
245
+					$this->depsCache->set($folder->getName().'-'.$depFileName, $deps);
246 246
 				}
247 247
 				$deps = json_decode($deps, true);
248 248
 
@@ -291,7 +291,7 @@  discard block
 block discarded – undo
291 291
 		$scss = new Compiler();
292 292
 		$scss->setImportPaths([
293 293
 			$path,
294
-			$this->serverRoot . '/core/css/'
294
+			$this->serverRoot.'/core/css/'
295 295
 		]);
296 296
 
297 297
 		// Continue after throw
@@ -311,7 +311,7 @@  discard block
 block discarded – undo
311 311
 			$cachedfile = $folder->newFile($fileNameCSS);
312 312
 		}
313 313
 
314
-		$depFileName = $fileNameCSS . '.deps';
314
+		$depFileName = $fileNameCSS.'.deps';
315 315
 		try {
316 316
 			$depFile = $folder->getFile($depFileName);
317 317
 		} catch (NotFoundException $e) {
@@ -321,11 +321,11 @@  discard block
 block discarded – undo
321 321
 		// Compile
322 322
 		try {
323 323
 			$compiledScss = $scss->compile(
324
-				'$webroot: \'' . $this->getRoutePrefix() . '\';' .
325
-				$this->getInjectedVariables() .
326
-				'@import "variables.scss";' .
327
-				'@import "functions.scss";' .
328
-				'@import "' . $fileNameSCSS . '";');
324
+				'$webroot: \''.$this->getRoutePrefix().'\';'.
325
+				$this->getInjectedVariables().
326
+				'@import "variables.scss";'.
327
+				'@import "functions.scss";'.
328
+				'@import "'.$fileNameSCSS.'";');
329 329
 		} catch (ParserException $e) {
330 330
 			$this->logger->logException($e, ['app' => 'core']);
331 331
 
@@ -337,9 +337,9 @@  discard block
 block discarded – undo
337 337
 
338 338
 		// Gzip file
339 339
 		try {
340
-			$gzipFile = $folder->getFile($fileNameCSS . '.gzip'); # Safari doesn't like .gz
340
+			$gzipFile = $folder->getFile($fileNameCSS.'.gzip'); # Safari doesn't like .gz
341 341
 		} catch (NotFoundException $e) {
342
-			$gzipFile = $folder->newFile($fileNameCSS . '.gzip'); # Safari doesn't like .gz
342
+			$gzipFile = $folder->newFile($fileNameCSS.'.gzip'); # Safari doesn't like .gz
343 343
 		}
344 344
 
345 345
 		try {
@@ -347,13 +347,13 @@  discard block
 block discarded – undo
347 347
 			$cachedfile->putContent($data);
348 348
 			$deps = json_encode($scss->getParsedFiles());
349 349
 			$depFile->putContent($deps);
350
-			$this->depsCache->set($folder->getName() . '-' . $depFileName, $deps);
350
+			$this->depsCache->set($folder->getName().'-'.$depFileName, $deps);
351 351
 			$gzipFile->putContent(gzencode($data, 9));
352
-			$this->logger->debug('SCSSCacher: ' . $webDir . '/' . $fileNameSCSS . ' compiled and successfully cached', ['app' => 'core']);
352
+			$this->logger->debug('SCSSCacher: '.$webDir.'/'.$fileNameSCSS.' compiled and successfully cached', ['app' => 'core']);
353 353
 
354 354
 			return true;
355 355
 		} catch (NotPermittedException $e) {
356
-			$this->logger->error('SCSSCacher: unable to cache: ' . $fileNameSCSS);
356
+			$this->logger->error('SCSSCacher: unable to cache: '.$fileNameSCSS);
357 357
 
358 358
 			return false;
359 359
 		}
@@ -376,7 +376,7 @@  discard block
 block discarded – undo
376 376
 				try {
377 377
 					$file->delete();
378 378
 				} catch (NotPermittedException $e) {
379
-					$this->logger->logException($e, ['message' => 'SCSSCacher: unable to delete file: ' . $file->getName()]);
379
+					$this->logger->logException($e, ['message' => 'SCSSCacher: unable to delete file: '.$file->getName()]);
380 380
 				}
381 381
 			}
382 382
 		}
@@ -392,7 +392,7 @@  discard block
 block discarded – undo
392 392
 		}
393 393
 		$variables = '';
394 394
 		foreach ($this->defaults->getScssVariables() as $key => $value) {
395
-			$variables .= '$' . $key . ': ' . $value . ' !default;';
395
+			$variables .= '$'.$key.': '.$value.' !default;';
396 396
 		}
397 397
 
398 398
 		// check for valid variables / otherwise fall back to defaults
@@ -415,7 +415,7 @@  discard block
 block discarded – undo
415 415
 	 */
416 416
 	private function rebaseUrls(string $css, string $webDir): string {
417 417
 		$re    = '/url\([\'"]([^\/][\.\w?=\/-]*)[\'"]\)/x';
418
-		$subst = 'url(\'' . $webDir . '/$1\')';
418
+		$subst = 'url(\''.$webDir.'/$1\')';
419 419
 
420 420
 		return preg_replace($re, $subst, $css);
421 421
 	}
@@ -444,12 +444,12 @@  discard block
 block discarded – undo
444 444
 	 * @return string
445 445
 	 */
446 446
 	private function prependBaseurlPrefix(string $cssFile): string {
447
-		return substr(md5($this->urlGenerator->getBaseUrl() . $this->getRoutePrefix()), 0, 4) . '-' . $cssFile;
447
+		return substr(md5($this->urlGenerator->getBaseUrl().$this->getRoutePrefix()), 0, 4).'-'.$cssFile;
448 448
 	}
449 449
 
450 450
 	private function getRoutePrefix() {
451 451
 		$frontControllerActive = ($this->config->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true');
452
-		$prefix = \OC::$WEBROOT . '/index.php';
452
+		$prefix = \OC::$WEBROOT.'/index.php';
453 453
 		if ($frontControllerActive) {
454 454
 			$prefix = \OC::$WEBROOT;
455 455
 		}
@@ -465,11 +465,11 @@  discard block
 block discarded – undo
465 465
 	private function prependVersionPrefix(string $cssFile, string $appId): string {
466 466
 		$appVersion = \OC_App::getAppVersion($appId);
467 467
 		if ($appVersion !== '0') {
468
-			return substr(md5($appVersion), 0, 4) . '-' . $cssFile;
468
+			return substr(md5($appVersion), 0, 4).'-'.$cssFile;
469 469
 		}
470 470
 		$coreVersion = \OC_Util::getVersionString();
471 471
 
472
-		return substr(md5($coreVersion), 0, 4) . '-' . $cssFile;
472
+		return substr(md5($coreVersion), 0, 4).'-'.$cssFile;
473 473
 	}
474 474
 
475 475
 	/**
@@ -487,10 +487,10 @@  discard block
 block discarded – undo
487 487
 			$appDirectoryPath = explode($appName, $path)[1];
488 488
 			// Remove the webroot
489 489
 
490
-			return str_replace($webRoot, '', $appWebPath . $appDirectoryPath);
490
+			return str_replace($webRoot, '', $appWebPath.$appDirectoryPath);
491 491
 		}
492 492
 
493
-		return $webRoot . substr($path, strlen($serverRoot));
493
+		return $webRoot.substr($path, strlen($serverRoot));
494 494
 	}
495 495
 
496 496
 	/**
Please login to merge, or discard this patch.