Exponential 5.x Kernel 7.5.40 Exponential New Stack (Symfony-based) Kernel — PHP Class Reference

Core/Repository/LocationService.php
Go to the documentation of this file.
1 <?php
2
7 namespace eZ\Publish\Core\Repository;
8
9 use eZ\Publish\API\Repository\ContentTypeService;
10 use eZ\Publish\API\Repository\PermissionCriterionResolver;
11 use eZ\Publish\API\Repository\Values\Content\Language;
12 use eZ\Publish\API\Repository\Values\Content\Location;
13 use eZ\Publish\API\Repository\Values\Content\LocationUpdateStruct;
14 use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct;
15 use eZ\Publish\API\Repository\Values\Content\ContentInfo;
16 use eZ\Publish\API\Repository\Values\Content\Location as APILocation;
17 use eZ\Publish\API\Repository\Values\Content\LocationList;
18 use eZ\Publish\API\Repository\Values\Content\VersionInfo;
19 use eZ\Publish\SPI\Limitation\Target;
20 use eZ\Publish\SPI\Persistence\Content\Location as SPILocation;
21 use eZ\Publish\SPI\Persistence\Content\Location\UpdateStruct;
22 use eZ\Publish\API\Repository\LocationService as LocationServiceInterface;
23 use eZ\Publish\API\Repository\Repository as RepositoryInterface;
24 use eZ\Publish\SPI\Persistence\Handler;
25 use eZ\Publish\API\Repository\Values\Content\Query;
26 use eZ\Publish\API\Repository\Values\Content\LocationQuery;
27 use eZ\Publish\API\Repository\Values\Content\Query\Criterion;
28 use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalAnd as CriterionLogicalAnd;
29 use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalNot as CriterionLogicalNot;
30 use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Subtree as CriterionSubtree;
31 use eZ\Publish\API\Repository\Exceptions\NotFoundException as APINotFoundException;
32 use eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue;
33 use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException;
34 use eZ\Publish\Core\Base\Exceptions\BadStateException;
35 use eZ\Publish\Core\Base\Exceptions\UnauthorizedException;
36 use Exception;
37 use Psr\Log\LoggerInterface;
38 use Psr\Log\NullLogger;
39 use eZ\Publish\API\Repository\Values\ContentType\ContentType;
40
46 class LocationService implements LocationServiceInterface
47 {
49 protected $repository;
50
52 protected $persistenceHandler;
53
55 protected $settings;
56
58 protected $domainMapper;
59
61 protected $nameSchemaService;
62
64 protected $permissionCriterionResolver;
65
67 private $logger;
68
70 protected $contentTypeService;
71
84 public function __construct(
85 RepositoryInterface $repository,
86 Handler $handler,
87 Helper\DomainMapper $domainMapper,
88 Helper\NameSchemaService $nameSchemaService,
89 PermissionCriterionResolver $permissionCriterionResolver,
90 ContentTypeService $contentTypeService,
91 array $settings = [],
92 LoggerInterface $logger = null
93 ) {
94 $this->repository = $repository;
95 $this->persistenceHandler = $handler;
96 $this->domainMapper = $domainMapper;
97 $this->nameSchemaService = $nameSchemaService;
98 // Union makes sure default settings are ignored if provided in argument
99 $this->settings = $settings + [
100 //'defaultSetting' => array(),
101 ];
102 $this->permissionCriterionResolver = $permissionCriterionResolver;
103 $this->contentTypeService = $contentTypeService;
104 $this->logger = null !== $logger ? $logger : new NullLogger();
105 }
106
121 public function copySubtree(APILocation $subtree, APILocation $targetParentLocation)
122 {
123 $loadedSubtree = $this->loadLocation($subtree->id);
124 $loadedTargetLocation = $this->loadLocation($targetParentLocation->id);
125
126 if (stripos($loadedTargetLocation->pathString, $loadedSubtree->pathString) !== false) {
127 throw new InvalidArgumentException('targetParentLocation', 'target parent location is a sub location of the given subtree');
128 }
129
130 // check create permission on target
131 if (!$this->repository->canUser('content', 'create', $loadedSubtree->getContentInfo(), $loadedTargetLocation)) {
132 throw new UnauthorizedException('content', 'create', ['locationId' => $loadedTargetLocation->id]);
133 }
134
135 // Check read access to whole source subtree
136 $contentReadCriterion = $this->permissionCriterionResolver->getPermissionsCriterion('content', 'read');
137 if ($contentReadCriterion === false) {
138 throw new UnauthorizedException('content', 'read');
139 } elseif ($contentReadCriterion !== true) {
140 // Query if there are any content in subtree current user don't have access to
141 $query = new Query(
142 [
143 'limit' => 0,
144 'filter' => new CriterionLogicalAnd(
145 [
146 new CriterionSubtree($loadedSubtree->pathString),
147 new CriterionLogicalNot($contentReadCriterion),
148 ]
149 ),
150 ]
151 );
152 $result = $this->repository->getSearchService()->findContent($query, [], false);
153 if ($result->totalCount > 0) {
154 throw new UnauthorizedException('content', 'read');
155 }
156 }
157
158 $this->repository->beginTransaction();
159 try {
160 $newLocation = $this->persistenceHandler->locationHandler()->copySubtree(
161 $loadedSubtree->id,
162 $loadedTargetLocation->id,
163 $this->repository->getPermissionResolver()->getCurrentUserReference()->getUserId()
164 );
165
166 $content = $this->repository->getContentService()->loadContent($newLocation->contentId);
167 $urlAliasNames = $this->nameSchemaService->resolveUrlAliasSchema($content);
168 foreach ($urlAliasNames as $languageCode => $name) {
169 $this->persistenceHandler->urlAliasHandler()->publishUrlAliasForLocation(
170 $newLocation->id,
171 $loadedTargetLocation->id,
172 $name,
173 $languageCode,
174 $content->contentInfo->alwaysAvailable
175 );
176 }
177
178 $this->persistenceHandler->urlAliasHandler()->locationCopied(
179 $loadedSubtree->id,
180 $newLocation->id,
181 $loadedTargetLocation->id
182 );
183
184 $this->repository->commit();
185 } catch (Exception $e) {
186 $this->repository->rollback();
187 throw $e;
188 }
189
190 return $this->domainMapper->buildLocationWithContent($newLocation, $content);
191 }
192
196 public function loadLocation($locationId, array $prioritizedLanguages = null, bool $useAlwaysAvailable = null)
197 {
198 $spiLocation = $this->persistenceHandler->locationHandler()->load($locationId, $prioritizedLanguages, $useAlwaysAvailable ?? true);
199 $location = $this->domainMapper->buildLocation($spiLocation, $prioritizedLanguages ?: [], $useAlwaysAvailable ?? true);
200 if (!$this->repository->canUser('content', 'read', $location->getContentInfo(), $location)) {
201 throw new UnauthorizedException('content', 'read', ['locationId' => $location->id]);
202 }
203
204 return $location;
205 }
206
210 public function loadLocationList(array $locationIds, array $prioritizedLanguages = null, bool $useAlwaysAvailable = null): iterable
211 {
212 $spiLocations = $this->persistenceHandler->locationHandler()->loadList(
213 $locationIds,
214 $prioritizedLanguages,
215 $useAlwaysAvailable ?? true
216 );
217 if (empty($spiLocations)) {
218 return [];
219 }
220
221 // Get content id's
222 $contentIds = [];
223 foreach ($spiLocations as $spiLocation) {
224 $contentIds[] = $spiLocation->contentId;
225 }
226
227 // Load content info and Get content proxy
228 $spiContentInfoList = $this->persistenceHandler->contentHandler()->loadContentInfoList($contentIds);
229 $contentProxyList = $this->domainMapper->buildContentProxyList(
230 $spiContentInfoList,
231 $prioritizedLanguages ?? [],
232 $useAlwaysAvailable ?? true
233 );
234
235 // Build locations using the bulk retrieved content info and bulk lazy loaded content proxies.
236 $locations = [];
237 $permissionResolver = $this->repository->getPermissionResolver();
238 foreach ($spiLocations as $spiLocation) {
239 $location = $this->domainMapper->buildLocationWithContent(
240 $spiLocation,
241 $contentProxyList[$spiLocation->contentId] ?? null,
242 $spiContentInfoList[$spiLocation->contentId] ?? null
243 );
244
245 if ($permissionResolver->canUser('content', 'read', $location->getContentInfo(), [$location])) {
246 $locations[$spiLocation->id] = $location;
247 }
248 }
249
250 return $locations;
251 }
252
256 public function loadLocationByRemoteId($remoteId, array $prioritizedLanguages = null, bool $useAlwaysAvailable = null)
257 {
258 if (!is_string($remoteId)) {
259 throw new InvalidArgumentValue('remoteId', $remoteId);
260 }
261
262 $spiLocation = $this->persistenceHandler->locationHandler()->loadByRemoteId($remoteId, $prioritizedLanguages, $useAlwaysAvailable ?? true);
263 $location = $this->domainMapper->buildLocation($spiLocation, $prioritizedLanguages ?: [], $useAlwaysAvailable ?? true);
264 if (!$this->repository->canUser('content', 'read', $location->getContentInfo(), $location)) {
265 throw new UnauthorizedException('content', 'read', ['locationId' => $location->id]);
266 }
267
268 return $location;
269 }
270
274 public function loadLocations(ContentInfo $contentInfo, APILocation $rootLocation = null, array $prioritizedLanguages = null)
275 {
276 if (!$contentInfo->published) {
277 throw new BadStateException('$contentInfo', 'ContentInfo has no published versions');
278 }
279
280 $spiLocations = $this->persistenceHandler->locationHandler()->loadLocationsByContent(
281 $contentInfo->id,
282 $rootLocation !== null ? $rootLocation->id : null
283 );
284
285 $locations = [];
286 $spiInfo = $this->persistenceHandler->contentHandler()->loadContentInfo($contentInfo->id);
287 $content = $this->domainMapper->buildContentProxy($spiInfo, $prioritizedLanguages ?: []);
288 foreach ($spiLocations as $spiLocation) {
289 $location = $this->domainMapper->buildLocationWithContent($spiLocation, $content, $spiInfo);
290 if ($this->repository->canUser('content', 'read', $location->getContentInfo(), $location)) {
291 $locations[] = $location;
292 }
293 }
294
295 return $locations;
296 }
297
301 public function loadLocationChildren(APILocation $location, $offset = 0, $limit = 25, array $prioritizedLanguages = null)
302 {
303 if (!$this->domainMapper->isValidLocationSortField($location->sortField)) {
304 throw new InvalidArgumentValue('sortField', $location->sortField, 'Location');
305 }
306
307 if (!$this->domainMapper->isValidLocationSortOrder($location->sortOrder)) {
308 throw new InvalidArgumentValue('sortOrder', $location->sortOrder, 'Location');
309 }
310
311 if (!is_int($offset)) {
312 throw new InvalidArgumentValue('offset', $offset);
313 }
314
315 if (!is_int($limit)) {
316 throw new InvalidArgumentValue('limit', $limit);
317 }
318
319 $childLocations = [];
320 $searchResult = $this->searchChildrenLocations($location, $offset, $limit, $prioritizedLanguages ?: []);
321 foreach ($searchResult->searchHits as $searchHit) {
322 $childLocations[] = $searchHit->valueObject;
323 }
324
325 return new LocationList(
326 [
327 'locations' => $childLocations,
328 'totalCount' => $searchResult->totalCount,
329 ]
330 );
331 }
332
336 public function loadParentLocationsForDraftContent(VersionInfo $versionInfo, array $prioritizedLanguages = null)
337 {
338 if (!$versionInfo->isDraft()) {
339 throw new BadStateException(
340 '$contentInfo',
341 sprintf(
342 'Content [%d] %s has been already published. Use LocationService::loadLocations instead.',
343 $versionInfo->contentInfo->id,
344 $versionInfo->contentInfo->name
345 )
346 );
347 }
348
349 $spiLocations = $this->persistenceHandler
350 ->locationHandler()
351 ->loadParentLocationsForDraftContent($versionInfo->contentInfo->id);
352
353 $contentIds = [];
354 foreach ($spiLocations as $spiLocation) {
355 $contentIds[] = $spiLocation->contentId;
356 }
357
358 $locations = [];
359 $permissionResolver = $this->repository->getPermissionResolver();
360 $spiContentInfoList = $this->persistenceHandler->contentHandler()->loadContentInfoList($contentIds);
361 $contentList = $this->domainMapper->buildContentProxyList($spiContentInfoList, $prioritizedLanguages ?: []);
362 foreach ($spiLocations as $spiLocation) {
363 $location = $this->domainMapper->buildLocationWithContent(
364 $spiLocation,
365 $contentList[$spiLocation->contentId],
366 $spiContentInfoList[$spiLocation->contentId]
367 );
368
369 if ($permissionResolver->canUser('content', 'read', $location->getContentInfo(), [$location])) {
370 $locations[] = $location;
371 }
372 }
373
374 return $locations;
375 }
376
384 public function getLocationChildCount(APILocation $location)
385 {
386 $searchResult = $this->searchChildrenLocations($location, 0, 0);
387
388 return $searchResult->totalCount;
389 }
390
400 protected function searchChildrenLocations(APILocation $location, $offset = 0, $limit = -1, array $prioritizedLanguages = null)
401 {
402 $query = new LocationQuery([
403 'filter' => new Criterion\ParentLocationId($location->id),
404 'offset' => $offset >= 0 ? (int)$offset : 0,
405 'limit' => $limit >= 0 ? (int)$limit : null,
406 'sortClauses' => $location->getSortClauses(),
407 ]);
408
409 return $this->repository->getSearchService()->findLocations($query, ['languages' => $prioritizedLanguages]);
410 }
411
425 public function createLocation(ContentInfo $contentInfo, LocationCreateStruct $locationCreateStruct)
426 {
427 $content = $this->domainMapper->buildContentDomainObjectFromPersistence(
428 $this->persistenceHandler->contentHandler()->load($contentInfo->id),
429 $this->persistenceHandler->contentTypeHandler()->load($contentInfo->contentTypeId)
430 );
431
432 $parentLocation = $this->domainMapper->buildLocation(
433 $this->persistenceHandler->locationHandler()->load($locationCreateStruct->parentLocationId)
434 );
435
436 $contentType = $content->getContentType();
437
438 $locationCreateStruct->sortField = $locationCreateStruct->sortField
439 ?? ($contentType->defaultSortField ?? Location::SORT_FIELD_NAME);
440 $locationCreateStruct->sortOrder = $locationCreateStruct->sortOrder
441 ?? ($contentType->defaultSortOrder ?? Location::SORT_ORDER_ASC);
442
443 $contentInfo = $content->contentInfo;
444
445 if (!$this->repository->canUser('content', 'manage_locations', $contentInfo, $parentLocation)) {
446 throw new UnauthorizedException('content', 'manage_locations', ['contentId' => $contentInfo->id]);
447 }
448
449 if (!$this->repository->canUser('content', 'create', $contentInfo, $parentLocation)) {
450 throw new UnauthorizedException('content', 'create', ['locationId' => $parentLocation->id]);
451 }
452
453 // Check if the parent is a sub location of one of the existing content locations (this also solves the
454 // situation where parent location actually one of the content locations),
455 // or if the content already has location below given location create struct parent
456 $existingContentLocations = $this->loadLocations($contentInfo);
457 if (!empty($existingContentLocations)) {
458 foreach ($existingContentLocations as $existingContentLocation) {
459 if (stripos($parentLocation->pathString, $existingContentLocation->pathString) !== false) {
460 throw new InvalidArgumentException(
461 '$locationCreateStruct',
462 'Specified parent is a sub location of one of the existing content locations.'
463 );
464 }
465 if ($parentLocation->id == $existingContentLocation->parentLocationId) {
466 throw new InvalidArgumentException(
467 '$locationCreateStruct',
468 'Content is already below the specified parent.'
469 );
470 }
471 }
472 }
473
474 $spiLocationCreateStruct = $this->domainMapper->buildSPILocationCreateStruct(
475 $locationCreateStruct,
476 $parentLocation,
477 $contentInfo->mainLocationId ?? true,
478 $contentInfo->id,
479 $contentInfo->currentVersionNo,
480 $contentInfo->isHidden
481 );
482
483 $this->repository->beginTransaction();
484 try {
485 $newLocation = $this->persistenceHandler->locationHandler()->create($spiLocationCreateStruct);
486 $urlAliasNames = $this->nameSchemaService->resolveUrlAliasSchema($content);
487 foreach ($urlAliasNames as $languageCode => $name) {
488 $this->persistenceHandler->urlAliasHandler()->publishUrlAliasForLocation(
489 $newLocation->id,
490 $newLocation->parentId,
491 $name,
492 $languageCode,
493 $contentInfo->alwaysAvailable,
494 // @todo: this is legacy storage specific for updating ezcontentobject_tree.path_identification_string, to be removed
495 $languageCode === $contentInfo->mainLanguageCode
496 );
497 }
498
499 $this->repository->commit();
500 } catch (Exception $e) {
501 $this->repository->rollback();
502 throw $e;
503 }
504
505 return $this->domainMapper->buildLocationWithContent($newLocation, $content);
506 }
507
519 public function updateLocation(APILocation $location, LocationUpdateStruct $locationUpdateStruct)
520 {
521 if (!$this->domainMapper->isValidLocationPriority($locationUpdateStruct->priority)) {
522 throw new InvalidArgumentValue('priority', $locationUpdateStruct->priority, 'LocationUpdateStruct');
523 }
524
525 if ($locationUpdateStruct->remoteId !== null && (!is_string($locationUpdateStruct->remoteId) || empty($locationUpdateStruct->remoteId))) {
526 throw new InvalidArgumentValue('remoteId', $locationUpdateStruct->remoteId, 'LocationUpdateStruct');
527 }
528
529 if ($locationUpdateStruct->sortField !== null && !$this->domainMapper->isValidLocationSortField($locationUpdateStruct->sortField)) {
530 throw new InvalidArgumentValue('sortField', $locationUpdateStruct->sortField, 'LocationUpdateStruct');
531 }
532
533 if ($locationUpdateStruct->sortOrder !== null && !$this->domainMapper->isValidLocationSortOrder($locationUpdateStruct->sortOrder)) {
534 throw new InvalidArgumentValue('sortOrder', $locationUpdateStruct->sortOrder, 'LocationUpdateStruct');
535 }
536
537 $loadedLocation = $this->loadLocation($location->id);
538
539 if ($locationUpdateStruct->remoteId !== null) {
540 try {
541 $existingLocation = $this->loadLocationByRemoteId($locationUpdateStruct->remoteId);
542 if ($existingLocation !== null && $existingLocation->id !== $loadedLocation->id) {
543 throw new InvalidArgumentException('locationUpdateStruct', 'location with provided remote ID already exists');
544 }
545 } catch (APINotFoundException $e) {
546 }
547 }
548
549 if (!$this->repository->canUser('content', 'edit', $loadedLocation->getContentInfo(), $loadedLocation)) {
550 throw new UnauthorizedException('content', 'edit', ['locationId' => $loadedLocation->id]);
551 }
552
553 $updateStruct = new UpdateStruct();
554 $updateStruct->priority = $locationUpdateStruct->priority !== null ? $locationUpdateStruct->priority : $loadedLocation->priority;
555 $updateStruct->remoteId = $locationUpdateStruct->remoteId !== null ? trim($locationUpdateStruct->remoteId) : $loadedLocation->remoteId;
556 $updateStruct->sortField = $locationUpdateStruct->sortField !== null ? $locationUpdateStruct->sortField : $loadedLocation->sortField;
557 $updateStruct->sortOrder = $locationUpdateStruct->sortOrder !== null ? $locationUpdateStruct->sortOrder : $loadedLocation->sortOrder;
558
559 $this->repository->beginTransaction();
560 try {
561 $this->persistenceHandler->locationHandler()->update($updateStruct, $loadedLocation->id);
562 $this->repository->commit();
563 } catch (Exception $e) {
564 $this->repository->rollback();
565 throw $e;
566 }
567
568 return $this->loadLocation($loadedLocation->id);
569 }
570
579 public function swapLocation(APILocation $location1, APILocation $location2)
580 {
581 $loadedLocation1 = $this->loadLocation($location1->id);
582 $loadedLocation2 = $this->loadLocation($location2->id);
583
584 if (!$this->repository->canUser('content', 'edit', $loadedLocation1->getContentInfo(), $loadedLocation1)) {
585 throw new UnauthorizedException('content', 'edit', ['locationId' => $loadedLocation1->id]);
586 }
587 if (!$this->repository->canUser('content', 'edit', $loadedLocation2->getContentInfo(), $loadedLocation2)) {
588 throw new UnauthorizedException('content', 'edit', ['locationId' => $loadedLocation2->id]);
589 }
590
591 $this->repository->beginTransaction();
592 try {
593 $this->persistenceHandler->locationHandler()->swap($loadedLocation1->id, $loadedLocation2->id);
594 $this->persistenceHandler->urlAliasHandler()->locationSwapped(
595 $location1->id,
596 $location1->parentLocationId,
597 $location2->id,
598 $location2->parentLocationId
599 );
600 $this->persistenceHandler->bookmarkHandler()->locationSwapped($loadedLocation1->id, $loadedLocation2->id);
601 $this->repository->commit();
602 } catch (Exception $e) {
603 $this->repository->rollback();
604 throw $e;
605 }
606 }
607
617 public function hideLocation(APILocation $location)
618 {
619 if (!$this->repository->canUser('content', 'hide', $location->getContentInfo(), $location)) {
620 throw new UnauthorizedException('content', 'hide', ['locationId' => $location->id]);
621 }
622
623 $this->repository->beginTransaction();
624 try {
625 $this->persistenceHandler->locationHandler()->hide($location->id);
626 $this->repository->commit();
627 } catch (Exception $e) {
628 $this->repository->rollback();
629 throw $e;
630 }
631
632 return $this->loadLocation($location->id);
633 }
634
647 public function unhideLocation(APILocation $location)
648 {
649 if (!$this->repository->canUser('content', 'hide', $location->getContentInfo(), $location)) {
650 throw new UnauthorizedException('content', 'hide', ['locationId' => $location->id]);
651 }
652
653 $this->repository->beginTransaction();
654 try {
655 $this->persistenceHandler->locationHandler()->unHide($location->id);
656 $this->repository->commit();
657 } catch (Exception $e) {
658 $this->repository->rollback();
659 throw $e;
660 }
661
662 return $this->loadLocation($location->id);
663 }
664
668 public function moveSubtree(APILocation $location, APILocation $newParentLocation)
669 {
670 $location = $this->loadLocation($location->id);
671 $newParentLocation = $this->loadLocation($newParentLocation->id);
672
673 if ($newParentLocation->id === $location->parentLocationId) {
674 throw new InvalidArgumentException(
675 '$newParentLocation', 'new parent Location is the same as the current one'
676 );
677 }
678 if (strpos($newParentLocation->pathString, $location->pathString) === 0) {
679 throw new InvalidArgumentException(
680 '$newParentLocation',
681 'new parent Location is in a subtree of the given $location'
682 );
683 }
684 $contentTypeId = $newParentLocation->contentInfo->contentTypeId;
685 if (!$this->contentTypeService->loadContentType($contentTypeId)->isContainer) {
686 throw new InvalidArgumentException(
687 '$newParentLocation',
688 'Cannot move Location to a parent that is not a container'
689 );
690 }
691
692 // check create permission on target location
693 if (!$this->repository->canUser('content', 'create', $location->getContentInfo(), $newParentLocation)) {
694 throw new UnauthorizedException('content', 'create', ['locationId' => $newParentLocation->id]);
695 }
696
700 $contentReadCriterion = $this->permissionCriterionResolver->getPermissionsCriterion('content', 'read');
701 if ($contentReadCriterion === false) {
702 throw new UnauthorizedException('content', 'read');
703 } elseif ($contentReadCriterion !== true) {
704 // Query if there are any content in subtree current user don't have access to
705 $query = new Query(
706 [
707 'limit' => 0,
708 'filter' => new CriterionLogicalAnd(
709 [
710 new CriterionSubtree($location->pathString),
711 new CriterionLogicalNot($contentReadCriterion),
712 ]
713 ),
714 ]
715 );
716 $result = $this->repository->getSearchService()->findContent($query, [], false);
717 if ($result->totalCount > 0) {
718 throw new UnauthorizedException('content', 'read');
719 }
720 }
721
722 $this->repository->beginTransaction();
723 try {
724 $this->persistenceHandler->locationHandler()->move($location->id, $newParentLocation->id);
725
726 $content = $this->repository->getContentService()->loadContent($location->contentId);
727 $urlAliasNames = $this->nameSchemaService->resolveUrlAliasSchema($content);
728 foreach ($urlAliasNames as $languageCode => $name) {
729 $this->persistenceHandler->urlAliasHandler()->publishUrlAliasForLocation(
730 $location->id,
731 $newParentLocation->id,
732 $name,
733 $languageCode,
734 $content->contentInfo->alwaysAvailable
735 );
736 }
737
738 $this->persistenceHandler->urlAliasHandler()->locationMoved(
739 $location->id,
740 $location->parentLocationId,
741 $newParentLocation->id
742 );
743
744 $this->repository->commit();
745 } catch (Exception $e) {
746 $this->repository->rollback();
747 throw $e;
748 }
749 }
750
758 public function deleteLocation(APILocation $location)
759 {
760 $location = $this->loadLocation($location->id);
761 $contentInfo = $location->contentInfo;
762 $versionInfo = $this->persistenceHandler->contentHandler()->loadVersionInfo(
763 $contentInfo->id,
764 $contentInfo->currentVersionNo
765 );
766 $translations = $versionInfo->languageCodes;
767 $target = (new Target\Version())->deleteTranslations($translations);
768
769 if (!$this->repository->canUser('content', 'manage_locations', $location->getContentInfo())) {
770 throw new UnauthorizedException('content', 'manage_locations', ['locationId' => $location->id]);
771 }
772 if (!$this->repository->canUser('content', 'remove', $location->getContentInfo(), [$location, $target])) {
773 throw new UnauthorizedException('content', 'remove', ['locationId' => $location->id]);
774 }
775
776 // Check remove access to descendants
777 $contentReadCriterion = $this->permissionCriterionResolver->getPermissionsCriterion('content', 'remove');
778 if ($contentReadCriterion === false) {
779 throw new UnauthorizedException('content', 'remove');
780 } elseif ($contentReadCriterion !== true) {
781 // Query if there are any content in subtree current user don't have access to
782 $query = new Query(
783 [
784 'limit' => 0,
785 'filter' => new CriterionLogicalAnd(
786 [
787 new CriterionSubtree($location->pathString),
788 new CriterionLogicalNot($contentReadCriterion),
789 ]
790 ),
791 ]
792 );
793 $result = $this->repository->getSearchService()->findContent($query, [], false);
794 if ($result->totalCount > 0) {
795 throw new UnauthorizedException('content', 'remove');
796 }
797 }
798
799 $this->repository->beginTransaction();
800 try {
801 $this->persistenceHandler->locationHandler()->removeSubtree($location->id);
802 $this->persistenceHandler->urlAliasHandler()->locationDeleted($location->id);
803 $this->repository->commit();
804 } catch (Exception $e) {
805 $this->repository->rollback();
806 throw $e;
807 }
808 }
809
818 public function newLocationCreateStruct($parentLocationId, ContentType $contentType = null)
819 {
820 $properties = [
821 'parentLocationId' => $parentLocationId,
822 ];
823 if ($contentType) {
824 $properties['sortField'] = $contentType->defaultSortField;
825 $properties['sortOrder'] = $contentType->defaultSortOrder;
826 }
827
828 return new LocationCreateStruct($properties);
829 }
830
836 public function newLocationUpdateStruct()
837 {
838 return new LocationUpdateStruct();
839 }
840
848 public function getAllLocationsCount(): int
849 {
850 return $this->persistenceHandler->locationHandler()->countAllLocations();
851 }
852
864 public function loadAllLocations(int $offset = 0, int $limit = 25): array
865 {
866 $spiLocations = $this->persistenceHandler->locationHandler()->loadAllLocations(
867 $offset,
868 $limit
869 );
870 $contentIds = array_unique(
871 array_map(
872 function (SPILocation $spiLocation) {
873 return $spiLocation->contentId;
874 },
875 $spiLocations
876 )
877 );
878
879 $permissionResolver = $this->repository->getPermissionResolver();
880 $spiContentInfoList = $this->persistenceHandler->contentHandler()->loadContentInfoList(
881 $contentIds
882 );
883 $contentList = $this->domainMapper->buildContentProxyList(
884 $spiContentInfoList,
885 Language::ALL,
886 false
887 );
888 $locations = [];
889 foreach ($spiLocations as $spiLocation) {
890 if (!isset($spiContentInfoList[$spiLocation->contentId], $contentList[$spiLocation->contentId])) {
891 $this->logger->warning(
892 sprintf(
893 'Location %d has missing Content %d',
894 $spiLocation->id,
895 $spiLocation->contentId
896 )
897 );
898 continue;
899 }
900
901 $location = $this->domainMapper->buildLocationWithContent(
902 $spiLocation,
903 $contentList[$spiLocation->contentId],
904 $spiContentInfoList[$spiLocation->contentId]
905 );
906
907 $contentInfo = $location->getContentInfo();
908 if (!$permissionResolver->canUser('content', 'read', $contentInfo, [$location])) {
909 continue;
910 }
911 $locations[] = $location;
912 }
913
914 return $locations;
915 }
916 }
eZ\Publish\API\Repository\Exceptions\NotFoundException
This Exception is thrown if an object referenced by an id or identifier could not be found in the rep...
Definition: API/Repository/Exceptions/NotFoundException.php:16
eZ\Publish\API\Repository\Values\ContentType\ContentType
Definition: API/Repository/Values/ContentType/ContentType.php:36
eZ\Publish\API\Repository\Values\Content\ContentInfo
Definition: API/Repository/Values/Content/ContentInfo.php:31
eZ\Publish\API\Repository\Values\Content\Language
Definition: API/Repository/Values/Content/Language.php:20
eZ\Publish\API\Repository\Values\Content\Language\ALL
const ALL
Constant for use in API's to specify that you want to load all languages.
Definition: API/Repository/Values/Content/Language.php:24
eZ\Publish\API\Repository\Values\Content\LocationCreateStruct
This class is used to create a new Location for a content object.
Definition: API/Repository/Values/Content/LocationCreateStruct.php:15
eZ\Publish\API\Repository\Values\Content\LocationList
Definition: API/Repository/Values/Content/LocationList.php:19
eZ\Publish\API\Repository\Values\Content\LocationQuery
This class is used to perform a Location query.
Definition: API/Repository/Values/Content/LocationQuery.php:13
eZ\Publish\API\Repository\Values\Content\LocationUpdateStruct
This class is used for updating location meta data.
Definition: API/Repository/Values/Content/LocationUpdateStruct.php:15
eZ\Publish\API\Repository\Values\Content\Location
Definition: API/Repository/Values/Content/Location.php:32
eZ\Publish\API\Repository\Values\Content\Location\SORT_ORDER_ASC
const SORT_ORDER_ASC
Definition: API/Repository/Values/Content/Location.php:53
eZ\Publish\API\Repository\Values\Content\Location\SORT_FIELD_NAME
const SORT_FIELD_NAME
Definition: API/Repository/Values/Content/Location.php:42
eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalAnd
This criterion implements a logical AND criterion and will only match if ALL of the given criteria ma...
Definition: API/Repository/Values/Content/Query/Criterion/LogicalAnd.php:14
eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalNot
A NOT logical criterion.
Definition: API/Repository/Values/Content/Query/Criterion/LogicalNot.php:15
eZ\Publish\API\Repository\Values\Content\Query\Criterion\ParentLocationId
A criterion that matches content based on its parent location id.
Definition: API/Repository/Values/Content/Query/Criterion/ParentLocationId.php:22
eZ\Publish\API\Repository\Values\Content\Query\Criterion\Subtree
Criterion that matches content that belongs to a given (list of) Subtree(s).
Definition: API/Repository/Values/Content/Query/Criterion/Subtree.php:19
eZ\Publish\API\Repository\Values\Content\Query\Criterion
Definition: API/Repository/Values/Content/Query/Criterion.php:15
eZ\Publish\API\Repository\Values\Content\Query
This class is used to perform a Content query.
Definition: API/Repository/Values/Content/Query.php:15
eZ\Publish\API\Repository\Values\Content\VersionInfo
Definition: API/Repository/Values/Content/VersionInfo.php:27
eZ\Publish\API\Repository\Values\Content\VersionInfo\isDraft
isDraft()
Returns true if version is a draft.
Definition: API/Repository/Values/Content/VersionInfo.php:103
eZ\Publish\Core\Base\Exceptions\BadStateException
BadState Exception implementation.
Definition: Core/Base/Exceptions/BadStateException.php:20
eZ\Publish\Core\Base\Exceptions\InvalidArgumentException
Invalid Argument Type Exception implementation.
Definition: Core/Base/Exceptions/InvalidArgumentException.php:20
eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue
Invalid Argument Type Exception implementation.
Definition: Base/Exceptions/InvalidArgumentValue.php:17
eZ\Publish\Core\Base\Exceptions\UnauthorizedException
UnauthorizedException Exception implementation.
Definition: Core/Base/Exceptions/UnauthorizedException.php:21
eZ\Publish\Core\Repository\LocationService
Definition: Core/Repository/LocationService.php:47
eZ\Publish\Core\Repository\LocationService\__construct
__construct(RepositoryInterface $repository, Handler $handler, Helper\DomainMapper $domainMapper, Helper\NameSchemaService $nameSchemaService, PermissionCriterionResolver $permissionCriterionResolver, ContentTypeService $contentTypeService, array $settings=[], LoggerInterface $logger=null)
Setups service with reference to repository object that created it & corresponding handler.
Definition: Core/Repository/LocationService.php:84
eZ\Publish\Core\Repository\LocationService\$nameSchemaService
$nameSchemaService
Definition: Core/Repository/LocationService.php:61
eZ\Publish\Core\Repository\LocationService\getLocationChildCount
getLocationChildCount(APILocation $location)
Returns the number of children which are readable by the current user of a location object.
Definition: Core/Repository/LocationService.php:384
eZ\Publish\Core\Repository\LocationService\loadLocationByRemoteId
loadLocationByRemoteId($remoteId, array $prioritizedLanguages=null, bool $useAlwaysAvailable=null)
{Loads a location object from its $remoteId.}
Definition: Core/Repository/LocationService.php:256
eZ\Publish\Core\Repository\LocationService\swapLocation
swapLocation(APILocation $location1, APILocation $location2)
Swaps the contents held by $location1 and $location2.
Definition: Core/Repository/LocationService.php:579
eZ\Publish\Core\Repository\LocationService\$settings
$settings
Definition: Core/Repository/LocationService.php:55
eZ\Publish\Core\Repository\LocationService\$permissionCriterionResolver
$permissionCriterionResolver
Definition: Core/Repository/LocationService.php:64
eZ\Publish\Core\Repository\LocationService\searchChildrenLocations
searchChildrenLocations(APILocation $location, $offset=0, $limit=-1, array $prioritizedLanguages=null)
Searches children locations of the provided parent location id.
Definition: Core/Repository/LocationService.php:400
eZ\Publish\Core\Repository\LocationService\unhideLocation
unhideLocation(APILocation $location)
Unhides the $location.
Definition: Core/Repository/LocationService.php:647
eZ\Publish\Core\Repository\LocationService\$repository
$repository
Definition: Core/Repository/LocationService.php:49
eZ\Publish\Core\Repository\LocationService\loadLocationList
loadLocationList(array $locationIds, array $prioritizedLanguages=null, bool $useAlwaysAvailable=null)
{Loads several location objects from its $locationIds.Returned list of Locations will be filtered by ...
Definition: Core/Repository/LocationService.php:210
eZ\Publish\Core\Repository\LocationService\$persistenceHandler
$persistenceHandler
Definition: Core/Repository/LocationService.php:52
eZ\Publish\Core\Repository\LocationService\newLocationCreateStruct
newLocationCreateStruct($parentLocationId, ContentType $contentType=null)
Instantiates a new location create class.
Definition: Core/Repository/LocationService.php:818
eZ\Publish\Core\Repository\LocationService\$logger
$logger
Definition: Core/Repository/LocationService.php:67
eZ\Publish\Core\Repository\LocationService\$contentTypeService
$contentTypeService
Definition: Core/Repository/LocationService.php:70
eZ\Publish\Core\Repository\LocationService\getAllLocationsCount
getAllLocationsCount()
Get the total number of all existing Locations.
Definition: Core/Repository/LocationService.php:848
eZ\Publish\Core\Repository\LocationService\deleteLocation
deleteLocation(APILocation $location)
Deletes $location and all its descendants.
Definition: Core/Repository/LocationService.php:758
eZ\Publish\Core\Repository\LocationService\loadLocation
loadLocation($locationId, array $prioritizedLanguages=null, bool $useAlwaysAvailable=null)
{Loads a location object from its $locationId.}
Definition: Core/Repository/LocationService.php:196
eZ\Publish\Core\Repository\LocationService\$domainMapper
$domainMapper
Definition: Core/Repository/LocationService.php:58
eZ\Publish\Core\Repository\LocationService\loadParentLocationsForDraftContent
loadParentLocationsForDraftContent(VersionInfo $versionInfo, array $prioritizedLanguages=null)
{Load parent Locations for Content Draft.Used as prioritized language code on translated properties o...
Definition: Core/Repository/LocationService.php:336
eZ\Publish\Core\Repository\LocationService\copySubtree
copySubtree(APILocation $subtree, APILocation $targetParentLocation)
Copies the subtree starting from $subtree as a new subtree of $targetLocation.
Definition: Core/Repository/LocationService.php:121
eZ\Publish\Core\Repository\LocationService\loadLocations
loadLocations(ContentInfo $contentInfo, APILocation $rootLocation=null, array $prioritizedLanguages=null)
{}
Definition: Core/Repository/LocationService.php:274
eZ\Publish\Core\Repository\LocationService\newLocationUpdateStruct
newLocationUpdateStruct()
Instantiates a new location update class.
Definition: Core/Repository/LocationService.php:836
eZ\Publish\Core\Repository\LocationService\loadLocationChildren
loadLocationChildren(APILocation $location, $offset=0, $limit=25, array $prioritizedLanguages=null)
{}
Definition: Core/Repository/LocationService.php:301
eZ\Publish\Core\Repository\LocationService\createLocation
createLocation(ContentInfo $contentInfo, LocationCreateStruct $locationCreateStruct)
Creates the new $location in the content repository for the given content.
Definition: Core/Repository/LocationService.php:425
eZ\Publish\Core\Repository\LocationService\updateLocation
updateLocation(APILocation $location, LocationUpdateStruct $locationUpdateStruct)
Updates $location in the content repository.
Definition: Core/Repository/LocationService.php:519
eZ\Publish\Core\Repository\LocationService\loadAllLocations
loadAllLocations(int $offset=0, int $limit=25)
Bulk-load all existing Locations, constrained by $limit and $offset to paginate results.
Definition: Core/Repository/LocationService.php:864
eZ\Publish\Core\Repository\LocationService\hideLocation
hideLocation(APILocation $location)
Hides the $location and marks invisible all descendants of $location.
Definition: Core/Repository/LocationService.php:617
eZ\Publish\SPI\Persistence\Content\Location\UpdateStruct
Definition: Content/Location/UpdateStruct.php:12
eZ\Publish\API\Repository\ContentTypeService
Definition: API/Repository/ContentTypeService.php:25
eZ\Publish\API\Repository\LocationService
Definition: API/Repository/LocationService.php:21
eZ\Publish\API\Repository\LocationService\moveSubtree
moveSubtree(Location $location, Location $newParentLocation)
Moves the subtree to $newParentLocation.
eZ\Publish\API\Repository\PermissionCriterionResolver
This service provides methods for resolving criterion permissions.
Definition: API/Repository/PermissionCriterionResolver.php:15
eZ\Publish\API\Repository\Repository
Repository interface.
Definition: API/Repository/Repository.php:16
eZ\Publish\SPI\Limitation\Target
Marker interface for PermissionResolver::canUser $targets objects.
Definition: SPI/Limitation/Target.php:19
eZ\Publish\SPI\Persistence\Content\Language\Handler
Language Handler interface.
Definition: SPI/Persistence/Content/Language/Handler.php:15
eZ\Publish\SPI\Persistence\Handler
The main handler for Storage Engine.
Definition: SPI/Persistence/Handler.php:15
eZ\Publish\API\Repository\Values\Content\Query\Criterion
Definition: API/Repository/Values/Content/Query/Criterion/Ancestor.php:7
eZ\Publish\Core\Repository
Definition: Core/Repository/BookmarkService.php:9
eZ\Publish\SPI\Persistence\Content\Location
Definition: SPI/Persistence/Content/Location/CreateStruct.php:7
eZ\Publish\SPI\Persistence\Content\Type
Definition: SPI/Persistence/Content/Type/CreateStruct.php:7
Copyright © 1998 – 2026 7x. All rights reserved. Licensed under the GNU GPLv2+.