vendor/ibexa/workflow/src/lib/Event/Subscriber/VersionLockSubscriber.php line 113

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\Workflow\Event\Subscriber;
  8. use Ibexa\AdminUi\Event\CancelEditVersionDraftEvent;
  9. use Ibexa\ContentForms\Content\View\ContentEditView;
  10. use Ibexa\Contracts\Core\Event\View\PostBuildViewEvent;
  11. use Ibexa\Contracts\Core\Repository\Events\Content\BeforePublishVersionEvent;
  12. use Ibexa\Contracts\Core\Repository\Events\Content\DeleteContentEvent;
  13. use Ibexa\Contracts\Core\Repository\Events\Content\DeleteVersionEvent;
  14. use Ibexa\Contracts\Core\Repository\Events\User\DeleteUserEvent;
  15. use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException as APINotFoundException;
  16. use Ibexa\Contracts\Core\Repository\PermissionResolver;
  17. use Ibexa\Contracts\Core\Repository\UserService;
  18. use Ibexa\Contracts\Core\Repository\Values\Content\Content;
  19. use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo;
  20. use Ibexa\Contracts\Workflow\Service\WorkflowServiceInterface;
  21. use Ibexa\Core\Base\Exceptions\NotFoundException;
  22. use Ibexa\Scheduler\Event\ScheduledPublish;
  23. use Ibexa\Workflow\Exception\VersionLockedException;
  24. use Ibexa\Workflow\Registry\WorkflowRegistry;
  25. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  26. use Symfony\Component\Workflow\Event\EnteredEvent;
  27. final class VersionLockSubscriber implements EventSubscriberInterface
  28. {
  29. /** @var \Ibexa\Contracts\Core\Repository\UserService */
  30. private $userService;
  31. /** @var \Ibexa\Contracts\Workflow\Service\WorkflowServiceInterface */
  32. private $workflowService;
  33. /** @var \Ibexa\Workflow\Registry\WorkflowRegistry */
  34. private $workflowRegistry;
  35. /** @var \Ibexa\Contracts\Core\Repository\PermissionResolver */
  36. private $permissionResolver;
  37. public function __construct(
  38. UserService $userService,
  39. WorkflowServiceInterface $workflowService,
  40. WorkflowRegistry $workflowRegistry,
  41. PermissionResolver $permissionResolver
  42. ) {
  43. $this->userService = $userService;
  44. $this->workflowService = $workflowService;
  45. $this->permissionResolver = $permissionResolver;
  46. $this->workflowRegistry = $workflowRegistry;
  47. }
  48. public static function getSubscribedEvents(): array
  49. {
  50. return [
  51. PostBuildViewEvent::class => 'onBuildContentEditView',
  52. BeforePublishVersionEvent::class => 'onBeforePublishVersion',
  53. ScheduledPublish::class => 'onScheduledPublish',
  54. CancelEditVersionDraftEvent::class => 'onCancelEditVersionDraft',
  55. 'workflow.entered' => 'onWorkflowEntered',
  56. DeleteUserEvent::class => 'onDeleteUser',
  57. DeleteContentEvent::class => 'onDeleteContent',
  58. DeleteVersionEvent::class => 'onDeleteVersion',
  59. ];
  60. }
  61. /**
  62. * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException
  63. * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException
  64. * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException
  65. * @throws \Ibexa\Workflow\Exception\VersionLockedException
  66. */
  67. public function onBuildContentEditView(PostBuildViewEvent $event): void
  68. {
  69. $view = $event->getView();
  70. if (!$view instanceof ContentEditView) {
  71. return;
  72. }
  73. $content = $view->getContent();
  74. $location = $view->getLocation();
  75. $workflows = $this->workflowRegistry->getSupportedWorkflows($content);
  76. if (!$this->hasNonInitialWorkflowPlace($workflows, $content)) {
  77. return;
  78. }
  79. if (!$this->permissionResolver->canUser('content', 'edit', $content, [$location])) {
  80. try {
  81. $versionLock = $this->workflowService->getVersionLock($content->versionInfo);
  82. $user = $this->userService->loadUser($versionLock->userId);
  83. throw new VersionLockedException($versionLock, $user);
  84. } catch (NotFoundException $exception) {
  85. // pass
  86. }
  87. } else {
  88. $this->workflowService->lockVersion($content->versionInfo);
  89. }
  90. }
  91. public function onCancelEditVersionDraft(CancelEditVersionDraftEvent $event): void
  92. {
  93. $this->unlockVersion($event->getContent()->versionInfo);
  94. }
  95. public function onDeleteContent(DeleteContentEvent $event)
  96. {
  97. $this->workflowService->deleteContentLocks($event->getContentInfo()->id);
  98. }
  99. public function onDeleteUser(DeleteUserEvent $event)
  100. {
  101. $this->workflowService->deleteUserLocks($event->getUser()->id);
  102. }
  103. public function onDeleteVersion(DeleteVersionEvent $event)
  104. {
  105. try {
  106. $this->workflowService->deleteLock(
  107. $this->workflowService->getVersionLock($event->getVersionInfo())
  108. );
  109. } catch (APINotFoundException $exception) {
  110. // pass
  111. }
  112. }
  113. public function onBeforePublishVersion(BeforePublishVersionEvent $event): void
  114. {
  115. $this->unlockVersion($event->getVersionInfo());
  116. }
  117. public function onScheduledPublish(ScheduledPublish $event)
  118. {
  119. $scheduledEntry = $event->getScheduledEntry();
  120. if (null !== $scheduledEntry->versionInfo) {
  121. $this->unlockVersion($scheduledEntry->versionInfo);
  122. }
  123. }
  124. public function onWorkflowEntered(EnteredEvent $event): void
  125. {
  126. $workflowName = $event->getWorkflowName();
  127. $subject = $event->getSubject();
  128. // guard is not needed for workflows not based on our content model
  129. if (!$subject instanceof Content || !$this->workflowRegistry->hasWorkflow($workflowName)) {
  130. return;
  131. }
  132. $this->unlockVersion($subject->versionInfo);
  133. }
  134. private function unlockVersion(VersionInfo $versionInfo): void
  135. {
  136. $isDraft = $versionInfo->isDraft();
  137. $canUnlock = $this->permissionResolver->canUser('content', 'unlock', $versionInfo);
  138. $isLocked = $this->workflowService->isVersionLocked($versionInfo);
  139. if ($isDraft && $isLocked && $canUnlock) {
  140. $this->workflowService->unlockVersion($versionInfo);
  141. }
  142. }
  143. private function hasNonInitialWorkflowPlace(array $workflows, Content $content): bool
  144. {
  145. foreach ($workflows as $workflow) {
  146. $marking = $workflow->getMarking($content);
  147. $places = array_keys($marking->getPlaces());
  148. $initialPlaces = $workflow->getDefinition()->getInitialPlaces();
  149. if (count(array_diff($places, $initialPlaces)) > 0) {
  150. return true;
  151. }
  152. }
  153. return false;
  154. }
  155. }