Test Failed
Push — master ( dc798f...d76e2d )
by Daniel
48:22
created

PicoService::getConfigFolder()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 2
c 1
b 0
f 0
dl 0
loc 5
ccs 0
cts 3
cp 0
rs 10
cc 1
nc 1
nop 0
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\PageInvalidPathException;
30
use OCA\CMSPico\Exceptions\PageNotFoundException;
31
use OCA\CMSPico\Exceptions\PageNotPermittedException;
32
use OCA\CMSPico\Exceptions\PicoRuntimeException;
33
use OCA\CMSPico\Exceptions\ThemeNotCompatibleException;
34
use OCA\CMSPico\Exceptions\ThemeNotFoundException;
35
use OCA\CMSPico\Exceptions\WebsiteInvalidFilesystemException;
36
use OCA\CMSPico\Exceptions\WebsiteNotPermittedException;
37
use OCA\CMSPico\Files\StorageFolder;
38
use OCA\CMSPico\Model\PicoPage;
39
use OCA\CMSPico\Model\Website;
40
use OCA\CMSPico\Pico;
41
use OCP\Files\InvalidPathException;
42
use OCP\Files\NotFoundException;
43
use OCP\Files\NotPermittedException;
44
use OCP\ILogger;
45
46
class PicoService
47
{
48
	/** @var string */
49
	public const DIR_TEMPLATES = 'templates';
50
51
	/** @var string */
52
	public const DIR_CONFIG = 'config';
53
54
	/** @var string */
55
	public const DIR_PLUGINS = 'plugins';
56
57
	/** @var string */
58
	public const DIR_THEMES = 'themes';
59
60
	/** @var string */
61
	public const DIR_CONTENT = 'content';
62
63
	/** @var string */
64
	public const DIR_ASSETS = 'assets';
65
66
	/** @var string */
67
	public const CONTENT_EXT = '.md';
68
69
	/** @var ILogger */
70
	private $logger;
71
72
	/** @var AssetsService */
73
	private $assetsService;
74
75
	/** @var ThemesService */
76
	private $themesService;
77
78
	/** @var PluginsService */
79
	private $pluginsService;
80
81
	/** @var FileService */
82
	private $fileService;
83
84
	/** @var MiscService */
85
	private $miscService;
86
87
	/**
88
	 * PicoService constructor.
89
	 *
90
	 * @param ILogger        $logger
91
	 * @param AssetsService  $assetsService
92
	 * @param ThemesService  $themesService
93
	 * @param PluginsService $pluginsService
94
	 * @param FileService    $fileService
95
	 * @param MiscService    $miscService
96
	 */
97 1
	public function __construct(
98
		ILogger $logger,
99
		AssetsService $assetsService,
100
		ThemesService $themesService,
101
		PluginsService $pluginsService,
102
		FileService $fileService,
103
		MiscService $miscService
104
	) {
105 1
		$this->logger = $logger;
106 1
		$this->assetsService = $assetsService;
107 1
		$this->themesService = $themesService;
108 1
		$this->pluginsService = $pluginsService;
109 1
		$this->fileService = $fileService;
110 1
		$this->miscService = $miscService;
111 1
	}
112
113
	/**
114
	 * @param Website $website
115
	 *
116
	 * @return PicoPage
117
	 * @throws WebsiteInvalidFilesystemException
118
	 * @throws WebsiteNotPermittedException
119
	 * @throws ThemeNotFoundException
120
	 * @throws ThemeNotCompatibleException
121
	 * @throws PageInvalidPathException
122
	 * @throws PageNotFoundException
123
	 * @throws PageNotPermittedException
124
	 * @throws PicoRuntimeException
125
	 */
126
	public function getPage(Website $website): PicoPage
127
	{
128
		try {
129
			$page = $website->getPage();
130
131
			$website->assertViewerAccess(self::DIR_CONTENT . '/' . ($page ?: 'index') . self::CONTENT_EXT);
132
133
			$this->themesService->assertValidTheme($website->getTheme());
134
135
			$pico = new Pico(
136
				$website->getWebsitePath(),
137
				$this->getConfigPath(),
138
				$this->pluginsService->getPluginsPath(),
139
				$this->themesService->getThemesPath(),
140
				false
141
			);
142
143
			try {
144
				$this->setupPico($website, $pico, $page);
145
				$this->loadPicoPlugins($pico);
146
147
				$output = $pico->run();
148
			} catch (WebsiteInvalidFilesystemException $e) {
149
				throw $e;
150
			} catch (InvalidPathException | NotFoundException | NotPermittedException $e) {
151
				throw $e;
152
			} catch (\Exception $e) {
153
				$exception = new PicoRuntimeException($e);
154
				$this->logger->logException($exception, [ 'app' => Application::APP_NAME ]);
0 ignored issues
show
Deprecated Code introduced by
The function OCP\ILogger::logException() has been deprecated: 20.0.0 use the `exception` entry in the context of any method in \Psr\Log\LoggerInterface ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

154
				/** @scrutinizer ignore-deprecated */ $this->logger->logException($exception, [ 'app' => Application::APP_NAME ]);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
155
				throw $exception;
156
			}
157
158
			$picoPage = new PicoPage($website, $pico, $output);
159
160
			$picoPagePath = self::DIR_CONTENT . '/' . $picoPage->getRelativePath() . self::CONTENT_EXT;
161
			$website->assertViewerAccess($picoPagePath, $picoPage->getMeta());
162
		} catch (InvalidPathException $e) {
163
			throw new PageInvalidPathException($e);
164
		} catch (NotFoundException $e) {
165
			throw new PageNotFoundException($e);
166
		} catch (NotPermittedException $e) {
167
			throw new PageNotPermittedException($e);
168
		}
169
170
		return $picoPage;
171
	}
172
173
	/**
174
	 * @param Website $website
175
	 * @param Pico    $pico
176
	 * @param string  $page
177
	 *
178
	 * @throws WebsiteInvalidFilesystemException
179
	 */
180
	private function setupPico(Website $website, Pico $pico, string $page): void
181
	{
182
		$pico->setRequestUrl($page);
183
		$pico->setNextcloudWebsite($website);
184
185
		$pico->setConfig(
186
			[
187
				'site_title'     => $website->getName(),
188
				'base_url'       => $website->getWebsiteUrl(),
189
				'rewrite_url'    => true,
190
				'debug'          => \OC::$server->getConfig()->getSystemValue('debug', false),
0 ignored issues
show
Deprecated Code introduced by
The function OC\Server::getConfig() has been deprecated: 20.0.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

190
				'debug'          => /** @scrutinizer ignore-deprecated */ \OC::$server->getConfig()->getSystemValue('debug', false),

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
191
				'timezone'       => $website->getTimeZone(),
192
				'theme'          => $website->getTheme(),
193
				'themes_url'     => $this->themesService->getThemesUrl(),
194
				'content_dir'    => $this->getContentPath($website),
195
				'content_ext'    => self::CONTENT_EXT,
196
				'assets_dir'     => $this->assetsService->getAssetsPath($website),
197
				'assets_url'     => $this->assetsService->getAssetsUrl($website),
198
				'plugins_url'    => $this->pluginsService->getPluginsUrl(),
199
				'nextcloud_site' => $website->getSite(),
200
			]
201
		);
202
	}
203
204
	/**
205
	 * @param Pico $pico
206
	 */
207
	private function loadPicoPlugins(Pico $pico): void
208
	{
209
		$includeClosure = static function (string $pluginFile) {
210
			/** @noinspection PhpIncludeInspection */
211
			require_once($pluginFile);
212
		};
213
214
		foreach ($this->pluginsService->getPlugins() as $pluginData) {
215
			if ($pluginData['compat']) {
216
				$pluginFile = $pluginData['name'] . '/' . $pluginData['name'] . '.php';
217
				$includeClosure($this->pluginsService->getPluginsPath() . '/' . $pluginFile);
218
219
				$pico->loadPlugin($pluginData['name']);
220
			}
221
		}
222
	}
223
224
	/**
225
	 * @param Website $website
226
	 * @param string  $absolutePath
227
	 *
228
	 * @return array
229
	 * @throws WebsiteInvalidFilesystemException
230
	 * @throws InvalidPathException
231
	 */
232
	public function getRelativePath(Website $website, string $absolutePath): array
233
	{
234
		$folder = $website->getWebsiteFolder();
235
		$basePath = $website->getWebsitePath();
236
237
		try {
238
			$relativePath = $this->miscService->getRelativePath($absolutePath, $basePath);
239
		} catch (InvalidPathException $e) {
240
			$folder = $this->pluginsService->getPluginsFolder();
241
			$basePath = $this->pluginsService->getPluginsPath();
242
243
			try {
244
				$relativePath = $this->miscService->getRelativePath($absolutePath, $basePath);
245
			} catch (InvalidPathException $e) {
246
				$folder = $this->themesService->getThemesFolder();
247
				$basePath = $this->themesService->getThemesPath();
248
249
				try {
250
					$relativePath = $this->miscService->getRelativePath($absolutePath, $basePath);
251
				} catch (InvalidPathException $e) {
252
					$folder = $this->getConfigFolder();
253
					$basePath = $this->getConfigPath();
254
255
					try {
256
						$relativePath = $this->miscService->getRelativePath($absolutePath, $basePath);
257
					} catch (InvalidPathException $e) {
258
						// the file is neither in the content nor assets, plugins, themes or config folder
259
						// Pico mustn't have access to any other directory
260
						throw new InvalidPathException();
261
					}
262
				}
263
			}
264
		}
265
266
		return [ $folder, rtrim($basePath, '/'), $relativePath ];
267
	}
268
269
	/**
270
	 * @param Website $website
271
	 *
272
	 * @return StorageFolder
273
	 * @throws WebsiteInvalidFilesystemException
274
	 */
275
	public function getContentFolder(Website $website): StorageFolder
276
	{
277
		try {
278
			/** @var StorageFolder $websiteFolder */
279
			$websiteFolder = $website->getWebsiteFolder()->getFolder(PicoService::DIR_CONTENT)->fakeRoot();
280
			return $websiteFolder;
281
		} catch (InvalidPathException | NotFoundException $e) {
282
			throw new WebsiteInvalidFilesystemException($e);
283
		}
284
	}
285
286
	/**
287
	 * @param Website $website
288
	 *
289
	 * @return string
290
	 * @throws WebsiteInvalidFilesystemException
291
	 */
292
	public function getContentPath(Website $website): string
293
	{
294
		try {
295
			return $this->getContentFolder($website)->getLocalPath() . '/';
296
		} catch (InvalidPathException | NotFoundException $e) {
297
			throw new WebsiteInvalidFilesystemException($e);
298
		}
299
	}
300
301
	/**
302
	 * @return StorageFolder
303
	 */
304
	public function getConfigFolder(): StorageFolder
305
	{
306
		/** @var StorageFolder $configFolder */
307
		$configFolder = $this->fileService->getAppDataFolder(self::DIR_CONFIG)->fakeRoot();
308
		return $configFolder;
309
	}
310
311
	/**
312
	 * @return string
313
	 */
314
	public function getConfigPath(): string
315
	{
316
		return $this->fileService->getAppDataFolderPath(self::DIR_CONFIG);
317
	}
318
}
319