Passed
Push — master ( 6b84e0...00c2c4 )
by Daniel
16:34
created

Website::setGroupAccess()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
eloc 5
c 0
b 0
f 0
dl 0
loc 10
ccs 0
cts 6
cp 0
rs 10
cc 4
nc 3
nop 1
crap 20
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\ThemesService;
38
use OCA\CMSPico\Service\WebsitesService;
39
use OCP\Files\InvalidPathException;
40
use OCP\Files\NotFoundException;
41
use OCP\IConfig;
42
use OCP\IGroupManager;
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 IGroupManager */
74
	private $groupManager;
75
76
	/** @var IURLGenerator */
77
	private $urlGenerator;
78
79
	/** @var WebsitesService */
80
	private $websitesService;
81
82
	/** @var ThemesService */
83
	private $themesService;
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->groupManager = \OC::$server->getGroupManager();
0 ignored issues
show
Deprecated Code introduced by
The function OC\Server::getGroupManager() 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->groupManager = /** @scrutinizer ignore-deprecated */ \OC::$server->getGroupManager();

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->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

102
		$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...
103 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

103
		$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...
104 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

104
		$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...
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
	 * @param string[] $groupAccess
129
	 *
130
	 * @return $this
131
	 */
132
	public function setGroupAccess(array $groupAccess): self
133
	{
134
		foreach ($groupAccess as $group) {
135
			if (!$this->groupManager->groupExists($group)) {
136
				throw new \UnexpectedValueException();
137
			}
138
		}
139
140
		$this->setOption('group_access', $groupAccess ?: null);
141
		return $this;
142
	}
143
144
	/**
145
	 * @return string[]
146
	 */
147 1
	public function getGroupAccess(): array
148
	{
149 1
		return $this->getOption('group_access') ?? [];
150
	}
151
152
	/**
153
	 * {@inheritDoc}
154
	 */
155 10
	public function getData(): array
156
	{
157 10
		$data = parent::getData();
158 10
		$data['timezone'] = $this->getTimeZone();
159 10
		return $data;
160
	}
161
162
	/**
163
	 * @throws WebsiteInvalidOwnerException
164
	 */
165 12
	public function assertValidOwner(): void
166
	{
167 12
		$user = $this->userManager->get($this->getUserId());
168 12
		if ($user === null) {
169
			throw new WebsiteInvalidOwnerException();
170
		}
171 12
		if (!$user->isEnabled()) {
172
			throw new WebsiteInvalidOwnerException();
173
		}
174 12
		if (!$this->websitesService->isUserAllowed($this->getUserId())) {
175
			throw new WebsiteInvalidOwnerException();
176
		}
177 12
	}
178
179
	/**
180
	 * @throws WebsiteInvalidDataException
181
	 */
182 5
	public function assertValidName(): void
183
	{
184 5
		if (strlen($this->getName()) < self::NAME_LENGTH_MIN) {
185
			throw new WebsiteInvalidDataException('name', $this->l10n->t('The name of the website must be longer.'));
186
		}
187 5
		if (strlen($this->getName()) > self::NAME_LENGTH_MAX) {
188
			throw new WebsiteInvalidDataException('name', $this->l10n->t('The name of the website is too long.'));
189
		}
190 5
	}
191
192
	/**
193
	 * @throws WebsiteInvalidDataException
194
	 */
195 5
	public function assertValidSite(): void
196
	{
197 5
		if (strlen($this->getSite()) < self::SITE_LENGTH_MIN) {
198
			$error = $this->l10n->t('The identifier of the website must be longer.');
199
			throw new WebsiteInvalidDataException('site', $error);
200
		}
201 5
		if (strlen($this->getSite()) > self::SITE_LENGTH_MAX) {
202
			$error = $this->l10n->t('The identifier of the website is too long.');
203
			throw new WebsiteInvalidDataException('site', $error);
204
		}
205 5
		if (preg_match('/' . self::SITE_REGEX . '/', $this->getSite()) !== 1) {
206
			$error = $this->l10n->t('The identifier of the website can only contain lowercase alpha numeric chars.');
207
			throw new WebsiteInvalidDataException('site', $error);
208
		}
209 5
	}
210
211
	/**
212
	 * @throws WebsiteInvalidDataException
213
	 */
214 5
	public function assertValidPath(): void
