Passed
Pull Request — master (#77)
by Daniel
47:29
created

ThemesService   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 255
Duplicated Lines 0 %

Test Coverage

Coverage 76.24%

Importance

Changes 10
Bugs 0 Features 0
Metric Value
wmc 35
eloc 95
c 10
b 0
f 0
dl 0
loc 255
ccs 77
cts 101
cp 0.7624
rs 9.6

13 Methods

Rating   Name   Duplication   Size   Complexity  
A publishCustomTheme() 0 20 3
A depublishCustomTheme() 0 17 3
A getNewCustomThemes() 0 16 4
A publishSystemTheme() 0 20 3
B getThemesFolder() 0 26 7
A __construct() 0 5 1
A publishTheme() 0 17 2
A getThemesUrl() 0 6 2
A getThemesPath() 0 6 2
A getSystemThemes() 0 4 2
A assertValidTheme() 0 13 3
A getThemes() 0 3 1
A getCustomThemes() 0 4 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\ThemeNotCompatibleException;
30
use OCA\CMSPico\Exceptions\ThemeNotFoundException;
31
use OCA\CMSPico\Files\FolderInterface;
32
use OCA\CMSPico\Files\LocalFolder;
33
use OCA\CMSPico\Model\Theme;
34
use OCP\Files\AlreadyExistsException;
35
use OCP\Files\NotFoundException;
36
37
class ThemesService
38
{
39
	/** @var ConfigService */
40
	private $configService;
41
42
	/** @var FileService */
43
	private $fileService;
44
45
	/** @var MiscService */
46
	private $miscService;
47
48
	/** @var bool */
49
	private $renewedETag = false;
50
51
	/**
52
	 * ThemesService constructor.
53
	 *
54
	 * @param ConfigService $configService
55
	 * @param FileService   $fileService
56
	 * @param MiscService   $miscService
57
	 */
58 1
	public function __construct(ConfigService $configService, FileService $fileService, MiscService $miscService)
59
	{
60 1
		$this->configService = $configService;
61 1
		$this->fileService = $fileService;
62 1
		$this->miscService = $miscService;
63 1
	}
64
65
	/**
66
	 * @param string $themeName
67
	 *
68
	 * @throws ThemeNotFoundException
69
	 * @throws ThemeNotCompatibleException
70
	 */
71 6
	public function assertValidTheme(string $themeName)
72
	{
73 6
		$themes = $this->getThemes();
74
75 6
		if (!isset($themes[$themeName])) {
76 1
			throw new ThemeNotFoundException();
77
		}
78
79 6
		if (!$themes[$themeName]['compat']) {
80
			throw new ThemeNotCompatibleException(
81
				$themeName,
82
				$themes[$themeName]['compatReason'],
83
				$themes[$themeName]['compatReasonData']
84
			);
85
		}
86 6
	}
87
88
	/**
89
	 * @return array[]
90
	 */
91 6
	public function getThemes(): array
92
	{
93 6
		return $this->getSystemThemes() + $this->getCustomThemes();
94
	}
95
96
	/**
97
	 * @return array[]
98
	 */
99 6
	public function getSystemThemes(): array
100
	{
101 6
		$json = $this->configService->getAppValue(ConfigService::SYSTEM_THEMES);
102 6
		return $json ? json_decode($json, true) : [];
103
	}
104
105
	/**
106
	 * @return array[]
107
	 */
108 6
	public function getCustomThemes(): array
109
	{
110 6
		$json = $this->configService->getAppValue(ConfigService::CUSTOM_THEMES);
111 6
		return $json ? json_decode($json, true) : [];
112
	}
113
114
	/**
115
	 * @return string[]
116
	 */
117 1
	public function getNewCustomThemes(): array
118
	{
119 1
		$customThemesFolder = $this->fileService->getAppDataFolder(PicoService::DIR_THEMES);
120 1
		$customThemesFolder->sync(FolderInterface::SYNC_SHALLOW);
121
122 1
		$currentThemes = $this->getThemes();
123
124 1
		$newCustomThemes = [];
125 1
		foreach ($customThemesFolder as $themeFolder) {
126 1
			$themeName = $themeFolder->getName();
127 1
			if ($themeFolder->isFolder() && !isset($currentThemes[$themeName])) {
128 1
				$newCustomThemes[] = $themeName;
129
			}
130
		}
131
132 1
		return $newCustomThemes;
133
	}
134
135
	/**
136
	 * @param string $themeName
137
	 *
138
	 * @return Theme
139
	 * @throws ThemeNotFoundException
140
	 */
141
	public function publishSystemTheme(string $themeName): Theme
142
	{
143
		if (!$themeName) {
144
			throw new ThemeNotFoundException();
145
		}
146
147
		$systemThemesFolder = $this->fileService->getSystemFolder(PicoService::DIR_THEMES);
148
		$systemThemesFolder->sync(FolderInterface::SYNC_SHALLOW);
149
150
		try {
151
			$systemThemeFolder = $systemThemesFolder->getFolder($themeName);
152
		} catch (NotFoundException $e) {
153
			throw new ThemeNotFoundException();
154
		}
155
156
		$themes = $this->getSystemThemes();
157
		$themes[$themeName] = $this->publishTheme($systemThemeFolder, Theme::THEME_TYPE_SYSTEM);
158
		$this->configService->setAppValue(ConfigService::SYSTEM_THEMES, json_encode($themes));
159
160
		return $themes[$themeName];
161
	}
162
163
	/**
164
	 * @param string $themeName
165
	 *
166
	 * @return Theme
167
	 * @throws ThemeNotFoundException
168
	 */
169 1
	public function publishCustomTheme(string $themeName): Theme
170
	{
171 1
		if (!$themeName) {
172
			throw new ThemeNotFoundException();
173
		}
174
175 1
		$appDataThemesFolder = $this->fileService->getAppDataFolder(PicoService::DIR_THEMES);
176 1
		$appDataThemesFolder->sync(FolderInterface::SYNC_SHALLOW);
177
178
		try {
179 1
			$appDataThemeFolder = $appDataThemesFolder->getFolder($themeName);
180
		} catch (NotFoundException $e) {
181
			throw new ThemeNotFoundException();
182
		}
183
184 1
		$themes = $this->getCustomThemes();
185 1
		$themes[$themeName] = $this->publishTheme($appDataThemeFolder, Theme::THEME_TYPE_CUSTOM);
186 1
		$this->configService->setAppValue(ConfigService::CUSTOM_THEMES, json_encode($themes));
187
188 1
		return $themes[$themeName];
189
	}
190
191
	/**
192
	 * @param FolderInterface $themeSourceFolder
193
	 * @param int             $themeType
194
	 *
195
	 * @return Theme
196
	 */
197 1
	private function publishTheme(FolderInterface $themeSourceFolder, int $themeType): Theme
198
	{
199 1
		$publicThemesFolder = $this->getThemesFolder(true);
200
201 1
		$themeName = $themeSourceFolder->getName();
202 1
		$themeSourceFolder->sync();
203
204
		try {
205 1
			$publicThemesFolder->getFolder($themeName);
206
			throw new AlreadyExistsException();
207 1
		} catch (NotFoundException $e) {
208
			// in fact we want the theme not to exist yet
209
		}
210
211
		/** @var LocalFolder $themeFolder */
212 1
		$themeFolder = $themeSourceFolder->copy($publicThemesFolder);
213 1
		return new Theme($themeFolder, $themeType);
214
	}
215
216
	/**
217
	 * @param string $themeName
218
	 */
219 1
	public function depublishCustomTheme(string $themeName)
220
	{
221 1
		if (!$themeName) {
222
			throw new ThemeNotFoundException();
223
		}
224
225 1
		$publicThemesFolder = $this->getThemesFolder();
226
227
		try {
228 1
			$publicThemesFolder->getFolder($themeName)->delete();
229
		} catch (NotFoundException $e) {
230
			throw new ThemeNotFoundException();
231
		}
232
233 1
		$customThemes = $this->getCustomThemes();
234 1
		unset($customThemes[$themeName]);
235 1
		$this->configService->setAppValue(ConfigService::CUSTOM_THEMES, json_encode($customThemes));
236 1
	}
237
238
	/**
239
	 * @param bool $renewETag
240
	 * @param bool $forceRenewETag
241
	 *
242
	 * @return LocalFolder
243
	 */
244 3
	public function getThemesFolder(bool $renewETag = false, bool $forceRenewETag = false): LocalFolder
245
	{
246 3
		$themesBaseFolder = $this->fileService->getPublicFolder(PicoService::DIR_THEMES);
247
248
		/** @var LocalFolder $themesFolder */
249 3
		$themesFolder = null;
250
251 3
		$themesETag = $this->configService->getAppValue(ConfigService::THEMES_ETAG);
252 3
		if ($themesETag) {
253 3
			$themesFolder = $themesBaseFolder->getFolder($themesETag);
254
		}
255
256 3
		if (($renewETag && !$this->renewedETag) || $forceRenewETag || !$themesFolder) {
257 1
			$themesETag = $this->miscService->getRandom();
258
259 1
			if ($themesFolder) {
260 1
				$themesFolder = $themesFolder->rename($themesETag);
261
			} else {
262
				$themesFolder = $themesBaseFolder->newFolder($themesETag);
263
			}
264
265 1
			$this->configService->setAppValue(ConfigService::THEMES_ETAG, $themesETag);
266 1
			$this->renewedETag = true;
267
		}
268
269 3
		return $themesFolder->fakeRoot();
270
	}
271
272
	/**
273
	 * @return string
274
	 */
275 2
	public function getThemesPath(): string
276
	{
277 2
		$appPath = Application::getAppPath() . '/';
278 2
		$themesPath = 'appdata_public/' . PicoService::DIR_THEMES . '/';
279 2
		$themesETag = $this->configService->getAppValue(ConfigService::THEMES_ETAG);
280 2
		return $appPath . $themesPath . ($themesETag ? $themesETag . '/' : '');
281
	}
282
283
	/**
284
	 * @return string
285
	 */
286 2
	public function getThemesUrl(): string
287
	{
288 2
		$appWebPath = Application::getAppWebPath() . '/';
289 2
		$themesPath = 'appdata_public/' . PicoService::DIR_THEMES . '/';
290 2
		$themesETag = $this->configService->getAppValue(ConfigService::THEMES_ETAG);
291 2
		return $appWebPath . $themesPath . ($themesETag ? $themesETag . '/' : '');
292
	}
293
}
294