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\Db\WebsitesRequest; |
29
|
|
|
use OCA\CMSPico\Exceptions\AssetInvalidPathException; |
30
|
|
|
use OCA\CMSPico\Exceptions\AssetNotFoundException; |
31
|
|
|
use OCA\CMSPico\Exceptions\AssetNotPermittedException; |
32
|
|
|
use OCA\CMSPico\Exceptions\FilesystemNotLocalException; |
33
|
|
|
use OCA\CMSPico\Exceptions\PageInvalidPathException; |
34
|
|
|
use OCA\CMSPico\Exceptions\PageNotFoundException; |
35
|
|
|
use OCA\CMSPico\Exceptions\PageNotPermittedException; |
36
|
|
|
use OCA\CMSPico\Exceptions\PicoRuntimeException; |
37
|
|
|
use OCA\CMSPico\Exceptions\TemplateNotCompatibleException; |
38
|
|
|
use OCA\CMSPico\Exceptions\TemplateNotFoundException; |
39
|
|
|
use OCA\CMSPico\Exceptions\ThemeNotCompatibleException; |
40
|
|
|
use OCA\CMSPico\Exceptions\ThemeNotFoundException; |
41
|
|
|
use OCA\CMSPico\Exceptions\WebsiteExistsException; |
42
|
|
|
use OCA\CMSPico\Exceptions\WebsiteInvalidDataException; |
43
|
|
|
use OCA\CMSPico\Exceptions\WebsiteInvalidFilesystemException; |
44
|
|
|
use OCA\CMSPico\Exceptions\WebsiteInvalidOwnerException; |
45
|
|
|
use OCA\CMSPico\Exceptions\WebsiteNotFoundException; |
46
|
|
|
use OCA\CMSPico\Exceptions\WebsiteNotPermittedException; |
47
|
|
|
use OCA\CMSPico\Model\PicoAsset; |
48
|
|
|
use OCA\CMSPico\Model\PicoPage; |
49
|
|
|
use OCA\CMSPico\Model\Website; |
50
|
|
|
use OCA\CMSPico\Model\WebsiteRequest; |
51
|
|
|
use OCP\Files\InvalidPathException; |
52
|
|
|
use OCP\IGroupManager; |
53
|
|
|
|
54
|
|
|
class WebsitesService |
55
|
|
|
{ |
56
|
|
|
/** @var int */ |
57
|
|
|
public const LINK_MODE_LONG = 1; |
58
|
|
|
|
59
|
|
|
/** @var int */ |
60
|
|
|
public const LINK_MODE_SHORT = 2; |
61
|
|
|
|
62
|
|
|
/** @var WebsitesRequest */ |
63
|
|
|
private $websiteRequest; |
64
|
|
|
|
65
|
|
|
/** @var IGroupManager */ |
66
|
|
|
private $groupManager; |
67
|
|
|
|
68
|
|
|
/** @var ConfigService */ |
69
|
|
|
private $configService; |
70
|
|
|
|
71
|
|
|
/** @var TemplatesService */ |
72
|
|
|
private $templatesService; |
73
|
|
|
|
74
|
|
|
/** @var PicoService */ |
75
|
|
|
private $picoService; |
76
|
|
|
|
77
|
|
|
/** @var AssetsService */ |
78
|
|
|
private $assetsService; |
79
|
|
|
|
80
|
|
|
/** @var MiscService */ |
81
|
|
|
private $miscService; |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* WebsitesService constructor. |
85
|
|
|
* |
86
|
|
|
* @param WebsitesRequest $websiteRequest |
87
|
|
|
* @param IGroupManager $groupManager |
88
|
|
|
* @param ConfigService $configService |
89
|
|
|
* @param TemplatesService $templatesService |
90
|
|
|
* @param PicoService $picoService |
91
|
|
|
* @param AssetsService $assetsService |
92
|
|
|
* @param MiscService $miscService |
93
|
|
|
* |
94
|
|
|
* @internal param Manager $encryptionManager |
95
|
1 |
|
*/ |
96
|
|
|
public function __construct( |
97
|
|
|
WebsitesRequest $websiteRequest, |
98
|
|
|
IGroupManager $groupManager, |
99
|
|
|
ConfigService $configService, |
100
|
|
|
TemplatesService $templatesService, |
101
|
|
|
PicoService $picoService, |
102
|
|
|
AssetsService $assetsService, |
103
|
|
|
MiscService $miscService |
104
|
1 |
|
) { |
105
|
1 |
|
$this->websiteRequest = $websiteRequest; |
106
|
1 |
|
$this->groupManager = $groupManager; |
107
|
1 |
|
$this->configService = $configService; |
108
|
1 |
|
$this->templatesService = $templatesService; |
109
|
1 |
|
$this->picoService = $picoService; |
110
|
1 |
|
$this->assetsService = $assetsService; |
111
|
1 |
|
$this->miscService = $miscService; |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
/** |
115
|
|
|
* Creates a new website. |
116
|
|
|
* |
117
|
|
|
* Warning: This method does not check whether the user is allowed to create websites! |
118
|
|
|
* Please use {@see Website::assertValidOwner()} beforehand. |
119
|
|
|
* |
120
|
|
|
* @param Website $website |
121
|
|
|
* @param string $templateName |
122
|
|
|
* |
123
|
|
|
* @throws WebsiteExistsException |
124
|
|
|
* @throws WebsiteInvalidDataException |
125
|
|
|
* @throws WebsiteInvalidOwnerException |
126
|
3 |
|
* @throws ThemeNotFoundException |
127
|
|
|
* @throws ThemeNotCompatibleException |
128
|
3 |
|
* @throws TemplateNotFoundException |
129
|
3 |
|
* @throws TemplateNotCompatibleException |
130
|
3 |
|
*/ |
131
|
3 |
|
public function createWebsite(Website $website, string $templateName): void |
132
|
3 |
|
{ |
133
|
3 |
|
$website->assertValidName(); |
134
|
|
|
$website->assertValidSite(); |
135
|
|
|
$website->assertValidPath(); |
136
|
3 |
|
$website->assertValidTheme(); |
137
|
|
|
|
138
|
3 |
|
try { |
139
|
|
|
$website = $this->websiteRequest->getWebsiteFromSite($website->getSite()); |
140
|
|
|
throw new WebsiteExistsException(); |
141
|
|
|
} catch (WebsiteNotFoundException $e) { |
142
|
3 |
|
// in fact we want the website not to exist yet |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
$this->templatesService->assertValidTemplate($templateName); |
146
|
|
|
$this->templatesService->installTemplate($website, $templateName); |
147
|
|
|
|
148
|
|
|
$this->websiteRequest->create($website); |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
/** |
152
|
|
|
* Updates a website. |
153
|
|
|
* |
154
|
|
|
* Warning: This method does not check the ownership of the website! |
155
|
|
|
* Please use {@see Website::assertOwnedBy()} beforehand. |
156
|
|
|
* |
157
|
|
|
* @param Website $website |
158
|
|
|
* |
159
|
|
|
* @throws WebsiteNotFoundException |
160
|
|
|
* @throws WebsiteInvalidDataException |
161
|
|
|
* @throws ThemeNotFoundException |
162
|
|
|
* @throws ThemeNotCompatibleException |
163
|
|
|
* @throws TemplateNotFoundException |
164
|
|
|
* @throws TemplateNotCompatibleException |
165
|
|
|
*/ |
166
|
|
|
public function updateWebsite(Website $website): void |
167
|
|
|
{ |
168
|
|
|
$originalWebsite = $this->websiteRequest->getWebsiteFromId($website->getId()); |
169
|
|
|
|
170
|
|
|
if ($website->getName() !== $originalWebsite->getName()) { |
171
|
|
|
$website->assertValidName(); |
172
|
|
|
} |
173
|
|
|
if ($website->getSite() !== $originalWebsite->getSite()) { |
174
|
|
|
$website->assertValidSite(); |
175
|
|
|
} |
176
|
|
|
if ($website->getPath() !== $originalWebsite->getPath()) { |
177
|
|
|
$website->assertValidPath(); |
178
|
|
|
} |
179
|
|
|
if ($website->getTheme() !== $originalWebsite->getTheme()) { |
180
|
|
|
$website->assertValidTheme(); |
181
|
|
|
} |
182
|
|
|
|
183
|
|
|
$this->websiteRequest->update($website); |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
/** |
187
|
|
|
* Deletes a website. |
188
|
|
|
* |
189
|
|
|
* Warning: This method does not check the ownership of the website! |
190
|
|
|
* Please use {@see Website::assertOwnedBy()} beforehand. |
191
|
|
|
* |
192
|
|
|
* @param Website $website |
193
|
|
|
* |
194
|
|
|
* @throws WebsiteNotFoundException |
195
|
|
|
*/ |
196
|
|
|
public function deleteWebsite(Website $website): void |
197
|
|
|
{ |
198
|
|
|
// check whether website actually exists |
199
|
|
|
$this->websiteRequest->getWebsiteFromId($website->getId()); |
200
|
|
|
|
201
|
|
|
$this->websiteRequest->delete($website); |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
/** |
205
|
|
|
* @param string $userId |
206
|
|
|
*/ |
207
|
|
|
public function deleteUserWebsites(string $userId): void |
208
|
|
|
{ |
209
|
|
|
$this->websiteRequest->deleteAllFromUserId($userId); |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
/** |
213
|
|
|
* @param int $siteId |
214
|
|
|
* |
215
|
|
|
* @return Website |
216
|
|
|
* @throws WebsiteNotFoundException |
217
|
|
|
*/ |
218
|
|
|
public function getWebsiteFromId(int $siteId): Website |
219
|
|
|
{ |
220
|
|
|
return $this->websiteRequest->getWebsiteFromId($siteId); |
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
/** |
224
|
|
|
* @param string $site |
225
|
|
|
* |
226
|
|
|
* @return Website |
227
|
|
|
* @throws WebsiteNotFoundException |
228
|
|
|
*/ |
229
|
3 |
|
public function getWebsiteFromSite(string $site): Website |
230
|
|
|
{ |
231
|
3 |
|
return $this->websiteRequest->getWebsiteFromSite($site); |
232
|
|
|
} |
233
|
|
|
|
234
|
|
|
/** |
235
|
|
|
* @param string $userId |
236
|
|
|
* |
237
|
|
|
* @return Website[] |
238
|
|
|
*/ |
239
|
4 |
|
public function getWebsitesFromUser(string $userId): array |
240
|
|
|
{ |
241
|
4 |
|
return $this->websiteRequest->getWebsitesFromUserId($userId); |
242
|
|
|
} |
243
|
|
|
|
244
|
|
|
/** |
245
|
|
|
* @param string $site |
246
|
|
|
* @param string $page |
247
|
|
|
* @param string|null $viewer |
248
|
|
|
* @param bool $proxyRequest |
249
|
|
|
* |
250
|
|
|
* @return PicoPage |
251
|
|
|
* @throws WebsiteNotFoundException |
252
|
|
|
* @throws WebsiteInvalidOwnerException |
253
|
|
|
* @throws WebsiteInvalidFilesystemException |
254
|
|
|
* @throws WebsiteNotPermittedException |
255
|
|
|
* @throws FilesystemNotLocalException |
256
|
|
|
* @throws PageInvalidPathException |
257
|
|
|
* @throws PageNotFoundException |
258
|
|
|
* @throws PageNotPermittedException |
259
|
|
|
* @throws ThemeNotFoundException |
260
|
|
|
* @throws ThemeNotCompatibleException |
261
|
|
|
* @throws PicoRuntimeException |
262
|
|
|
*/ |
263
|
1 |
|
public function getPage(string $site, string $page, ?string $viewer, bool $proxyRequest = false): PicoPage |
264
|
|
|
{ |
265
|
|
|
try { |
266
|
1 |
|
$page = $this->miscService->normalizePath($page); |
267
|
|
|
} catch (InvalidPathException $e) { |
268
|
|
|
throw new PageInvalidPathException($e); |
269
|
|
|
} |
270
|
|
|
|
271
|
1 |
|
$website = $this->getWebsiteFromSite($site); |
272
|
|
|
$website->assertValidOwner(); |
273
|
|
|
|
274
|
|
|
if (!$website->getWebsiteFolder()->isLocal()) { |
275
|
|
|
throw new FilesystemNotLocalException(); |
276
|
|
|
} |
277
|
|
|
|
278
|
|
|
$websiteRequest = new WebsiteRequest($website, $viewer, $page, $proxyRequest); |
279
|
|
|
return $this->picoService->getPage($websiteRequest); |
280
|
|
|
} |
281
|
|
|
|
282
|
|
|
/** |
283
|
|
|
* @param string $site |
284
|
|
|
* @param string $asset |
285
|
|
|
* @param string|null $viewer |
286
|
|
|
* |
287
|
|
|
* @return PicoAsset |
288
|
|
|
* @throws WebsiteNotFoundException |
289
|
|
|
* @throws WebsiteInvalidOwnerException |
290
|
|
|
* @throws WebsiteInvalidFilesystemException |
291
|
|
|
* @throws WebsiteNotPermittedException |
292
|
|
|
* @throws FilesystemNotLocalException |
293
|
|
|
* @throws AssetInvalidPathException |
294
|
|
|
* @throws AssetNotFoundException |
295
|
|
|
* @throws AssetNotPermittedException |
296
|
|
|
*/ |
297
|
|
|
public function getAsset(string $site, string $asset, ?string $viewer): PicoAsset |
298
|
|
|
{ |
299
|
|
|
try { |
300
|
|
|
$asset = $this->miscService->normalizePath($asset); |
301
|
|
|
if ($asset === '') { |
302
|
|
|
throw new InvalidPathException(); |
303
|
|
|
} |
304
|
|
|
} catch (InvalidPathException $e) { |
305
|
|
|
throw new AssetInvalidPathException($e); |
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
$website = $this->getWebsiteFromSite($site); |
309
|
|
|
$website->assertValidOwner(); |
310
|
|
|
|
311
|
|
|
if (!$website->getWebsiteFolder()->isLocal()) { |
312
|
|
|
throw new FilesystemNotLocalException(); |
313
|
|
|
} |
314
|
|
|
|
315
|
|
|
$websiteRequest = new WebsiteRequest($website, $viewer, PicoService::DIR_ASSETS . '/' . $asset); |
316
|
|
|
return $this->assetsService->getAsset($websiteRequest); |
317
|
|
|
} |
318
|
|
|
|
319
|
|
|
/** |
320
|
|
|
* @param string[] $limitGroups |
321
|
|
|
* |
322
|
|
|
* @throws \UnexpectedValueException |
323
|
|
|
*/ |
324
|
|
|
public function setLimitGroups(array $limitGroups): void |
325
|
|
|
{ |
326
|
|
|
foreach ($limitGroups as $group) { |
327
|
|
|
if (!$this->groupManager->groupExists($group)) { |
328
|
|
|
throw new \UnexpectedValueException(); |
329
|
|
|
} |
330
|
|
|
} |
331
|
|
|
|
332
|
|
|
$this->configService->setAppValue(ConfigService::LIMIT_GROUPS, json_encode($limitGroups)); |
333
|
|
|
} |
334
|
|
|
|
335
|
|
|
/** |
336
|
|
|
* @return string[] |
337
|
|
|
*/ |
338
|
3 |
|
public function getLimitGroups(): array |
339
|
|
|
{ |
340
|
3 |
|
$json = $this->configService->getAppValue(ConfigService::LIMIT_GROUPS); |
341
|
3 |
|
return $json ? json_decode($json, true) : []; |
342
|
|
|
} |
343
|
|
|
|
344
|
|
|
/** |
345
|
|
|
* @param string|null $userId |
346
|
|
|
* |
347
|
|
|
* @return bool |
348
|
|
|
*/ |
349
|
3 |
|
public function isUserAllowed(?string $userId): bool |
350
|
|
|
{ |
351
|
3 |
|
if (!$userId) { |
352
|
|
|
return false; |
353
|
|
|
} |
354
|
|
|
|
355
|
3 |
|
$limitGroups = $this->getLimitGroups(); |
356
|
3 |
|
if (!$limitGroups) { |
|
|
|
|
357
|
3 |
|
return true; |
358
|
|
|
} |
359
|
|
|
|
360
|
|
|
foreach ($limitGroups as $groupId) { |
361
|
|
|
if ($this->groupManager->isInGroup($userId, $groupId)) { |
362
|
|
|
return true; |
363
|
|
|
} |
364
|
|
|
} |
365
|
|
|
|
366
|
|
|
return false; |
367
|
|
|
} |
368
|
|
|
|
369
|
|
|
/** |
370
|
|
|
* @param int $linkMode |
371
|
|
|
* |
372
|
|
|
* @throws \UnexpectedValueException |
373
|
|
|
*/ |
374
|
|
|
public function setLinkMode(int $linkMode): void |
375
|
|
|
{ |
376
|
|
|
if (($linkMode !== self::LINK_MODE_LONG) && ($linkMode !== self::LINK_MODE_SHORT)) { |
377
|
|
|
throw new \UnexpectedValueException(); |
378
|
|
|
} |
379
|
|
|
|
380
|
|
|
$this->configService->setAppValue(ConfigService::LINK_MODE, (string) $linkMode); |
381
|
|
|
} |
382
|
|
|
|
383
|
|
|
/** |
384
|
|
|
* @return int |
385
|
|
|
*/ |
386
|
|
|
public function getLinkMode(): int |
387
|
|
|
{ |
388
|
|
|
return (int) $this->configService->getAppValue(ConfigService::LINK_MODE); |
389
|
|
|
} |
390
|
|
|
} |
391
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.