215
	{
216
		try {
217 5
			$path = $this->miscService->normalizePath($this->getPath());
218 5
			if ($path === '') {
219 5
				throw new InvalidPathException();
220
			}
221
		} catch (InvalidPathException $e) {
222
			throw new WebsiteInvalidDataException(
223
				'path',
224
				$this->l10n->t('The path of the website is invalid.')
225
			);
226
		}
227
228 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

228
		$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

228
		$userFolder = new StorageFolder(/** @scrutinizer ignore-type */ \OC::$server->getUserFolder($this->getUserId()));
Loading history...
229
230
		try {
231 5
			$websiteBaseFolder = $userFolder->getFolder(dirname($path));
232
233
			try {
234 5
				$websiteFolder = $websiteBaseFolder->getFolder(basename($path));
235
236 2
				if (!$websiteFolder->isLocal()) {
237
					throw new WebsiteInvalidDataException(
238
						'path',
239 2
						$this->l10n->t('The website\'s path is stored on a non-local storage.')
240
					);
241
				}
242 3
			} catch (NotFoundException $e) {
243 3
				if (!$websiteBaseFolder->isLocal()) {
244
					throw new WebsiteInvalidDataException(
245
						'path',
246 5
						$this->l10n->t('The website\'s path is stored on a non-local storage.')
247
					);
248
				}
249
			}
250
		} catch (InvalidPathException | NotFoundException $e) {
251
			throw new WebsiteInvalidDataException(
252
				'path',
253
				$this->l10n->t('Parent folder of the website\'s path not found.')
254
			);
255
		}
256 5
	}
257
258
	/**
259
	 * @throws ThemeNotFoundException
260
	 * @throws ThemeNotCompatibleException
261
	 */
262 5
	public function assertValidTheme(): void
263
	{
264 5
		$this->themesService->assertValidTheme($this->getTheme());
265 5
	}
266
267
	/**
268
	 * @param string $userId
269
	 *
270
	 * @throws WebsiteForeignOwnerException
271
	 */
272 2
	public function assertOwnedBy(string $userId): void
273
	{
274 2
		if ($this->getUserId() !== $userId) {
275
			throw new WebsiteForeignOwnerException();
276
		}
277 2
	}
278
279
	/**
280
	 * @return StorageFolder
281
	 * @throws WebsiteInvalidFilesystemException
282
	 */
283 9
	public function getWebsiteFolder(): StorageFolder
284
	{
285 9
		if ($this->folder !== null) {
286
			try {
287
				// NC doesn't guarantee that mounts are present for the whole request lifetime
288
				// for example, if you call \OC\Files\Utils\Scanner::scan(), all mounts are reset
289
				// this makes OCNode instances, which rely on mounts of different users than the current, unusable
290
				// by calling OCFolder::get('') we can detect this situation and re-init the required mounts
291 9
				$this->folder->get('');
292
			} catch (\Exception $e) {
293
				$this->folder = null;
294
			}
295
		}
296
297 9
		if ($this->folder === null) {
298
			try {
299 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

299
				$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...
300 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

300
				$userFolder = new StorageFolder(/** @scrutinizer ignore-type */ $ocUserFolder);
Loading history...
301
302 9
				$websiteFolder = $userFolder->getFolder($this->getPath());
303 9
				$this->folder = $websiteFolder->fakeRoot();
304
			} catch (InvalidPathException | NotFoundException $e) {
305
				throw new WebsiteInvalidFilesystemException($e);
306
			}
307
		}
308
309 9
		return $this->folder;
310
	}
311
312
	/**
313
	 * @return string
314
	 * @throws WebsiteInvalidFilesystemException
315
	 */
316 5
	public function getWebsitePath(): string
317
	{
318
		try {
319 5
			return $this->getWebsiteFolder()->getLocalPath() . '/';
320
		} catch (InvalidPathException | NotFoundException $e) {
321
			throw new WebsiteInvalidFilesystemException($e);
322
		}
323
	}
324
325
	/**
326
	 * @param bool $proxyRequest
327
	 *
328
	 * @return string
329
	 */
330 5
	public function getWebsiteUrl(bool $proxyRequest = false): string
331
	{
332 5
		if (!$proxyRequest) {
333 5
			$route = Application::APP_NAME . '.Pico.getPage';
334 5
			$parameters = [ 'site' => $this->getSite(), 'page' => '' ];
335 5
			return $this->urlGenerator->linkToRoute($route, $parameters) . '/';
336
		} else {
337
			return \OC::$WEBROOT . '/sites/' . urlencode($this->getSite()) . '/';
338
		}
339
	}
340
}
341