Completed
Push — master ( 6ed87c...52995a )
by Julius
23:46
created

AccessibilityController::getInjectedVariables()   A

Complexity

Conditions 4
Paths 7

Size

Total Lines 19

Duplication

Lines 19
Ratio 100 %

Importance

Changes 0
Metric Value
cc 4
nc 7
nop 0
dl 19
loc 19
rs 9.6333
c 0
b 0
f 0
1
<?php
2
declare (strict_types = 1);
3
/**
4
 * @copyright Copyright (c) 2018 John Molakvoæ (skjnldsv) <[email protected]>
5
 *
6
 * @license GNU AGPL version 3 or any later version
7
 *
8
 * This program is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU Affero General Public License as
10
 * published by the Free Software Foundation, either version 3 of the
11
 * License, or (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU Affero General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Affero General Public License
19
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
 *
21
 */
22
23
namespace OCA\Accessibility\Controller;
24
25
use Leafo\ScssPhp\Compiler;
26
use Leafo\ScssPhp\Exception\ParserException;
27
use Leafo\ScssPhp\Formatter\Crunched;
28
use OCP\AppFramework\Controller;
29
use OCP\AppFramework\Http;
30
use OCP\AppFramework\Http\DataDisplayResponse;
31
use OCP\AppFramework\Utility\ITimeFactory;
32
use OCP\App\IAppManager;
33
use OCP\IConfig;
34
use OCP\ILogger;
35
use OCP\IRequest;
36
use OCP\IURLGenerator;
37
use OCP\IUserManager;
38
use OCP\IUserSession;
39
use OC\Template\IconsCacher;
40
41
class AccessibilityController extends Controller {
42
43
	/** @var string */
44
	protected $appName;
45
46
	/** @var string */
47
	protected $serverRoot;
48
49
	/** @var IConfig */
50
	private $config;
51
52
	/** @var IUserManager */
53
	private $userManager;
54
55
	/** @var ILogger */
56
	private $logger;
57
58
	/** @var IURLGenerator */
59
	private $urlGenerator;
60
61
	/** @var ITimeFactory */
62
	protected $timeFactory;
63
64
	/** @var IUserSession */
65
	private $userSession;
66
67
	/** @var IAppManager */
68
	private $appManager;
69
70
	/** @var IconsCacher */
71
	protected $iconsCacher;
72
73
	/** @var \OC_Defaults */
74
	private $defaults;
75
76
	/** @var null|string */
77
	private $injectedVariables;
78
79
	/**
80
	 * Account constructor.
81
	 *
82
	 * @param string $appName
83
	 * @param IRequest $request
84
	 * @param IConfig $config
85
	 * @param IUserManager $userManager
86
	 * @param ILogger $logger
87
	 * @param IURLGenerator $urlGenerator
88
	 * @param ITimeFactory $timeFactory
89
	 * @param IUserSession $userSession
90
	 * @param IAppManager $appManager
91
	 * @param \OC_Defaults $defaults
92
	 */
93
	public function __construct(string $appName,
94
								IRequest $request,
95
								IConfig $config,
96
								IUserManager $userManager,
97
								ILogger $logger,
98
								IURLGenerator $urlGenerator,
99
								ITimeFactory $timeFactory,
100
								IUserSession $userSession,
101
								IAppManager $appManager,
102
								IconsCacher $iconsCacher,
103
								\OC_Defaults $defaults) {
104
		parent::__construct($appName, $request);
105
		$this->appName      = $appName;
106
		$this->config       = $config;
107
		$this->userManager  = $userManager;
108
		$this->logger       = $logger;
109
		$this->urlGenerator = $urlGenerator;
110
		$this->timeFactory  = $timeFactory;
111
		$this->userSession  = $userSession;
112
		$this->appManager   = $appManager;
113
		$this->iconsCacher  = $iconsCacher;
114
		$this->defaults     = $defaults;
115
116
		$this->serverRoot = \OC::$SERVERROOT;
117
		$this->appRoot    = $this->appManager->getAppPath($this->appName);
0 ignored issues
show
Bug introduced by
The property appRoot does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
118
	}
119
120
	/**
121
	 * @NoAdminRequired
122
	 * @NoCSRFRequired
123
	 *
124
	 * @return DataDisplayResponse
125
	 */
126
	public function getCss(): DataDisplayResponse {
127
		$css        = '';
128
		$imports    = '';
129
		$userValues = $this->getUserValues();
130
131
		foreach ($userValues as $key => $scssFile) {
132
			if ($scssFile !== false) {
133
				$imports .= '@import "' . $scssFile . '";';
134
			}
135
		}
136
137
		if ($imports !== '') {
138
			$scss = new Compiler();
139
			$scss->setImportPaths([
140
				$this->appRoot . '/css/',
141
				$this->serverRoot . '/core/css/'
142
			]);
143
144
			// Continue after throw
145
			$scss->setIgnoreErrors(true);
146
			$scss->setFormatter(Crunched::class);
147
148
			// Import theme, variables and compile css4 variables
149
			try {
150
				$css .= $scss->compile(
151
					$imports .
152
					'@import "variables.scss";' .
153
					$this->getInjectedVariables() .
154
					'@import "css-variables.scss";'
155
				);
156
			} catch (ParserException $e) {
0 ignored issues
show
Bug introduced by
The class Leafo\ScssPhp\Exception\ParserException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
157
				$this->logger->error($e->getMessage(), ['app' => 'core']);
158
			}
159
		}
160
161
		// We don't want to override vars with url since path is different
162
		$css = $this->filterOutRule('/--[a-z-:]+url\([^;]+\)/mi', $css);
163
164
		// Rebase all urls
165
		$appWebRoot = substr($this->appRoot, strlen($this->serverRoot) - strlen(\OC::$WEBROOT));
166
		$css        = $this->rebaseUrls($css, $appWebRoot . '/css');
167
168
		if (in_array('themedark', $userValues) && $this->iconsCacher->getCachedCSS() && $this->iconsCacher->getCachedCSS()->getSize() > 0) {
169
			$iconsCss = $this->invertSvgIconsColor($this->iconsCacher->getCachedCSS()->getContent());
170
			$css = $css . $iconsCss;
171
		}
172
173
		$response = new DataDisplayResponse($css, Http::STATUS_OK, ['Content-Type' => 'text/css']);
174
175
		// Set cache control
176
		$ttl = 31536000;
177
		$response->addHeader('Cache-Control', 'max-age=' . $ttl . ', immutable');
178
		$expires = new \DateTime();
179
		$expires->setTimestamp($this->timeFactory->getTime());
180
		$expires->add(new \DateInterval('PT' . $ttl . 'S'));
181
		$response->addHeader('Expires', $expires->format(\DateTime::RFC1123));
182
		$response->addHeader('Pragma', 'cache');
183
184
		return $response;
185
	}
186
187
	/**
188
	 * Return an array with the user theme & font settings
189
	 *
190
	 * @return array
191
	 */
192
	private function getUserValues(): array{
193
		$userTheme = $this->config->getUserValue($this->userSession->getUser()->getUID(), $this->appName, 'theme', false);
194
		$userFont  = $this->config->getUserValue($this->userSession->getUser()->getUID(), $this->appName, 'font', false);
195
196
		return [$userTheme, $userFont];
197
	}
198
199
	/**
200
	 * Remove all matches from the $rule regex
201
	 *
202
	 * @param string $rule regex to match
203
	 * @param string $css string to parse
204
	 * @return string
205
	 */
206
	private function filterOutRule(string $rule, string $css): string {
207
		return preg_replace($rule, '', $css);
208
	}
209
210
	/**
211
	 * Add the correct uri prefix to make uri valid again
212
	 *
213
	 * @param string $css
214
	 * @param string $webDir
215
	 * @return string
216
	 */
217
	private function rebaseUrls(string $css, string $webDir): string {
218
		$re    = '/url\([\'"]([^\/][\.\w?=\/-]*)[\'"]\)/x';
219
		$subst = 'url(\'' . $webDir . '/$1\')';
220
221
		return preg_replace($re, $subst, $css);
222
	}
223
224
	/**
225
	 * Remove all matches from the $rule regex
226
	 *
227
	 * @param string $css string to parse
228
	 * @return string
229
	 */
230
	private function invertSvgIconsColor(string $css) {
231
		return str_replace(['/000', '/fff', '/***'], ['/***', '/000', '/fff'], $css);
232
	}
233
234
	/**
235
	 * @return string SCSS code for variables from OC_Defaults
236
	 */
237 View Code Duplication
	private function getInjectedVariables(): string {
238
		if ($this->injectedVariables !== null) {
239
			return $this->injectedVariables;
240
		}
241
		$variables = '';
242
		foreach ($this->defaults->getScssVariables() as $key => $value) {
243
			$variables .= '$' . $key . ': ' . $value . ';';
244
		}
245
246
		// check for valid variables / otherwise fall back to defaults
247
		try {
248
			$scss = new Compiler();
249
			$scss->compile($variables);
250
			$this->injectedVariables = $variables;
251
		} catch (ParserException $e) {
0 ignored issues
show
Bug introduced by
The class Leafo\ScssPhp\Exception\ParserException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
252
			$this->logger->error($e, ['app' => 'core']);
253
		}
254
		return $variables;
255
	}
256
}
257