diff --git a/Classes/Application/ReloadNodes/ReloadNodesQueryHandler.php b/Classes/Application/ReloadNodes/ReloadNodesQueryHandler.php index b9d6ad520f..b53287e2d5 100644 --- a/Classes/Application/ReloadNodes/ReloadNodesQueryHandler.php +++ b/Classes/Application/ReloadNodes/ReloadNodesQueryHandler.php @@ -23,7 +23,6 @@ use Neos\Flow\Annotations as Flow; use Neos\Flow\Mvc\ActionRequest; use Neos\Neos\Domain\Service\NodeTypeNameFactory; -use Neos\Neos\Domain\Workspace\WorkspaceProvider; use Neos\Neos\FrontendRouting\NodeAddressFactory; use Neos\Neos\Ui\Fusion\Helper\NodeInfoHelper; @@ -36,9 +35,6 @@ #[Flow\Scope("singleton")] final class ReloadNodesQueryHandler { - #[Flow\Inject] - protected WorkspaceProvider $workspaceProvider; - #[Flow\Inject] protected ContentRepositoryRegistry $contentRepositoryRegistry; diff --git a/Classes/Application/SyncWorkspace/SyncWorkspaceCommandHandler.php b/Classes/Application/SyncWorkspace/SyncWorkspaceCommandHandler.php index 834837747e..48656206f1 100644 --- a/Classes/Application/SyncWorkspace/SyncWorkspaceCommandHandler.php +++ b/Classes/Application/SyncWorkspace/SyncWorkspaceCommandHandler.php @@ -18,7 +18,7 @@ use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry; use Neos\Flow\Annotations as Flow; use Neos\Neos\Domain\NodeLabel\NodeLabelGeneratorInterface; -use Neos\Neos\Domain\Workspace\WorkspaceProvider; +use Neos\Neos\Domain\Service\WorkspacePublishingService; /** * The application layer level command handler to for rebasing the workspace @@ -32,7 +32,7 @@ final class SyncWorkspaceCommandHandler protected ContentRepositoryRegistry $contentRepositoryRegistry; #[Flow\Inject] - protected WorkspaceProvider $workspaceProvider; + protected WorkspacePublishingService $workspacePublishingService; #[Flow\Inject] protected NodeLabelGeneratorInterface $nodeLabelGenerator; @@ -40,12 +40,11 @@ final class SyncWorkspaceCommandHandler public function handle(SyncWorkspaceCommand $command): void { try { - $workspace = $this->workspaceProvider->provideForWorkspaceName( + $this->workspacePublishingService->rebaseWorkspace( $command->contentRepositoryId, - $command->workspaceName + $command->workspaceName, + $command->rebaseErrorHandlingStrategy ); - - $workspace->rebase($command->rebaseErrorHandlingStrategy); } catch (WorkspaceRebaseFailed $e) { $conflictsBuilder = Conflicts::builder( contentRepository: $this->contentRepositoryRegistry diff --git a/Classes/ContentRepository/Service/WorkspaceService.php b/Classes/ContentRepository/Service/WorkspaceService.php index b1d71bfb65..479d2f077f 100644 --- a/Classes/ContentRepository/Service/WorkspaceService.php +++ b/Classes/ContentRepository/Service/WorkspaceService.php @@ -11,21 +11,18 @@ * source code. */ -use Neos\ContentRepository\Core\ContentRepository; -use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId; use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindClosestNodeFilter; use Neos\ContentRepository\Core\Projection\ContentGraph\Node; -use Neos\Neos\Domain\Service\NodeTypeNameFactory; -use Neos\Neos\FrontendRouting\NodeAddress; -use Neos\Neos\FrontendRouting\NodeAddressFactory; use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints; +use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry; use Neos\Flow\Annotations as Flow; -use Neos\Neos\Domain\Service\UserService as DomainUserService; +use Neos\Neos\Domain\Service\NodeTypeNameFactory; +use Neos\Neos\Domain\Service\WorkspacePublishingService; +use Neos\Neos\FrontendRouting\NodeAddress; +use Neos\Neos\FrontendRouting\NodeAddressFactory; use Neos\Neos\PendingChangesProjection\Change; -use Neos\Neos\PendingChangesProjection\ChangeFinder; -use Neos\Neos\Service\UserService; use Neos\Neos\Utility\NodeTypeWithFallbackProvider; /** @@ -44,17 +41,8 @@ class WorkspaceService #[Flow\Inject] protected ContentRepositoryRegistry $contentRepositoryRegistry; - /** - * @Flow\Inject - * @var UserService - */ - protected $userService; - - /** - * @Flow\Inject - * @var DomainUserService - */ - protected $domainUserService; + #[Flow\Inject] + protected WorkspacePublishingService $workspacePublishingService; /** * Get all publishable node context paths for a workspace @@ -64,15 +52,10 @@ class WorkspaceService public function getPublishableNodeInfo(WorkspaceName $workspaceName, ContentRepositoryId $contentRepositoryId): array { $contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId); - $workspace = $contentRepository->getWorkspaceFinder()->findOneByName($workspaceName); - if (is_null($workspace) || $workspace->baseWorkspaceName === null) { - return []; - } - $changeFinder = $contentRepository->projectionState(ChangeFinder::class); - $changes = $changeFinder->findByContentStreamId($workspace->currentContentStreamId); + $pendingChanges = $this->workspacePublishingService->pendingWorkspaceChanges($contentRepositoryId, $workspaceName); /** @var array{contextPath:string,documentContextPath:string,typeOfChange:int}[] $unpublishedNodes */ $unpublishedNodes = []; - foreach ($changes as $change) { + foreach ($pendingChanges as $change) { if ($change->removalAttachmentPoint) { $nodeAddress = new NodeAddress( $change->contentStreamId, @@ -106,7 +89,6 @@ public function getPublishableNodeInfo(WorkspaceName $workspaceName, ContentRepo if ($node instanceof Node) { $documentNode = $subgraph->findClosestNode($node->aggregateId, FindClosestNodeFilter::create(nodeTypes: NodeTypeNameFactory::NAME_DOCUMENT)); if ($documentNode instanceof Node) { - $contentRepository = $this->contentRepositoryRegistry->get($documentNode->contentRepositoryId); $nodeAddressFactory = NodeAddressFactory::create($contentRepository); $unpublishedNodes[] = [ 'contextPath' => $nodeAddressFactory->createFromNode($node)->serializeForUri(), @@ -124,44 +106,6 @@ public function getPublishableNodeInfo(WorkspaceName $workspaceName, ContentRepo })); } - /** - * Get allowed target workspaces for current user - * - * @return array> - */ - public function getAllowedTargetWorkspaces(ContentRepository $contentRepository): array - { - $user = $this->domainUserService->getCurrentUser(); - - $workspacesArray = []; - foreach ($contentRepository->getWorkspaceFinder()->findAll() as $workspace) { - // FIXME: This check should be implemented through a specialized Workspace Privilege or something similar - // Skip workspace not owned by current user - if ($workspace->workspaceOwner !== null && $workspace->workspaceOwner !== $user) { - continue; - } - // Skip own personal workspace - if ($workspace->workspaceName->value === $this->userService->getPersonalWorkspaceName()) { - continue; - } - - if ($workspace->isPersonalWorkspace()) { - // Skip other personal workspaces - continue; - } - - $workspaceArray = [ - 'name' => $workspace->workspaceName->jsonSerialize(), - 'title' => $workspace->workspaceTitle->jsonSerialize(), - 'description' => $workspace->workspaceDescription->jsonSerialize(), - 'readonly' => !$this->domainUserService->currentUserCanPublishToWorkspace($workspace) - ]; - $workspacesArray[$workspace->workspaceName->jsonSerialize()] = $workspaceArray; - } - - return $workspacesArray; - } - private function getTypeOfChange(Change $change): int { $result = 0; diff --git a/Classes/Controller/BackendController.php b/Classes/Controller/BackendController.php index 964e2e9c47..60dfb5b8dc 100644 --- a/Classes/Controller/BackendController.php +++ b/Classes/Controller/BackendController.php @@ -19,11 +19,10 @@ use Neos\Flow\Annotations as Flow; use Neos\Flow\Mvc\Controller\ActionController; use Neos\Flow\Persistence\PersistenceManagerInterface; -use Neos\Flow\Security\Context; use Neos\Neos\Domain\Repository\DomainRepository; use Neos\Neos\Domain\Repository\SiteRepository; use Neos\Neos\Domain\Service\NodeTypeNameFactory; -use Neos\Neos\Domain\Service\WorkspaceNameBuilder; +use Neos\Neos\Domain\Service\WorkspaceService; use Neos\Neos\FrontendRouting\NodeAddressFactory; use Neos\Neos\FrontendRouting\NodeUriBuilderFactory; use Neos\Neos\FrontendRouting\SiteDetection\SiteDetectionResult; @@ -78,12 +77,6 @@ class BackendController extends ActionController */ protected $contentRepositoryRegistry; - /** - * @Flow\Inject - * @var Context - */ - protected $securityContext; - /** * @Flow\Inject * @var ConfigurationProviderInterface @@ -126,6 +119,12 @@ class BackendController extends ActionController */ protected $nodeUriBuilderFactory; + /** + * @Flow\Inject + * @var WorkspaceService + */ + protected $workspaceService; + /** * Displays the backend interface * @@ -138,23 +137,19 @@ public function indexAction(string $node = null) $contentRepository = $this->contentRepositoryRegistry->get($siteDetectionResult->contentRepositoryId); $nodeAddress = $node !== null ? NodeAddressFactory::create($contentRepository)->createFromUriString($node) : null; - unset($node); $user = $this->userService->getBackendUser(); if ($user === null) { $this->redirectToUri($this->uriBuilder->uriFor('index', [], 'Login', 'Neos.Neos')); } - $currentAccount = $this->securityContext->getAccount(); - assert($currentAccount !== null); - $workspaceName = WorkspaceNameBuilder::fromAccountIdentifier($currentAccount->getAccountIdentifier()); - try { - $contentGraph = $contentRepository->getContentGraph($workspaceName); + $workspace = $this->workspaceService->getPersonalWorkspaceForUser($siteDetectionResult->contentRepositoryId, $user->getId()); } catch (WorkspaceDoesNotExist) { // todo will cause infinite loop: https://github.com/neos/neos-development-collection/issues/4401 $this->redirectToUri($this->uriBuilder->uriFor('index', [], 'Login', 'Neos.Neos')); } + $contentGraph = $contentRepository->getContentGraph($workspace->workspaceName); $backendControllerInternals = $this->contentRepositoryRegistry->buildService( $siteDetectionResult->contentRepositoryId, diff --git a/Classes/Controller/BackendServiceController.php b/Classes/Controller/BackendServiceController.php index 2ad2734943..faa6f7217f 100644 --- a/Classes/Controller/BackendServiceController.php +++ b/Classes/Controller/BackendServiceController.php @@ -17,7 +17,6 @@ use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint; use Neos\ContentRepository\Core\Feature\WorkspaceModification\Exception\WorkspaceIsNotEmptyException; use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Dto\RebaseErrorHandlingStrategy; -use Neos\ContentRepository\Core\Projection\ContentGraph\Node; use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints; use Neos\ContentRepository\Core\SharedModel\Exception\NodeAggregateCurrentlyDoesNotExist; use Neos\ContentRepository\Core\SharedModel\Exception\NodeAggregateDoesCurrentlyNotCoverDimensionSpacePoint; @@ -32,8 +31,8 @@ use Neos\Flow\Mvc\View\JsonView; use Neos\Flow\Property\PropertyMapper; use Neos\Flow\Security\Context; -use Neos\Neos\Domain\Service\WorkspaceNameBuilder; -use Neos\Neos\Domain\Workspace\WorkspaceProvider; +use Neos\Neos\Domain\Service\WorkspacePublishingService; +use Neos\Neos\Domain\Service\WorkspaceService; use Neos\Neos\FrontendRouting\NodeAddress; use Neos\Neos\FrontendRouting\NodeAddressFactory; use Neos\Neos\FrontendRouting\SiteDetection\SiteDetectionResult; @@ -136,9 +135,15 @@ class BackendServiceController extends ActionController /** * @Flow\Inject - * @var WorkspaceProvider + * @var WorkspaceService */ - protected $workspaceProvider; + protected $workspaceService; + + /** + * @Flow\Inject + * @var WorkspacePublishingService + */ + protected $workspacePublishingService; /** * @Flow\Inject @@ -170,10 +175,10 @@ public function changeAction(array $changes): void { $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; - $changes = $this->changeCollectionConverter->convert($changes, $contentRepositoryId); + $changeCollection = $this->changeCollectionConverter->convert($changes, $contentRepositoryId); try { - $count = $changes->count(); - $changes->apply(); + $count = $changeCollection->count(); + $changeCollection->apply(); $success = new Info(); $success->setMessage( @@ -207,17 +212,15 @@ public function publishChangesInSiteAction(array $command): void $contentRepositoryId )->nodeAggregateId->value; $command = PublishChangesInSite::fromArray($command); - $workspace = $this->workspaceProvider->provideForWorkspaceName( + $publishingResult = $this->workspacePublishingService->publishChangesInSite( $command->contentRepositoryId, - $command->workspaceName + $command->workspaceName, + $command->siteId, ); - $publishingResult = $workspace - ->publishChangesInSite($command->siteId); - $this->view->assign('value', [ 'success' => [ 'numberOfAffectedChanges' => $publishingResult->numberOfPublishedChanges, - 'baseWorkspaceName' => $workspace->getCurrentBaseWorkspaceName()?->value + 'baseWorkspaceName' => $publishingResult->targetWorkspaceName->value ] ]); } catch (\Exception $e) { @@ -249,19 +252,17 @@ public function publishChangesInDocumentAction(array $command): void )->nodeAggregateId->value; $command = PublishChangesInDocument::fromArray($command); - $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; - try { - $workspace = $this->workspaceProvider->provideForWorkspaceName( + $publishingResult = $this->workspacePublishingService->publishChangesInDocument( $command->contentRepositoryId, - $command->workspaceName + $command->workspaceName, + $command->documentId, ); - $publishingResult = $workspace->publishChangesInDocument($command->documentId); $this->view->assign('value', [ 'success' => [ 'numberOfAffectedChanges' => $publishingResult->numberOfPublishedChanges, - 'baseWorkspaceName' => $workspace->getCurrentBaseWorkspaceName()?->value + 'baseWorkspaceName' => $publishingResult->targetWorkspaceName->value, ] ]); } catch (NodeAggregateCurrentlyDoesNotExist $e) { @@ -302,11 +303,10 @@ public function discardAllChangesAction(array $command): void $command['contentRepositoryId'] = $contentRepositoryId->value; $command = DiscardAllChanges::fromArray($command); - $workspace = $this->workspaceProvider->provideForWorkspaceName( + $discardingResult = $this->workspacePublishingService->discardAllWorkspaceChanges( $command->contentRepositoryId, $command->workspaceName ); - $discardingResult = $workspace->discardAllChanges(); $this->view->assign('value', [ 'success' => [ @@ -342,11 +342,11 @@ public function discardChangesInSiteAction(array $command): void )->nodeAggregateId->value; $command = DiscardChangesInSite::fromArray($command); - $workspace = $this->workspaceProvider->provideForWorkspaceName( + $discardingResult = $this->workspacePublishingService->discardChangesInSite( $command->contentRepositoryId, - $command->workspaceName + $command->workspaceName, + $command->siteId ); - $discardingResult = $workspace->discardChangesInSite($command->siteId); $this->view->assign('value', [ 'success' => [ @@ -382,11 +382,11 @@ public function discardChangesInDocumentAction(array $command): void )->nodeAggregateId->value; $command = DiscardChangesInDocument::fromArray($command); - $workspace = $this->workspaceProvider->provideForWorkspaceName( + $discardingResult = $this->workspacePublishingService->discardChangesInDocument( $command->contentRepositoryId, - $command->workspaceName + $command->workspaceName, + $command->documentId ); - $discardingResult = $workspace->discardChangesInDocument($command->documentId); $this->view->assign('value', [ 'success' => [ @@ -420,26 +420,26 @@ public function changeBaseWorkspaceAction(string $targetWorkspaceName, string $d $nodeAddressFactory = NodeAddressFactory::create($contentRepository); - $currentAccount = $this->securityContext->getAccount(); - assert($currentAccount !== null); - $userWorkspaceName = WorkspaceNameBuilder::fromAccountIdentifier( - $currentAccount->getAccountIdentifier() - ); + $user = $this->userService->getBackendUser(); + if ($user === null) { + $error = new Error(); + $error->setMessage('No authenticated account'); + $this->feedbackCollection->add($error); + $this->view->assign('value', $this->feedbackCollection); + return; + } + $userWorkspace = $this->workspaceService->getPersonalWorkspaceForUser($contentRepositoryId, $user->getId()); /** @todo send from UI */ $command = new ChangeTargetWorkspace( $contentRepositoryId, - $userWorkspaceName, + $userWorkspace->workspaceName, WorkspaceName::fromString($targetWorkspaceName), $nodeAddressFactory->createFromUriString($documentNode) ); try { - $workspace = $this->workspaceProvider->provideForWorkspaceName( - $command->contentRepositoryId, - $command->workspaceName - ); - $workspace->changeBaseWorkspace($command->targetWorkspaceName); + $this->workspacePublishingService->changeBaseWorkspace($contentRepositoryId, $userWorkspace->workspaceName, WorkspaceName::fromString($targetWorkspaceName)); } catch (WorkspaceIsNotEmptyException $exception) { $error = new Error(); $error->setMessage( @@ -458,13 +458,14 @@ public function changeBaseWorkspaceAction(string $targetWorkspaceName, string $d return; } - $subgraph = $contentRepository->getContentGraph($workspace->name) + $subgraph = $contentRepository->getContentGraph($userWorkspace->workspaceName) ->getSubgraph( $command->documentNode->dimensionSpacePoint, VisibilityConstraints::withoutRestrictions() ); - $documentNode = $subgraph->findNodeById($command->documentNode->nodeAggregateId); + $documentNodeInstance = $subgraph->findNodeById($command->documentNode->nodeAggregateId); + assert($documentNodeInstance !== null); $success = new Success(); $success->setMessage( @@ -472,41 +473,36 @@ public function changeBaseWorkspaceAction(string $targetWorkspaceName, string $d ); $this->feedbackCollection->add($success); - $updateWorkspaceInfo = new UpdateWorkspaceInfo($contentRepositoryId, $userWorkspaceName); + $updateWorkspaceInfo = new UpdateWorkspaceInfo($contentRepositoryId, $userWorkspace->workspaceName); $this->feedbackCollection->add($updateWorkspaceInfo); // If current document node doesn't exist in the base workspace, // traverse its parents to find the one that exists // todo ensure that https://github.com/neos/neos-ui/pull/3734 doesnt need to be refixed in Neos 9.0 - $redirectNode = $documentNode; + $redirectNode = $documentNodeInstance; while (true) { - // @phpstan-ignore-next-line $redirectNodeInBaseWorkspace = $subgraph->findNodeById($redirectNode->aggregateId); if ($redirectNodeInBaseWorkspace) { break; - } else { - // @phpstan-ignore-next-line - $redirectNode = $subgraph->findParentNode($redirectNode->aggregateId); - // get parent always returns Node - if (!$redirectNode) { - throw new \Exception( - sprintf( - 'Wasn\'t able to locate any valid node in rootline of node %s in the workspace %s.', - $documentNode?->aggregateId->value, - $targetWorkspaceName - ), - 1458814469 - ); - } + } + $redirectNode = $subgraph->findParentNode($redirectNode->aggregateId); + // get parent always returns Node + if (!$redirectNode) { + throw new \Exception( + sprintf( + 'Wasn\'t able to locate any valid node in rootline of node %s in the workspace %s.', + $documentNodeInstance->aggregateId->value, + $targetWorkspaceName + ), + 1458814469 + ); } } - /** @var Node $documentNode */ - /** @var Node $redirectNode */ // If current document node exists in the base workspace, then reload, else redirect - if ($redirectNode->equals($documentNode)) { + if ($redirectNode->equals($documentNodeInstance)) { $reloadDocument = new ReloadDocument(); - $reloadDocument->setNode($documentNode); + $reloadDocument->setNode($documentNodeInstance); $this->feedbackCollection->add($reloadDocument); } else { $redirect = new Redirect(); @@ -575,8 +571,7 @@ public function cutNodesAction(array $nodes): void public function getWorkspaceInfoAction(): void { $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; - $workspaceHelper = new WorkspaceHelper(); - $personalWorkspaceInfo = $workspaceHelper->getPersonalWorkspace($contentRepositoryId); + $personalWorkspaceInfo = (new WorkspaceHelper())->getPersonalWorkspace($contentRepositoryId); $this->view->assign('value', $personalWorkspaceInfo); } diff --git a/Classes/Domain/Model/Feedback/Operations/UpdateWorkspaceInfo.php b/Classes/Domain/Model/Feedback/Operations/UpdateWorkspaceInfo.php index 53577a573b..7fd86f0961 100644 --- a/Classes/Domain/Model/Feedback/Operations/UpdateWorkspaceInfo.php +++ b/Classes/Domain/Model/Feedback/Operations/UpdateWorkspaceInfo.php @@ -11,15 +11,14 @@ * source code. */ -use Neos\ContentRepository\Core\Projection\Workspace\Workspace; -use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId; +use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; +use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry; use Neos\Flow\Annotations as Flow; -use Neos\Neos\Ui\ContentRepository\Service\WorkspaceService; +use Neos\Flow\Mvc\Controller\ControllerContext; +use Neos\Neos\Ui\ContentRepository\Service\WorkspaceService as UiWorkspaceService; use Neos\Neos\Ui\Domain\Model\AbstractFeedback; use Neos\Neos\Ui\Domain\Model\FeedbackInterface; -use Neos\Flow\Mvc\Controller\ControllerContext; -use Neos\Neos\Domain\Workspace\WorkspaceProvider; /** * @internal @@ -28,15 +27,15 @@ class UpdateWorkspaceInfo extends AbstractFeedback { /** * @Flow\Inject - * @var WorkspaceService + * @var ContentRepositoryRegistry */ - protected $workspaceService; + protected $contentRepositoryRegistry; /** * @Flow\Inject - * @var WorkspaceProvider + * @var UiWorkspaceService */ - protected $workspaceProvider; + protected $uiWorkspaceService; /** * UpdateWorkspaceInfo constructor. @@ -87,8 +86,8 @@ public function isSimilarTo(FeedbackInterface $feedback) if (!$feedback instanceof UpdateWorkspaceInfo) { return false; } - - return $this->getWorkspaceName()->equals($feedback->getWorkspaceName()); + $feedbackWorkspaceName = $feedback->getWorkspaceName(); + return $feedbackWorkspaceName !== null && $this->getWorkspaceName()->equals($feedbackWorkspaceName); } /** @@ -99,21 +98,18 @@ public function isSimilarTo(FeedbackInterface $feedback) */ public function serializePayload(ControllerContext $controllerContext) { - $workspace = $this->workspaceProvider->provideForWorkspaceName( - $this->contentRepositoryId, - $this->workspaceName - ); - $totalNumberOfChanges = $workspace->countAllChanges(); - + $contentRepository = $this->contentRepositoryRegistry->get($this->contentRepositoryId); + $workspace = $contentRepository->getWorkspaceFinder()->findOneByName($this->workspaceName); + if ($workspace === null) { + return null; + } + $publishableNodes = $this->uiWorkspaceService->getPublishableNodeInfo($workspace->workspaceName, $contentRepository->id); return [ 'name' => $this->workspaceName->value, - 'totalNumberOfChanges' => $totalNumberOfChanges, - 'publishableNodes' => $this->workspaceService->getPublishableNodeInfo( - $this->workspaceName, - $this->contentRepositoryId - ), - 'baseWorkspace' => $workspace->getCurrentBaseWorkspaceName()?->value, - 'status' => $workspace->getCurrentStatus() + 'totalNumberOfChanges' => count($publishableNodes), + 'publishableNodes' => $publishableNodes, + 'baseWorkspace' => $workspace->baseWorkspaceName?->value, + 'status' => $workspace->status->value, ]; } } diff --git a/Classes/Fusion/Helper/WorkspaceHelper.php b/Classes/Fusion/Helper/WorkspaceHelper.php index c22e35837f..a3b316ff22 100644 --- a/Classes/Fusion/Helper/WorkspaceHelper.php +++ b/Classes/Fusion/Helper/WorkspaceHelper.php @@ -16,13 +16,12 @@ use Neos\Eel\ProtectedContextAwareInterface; use Neos\Flow\Annotations as Flow; use Neos\Flow\Security\Context; -use Neos\Neos\Domain\Service\WorkspaceNameBuilder; -use Neos\Neos\Domain\Workspace\WorkspaceProvider; -use Neos\Neos\Ui\ContentRepository\Service\WorkspaceService; +use Neos\Neos\Domain\Service\UserService; +use Neos\Neos\Domain\Service\WorkspaceService; +use Neos\Neos\Ui\ContentRepository\Service\WorkspaceService as UiWorkspaceService; /** - * @internal implementation detail of the Neos Ui to build its initialState. - * and used for the workspace-info endpoint. + * @internal implementation detail of the Neos Ui to build its initialState {@see \Neos\Neos\Ui\Infrastructure\Configuration\InitialStateProvider} */ class WorkspaceHelper implements ProtectedContextAwareInterface { @@ -34,46 +33,48 @@ class WorkspaceHelper implements ProtectedContextAwareInterface /** * @Flow\Inject - * @var WorkspaceService + * @var Context */ - protected $workspaceService; + protected $securityContext; /** * @Flow\Inject - * @var Context + * @var UiWorkspaceService */ - protected $securityContext; + protected $uiWorkspaceService; + + /** + * @Flow\Inject + * @var UserService + */ + protected $userService; /** * @Flow\Inject - * @var WorkspaceProvider + * @var WorkspaceService */ - protected $workspaceProvider; + protected $workspaceService; /** * @return array */ public function getPersonalWorkspace(ContentRepositoryId $contentRepositoryId): array { - $currentAccount = $this->securityContext->getAccount(); - assert($currentAccount !== null); - // todo use \Neos\Neos\Service\UserService::getPersonalWorkspaceName instead? - $personalWorkspaceName = WorkspaceNameBuilder::fromAccountIdentifier($currentAccount->getAccountIdentifier()); - - $workspace = $this->workspaceProvider->provideForWorkspaceName( - $contentRepositoryId, - $personalWorkspaceName - ); - + $currentUser = $this->userService->getCurrentUser(); + if ($currentUser === null) { + return []; + } + $contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId); + $personalWorkspace = $this->workspaceService->getPersonalWorkspaceForUser($contentRepositoryId, $currentUser->getId()); + $personalWorkspacePermissions = $this->workspaceService->getWorkspacePermissionsForUser($contentRepositoryId, $personalWorkspace->workspaceName, $currentUser); + $publishableNodes = $this->uiWorkspaceService->getPublishableNodeInfo($personalWorkspace->workspaceName, $contentRepository->id); return [ - 'name' => $workspace->name, - 'totalNumberOfChanges' => $workspace->countAllChanges(), - 'publishableNodes' => $this->workspaceService->getPublishableNodeInfo($personalWorkspaceName, $contentRepositoryId), - 'baseWorkspace' => $workspace->getCurrentBaseWorkspaceName(), - // TODO: FIX readonly flag! - //'readOnly' => !$this->domainUserService->currentUserCanPublishToWorkspace($baseWorkspace) - 'readOnly' => false, - 'status' => $workspace->getCurrentStatus() + 'name' => $personalWorkspace->workspaceName->value, + 'totalNumberOfChanges' => count($publishableNodes), + 'publishableNodes' => $publishableNodes, + 'baseWorkspace' => $personalWorkspace->baseWorkspaceName?->value, + 'readOnly' => !($personalWorkspace->baseWorkspaceName !== null && $personalWorkspacePermissions->write), + 'status' => $personalWorkspace->status->value, ]; } diff --git a/Classes/Infrastructure/Configuration/ConfigurationProvider.php b/Classes/Infrastructure/Configuration/ConfigurationProvider.php index 36e4410d53..f18d4e05f3 100644 --- a/Classes/Infrastructure/Configuration/ConfigurationProvider.php +++ b/Classes/Infrastructure/Configuration/ConfigurationProvider.php @@ -18,8 +18,9 @@ use Neos\Flow\Annotations as Flow; use Neos\Flow\Configuration\ConfigurationManager; use Neos\Flow\Mvc\Routing\UriBuilder; +use Neos\Neos\Domain\Model\WorkspaceClassification; +use Neos\Neos\Domain\Service\WorkspaceService; use Neos\Neos\Service\UserService; -use Neos\Neos\Ui\ContentRepository\Service\WorkspaceService; use Neos\Neos\Ui\Domain\InitialData\CacheConfigurationVersionProviderInterface; use Neos\Neos\Ui\Domain\InitialData\ConfigurationProviderInterface; @@ -54,10 +55,7 @@ public function getConfiguration( ConfigurationManager::CONFIGURATION_TYPE_SETTINGS, 'Neos.Neos.userInterface.navigateComponent.structureTree', ), - 'allowedTargetWorkspaces' => - $this->workspaceService->getAllowedTargetWorkspaces( - $contentRepository - ), + 'allowedTargetWorkspaces' => $this->getAllowedTargetWorkspaces($contentRepository), 'endpoints' => [ 'nodeTypeSchema' => $uriBuilder->reset() ->setCreateAbsoluteUri(true) @@ -89,4 +87,32 @@ public function getConfiguration( ] ]; } + + /** + * @return array + */ + private function getAllowedTargetWorkspaces(ContentRepository $contentRepository): array + { + $backendUser = $this->userService->getBackendUser(); + if ($backendUser === null) { + return []; + } + $result = []; + foreach ($contentRepository->getWorkspaceFinder()->findAll() as $workspace) { + $workspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepository->id, $workspace->workspaceName); + if (!in_array($workspaceMetadata->classification, [WorkspaceClassification::ROOT, WorkspaceClassification::SHARED], true)) { + continue; + } + $workspacePermissions = $this->workspaceService->getWorkspacePermissionsForUser($contentRepository->id, $workspace->workspaceName, $backendUser); + if ($workspacePermissions->read === false) { + continue; + } + $result[$workspace->workspaceName->value] = [ + 'name' => $workspace->workspaceName->value, + 'title' => $workspaceMetadata->title->value, + 'readonly' => !$workspacePermissions->write, + ]; + } + return $result; + } } diff --git a/Tests/IntegrationTests/TestDistribution/composer.json b/Tests/IntegrationTests/TestDistribution/composer.json index 24e9fb8c31..d3cb3c304c 100644 --- a/Tests/IntegrationTests/TestDistribution/composer.json +++ b/Tests/IntegrationTests/TestDistribution/composer.json @@ -14,7 +14,7 @@ "require": { "neos/flow-development-collection": "9.0.x-dev as 9.0", - "neos/neos-development-collection": "9.0.x-dev as 9.0", + "neos/neos-development-collection": "feature/4726-extract-workspace-metadata-and-user-assignment-to-neos-dev as 9.0", "neos/neos-ui": "9.0.x-dev as 9.0", "neos/neos-ui-compiled": "9.0.x-dev as 9.0",