vendor/ibexa/personalization/src/lib/Event/Subscriber/TrashEventSubscriber.php line 78

Open in your IDE?
  1. <?php
  2. /**
  3. * @copyright Copyright (C) Ibexa AS. All rights reserved.
  4. * @license For full copyright and license information view LICENSE file distributed with this source code.
  5. */
  6. declare(strict_types=1);
  7. namespace Ibexa\Personalization\Event\Subscriber;
  8. use Ibexa\Contracts\Core\Limitation\Target;
  9. use Ibexa\Contracts\Core\Repository\ContentService as CoreContentService;
  10. use Ibexa\Contracts\Core\Repository\Events\Trash\BeforeTrashEvent;
  11. use Ibexa\Contracts\Core\Repository\Events\Trash\RecoverEvent;
  12. use Ibexa\Contracts\Core\Repository\LocationService;
  13. use Ibexa\Contracts\Core\Repository\PermissionCriterionResolver;
  14. use Ibexa\Contracts\Core\Repository\PermissionResolver;
  15. use Ibexa\Contracts\Core\Repository\Repository;
  16. use Ibexa\Contracts\Core\Repository\SearchService;
  17. use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo;
  18. use Ibexa\Contracts\Core\Repository\Values\Content\Location;
  19. use Ibexa\Contracts\Core\Repository\Values\Content\Query;
  20. use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion;
  21. use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\LogicalAnd as CriterionLogicalAnd;
  22. use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\LogicalNot as CriterionLogicalNot;
  23. use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Subtree as CriterionSubtree;
  24. use Ibexa\Core\Base\Exceptions\UnauthorizedException;
  25. use Ibexa\Core\Query\QueryFactoryInterface;
  26. use Ibexa\Personalization\Config\ItemType\IncludedItemTypeResolverInterface;
  27. use Ibexa\Personalization\Event\PersonalizationEvent;
  28. use Ibexa\Personalization\Service\Content\ContentServiceInterface;
  29. use Ibexa\Personalization\Service\Setting\SettingServiceInterface;
  30. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  31. final class TrashEventSubscriber extends AbstractRepositoryEventSubscriber implements EventSubscriberInterface
  32. {
  33. private PermissionResolver $permissionResolver;
  34. private PermissionCriterionResolver $permissionCriterionResolver;
  35. private SettingServiceInterface $settingService;
  36. public function __construct(
  37. CoreContentService $coreContentService,
  38. ContentServiceInterface $contentService,
  39. IncludedItemTypeResolverInterface $includedItemTypeResolver,
  40. LocationService $locationService,
  41. Repository $repository,
  42. PermissionResolver $permissionResolver,
  43. PermissionCriterionResolver $permissionCriterionResolver,
  44. SearchService $searchService,
  45. SettingServiceInterface $settingService,
  46. QueryFactoryInterface $queryFactory
  47. ) {
  48. parent::__construct(
  49. $coreContentService,
  50. $contentService,
  51. $includedItemTypeResolver,
  52. $locationService,
  53. $repository,
  54. $queryFactory,
  55. $searchService
  56. );
  57. $this->permissionResolver = $permissionResolver;
  58. $this->permissionCriterionResolver = $permissionCriterionResolver;
  59. $this->settingService = $settingService;
  60. }
  61. public static function getSubscribedEvents(): array
  62. {
  63. return [
  64. RecoverEvent::class => ['onRecover', PersonalizationEvent::DEFAULT_PRIORITY],
  65. BeforeTrashEvent::class => ['onBeforeTrash', PersonalizationEvent::DEFAULT_PRIORITY],
  66. ];
  67. }
  68. public function onRecover(RecoverEvent $event): void
  69. {
  70. if (!$this->settingService->isInstallationKeyFound()) {
  71. return;
  72. }
  73. $location = $event->getLocation();
  74. $content = $location->getContent();
  75. if (!$this->includedItemTypeResolver->isContentIncluded($content)) {
  76. return;
  77. }
  78. $contentItems = array_merge(
  79. [$content],
  80. $this->getContentRelations($location->getContentInfo()),
  81. );
  82. $this->contentService->updateContentItems($contentItems);
  83. }
  84. /**
  85. * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException
  86. * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidCriterionArgumentException
  87. * @throws \Ibexa\Core\Base\Exceptions\UnauthorizedException
  88. */
  89. public function onBeforeTrash(BeforeTrashEvent $event): void
  90. {
  91. if (!$this->settingService->isInstallationKeyFound()) {
  92. return;
  93. }
  94. $location = $event->getLocation();
  95. $contentInfo = $location->getContentInfo();
  96. if (!$this->canDelete($contentInfo, $location)) {
  97. throw new UnauthorizedException('content', 'remove');
  98. }
  99. $this->deleteLocationWithChildren($location);
  100. $relations = $this->getContentRelations($location->getContentInfo());
  101. if (empty($relations)) {
  102. return;
  103. }
  104. $this->contentService->updateContentItems($relations);
  105. }
  106. /**
  107. * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidCriterionArgumentException
  108. * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException
  109. */
  110. private function canDelete(ContentInfo $contentInfo, Location $location): bool
  111. {
  112. if (!$this->hasContentRemovePermission($contentInfo, $location)) {
  113. return false;
  114. }
  115. return $this->hasSubtreeRemovePermission($location);
  116. }
  117. private function hasContentRemovePermission(ContentInfo $contentInfo, Location $location): bool
  118. {
  119. $target = (new Target\Version())->deleteTranslations($location->getContent()->getVersionInfo()->languageCodes);
  120. return $this->permissionResolver->canUser('content', 'remove', $contentInfo, [$location, $target]);
  121. }
  122. /**
  123. * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException
  124. * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidCriterionArgumentException
  125. */
  126. private function hasSubtreeRemovePermission(Location $location): bool
  127. {
  128. $contentRemoveCriterion = $this->permissionCriterionResolver->getPermissionsCriterion('content', 'remove');
  129. if (is_bool($contentRemoveCriterion)) {
  130. return $contentRemoveCriterion;
  131. }
  132. $result = $this->searchService->findContent(
  133. $this->getQuery($location, $contentRemoveCriterion),
  134. [],
  135. false
  136. );
  137. return $result->totalCount === 0;
  138. }
  139. /**
  140. * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidCriterionArgumentException
  141. */
  142. private function getQuery(Location $location, Criterion $criterion): Query
  143. {
  144. return new Query(
  145. [
  146. 'limit' => 0,
  147. 'filter' => new CriterionLogicalAnd(
  148. [
  149. new CriterionSubtree($location->getPathString()),
  150. new CriterionLogicalNot($criterion),
  151. ]
  152. ),
  153. ]
  154. );
  155. }
  156. }
  157. class_alias(TrashEventSubscriber::class, 'EzSystems\EzRecommendationClient\Event\Subscriber\TrashEventSubscriber');