Passed
Pull Request — master (#99)
by Daniel
08:11
created

Website   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 266
Duplicated Lines 0 %

Test Coverage

Coverage 69.31%

Importance

Changes 6
Bugs 0 Features 0
Metric Value
wmc 35
eloc 103
c 6
b 0
f 0
dl 0
loc 266
ccs 70
cts 101
cp 0.6931
rs 9.6

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 12 1
A getOptionsJSON() 0 3 1
A assertValidSite() 0 13 4
A assertValidOwner() 0 11 4
A getWebsitePath() 0 6 2
A assertOwnedBy() 0 4 2
A getData() 0 5 1
A getTimeZone() 0 4 2
A assertValidName() 0 7 3
A getWebsiteFolder() 0 27 5
B assertValidPath() 0 40 7
A getWebsiteUrl() 0 8 2
A assertValidTheme() 0 3 1
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\Model;
27
28
use OCA\CMSPico\AppInfo\Application;
29
use OCA\CMSPico\Exceptions\ThemeNotCompatibleException;
30
use OCA\CMSPico\Exceptions\ThemeNotFoundException;
31
use OCA\CMSPico\Exceptions\WebsiteForeignOwnerException;
32
use OCA\CMSPico\Exceptions\WebsiteInvalidDataException;
33
use OCA\CMSPico\Exceptions\WebsiteInvalidFilesystemException;
34
use OCA\CMSPico\Exceptions\WebsiteInvalidOwnerException;
35
use OCA\CMSPico\Files\StorageFolder;
36
use OCA\CMSPico\Service\MiscService;
37
use OCA\CMSPico\Service\TemplatesService;
38
use OCA\CMSPico\Service\ThemesService;
39
use OCA\CMSPico\Service\WebsitesService;
40
use OCP\Files\InvalidPathException;
41
use OCP\Files\NotFoundException;
42
use OCP\IConfig;
43
use OCP\IL10N;
44
use OCP\IURLGenerator;
45
use OCP\IUserManager;
46
47
class Website extends WebsiteCore
48
{
49
	/** @var int */
50
	public const SITE_LENGTH_MIN = 3;
51
52
	/** @var int */
53
	public const SITE_LENGTH_MAX = 255;
54
55
	/** @var string */
56
	public const SITE_REGEX = '^[a-z0-9][a-z0-9_-]+[a-z0-9]$';
57
58
	/** @var int */
59
	public const NAME_LENGTH_MIN = 3;
60
61
	/** @var int */
62
	public const NAME_LENGTH_MAX = 255;
63
64
	/** @var IConfig */
65
	private $config;
66
67
	/** @var IL10N */
68
	private $l10n;
69
70
	/** @var IUserManager */
71
	private $userManager;
72
73
	/** @var IURLGenerator */
74
	private $urlGenerator;
75
76
	/** @var WebsitesService */
77
	private $websitesService;
78
79
	/** @var ThemesService */
80
	private $themesService;
81
82
	/** @var TemplatesService */
83
	private $templatesService;
84
85
	/** @var MiscService */
86
	private $miscService;
87
88
	/** @var StorageFolder */
89
	private $folder;
90
91
	/**
92
	 * Website constructor.
93
	 *
94
	 * @param array|null $data
95
	 */
96 15
	public function __construct($data = null)
97
	{
98 15
		$this->config = \OC::$server->getConfig();
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

98
		$this->config = /** @scrutinizer ignore-deprecated */ \OC::$server->getConfig();

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...
99 15
		$this->l10n = \OC::$server->getL10N(Application::APP_NAME);
0 ignored issues
show
Deprecated Code introduced by
The function OC\Server::getL10N() 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

99
		$this->l10n = /** @scrutinizer ignore-deprecated */ \OC::$server->getL10N(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...
100 15
		$this->userManager = \OC::$server->getUserManager();
0 ignored issues
show
Deprecated Code introduced by
The function OC\Server::getUserManager() 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

100
		$this->userManager = /** @scrutinizer ignore-deprecated */ \OC::$server->getUserManager();

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...
101 15
		$this->urlGenerator = \OC::$server->getURLGenerator();
0 ignored issues
show
Deprecated Code introduced by
The function OC\Server::getURLGenerator() 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

101
		$this->urlGenerator = /** @scrutinizer ignore-deprecated */ \OC::$server->getURLGenerator();

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...
102 15
		$this->websitesService = \OC::$server->query(WebsitesService::class);
0 ignored issues
show
Deprecated Code introduced by
The function OC\ServerContainer::query() has been deprecated: 20.0.0 use \Psr\Container\ContainerInterface::get ( Ignorable by Annotation )

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

102
		$this->websitesService = /** @scrutinizer ignore-deprecated */ \OC::$server->query(WebsitesService::class);

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...
103 15
		$this->themesService = \OC::$server->query(ThemesService::class);
0 ignored issues
show
Deprecated Code introduced by
The function OC\ServerContainer::query() has been deprecated: 20.0.0 use \Psr\Container\ContainerInterface::get ( Ignorable by Annotation )

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

103
		$this->themesService = /** @scrutinizer ignore-deprecated */ \OC::$server->query(ThemesService::class);

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...
104 15
		$this->templatesService = \OC::$server->query(TemplatesService::class);
0 ignored issues
show
Deprecated Code introduced by
The function OC\ServerContainer::query() has been deprecated: 20.0.0 use \Psr\Container\ContainerInterface::get ( Ignorable by Annotation )

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

104
		$this->templatesService = /** @scrutinizer ignore-deprecated */ \OC::$server->query(TemplatesService::class);

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...
105 15
		$this->miscService = \OC::$server->query(MiscService::class);
0 ignored issues
show
Deprecated Code introduced by
The function OC\ServerContainer::query() has been deprecated: 20.0.0 use \Psr\Container\ContainerInterface::get ( Ignorable by Annotation )

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

105
		$this->miscService = /** @scrutinizer ignore-deprecated */ \OC::$server->query(MiscService::class);

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...
106
107 15
		parent::__construct($data);
108 15
	}
109
110
	/**
111
	 * @return string
112
	 */
113 5
	public function getOptionsJSON(): string
114
	{
115 5
		return json_encode($this->getOptions());
116
	}
117
118
	/**
119
	 * @return string
120
	 */
121 13
	public function getTimeZone(): string
122
	{
123 13
		$serverTimeZone = date_default_timezone_get() ?: 'UTC';
124 13
		return $this->config->getUserValue($this->getUserId(), 'core', 'timezone', $serverTimeZone);
125
	}
126
127
	/**
128
	 * {@inheritDoc}
129
	 */
130 10
	public function getData(): array
131
	{
132 10
		$data = parent::getData();
133 10
		$data['timezone'] = $this->getTimeZone();
134 10
		return $data;
135
	}
136
137
	/**
138
	 * @throws WebsiteInvalidOwnerException
139
	 */
140 12
	public function assertValidOwner(): void
141
	{
142 12
		$user = $this->userManager->get($this->getUserId());
143 12
		if ($user === null) {
144
			throw new WebsiteInvalidOwnerException();
145
		}
146 12
		if (!$user->isEnabled()) {
147
			throw new WebsiteInvalidOwnerException();
148
		}
149 12
		if (!$this->websitesService->isUserAllowed($this->getUserId())) {
150
			throw new WebsiteInvalidOwnerException();
151
		}
152 12
	}
153
154
	/**
155
	 * @throws WebsiteInvalidDataException
156
	 */
157 5
	public function assertValidName(): void
158
	{
159 5
		if (strlen($this->getName()) < self::NAME_LENGTH_MIN) {
160
			throw new WebsiteInvalidDataException('name', $this->l10n->t('The name of the website must be longer.'));
161
		}
162 5
		if (strlen($this->getName()) > self::NAME_LENGTH_MAX) {
163
			throw new WebsiteInvalidDataException('name', $this->l10n->t('The name of the website is too long.'));
164
		}
165 5
	}
166
167
	/**
168
	 * @throws WebsiteInvalidDataException
169
	 */
170 5
	public function assertValidSite(): void
171
	{
172 5
		if (strlen($this->getSite()) < self::SITE_LENGTH_MIN) {
173
			$error = $this->l10n->t('The identifier of the website must be longer.');
174
			throw new WebsiteInvalidDataException('site', $error);
175
		}
176 5
		if (strlen($this->getSite()) > self::SITE_LENGTH_MAX) {
177
			$error = $this->l10n->t('The identifier of the website is too long.');
178
			throw new WebsiteInvalidDataException('site', $error);
179
		}
180 5
		if (preg_match('/' . self::SITE_REGEX . '/', $this->getSite()) !== 1) {
181
			$error = $this->l10n->t('The identifier of the website can only contain lowercase alpha numeric chars.');
182
			throw new WebsiteInvalidDataException('site', $error);
183
		}
184 5
	}
185
186
	/**
187
	 * @throws WebsiteInvalidDataException
188
	 */
189 5
	public function assertValidPath(): void
190
	{
191
		try {
192 5
			$path = $this->miscService->normalizePath($this->getPath());
193 5
			if ($path === '') {
194 5
				throw new InvalidPathException();
195
			}
196
		} catch (InvalidPathException $e) {
197
			throw new WebsiteInvalidDataException(
198
				'path',
199
				$this->l10n->t('The path of the website is invalid.')
200
			);
201
		}
202
203 5
		$userFolder = new StorageFolder(\OC::$server->getUserFolder($this->getUserId()));
0 ignored issues
show
Deprecated Code introduced by
The function OC\Server::getUserFolder() 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

203
		$userFolder = new StorageFolder(/** @scrutinizer ignore-deprecated */ \OC::$server->getUserFolder($this->getUserId()));

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...
Bug introduced by
It seems like OC::server->getUserFolder($this->getUserId()) can also be of type null; however, parameter $folder of OCA\CMSPico\Files\StorageFolder::__construct() does only seem to accept OCP\Files\Folder, maybe add an additional type check? ( Ignorable by Annotation )

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

203
		$userFolder = new StorageFolder(/** @scrutinizer ignore-type */ \OC::$server->getUserFolder($this->getUserId()));
Loading history...
204
205
		try {
206 5
			$websiteBaseFolder = $userFolder->getFolder(dirname($path));
207
208
			try {
209 5
				$websiteFolder = $websiteBaseFolder->getFolder(basename($path));
210
211 2
				if (!$websiteFolder->isLocal()) {
212
					throw new WebsiteInvalidDataException(
213
						'path',
214 2
						$this->l10n->t('The website\'s path is stored on a non-local storage.')
215
					);
216
				}
217 3
			} catch (NotFoundException $e) {
218 3
				if (!$websiteBaseFolder->isLocal()) {
219
					throw new WebsiteInvalidDataException(
220
						'path',
221 5
						$this->l10n->t('The website\'s path is stored on a non-local storage.')
222
					);
223
				}
224
			}
225
		} catch (InvalidPathException | NotFoundException $e) {
226
			throw new WebsiteInvalidDataException(
227
				'path',
228
				$this->l10n->t('Parent folder of the website\'s path not found.')
229
			);
230
		}
231 5
	}
232
233
	/**
234
	 * @throws ThemeNotFoundException
235
	 * @throws ThemeNotCompatibleException
236
	 */
237 5
	public function assertValidTheme(): void
238
	{
239 5
		$this->themesService->assertValidTheme($this->getTheme());
240 5
	}
241
242
	/**
243
	 * @param string $userId
244
	 *
245
	 * @throws WebsiteForeignOwnerException
246
	 */
247 2
	public function assertOwnedBy(string $userId): void
248
	{
249 2
		if ($this->getUserId() !== $userId) {
250
			throw new WebsiteForeignOwnerException();
251
		}
252 2
	}
253
254
	/**
255
	 * @return StorageFolder
256
	 * @throws WebsiteInvalidFilesystemException
257
	 */
258 9
	public function getWebsiteFolder(): StorageFolder
259
	{
260 9
		if ($this->folder !== null) {
261
			try {
262
				// NC doesn't guarantee that mounts are present for the whole request lifetime
263
				// for example, if you call \OC\Files\Utils\Scanner::scan(), all mounts are reset
264
				// this makes OCNode instances, which rely on mounts of different users than the current, unusable
265
				// by calling OCFolder::get('') we can detect this situation and re-init the required mounts
266 9
				$this->folder->get('');
267
			} catch (\Exception $e) {
268
				$this->folder = null;
269
			}
270
		}
271
272 9
		if ($this->folder === null) {
273
			try {
274 9
				$ocUserFolder = \OC::$server->getUserFolder($this->getUserId());
0 ignored issues
show
Deprecated Code introduced by
The function OC\Server::getUserFolder() 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

274
				$ocUserFolder = /** @scrutinizer ignore-deprecated */ \OC::$server->getUserFolder($this->getUserId());

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...
275 9
				$userFolder = new StorageFolder($ocUserFolder);
0 ignored issues
show
Bug introduced by
It seems like $ocUserFolder can also be of type null; however, parameter $folder of OCA\CMSPico\Files\StorageFolder::__construct() does only seem to accept OCP\Files\Folder, maybe add an additional type check? ( Ignorable by Annotation )

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

275
				$userFolder = new StorageFolder(/** @scrutinizer ignore-type */ $ocUserFolder);
Loading history...
276
277 9
				$websiteFolder = $userFolder->getFolder($this->getPath());
278 9
				$this->folder = $websiteFolder->fakeRoot();
279
			} catch (InvalidPathException | NotFoundException $e) {
280
				throw new WebsiteInvalidFilesystemException($e);
281
			}
282
		}
283
284 9
		return $this->folder;
285
	}
286
287
	/**
288
	 * @return string
289
	 * @throws WebsiteInvalidFilesystemException
290
	 */
291 5
	public function getWebsitePath(): string
292
	{
293
		try {
294 5
			return $this->getWebsiteFolder()->getLocalPath() . '/';
295
		} catch (InvalidPathException | NotFoundException $e) {
296
			throw new WebsiteInvalidFilesystemException($e);
297
		}
298
	}
299
300
	/**
301
	 * @param bool $proxyRequest
302
	 *
303
	 * @return string
304
	 */
305 5
	public function getWebsiteUrl(bool $proxyRequest = false): string
306
	{
307 5
		if (!$proxyRequest) {
308 5
			$route = Application::APP_NAME . '.Pico.getPage';
309 5
			$parameters = [ 'site' => $this->getSite(), 'page' => '' ];
310 5
			return $this->urlGenerator->linkToRoute($route, $parameters) . '/';
311
		} else {
312
			return \OC::$WEBROOT . '/sites/' . urlencode($this->getSite()) . '/';
313
		}
314
	}
315
}
316