ThemesService::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 3
dl 0
loc 5
c 0
b 0
f 0
ccs 0
cts 4
cp 0
rs 10
cc 1
nc 1
nop 3
crap 2
1
<?php
2
/**
3
 * CMS Pico - Create websites using Pico CMS for Nextcloud.
4
 *
5
 * @copyright Copyright (c) 2017, Maxence Lange (<[email protected]>)
6
 * @copyright Copyright (c) 2019, Daniel Rudolf (<[email protected]>)
7
 *
8
 * @license GNU AGPL version 3 or any later version
9
 *
10
 * This program is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU Affero General Public License as
12
 * published by the Free Software Foundation, either version 3 of the
13
 * License, or (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 * GNU Affero General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU Affero General Public License
21
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
22
 */
23
24
declare(strict_types=1);
25
26
namespace OCA\CMSPico\Service;
27
28
use OCA\CMSPico\AppInfo\Application;
29
use OCA\CMSPico\Exceptions\ThemeAlreadyExistsException;
30
use OCA\CMSPico\Exceptions\ThemeNotCompatibleException;
31
use OCA\CMSPico\Exceptions\ThemeNotFoundException;
32
use OCA\CMSPico\Files\FolderInterface;
33
use OCA\CMSPico\Files\LocalFolder;
34
use OCA\CMSPico\Model\Theme;
35
use OCP\Files\AlreadyExistsException;
36
use OCP\Files\NotFoundException;
37
38
class ThemesService
39
{
40
	/** @var ConfigService */
41
	private $configService;
42
43
	/** @var FileService */
44
	private $fileService;
45
46
	/** @var MiscService */
47
	private $miscService;
48
49
	/** @var bool */
50
	private $renewedETag = false;
51
52
	/**
53
	 * ThemesService constructor.
54
	 *
55
	 * @param ConfigService $configService
56
	 * @param FileService   $fileService
57
	 * @param MiscService   $miscService
58
	 */
59
	public function __construct(ConfigService $configService, FileService $fileService, MiscService $miscService)
60
	{
61
		$this->configService = $configService;
62
		$this->fileService = $fileService;
63
		$this->miscService = $miscService;
64
	}
65
66
	/**
67
	 * @param string $themeName
68
	 *
69
	 * @throws ThemeNotFoundException
70
	 * @throws ThemeNotCompatibleException
71
	 */
72 8
	public function assertValidTheme(string $themeName): void
73
	{
74 8
		$themes = $this->getThemes();
75
76 8
		if (!isset($themes[$themeName])) {
77
			throw new ThemeNotFoundException($themeName);
78
		}
79
80 8
		if (!$themes[$themeName]['compat']) {
81
			throw new ThemeNotCompatibleException(
82
				$themeName,
83
				$themes[$themeName]['compatReason'],
84
				$themes[$themeName]['compatReasonData']
85
			);
86
		}
87 8
	}
88
89
	/**
90
	 * @return array[]
91
	 */
92 14
	public function getThemes(): array
93
	{
94 14
		return $this->getSystemThemes() + $this->getCustomThemes();
95
	}
96
97
	/**
98
	 * @return array[]
99
	 */
100 14
	public function getSystemThemes(): array
101
	{
102 14
		$json = $this->configService->getAppValue(ConfigService::SYSTEM_THEMES);
103 14
		return $json ? json_decode($json, true) : [];
104
	}
105
106
	/**
107
	 * @return array[]
108
	 */
109 14
	public function getCustomThemes(): array
110
	{
111 14
		$json = $this->configService->getAppValue(ConfigService::CUSTOM_THEMES);
112 14
		return $json ? json_decode($json, true) : [];
113
	}
114
115
	/**
116
	 * @return string[]
117
	 */
118 7
	public function getNewCustomThemes(): array
119
	{
120 7
		$customThemesFolder = $this->fileService->getAppDataFolder(PicoService::DIR_THEMES);
121 7
		$customThemesFolder->sync(FolderInterface::SYNC_SHALLOW);
122
123 7
		$currentThemes = $this->getThemes();
124
125 7
		$newCustomThemes = [];
126 7
		foreach ($customThemesFolder as $themeFolder) {
127 6
			$themeName = $themeFolder->getName();
128 6
			if ($themeFolder->isFolder() && !isset($currentThemes[$themeName])) {
129 2
				$newCustomThemes[] = $themeName;
130
			}
131
		}
132
133 7
		return $newCustomThemes;
134
	}
135
136
	/**
137
	 * @param string $themeName
138
	 *
139
	 * @return Theme
140
	 * @throws ThemeNotFoundException
141
	 * @throws ThemeAlreadyExistsException
142
	 */
143
	public function publishSystemTheme(string $themeName): Theme
144
	{
145
		if (!$themeName) {
146
			throw new ThemeNotFoundException($themeName);
147
		}
148
149
		$systemThemesFolder = $this->fileService->getSystemFolder(PicoService::DIR_THEMES);
150
		$systemThemesFolder->sync(FolderInterface::SYNC_SHALLOW);
151
152
		try {
153
			$systemThemeFolder = $systemThemesFolder->getFolder($themeName);
154
		} catch (NotFoundException $e) {
155
			throw new ThemeNotFoundException($themeName, $e);
156
		}
157
158
		$themes = $this->getSystemThemes();
159
		$themes[$themeName] = $this->publishTheme($systemThemeFolder, Theme::TYPE_SYSTEM);
160
		$this->configService->setAppValue(ConfigService::SYSTEM_THEMES, json_encode($themes));
161
162
		return $themes[$themeName];
163
	}
164
165
	/**
166
	 * @param string $themeName
167
	 *
168
	 * @return Theme
169
	 * @throws ThemeNotFoundException
170
	 * @throws ThemeAlreadyExistsException
171
	 */
172 4
	public function publishCustomTheme(string $themeName): Theme
173
	{
174 4
		if (!$themeName) {
175
			throw new ThemeNotFoundException($themeName);
176
		}
177
178 4
		$systemThemes = $this->getSystemThemes();
179 4
		if (isset($systemThemes[$themeName])) {
180
			throw new ThemeAlreadyExistsException($themeName);
181
		}
182
183 4
		$appDataThemesFolder = $this->fileService->getAppDataFolder(PicoService::DIR_THEMES);
184 4
		$appDataThemesFolder->sync(FolderInterface::SYNC_SHALLOW);
185
186
		try {
187 4
			$appDataThemeFolder = $appDataThemesFolder->getFolder($themeName);
188
		} catch (NotFoundException $e) {
189
			throw new ThemeNotFoundException($themeName, $e);
190
		}
191
192 4
		$themes = $this->getCustomThemes();
193 4
		$themes[$themeName] = $this->publishTheme($appDataThemeFolder, Theme::TYPE_CUSTOM);
194 4
		$this->configService->setAppValue(ConfigService::CUSTOM_THEMES, json_encode($themes));
195
196 4
		return $themes[$themeName];
197
	}
198
199
	/**
200
	 * @param FolderInterface $themeSourceFolder
201
	 * @param int             $themeType
202
	 *
203
	 * @return Theme
204
	 * @throws ThemeAlreadyExistsException
205
	 */
206 4
	private function publishTheme(FolderInterface $themeSourceFolder, int $themeType): Theme
207
	{
208 4
		$publicThemesFolder = $this->getThemesFolder(true);
209
210 4
		$themeName = $themeSourceFolder->getName();
211 4
		$themeSourceFolder->sync();
212
213
		try {
214 4
			$publicThemesFolder->getFolder($themeName);
215
			throw new ThemeAlreadyExistsException($themeName);
216 4
		} catch (NotFoundException $e) {
217
			// in fact we want the theme not to exist yet
218
		}
219
220
		/** @var LocalFolder $themeFolder */
221 4
		$themeFolder = $themeSourceFolder->copy($publicThemesFolder);
222 4
		return new Theme($themeFolder, $themeType);
223
	}
224
225
	/**
226
	 * @param string $themeName
227
	 *
228
	 * @throws ThemeNotFoundException
229
	 */
230 2
	public function depublishCustomTheme(string $themeName): void
231
	{
232 2
		if (!$themeName) {
233
			throw new ThemeNotFoundException($themeName);
234
		}
235
236 2
		$publicThemesFolder = $this->getThemesFolder();
237
238
		try {
239 2
			$publicThemesFolder->getFolder($themeName)->delete();
240
		} catch (NotFoundException $e) {
241
			throw new ThemeNotFoundException($themeName, $e);
242
		}
243
244 2
		$customThemes = $this->getCustomThemes();
245 2
		unset($customThemes[$themeName]);
246 2
		$this->configService->setAppValue(ConfigService::CUSTOM_THEMES, json_encode($customThemes));
247 2
	}
248
249
	/**
250
	 * @param string $baseThemeName
251
	 * @param string $themeName
252
	 *
253
	 * @return Theme
254
	 * @throws ThemeNotFoundException
255
	 * @throws ThemeAlreadyExistsException
256
	 */
257 1
	public function copyTheme(string $baseThemeName, string $themeName): Theme
258
	{
259 1
		if (!$baseThemeName) {
260
			throw new ThemeNotFoundException($baseThemeName);
261 1
		} elseif (!$themeName) {
262
			throw new ThemeNotFoundException($themeName);
263
		}
264
265 1
		$systemThemes = $this->getSystemThemes();
266 1
		$customThemes = $this->getCustomThemes();
267
268 1
		if (isset($systemThemes[$themeName]) || isset($customThemes[$themeName])) {
269
			throw new ThemeAlreadyExistsException($themeName);
270
		}
271
272
		try {
273 1
			$baseThemeFolder = $this->getThemesFolder()->getFolder($baseThemeName);
274
		} catch (NotFoundException $e) {
275
			throw new ThemeNotFoundException($baseThemeName, $e);
276
		}
277
278
		try {
279 1
			$appDataThemesFolder = $this->fileService->getAppDataFolder(PicoService::DIR_THEMES);
280 1
			$baseThemeFolder->copy($appDataThemesFolder, $themeName);
281
		} catch (AlreadyExistsException $e) {
282
			throw new ThemeAlreadyExistsException($themeName, $e);
283
		}
284
285 1
		return $this->publishCustomTheme($themeName);
286
	}
287
288
	/**
289
	 * @param bool $renewETag
290
	 * @param bool $forceRenewETag
291
	 *
292
	 * @return LocalFolder
293
	 */
294 9
	public function getThemesFolder(bool $renewETag = false, bool $forceRenewETag = false): LocalFolder
295
	{
296 9
		$themesBaseFolder = $this->fileService->getPublicFolder(PicoService::DIR_THEMES);
297
298
		/** @var LocalFolder $themesFolder */
299 9
		$themesFolder = null;
300
301 9
		$themesETag = $this->configService->getAppValue(ConfigService::THEMES_ETAG);
302 9
		if ($themesETag) {
303 9
			$themesFolder = $themesBaseFolder->getFolder($themesETag);
304
		}
305
306 9
		if (($renewETag && !$this->renewedETag) || $forceRenewETag || !$themesFolder) {
307
			$themesETag = $this->miscService->getRandom();
308
309
			if ($themesFolder) {
310
				$themesFolder = $themesFolder->rename($themesETag);
311
			} else {
312
				$themesFolder = $themesBaseFolder->newFolder($themesETag);
313
			}
314
315
			$this->configService->setAppValue(ConfigService::THEMES_ETAG, $themesETag);
316
			$this->renewedETag = true;
317
		}
318
319 9
		return $themesFolder->fakeRoot();
320
	}
321
322
	/**
323
	 * @return string
324
	 */
325 5
	public function getThemesPath(): string
326
	{
327 5
		$appPath = Application::getAppPath() . '/';
328 5
		$themesPath = 'appdata_public/' . PicoService::DIR_THEMES . '/';
329 5
		$themesETag = $this->configService->getAppValue(ConfigService::THEMES_ETAG);
330 5
		return $appPath . $themesPath . ($themesETag ? $themesETag . '/' : '');
331
	}
332
333
	/**
334
	 * @return string
335
	 */
336 5
	public function getThemesUrl(): string
337
	{
338 5
		$appWebPath = Application::getAppWebPath() . '/';
339 5
		$themesPath = 'appdata_public/' . PicoService::DIR_THEMES . '/';
340 5
		$themesETag = $this->configService->getAppValue(ConfigService::THEMES_ETAG);
341 5
		return $appWebPath . $themesPath . ($themesETag ? $themesETag . '/' : '');
342
	}
343
}
344