From d588fbcdd725fcdb80a7f47ed85e4a28b5e97f21 Mon Sep 17 00:00:00 2001 From: Bastian Waidelich Date: Fri, 9 Aug 2024 14:12:07 +0200 Subject: [PATCH 01/11] Draft: FEATURE: Extract workspace metadata and user-assignment to Neos Counter-part to https://github.com/neos/neos-development-collection/pull/5146 --- .../ReloadNodes/ReloadNodesQueryHandler.php | 3 - .../SyncWorkspaceCommandHandler.php | 11 +- .../Service/WorkspaceService.php | 12 +- Classes/Controller/BackendController.php | 23 ++-- .../Controller/BackendServiceController.php | 124 +++++++----------- Classes/Domain/Model/Changes/Property.php | 3 +- .../Operations/UpdateWorkspaceInfo.php | 36 ++--- Classes/Fusion/Helper/WorkspaceHelper.php | 53 ++++---- 8 files changed, 125 insertions(+), 140 deletions(-) diff --git a/Classes/Application/ReloadNodes/ReloadNodesQueryHandler.php b/Classes/Application/ReloadNodes/ReloadNodesQueryHandler.php index b9d6ad520f..c0f9929e02 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,8 +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..85253b568e 100644 --- a/Classes/ContentRepository/Service/WorkspaceService.php +++ b/Classes/ContentRepository/Service/WorkspaceService.php @@ -56,6 +56,12 @@ class WorkspaceService */ protected $domainUserService; + /** + * @Flow\Inject + * @var \Neos\Neos\Domain\Service\WorkspaceService + */ + protected $neosWorkspaceService; + /** * Get all publishable node context paths for a workspace * @@ -132,6 +138,9 @@ public function getPublishableNodeInfo(WorkspaceName $workspaceName, ContentRepo public function getAllowedTargetWorkspaces(ContentRepository $contentRepository): array { $user = $this->domainUserService->getCurrentUser(); + if ($user === null) { + return []; + } $workspacesArray = []; foreach ($contentRepository->getWorkspaceFinder()->findAll() as $workspace) { @@ -149,12 +158,13 @@ public function getAllowedTargetWorkspaces(ContentRepository $contentRepository) // Skip other personal workspaces continue; } + $workspacePermissions = $this->neosWorkspaceService->getWorkspacePermissionsForUser($contentRepository->id, $workspace->workspaceName, $user); $workspaceArray = [ 'name' => $workspace->workspaceName->jsonSerialize(), 'title' => $workspace->workspaceTitle->jsonSerialize(), 'description' => $workspace->workspaceDescription->jsonSerialize(), - 'readonly' => !$this->domainUserService->currentUserCanPublishToWorkspace($workspace) + 'readonly' => !$workspacePermissions->write, ]; $workspacesArray[$workspace->workspaceName->jsonSerialize()] = $workspaceArray; } diff --git a/Classes/Controller/BackendController.php b/Classes/Controller/BackendController.php index a70a803b48..bac47b7357 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 4454e852eb..3ff4e2cf50 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; @@ -33,17 +32,12 @@ 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\FrontendRouting\NodeAddress; use Neos\Neos\FrontendRouting\NodeAddressFactory; use Neos\Neos\FrontendRouting\SiteDetection\SiteDetectionResult; use Neos\Neos\Service\UserService; use Neos\Neos\Ui\Application\ChangeTargetWorkspace; -use Neos\Neos\Ui\Application\DiscardAllChanges; -use Neos\Neos\Ui\Application\DiscardChangesInDocument; -use Neos\Neos\Ui\Application\DiscardChangesInSite; -use Neos\Neos\Ui\Application\PublishChangesInDocument; -use Neos\Neos\Ui\Application\PublishChangesInSite; use Neos\Neos\Ui\Application\ReloadNodes\ReloadNodesQuery; use Neos\Neos\Ui\Application\ReloadNodes\ReloadNodesQueryHandler; use Neos\Neos\Ui\Application\SyncWorkspace\ConflictsOccurred; @@ -136,9 +130,9 @@ class BackendServiceController extends ActionController /** * @Flow\Inject - * @var WorkspaceProvider + * @var WorkspacePublishingService */ - protected $workspaceProvider; + protected $workspacePublishingService; /** * @Flow\Inject @@ -164,16 +158,16 @@ protected function initializeController(ActionRequest $request, ActionResponse $ /** * Apply a set of changes to the system - * @psalm-param list> $changes + * @psalm-param list> $changeCollection */ 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( @@ -201,23 +195,19 @@ public function publishChangesInSiteAction(array $command): void try { /** @todo send from UI */ $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; - $command['contentRepositoryId'] = $contentRepositoryId->value; - $command['siteId'] = $this->nodeService->deserializeNodeAddress( + $siteNodeAddress = $this->nodeService->deserializeNodeAddress( $command['siteId'], $contentRepositoryId - )->nodeAggregateId->value; - $command = PublishChangesInSite::fromArray($command); - $workspace = $this->workspaceProvider->provideForWorkspaceName( - $command->contentRepositoryId, - $command->workspaceName ); - $publishingResult = $workspace - ->publishChangesInSite($command->siteId); - + $publishingResult = $this->workspacePublishingService->publishChangesInSite( + $contentRepositoryId, + $siteNodeAddress->workspaceName, + $siteNodeAddress->nodeAggregateId, + ); $this->view->assign('value', [ 'success' => [ 'numberOfAffectedChanges' => $publishingResult->numberOfPublishedChanges, - 'baseWorkspaceName' => $workspace->getCurrentBaseWorkspaceName()?->value + 'baseWorkspaceName' => $publishingResult->targetWorkspaceName->value ] ]); } catch (\Exception $e) { @@ -242,26 +232,21 @@ public function publishChangesInDocumentAction(array $command): void try { /** @todo send from UI */ $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; - $command['contentRepositoryId'] = $contentRepositoryId->value; - $command['documentId'] = $this->nodeService->deserializeNodeAddress( + $documentNodeAddress = $this->nodeService->deserializeNodeAddress( $command['documentId'], $contentRepositoryId - )->nodeAggregateId->value; - $command = PublishChangesInDocument::fromArray($command); - - $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; - + ); try { - $workspace = $this->workspaceProvider->provideForWorkspaceName( - $command->contentRepositoryId, - $command->workspaceName + $publishingResult = $this->workspacePublishingService->publishChangesInDocument( + $contentRepositoryId, + $documentNodeAddress->workspaceName, + $documentNodeAddress->nodeAggregateId, ); - $publishingResult = $workspace->publishChangesInDocument($command->documentId); $this->view->assign('value', [ 'success' => [ 'numberOfAffectedChanges' => $publishingResult->numberOfPublishedChanges, - 'baseWorkspaceName' => $workspace->getCurrentBaseWorkspaceName()?->value + 'baseWorkspaceName' => $publishingResult->targetWorkspaceName->value, ] ]); } catch (NodeAggregateCurrentlyDoesNotExist $e) { @@ -299,15 +284,9 @@ public function discardAllChangesAction(array $command): void try { /** @todo send from UI */ $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; - $command['contentRepositoryId'] = $contentRepositoryId->value; - $command = DiscardAllChanges::fromArray($command); - - $workspace = $this->workspaceProvider->provideForWorkspaceName( - $command->contentRepositoryId, - $command->workspaceName - ); - $discardingResult = $workspace->discardAllChanges(); + $workspaceName = WorkspaceName::fromString($command['workspaceName']); + $discardingResult = $this->workspacePublishingService->discardAllWorkspaceChanges($contentRepositoryId, $workspaceName); $this->view->assign('value', [ 'success' => [ 'numberOfAffectedChanges' => $discardingResult->numberOfDiscardedChanges @@ -335,18 +314,16 @@ public function discardChangesInSiteAction(array $command): void try { /** @todo send from UI */ $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; - $command['contentRepositoryId'] = $contentRepositoryId->value; - $command['siteId'] = $this->nodeService->deserializeNodeAddress( + $siteNodeAddress = $this->nodeService->deserializeNodeAddress( $command['siteId'], $contentRepositoryId - )->nodeAggregateId->value; - $command = DiscardChangesInSite::fromArray($command); + ); - $workspace = $this->workspaceProvider->provideForWorkspaceName( - $command->contentRepositoryId, - $command->workspaceName + $discardingResult = $this->workspacePublishingService->discardChangesInSite( + $contentRepositoryId, + $siteNodeAddress->workspaceName, + $siteNodeAddress->nodeAggregateId ); - $discardingResult = $workspace->discardChangesInSite($command->siteId); $this->view->assign('value', [ 'success' => [ @@ -375,18 +352,11 @@ public function discardChangesInDocumentAction(array $command): void try { /** @todo send from UI */ $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; - $command['contentRepositoryId'] = $contentRepositoryId->value; - $command['documentId'] = $this->nodeService->deserializeNodeAddress( + $documentNodeAddress = $this->nodeService->deserializeNodeAddress( $command['documentId'], $contentRepositoryId - )->nodeAggregateId->value; - $command = DiscardChangesInDocument::fromArray($command); - - $workspace = $this->workspaceProvider->provideForWorkspaceName( - $command->contentRepositoryId, - $command->workspaceName ); - $discardingResult = $workspace->discardChangesInDocument($command->documentId); + $discardingResult = $this->workspacePublishingService->discardChangesInDocument($contentRepositoryId, $documentNodeAddress->workspaceName, $documentNodeAddress->nodeAggregateId); $this->view->assign('value', [ 'success' => [ @@ -421,7 +391,13 @@ public function changeBaseWorkspaceAction(string $targetWorkspaceName, string $d $nodeAddressFactory = NodeAddressFactory::create($contentRepository); $currentAccount = $this->securityContext->getAccount(); - assert($currentAccount !== null); + if ($currentAccount === null) { + $error = new Error(); + $error->setMessage('No authenticated account'); + $this->feedbackCollection->add($error); + $this->view->assign('value', $this->feedbackCollection); + return; + } $userWorkspaceName = WorkspaceNameBuilder::fromAccountIdentifier( $currentAccount->getAccountIdentifier() ); @@ -435,11 +411,7 @@ public function changeBaseWorkspaceAction(string $targetWorkspaceName, string $d ); try { - $workspace = $this->workspaceProvider->provideForWorkspaceName( - $command->contentRepositoryId, - $command->workspaceName - ); - $workspace->changeBaseWorkspace($command->targetWorkspaceName); + $this->workspacePublishingService->changeBaseWorkspace($contentRepositoryId, $userWorkspaceName, WorkspaceName::fromString($targetWorkspaceName)); } catch (WorkspaceIsNotEmptyException $exception) { $error = new Error(); $error->setMessage( @@ -458,13 +430,14 @@ public function changeBaseWorkspaceAction(string $targetWorkspaceName, string $d return; } - $subgraph = $contentRepository->getContentGraph($workspace->name) + $subgraph = $contentRepository->getContentGraph($userWorkspaceName) ->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( @@ -478,7 +451,7 @@ public function changeBaseWorkspaceAction(string $targetWorkspaceName, string $d // 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); @@ -492,7 +465,7 @@ public function changeBaseWorkspaceAction(string $targetWorkspaceName, string $d throw new \Exception( sprintf( 'Wasn\'t able to locate any valid node in rootline of node %s in the workspace %s.', - $documentNode?->aggregateId->value, + $documentNodeInstance->aggregateId->value, $targetWorkspaceName ), 1458814469 @@ -500,13 +473,11 @@ public function changeBaseWorkspaceAction(string $targetWorkspaceName, string $d } } } - /** @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 +546,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/Changes/Property.php b/Classes/Domain/Model/Changes/Property.php index dbff01dffc..bac13bd64c 100644 --- a/Classes/Domain/Model/Changes/Property.php +++ b/Classes/Domain/Model/Changes/Property.php @@ -239,8 +239,7 @@ private function handleNodeReferenceChange(Node $subject, string $propertyName): $subject->workspaceName, $subject->aggregateId, $subject->originDimensionSpacePoint, - ReferenceName::fromString($propertyName), - NodeReferencesToWrite::fromNodeAggregateIds(NodeAggregateIds::fromArray($destinationNodeAggregateIds)) + NodeReferencesToWrite::fromNodeAggregateIds(ReferenceName::fromString($propertyName), NodeAggregateIds::fromArray($destinationNodeAggregateIds)) ) ); } diff --git a/Classes/Domain/Model/Feedback/Operations/UpdateWorkspaceInfo.php b/Classes/Domain/Model/Feedback/Operations/UpdateWorkspaceInfo.php index 53577a573b..82762f33f8 100644 --- a/Classes/Domain/Model/Feedback/Operations/UpdateWorkspaceInfo.php +++ b/Classes/Domain/Model/Feedback/Operations/UpdateWorkspaceInfo.php @@ -11,15 +11,15 @@ * 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\Flow\Mvc\Controller\ControllerContext; +use Neos\Neos\PendingChangesProjection\ChangeFinder; use Neos\Neos\Ui\ContentRepository\Service\WorkspaceService; 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 @@ -34,9 +34,9 @@ class UpdateWorkspaceInfo extends AbstractFeedback /** * @Flow\Inject - * @var WorkspaceProvider + * @var ContentRepositoryRegistry */ - protected $workspaceProvider; + protected $contentRepositoryRegistry; /** * UpdateWorkspaceInfo constructor. @@ -87,8 +87,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,11 +99,17 @@ public function isSimilarTo(FeedbackInterface $feedback) */ public function serializePayload(ControllerContext $controllerContext) { - $workspace = $this->workspaceProvider->provideForWorkspaceName( - $this->contentRepositoryId, - $this->workspaceName - ); - $totalNumberOfChanges = $workspace->countAllChanges(); + if (!$this->workspaceName) { + return null; + } + $contentRepository = $this->contentRepositoryRegistry->get($this->contentRepositoryId); + $workspace = $contentRepository->findWorkspaceByName($this->workspaceName); + if ($workspace === null) { + return null; + } + /** @var ChangeFinder $changeFinder */ + $changeFinder = $contentRepository->projectionState(ChangeFinder::class); + $totalNumberOfChanges = $changeFinder->countByContentStreamId($workspace->currentContentStreamId); return [ 'name' => $this->workspaceName->value, @@ -112,8 +118,8 @@ public function serializePayload(ControllerContext $controllerContext) $this->workspaceName, $this->contentRepositoryId ), - 'baseWorkspace' => $workspace->getCurrentBaseWorkspaceName()?->value, - 'status' => $workspace->getCurrentStatus() + 'baseWorkspace' => $workspace->baseWorkspaceName?->value, + 'status' => $workspace->status->value, ]; } } diff --git a/Classes/Fusion/Helper/WorkspaceHelper.php b/Classes/Fusion/Helper/WorkspaceHelper.php index c22e35837f..586373e708 100644 --- a/Classes/Fusion/Helper/WorkspaceHelper.php +++ b/Classes/Fusion/Helper/WorkspaceHelper.php @@ -16,13 +16,16 @@ 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\Domain\Service\UserService; +use Neos\Neos\Domain\Service\WorkspacePublishingService; +use Neos\Neos\Domain\Service\WorkspaceService as NeosWorkspaceService; use Neos\Neos\Ui\ContentRepository\Service\WorkspaceService; /** * @internal implementation detail of the Neos Ui to build its initialState. * and used for the workspace-info endpoint. + * + * TODO REMOVE */ class WorkspaceHelper implements ProtectedContextAwareInterface { @@ -46,34 +49,40 @@ class WorkspaceHelper implements ProtectedContextAwareInterface /** * @Flow\Inject - * @var WorkspaceProvider + * @var WorkspacePublishingService + */ + protected $workspacePublishingService; + + /** + * @Flow\Inject + * @var UserService */ - protected $workspaceProvider; + protected $userService; + + /** + * @Flow\Inject + * @var NeosWorkspaceService + */ + protected $neosWorkspaceService; /** * @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 []; + } + $personalWorkspace = $this->neosWorkspaceService->getPersonalWorkspaceForUser($contentRepositoryId, $currentUser->getId()); + $personalWorkspacePermissions = $this->neosWorkspaceService->getWorkspacePermissionsForUser($contentRepositoryId, $personalWorkspace->workspaceName, $currentUser); 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' => $this->workspacePublishingService->countPendingWorkspaceChanges($contentRepositoryId, $personalWorkspace->workspaceName), + 'publishableNodes' => $this->workspaceService->getPublishableNodeInfo($personalWorkspace->workspaceName, $contentRepositoryId), + 'baseWorkspace' => $personalWorkspace->baseWorkspaceName?->value, + 'readOnly' => !($personalWorkspace->baseWorkspaceName !== null && $personalWorkspacePermissions->write), + 'status' => $personalWorkspace->status->value, ]; } From 9788dce6f8146517d95eaf8328827868bf4967d9 Mon Sep 17 00:00:00 2001 From: Bastian Waidelich Date: Fri, 6 Sep 2024 10:44:53 +0200 Subject: [PATCH 02/11] wip --- .../Controller/BackendServiceController.php | 27 +++++++++---------- Classes/Domain/Model/Changes/Property.php | 3 ++- .../Operations/UpdateWorkspaceInfo.php | 7 ++--- 3 files changed, 16 insertions(+), 21 deletions(-) diff --git a/Classes/Controller/BackendServiceController.php b/Classes/Controller/BackendServiceController.php index 3272758128..e53ef393e9 100644 --- a/Classes/Controller/BackendServiceController.php +++ b/Classes/Controller/BackendServiceController.php @@ -453,24 +453,21 @@ public function changeBaseWorkspaceAction(string $targetWorkspaceName, string $d // todo ensure that https://github.com/neos/neos-ui/pull/3734 doesnt need to be refixed in Neos 9.0 $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.', - $documentNodeInstance->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 + ); } } diff --git a/Classes/Domain/Model/Changes/Property.php b/Classes/Domain/Model/Changes/Property.php index bac13bd64c..dbff01dffc 100644 --- a/Classes/Domain/Model/Changes/Property.php +++ b/Classes/Domain/Model/Changes/Property.php @@ -239,7 +239,8 @@ private function handleNodeReferenceChange(Node $subject, string $propertyName): $subject->workspaceName, $subject->aggregateId, $subject->originDimensionSpacePoint, - NodeReferencesToWrite::fromNodeAggregateIds(ReferenceName::fromString($propertyName), NodeAggregateIds::fromArray($destinationNodeAggregateIds)) + ReferenceName::fromString($propertyName), + NodeReferencesToWrite::fromNodeAggregateIds(NodeAggregateIds::fromArray($destinationNodeAggregateIds)) ) ); } diff --git a/Classes/Domain/Model/Feedback/Operations/UpdateWorkspaceInfo.php b/Classes/Domain/Model/Feedback/Operations/UpdateWorkspaceInfo.php index 82762f33f8..28d7cd2433 100644 --- a/Classes/Domain/Model/Feedback/Operations/UpdateWorkspaceInfo.php +++ b/Classes/Domain/Model/Feedback/Operations/UpdateWorkspaceInfo.php @@ -88,7 +88,7 @@ public function isSimilarTo(FeedbackInterface $feedback) return false; } $feedbackWorkspaceName = $feedback->getWorkspaceName(); - return $feedbackWorkspaceName !== null && $this->getWorkspaceName()?->equals($feedbackWorkspaceName); + return $feedbackWorkspaceName !== null && $this->getWorkspaceName()->equals($feedbackWorkspaceName); } /** @@ -99,11 +99,8 @@ public function isSimilarTo(FeedbackInterface $feedback) */ public function serializePayload(ControllerContext $controllerContext) { - if (!$this->workspaceName) { - return null; - } $contentRepository = $this->contentRepositoryRegistry->get($this->contentRepositoryId); - $workspace = $contentRepository->findWorkspaceByName($this->workspaceName); + $workspace = $contentRepository->getWorkspaceFinder()->findOneByName($this->workspaceName); if ($workspace === null) { return null; } From 3aefeb2315e2af3d8d02d2e3d32013dbf07b5601 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Tue, 17 Sep 2024 12:27:01 +0200 Subject: [PATCH 03/11] WIP REVERT ME, PATCH E2E --- Tests/IntegrationTests/TestDistribution/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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", From bd04359867633d5eb73ff8bf6e480e018beed46d Mon Sep 17 00:00:00 2001 From: Bastian Waidelich Date: Wed, 25 Sep 2024 17:58:55 +0200 Subject: [PATCH 04/11] Remove Neos UI `WorkspaceService` --- .../Service/WorkspaceService.php | 197 ------------------ .../Operations/UpdateWorkspaceInfo.php | 19 +- Classes/Fusion/Helper/WorkspaceHelper.php | 26 +-- .../Configuration/ConfigurationProvider.php | 33 ++- 4 files changed, 44 insertions(+), 231 deletions(-) delete mode 100644 Classes/ContentRepository/Service/WorkspaceService.php diff --git a/Classes/ContentRepository/Service/WorkspaceService.php b/Classes/ContentRepository/Service/WorkspaceService.php deleted file mode 100644 index 85253b568e..0000000000 --- a/Classes/ContentRepository/Service/WorkspaceService.php +++ /dev/null @@ -1,197 +0,0 @@ -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); - /** @var array{contextPath:string,documentContextPath:string,typeOfChange:int}[] $unpublishedNodes */ - $unpublishedNodes = []; - foreach ($changes as $change) { - if ($change->removalAttachmentPoint) { - $nodeAddress = new NodeAddress( - $change->contentStreamId, - $change->originDimensionSpacePoint->toDimensionSpacePoint(), - $change->nodeAggregateId, - $workspaceName - ); - - /** - * See {@see Remove::apply} -> Removal Attachment Point == closest document node. - */ - $documentNodeAddress = new NodeAddress( - $change->contentStreamId, - $change->originDimensionSpacePoint->toDimensionSpacePoint(), - $change->removalAttachmentPoint, - $workspaceName - ); - - $unpublishedNodes[] = [ - 'contextPath' => $nodeAddress->serializeForUri(), - 'documentContextPath' => $documentNodeAddress->serializeForUri(), - 'typeOfChange' => $this->getTypeOfChange($change) - ]; - } else { - $subgraph = $contentRepository->getContentGraph($workspaceName)->getSubgraph( - $change->originDimensionSpacePoint->toDimensionSpacePoint(), - VisibilityConstraints::withoutRestrictions() - ); - $node = $subgraph->findNodeById($change->nodeAggregateId); - - 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(), - 'documentContextPath' => $nodeAddressFactory->createFromNode($documentNode) - ->serializeForUri(), - 'typeOfChange' => $this->getTypeOfChange($change) - ]; - } - } - } - } - - return array_values(array_filter($unpublishedNodes, function ($item) { - return (bool)$item; - })); - } - - /** - * Get allowed target workspaces for current user - * - * @return array> - */ - public function getAllowedTargetWorkspaces(ContentRepository $contentRepository): array - { - $user = $this->domainUserService->getCurrentUser(); - if ($user === null) { - return []; - } - - $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; - } - $workspacePermissions = $this->neosWorkspaceService->getWorkspacePermissionsForUser($contentRepository->id, $workspace->workspaceName, $user); - - $workspaceArray = [ - 'name' => $workspace->workspaceName->jsonSerialize(), - 'title' => $workspace->workspaceTitle->jsonSerialize(), - 'description' => $workspace->workspaceDescription->jsonSerialize(), - 'readonly' => !$workspacePermissions->write, - ]; - $workspacesArray[$workspace->workspaceName->jsonSerialize()] = $workspaceArray; - } - - return $workspacesArray; - } - - private function getTypeOfChange(Change $change): int - { - $result = 0; - - if ($change->created) { - $result = $result | self::NODE_HAS_BEEN_CREATED; - } - - if ($change->changed) { - $result = $result | self::NODE_HAS_BEEN_CHANGED; - } - - if ($change->moved) { - $result = $result | self::NODE_HAS_BEEN_MOVED; - } - - if ($change->deleted) { - $result = $result | self::NODE_HAS_BEEN_DELETED; - } - - return $result; - } -} diff --git a/Classes/Domain/Model/Feedback/Operations/UpdateWorkspaceInfo.php b/Classes/Domain/Model/Feedback/Operations/UpdateWorkspaceInfo.php index 28d7cd2433..3af6577213 100644 --- a/Classes/Domain/Model/Feedback/Operations/UpdateWorkspaceInfo.php +++ b/Classes/Domain/Model/Feedback/Operations/UpdateWorkspaceInfo.php @@ -16,8 +16,7 @@ use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry; use Neos\Flow\Annotations as Flow; use Neos\Flow\Mvc\Controller\ControllerContext; -use Neos\Neos\PendingChangesProjection\ChangeFinder; -use Neos\Neos\Ui\ContentRepository\Service\WorkspaceService; +use Neos\Neos\Domain\Service\WorkspacePublishingService; use Neos\Neos\Ui\Domain\Model\AbstractFeedback; use Neos\Neos\Ui\Domain\Model\FeedbackInterface; @@ -28,9 +27,9 @@ class UpdateWorkspaceInfo extends AbstractFeedback { /** * @Flow\Inject - * @var WorkspaceService + * @var WorkspacePublishingService */ - protected $workspaceService; + protected $workspacePublishingService; /** * @Flow\Inject @@ -104,17 +103,11 @@ public function serializePayload(ControllerContext $controllerContext) if ($workspace === null) { return null; } - /** @var ChangeFinder $changeFinder */ - $changeFinder = $contentRepository->projectionState(ChangeFinder::class); - $totalNumberOfChanges = $changeFinder->countByContentStreamId($workspace->currentContentStreamId); - + $pendingChanges = $this->workspacePublishingService->pendingWorkspaceChanges($this->contentRepositoryId, $this->workspaceName); return [ 'name' => $this->workspaceName->value, - 'totalNumberOfChanges' => $totalNumberOfChanges, - 'publishableNodes' => $this->workspaceService->getPublishableNodeInfo( - $this->workspaceName, - $this->contentRepositoryId - ), + 'totalNumberOfChanges' => $pendingChanges->count(), + 'publishableNodes' => $pendingChanges->toPublishableNodeInfo($contentRepository, $workspace->workspaceName), 'baseWorkspace' => $workspace->baseWorkspaceName?->value, 'status' => $workspace->status->value, ]; diff --git a/Classes/Fusion/Helper/WorkspaceHelper.php b/Classes/Fusion/Helper/WorkspaceHelper.php index 586373e708..d6c5bc9e3d 100644 --- a/Classes/Fusion/Helper/WorkspaceHelper.php +++ b/Classes/Fusion/Helper/WorkspaceHelper.php @@ -18,12 +18,10 @@ use Neos\Flow\Security\Context; use Neos\Neos\Domain\Service\UserService; use Neos\Neos\Domain\Service\WorkspacePublishingService; -use Neos\Neos\Domain\Service\WorkspaceService as NeosWorkspaceService; -use Neos\Neos\Ui\ContentRepository\Service\WorkspaceService; +use Neos\Neos\Domain\Service\WorkspaceService; /** - * @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} * * TODO REMOVE */ @@ -35,12 +33,6 @@ class WorkspaceHelper implements ProtectedContextAwareInterface */ protected $contentRepositoryRegistry; - /** - * @Flow\Inject - * @var WorkspaceService - */ - protected $workspaceService; - /** * @Flow\Inject * @var Context @@ -61,9 +53,9 @@ class WorkspaceHelper implements ProtectedContextAwareInterface /** * @Flow\Inject - * @var NeosWorkspaceService + * @var WorkspaceService */ - protected $neosWorkspaceService; + protected $workspaceService; /** * @return array @@ -74,12 +66,14 @@ public function getPersonalWorkspace(ContentRepositoryId $contentRepositoryId): if ($currentUser === null) { return []; } - $personalWorkspace = $this->neosWorkspaceService->getPersonalWorkspaceForUser($contentRepositoryId, $currentUser->getId()); - $personalWorkspacePermissions = $this->neosWorkspaceService->getWorkspacePermissionsForUser($contentRepositoryId, $personalWorkspace->workspaceName, $currentUser); + $contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId); + $personalWorkspace = $this->workspaceService->getPersonalWorkspaceForUser($contentRepositoryId, $currentUser->getId()); + $personalWorkspacePermissions = $this->workspaceService->getWorkspacePermissionsForUser($contentRepositoryId, $personalWorkspace->workspaceName, $currentUser); + $pendingChanges = $this->workspacePublishingService->pendingWorkspaceChanges($contentRepositoryId, $personalWorkspace->workspaceName); return [ 'name' => $personalWorkspace->workspaceName->value, - 'totalNumberOfChanges' => $this->workspacePublishingService->countPendingWorkspaceChanges($contentRepositoryId, $personalWorkspace->workspaceName), - 'publishableNodes' => $this->workspaceService->getPublishableNodeInfo($personalWorkspace->workspaceName, $contentRepositoryId), + 'totalNumberOfChanges' => $pendingChanges->count(), + 'publishableNodes' => $pendingChanges->toPublishableNodeInfo($contentRepository, $personalWorkspace->workspaceName), '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..fe9ca959ae 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,29 @@ public function getConfiguration( ] ]; } + + 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; + } } From a398e65f45e578bd375a2d3fd1810049674eca5c Mon Sep 17 00:00:00 2001 From: Bastian Waidelich Date: Fri, 27 Sep 2024 10:44:02 +0200 Subject: [PATCH 05/11] Re-add UI WorkspaceService for now --- .../Service/WorkspaceService.php | 131 ++++++++++++++++++ .../Operations/UpdateWorkspaceInfo.php | 16 +-- Classes/Fusion/Helper/WorkspaceHelper.php | 14 +- 3 files changed, 145 insertions(+), 16 deletions(-) create mode 100644 Classes/ContentRepository/Service/WorkspaceService.php diff --git a/Classes/ContentRepository/Service/WorkspaceService.php b/Classes/ContentRepository/Service/WorkspaceService.php new file mode 100644 index 0000000000..479d2f077f --- /dev/null +++ b/Classes/ContentRepository/Service/WorkspaceService.php @@ -0,0 +1,131 @@ +contentRepositoryRegistry->get($contentRepositoryId); + $pendingChanges = $this->workspacePublishingService->pendingWorkspaceChanges($contentRepositoryId, $workspaceName); + /** @var array{contextPath:string,documentContextPath:string,typeOfChange:int}[] $unpublishedNodes */ + $unpublishedNodes = []; + foreach ($pendingChanges as $change) { + if ($change->removalAttachmentPoint) { + $nodeAddress = new NodeAddress( + $change->contentStreamId, + $change->originDimensionSpacePoint->toDimensionSpacePoint(), + $change->nodeAggregateId, + $workspaceName + ); + + /** + * See {@see Remove::apply} -> Removal Attachment Point == closest document node. + */ + $documentNodeAddress = new NodeAddress( + $change->contentStreamId, + $change->originDimensionSpacePoint->toDimensionSpacePoint(), + $change->removalAttachmentPoint, + $workspaceName + ); + + $unpublishedNodes[] = [ + 'contextPath' => $nodeAddress->serializeForUri(), + 'documentContextPath' => $documentNodeAddress->serializeForUri(), + 'typeOfChange' => $this->getTypeOfChange($change) + ]; + } else { + $subgraph = $contentRepository->getContentGraph($workspaceName)->getSubgraph( + $change->originDimensionSpacePoint->toDimensionSpacePoint(), + VisibilityConstraints::withoutRestrictions() + ); + $node = $subgraph->findNodeById($change->nodeAggregateId); + + if ($node instanceof Node) { + $documentNode = $subgraph->findClosestNode($node->aggregateId, FindClosestNodeFilter::create(nodeTypes: NodeTypeNameFactory::NAME_DOCUMENT)); + if ($documentNode instanceof Node) { + $nodeAddressFactory = NodeAddressFactory::create($contentRepository); + $unpublishedNodes[] = [ + 'contextPath' => $nodeAddressFactory->createFromNode($node)->serializeForUri(), + 'documentContextPath' => $nodeAddressFactory->createFromNode($documentNode) + ->serializeForUri(), + 'typeOfChange' => $this->getTypeOfChange($change) + ]; + } + } + } + } + + return array_values(array_filter($unpublishedNodes, function ($item) { + return (bool)$item; + })); + } + + private function getTypeOfChange(Change $change): int + { + $result = 0; + + if ($change->created) { + $result = $result | self::NODE_HAS_BEEN_CREATED; + } + + if ($change->changed) { + $result = $result | self::NODE_HAS_BEEN_CHANGED; + } + + if ($change->moved) { + $result = $result | self::NODE_HAS_BEEN_MOVED; + } + + if ($change->deleted) { + $result = $result | self::NODE_HAS_BEEN_DELETED; + } + + return $result; + } +} diff --git a/Classes/Domain/Model/Feedback/Operations/UpdateWorkspaceInfo.php b/Classes/Domain/Model/Feedback/Operations/UpdateWorkspaceInfo.php index 3af6577213..7fd86f0961 100644 --- a/Classes/Domain/Model/Feedback/Operations/UpdateWorkspaceInfo.php +++ b/Classes/Domain/Model/Feedback/Operations/UpdateWorkspaceInfo.php @@ -16,7 +16,7 @@ use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry; use Neos\Flow\Annotations as Flow; use Neos\Flow\Mvc\Controller\ControllerContext; -use Neos\Neos\Domain\Service\WorkspacePublishingService; +use Neos\Neos\Ui\ContentRepository\Service\WorkspaceService as UiWorkspaceService; use Neos\Neos\Ui\Domain\Model\AbstractFeedback; use Neos\Neos\Ui\Domain\Model\FeedbackInterface; @@ -27,15 +27,15 @@ class UpdateWorkspaceInfo extends AbstractFeedback { /** * @Flow\Inject - * @var WorkspacePublishingService + * @var ContentRepositoryRegistry */ - protected $workspacePublishingService; + protected $contentRepositoryRegistry; /** * @Flow\Inject - * @var ContentRepositoryRegistry + * @var UiWorkspaceService */ - protected $contentRepositoryRegistry; + protected $uiWorkspaceService; /** * UpdateWorkspaceInfo constructor. @@ -103,11 +103,11 @@ public function serializePayload(ControllerContext $controllerContext) if ($workspace === null) { return null; } - $pendingChanges = $this->workspacePublishingService->pendingWorkspaceChanges($this->contentRepositoryId, $this->workspaceName); + $publishableNodes = $this->uiWorkspaceService->getPublishableNodeInfo($workspace->workspaceName, $contentRepository->id); return [ 'name' => $this->workspaceName->value, - 'totalNumberOfChanges' => $pendingChanges->count(), - 'publishableNodes' => $pendingChanges->toPublishableNodeInfo($contentRepository, $workspace->workspaceName), + '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 d6c5bc9e3d..a3b316ff22 100644 --- a/Classes/Fusion/Helper/WorkspaceHelper.php +++ b/Classes/Fusion/Helper/WorkspaceHelper.php @@ -17,13 +17,11 @@ use Neos\Flow\Annotations as Flow; use Neos\Flow\Security\Context; use Neos\Neos\Domain\Service\UserService; -use Neos\Neos\Domain\Service\WorkspacePublishingService; 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 {@see \Neos\Neos\Ui\Infrastructure\Configuration\InitialStateProvider} - * - * TODO REMOVE */ class WorkspaceHelper implements ProtectedContextAwareInterface { @@ -41,9 +39,9 @@ class WorkspaceHelper implements ProtectedContextAwareInterface /** * @Flow\Inject - * @var WorkspacePublishingService + * @var UiWorkspaceService */ - protected $workspacePublishingService; + protected $uiWorkspaceService; /** * @Flow\Inject @@ -69,11 +67,11 @@ public function getPersonalWorkspace(ContentRepositoryId $contentRepositoryId): $contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId); $personalWorkspace = $this->workspaceService->getPersonalWorkspaceForUser($contentRepositoryId, $currentUser->getId()); $personalWorkspacePermissions = $this->workspaceService->getWorkspacePermissionsForUser($contentRepositoryId, $personalWorkspace->workspaceName, $currentUser); - $pendingChanges = $this->workspacePublishingService->pendingWorkspaceChanges($contentRepositoryId, $personalWorkspace->workspaceName); + $publishableNodes = $this->uiWorkspaceService->getPublishableNodeInfo($personalWorkspace->workspaceName, $contentRepository->id); return [ 'name' => $personalWorkspace->workspaceName->value, - 'totalNumberOfChanges' => $pendingChanges->count(), - 'publishableNodes' => $pendingChanges->toPublishableNodeInfo($contentRepository, $personalWorkspace->workspaceName), + 'totalNumberOfChanges' => count($publishableNodes), + 'publishableNodes' => $publishableNodes, 'baseWorkspace' => $personalWorkspace->baseWorkspaceName?->value, 'readOnly' => !($personalWorkspace->baseWorkspaceName !== null && $personalWorkspacePermissions->write), 'status' => $personalWorkspace->status->value, From 8b4ed98217873b38625f93d47612b53822a26bc8 Mon Sep 17 00:00:00 2001 From: Bastian Waidelich Date: Fri, 27 Sep 2024 10:56:58 +0200 Subject: [PATCH 06/11] Remove `WorkspaceNameBuilder` usages --- .../Controller/BackendServiceController.php | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/Classes/Controller/BackendServiceController.php b/Classes/Controller/BackendServiceController.php index e53ef393e9..705614c09e 100644 --- a/Classes/Controller/BackendServiceController.php +++ b/Classes/Controller/BackendServiceController.php @@ -33,6 +33,7 @@ use Neos\Flow\Security\Context; use Neos\Neos\Domain\Service\WorkspaceNameBuilder; 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; @@ -128,6 +129,12 @@ class BackendServiceController extends ActionController */ protected $nodeUriPathSegmentGenerator; + /** + * @Flow\Inject + * @var WorkspaceService + */ + protected $workspaceService; + /** * @Flow\Inject * @var WorkspacePublishingService @@ -390,28 +397,26 @@ public function changeBaseWorkspaceAction(string $targetWorkspaceName, string $d $nodeAddressFactory = NodeAddressFactory::create($contentRepository); - $currentAccount = $this->securityContext->getAccount(); - if ($currentAccount === null) { + $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; } - $userWorkspaceName = WorkspaceNameBuilder::fromAccountIdentifier( - $currentAccount->getAccountIdentifier() - ); + $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 { - $this->workspacePublishingService->changeBaseWorkspace($contentRepositoryId, $userWorkspaceName, WorkspaceName::fromString($targetWorkspaceName)); + $this->workspacePublishingService->changeBaseWorkspace($contentRepositoryId, $userWorkspace->workspaceName, WorkspaceName::fromString($targetWorkspaceName)); } catch (WorkspaceIsNotEmptyException $exception) { $error = new Error(); $error->setMessage( @@ -430,7 +435,7 @@ public function changeBaseWorkspaceAction(string $targetWorkspaceName, string $d return; } - $subgraph = $contentRepository->getContentGraph($userWorkspaceName) + $subgraph = $contentRepository->getContentGraph($userWorkspace->workspaceName) ->getSubgraph( $command->documentNode->dimensionSpacePoint, VisibilityConstraints::withoutRestrictions() @@ -445,7 +450,7 @@ 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, From 71f8981f6a6826a581bc7c0745e9631ce072ea49 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Fri, 27 Sep 2024 17:35:09 +0200 Subject: [PATCH 07/11] TASK: Reintroduce usage of neos ui command value objects --- .../ReloadNodes/ReloadNodesQueryHandler.php | 1 - .../Controller/BackendServiceController.php | 63 +++++++++++++------ .../Configuration/ConfigurationProvider.php | 3 + 3 files changed, 46 insertions(+), 21 deletions(-) diff --git a/Classes/Application/ReloadNodes/ReloadNodesQueryHandler.php b/Classes/Application/ReloadNodes/ReloadNodesQueryHandler.php index c0f9929e02..b53287e2d5 100644 --- a/Classes/Application/ReloadNodes/ReloadNodesQueryHandler.php +++ b/Classes/Application/ReloadNodes/ReloadNodesQueryHandler.php @@ -35,7 +35,6 @@ #[Flow\Scope("singleton")] final class ReloadNodesQueryHandler { - #[Flow\Inject] protected ContentRepositoryRegistry $contentRepositoryRegistry; diff --git a/Classes/Controller/BackendServiceController.php b/Classes/Controller/BackendServiceController.php index 705614c09e..faa6f7217f 100644 --- a/Classes/Controller/BackendServiceController.php +++ b/Classes/Controller/BackendServiceController.php @@ -31,7 +31,6 @@ 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\Service\WorkspacePublishingService; use Neos\Neos\Domain\Service\WorkspaceService; use Neos\Neos\FrontendRouting\NodeAddress; @@ -39,6 +38,11 @@ use Neos\Neos\FrontendRouting\SiteDetection\SiteDetectionResult; use Neos\Neos\Service\UserService; use Neos\Neos\Ui\Application\ChangeTargetWorkspace; +use Neos\Neos\Ui\Application\DiscardAllChanges; +use Neos\Neos\Ui\Application\DiscardChangesInDocument; +use Neos\Neos\Ui\Application\DiscardChangesInSite; +use Neos\Neos\Ui\Application\PublishChangesInDocument; +use Neos\Neos\Ui\Application\PublishChangesInSite; use Neos\Neos\Ui\Application\ReloadNodes\ReloadNodesQuery; use Neos\Neos\Ui\Application\ReloadNodes\ReloadNodesQueryHandler; use Neos\Neos\Ui\Application\SyncWorkspace\ConflictsOccurred; @@ -202,14 +206,16 @@ public function publishChangesInSiteAction(array $command): void try { /** @todo send from UI */ $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; - $siteNodeAddress = $this->nodeService->deserializeNodeAddress( + $command['contentRepositoryId'] = $contentRepositoryId->value; + $command['siteId'] = $this->nodeService->deserializeNodeAddress( $command['siteId'], $contentRepositoryId - ); + )->nodeAggregateId->value; + $command = PublishChangesInSite::fromArray($command); $publishingResult = $this->workspacePublishingService->publishChangesInSite( - $contentRepositoryId, - $siteNodeAddress->workspaceName, - $siteNodeAddress->nodeAggregateId, + $command->contentRepositoryId, + $command->workspaceName, + $command->siteId, ); $this->view->assign('value', [ 'success' => [ @@ -239,15 +245,18 @@ public function publishChangesInDocumentAction(array $command): void try { /** @todo send from UI */ $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; - $documentNodeAddress = $this->nodeService->deserializeNodeAddress( + $command['contentRepositoryId'] = $contentRepositoryId->value; + $command['documentId'] = $this->nodeService->deserializeNodeAddress( $command['documentId'], $contentRepositoryId - ); + )->nodeAggregateId->value; + $command = PublishChangesInDocument::fromArray($command); + try { $publishingResult = $this->workspacePublishingService->publishChangesInDocument( - $contentRepositoryId, - $documentNodeAddress->workspaceName, - $documentNodeAddress->nodeAggregateId, + $command->contentRepositoryId, + $command->workspaceName, + $command->documentId, ); $this->view->assign('value', [ @@ -291,9 +300,14 @@ public function discardAllChangesAction(array $command): void try { /** @todo send from UI */ $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; - $workspaceName = WorkspaceName::fromString($command['workspaceName']); + $command['contentRepositoryId'] = $contentRepositoryId->value; + $command = DiscardAllChanges::fromArray($command); + + $discardingResult = $this->workspacePublishingService->discardAllWorkspaceChanges( + $command->contentRepositoryId, + $command->workspaceName + ); - $discardingResult = $this->workspacePublishingService->discardAllWorkspaceChanges($contentRepositoryId, $workspaceName); $this->view->assign('value', [ 'success' => [ 'numberOfAffectedChanges' => $discardingResult->numberOfDiscardedChanges @@ -321,15 +335,17 @@ public function discardChangesInSiteAction(array $command): void try { /** @todo send from UI */ $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; - $siteNodeAddress = $this->nodeService->deserializeNodeAddress( + $command['contentRepositoryId'] = $contentRepositoryId->value; + $command['siteId'] = $this->nodeService->deserializeNodeAddress( $command['siteId'], $contentRepositoryId - ); + )->nodeAggregateId->value; + $command = DiscardChangesInSite::fromArray($command); $discardingResult = $this->workspacePublishingService->discardChangesInSite( - $contentRepositoryId, - $siteNodeAddress->workspaceName, - $siteNodeAddress->nodeAggregateId + $command->contentRepositoryId, + $command->workspaceName, + $command->siteId ); $this->view->assign('value', [ @@ -359,11 +375,18 @@ public function discardChangesInDocumentAction(array $command): void try { /** @todo send from UI */ $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; - $documentNodeAddress = $this->nodeService->deserializeNodeAddress( + $command['contentRepositoryId'] = $contentRepositoryId->value; + $command['documentId'] = $this->nodeService->deserializeNodeAddress( $command['documentId'], $contentRepositoryId + )->nodeAggregateId->value; + $command = DiscardChangesInDocument::fromArray($command); + + $discardingResult = $this->workspacePublishingService->discardChangesInDocument( + $command->contentRepositoryId, + $command->workspaceName, + $command->documentId ); - $discardingResult = $this->workspacePublishingService->discardChangesInDocument($contentRepositoryId, $documentNodeAddress->workspaceName, $documentNodeAddress->nodeAggregateId); $this->view->assign('value', [ 'success' => [ diff --git a/Classes/Infrastructure/Configuration/ConfigurationProvider.php b/Classes/Infrastructure/Configuration/ConfigurationProvider.php index fe9ca959ae..f18d4e05f3 100644 --- a/Classes/Infrastructure/Configuration/ConfigurationProvider.php +++ b/Classes/Infrastructure/Configuration/ConfigurationProvider.php @@ -88,6 +88,9 @@ public function getConfiguration( ]; } + /** + * @return array + */ private function getAllowedTargetWorkspaces(ContentRepository $contentRepository): array { $backendUser = $this->userService->getBackendUser(); From 8a2445eb4ce6632aa1ae3fd215e1c4796e214a9c Mon Sep 17 00:00:00 2001 From: Bastian Waidelich Date: Tue, 1 Oct 2024 12:55:58 +0200 Subject: [PATCH 08/11] Remove obsolete namespace import --- Classes/Controller/BackendServiceController.php | 1 - 1 file changed, 1 deletion(-) diff --git a/Classes/Controller/BackendServiceController.php b/Classes/Controller/BackendServiceController.php index 705614c09e..39d686eb1c 100644 --- a/Classes/Controller/BackendServiceController.php +++ b/Classes/Controller/BackendServiceController.php @@ -31,7 +31,6 @@ 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\Service\WorkspacePublishingService; use Neos\Neos\Domain\Service\WorkspaceService; use Neos\Neos\FrontendRouting\NodeAddress; From 04ab0195eb7d869a5ef6c016bc8422abc109feac Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Tue, 1 Oct 2024 14:12:07 +0200 Subject: [PATCH 09/11] WIP: Try to adjust e2e to work with new Neos workspace metadata --- .../IntegrationTests/Fixtures/1Dimension/discarding.e2e.js | 2 +- Tests/IntegrationTests/Fixtures/1Dimension/syncing.e2e.js | 2 +- .../Fixtures/1Dimension/treeMultiselect.e2e.js | 6 +++--- Tests/IntegrationTests/e2e-docker.sh | 4 ++++ Tests/IntegrationTests/e2e.sh | 4 ++++ 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Tests/IntegrationTests/Fixtures/1Dimension/discarding.e2e.js b/Tests/IntegrationTests/Fixtures/1Dimension/discarding.e2e.js index db382f1d70..f7ed4606a3 100644 --- a/Tests/IntegrationTests/Fixtures/1Dimension/discarding.e2e.js +++ b/Tests/IntegrationTests/Fixtures/1Dimension/discarding.e2e.js @@ -38,7 +38,7 @@ test('Discarding: create multiple nodes nested within each other and then discar .expect(ReactSelector('Provider').getReact(({props}) => { const reduxState = props.store.getState(); return reduxState.cr.nodes.documentNode; - })).eql('user-admin__eyJsYW5ndWFnZSI6ImVuX1VTIn0=__f676459d-ca77-44bc-aeea-44114814c279', 'After discarding we are back to the main page'); + })).eql('admin-admington__eyJsYW5ndWFnZSI6ImVuX1VTIn0=__f676459d-ca77-44bc-aeea-44114814c279', 'After discarding we are back to the main page'); }); test('Discarding: create a document node and then discard it', async t => { diff --git a/Tests/IntegrationTests/Fixtures/1Dimension/syncing.e2e.js b/Tests/IntegrationTests/Fixtures/1Dimension/syncing.e2e.js index 5a148b4b52..ff4166aa10 100644 --- a/Tests/IntegrationTests/Fixtures/1Dimension/syncing.e2e.js +++ b/Tests/IntegrationTests/Fixtures/1Dimension/syncing.e2e.js @@ -150,7 +150,7 @@ async function chooseDiscardAllAndFinishSynchronization(t) { // Choose "Discard All" as resolution strategy // await t.click(Selector('#neos-SelectResolutionStrategy-SelectBox')); - await t.click(Selector('[role="button"]').withText('Discard workspace "user-admin"')); + await t.click(Selector('[role="button"]').withText('Discard workspace "admin-admington"')); await t.click(Selector('#neos-SelectResolutionStrategy-Accept')); // diff --git a/Tests/IntegrationTests/Fixtures/1Dimension/treeMultiselect.e2e.js b/Tests/IntegrationTests/Fixtures/1Dimension/treeMultiselect.e2e.js index 885bfd3c00..0bf502bb50 100644 --- a/Tests/IntegrationTests/Fixtures/1Dimension/treeMultiselect.e2e.js +++ b/Tests/IntegrationTests/Fixtures/1Dimension/treeMultiselect.e2e.js @@ -26,7 +26,7 @@ test('Move multiple nodes via toolbar', async t => { .expect(ReactSelector('Provider').getReact(({props}) => { const reduxState = props.store.getState(); return reduxState.cr.nodes.documentNode; - })).eql('user-admin__eyJsYW5ndWFnZSI6ImVuX1VTIn0=__5b0d6ac0-40ab-47e8-b79e-39de6c0700df', 'Node B\'s node address changed'); + })).eql('admin-admington__eyJsYW5ndWFnZSI6ImVuX1VTIn0=__5b0d6ac0-40ab-47e8-b79e-39de6c0700df', 'Node B\'s node address changed'); await t.click(Page.getTreeNodeButton('Home')) }); @@ -43,7 +43,7 @@ test('Move multiple nodes via DND, CMD-click', async t => { .expect(ReactSelector('Provider').getReact(({props}) => { const reduxState = props.store.getState(); return reduxState.cr.nodes.documentNode; - })).eql('user-admin__eyJsYW5ndWFnZSI6ImVuX1VTIn0=__5b0d6ac0-40ab-47e8-b79e-39de6c0700df', 'Node B\'s node address changed'); + })).eql('admin-admington__eyJsYW5ndWFnZSI6ImVuX1VTIn0=__5b0d6ac0-40ab-47e8-b79e-39de6c0700df', 'Node B\'s node address changed'); await t.click(Page.getTreeNodeButton('Home')) }); @@ -60,6 +60,6 @@ test('Move multiple nodes via DND, SHIFT-click', async t => { .expect(ReactSelector('Provider').getReact(({props}) => { const reduxState = props.store.getState(); return reduxState.cr.nodes.documentNode; - })).eql('user-admin__eyJsYW5ndWFnZSI6ImVuX1VTIn0=__84eb0340-ba34-4fdb-98b1-da503f967121', 'Node C\'s node address changed'); + })).eql('admin-admington__eyJsYW5ndWFnZSI6ImVuX1VTIn0=__84eb0340-ba34-4fdb-98b1-da503f967121', 'Node C\'s node address changed'); await t.click(Page.getTreeNodeButton('Home')) }); diff --git a/Tests/IntegrationTests/e2e-docker.sh b/Tests/IntegrationTests/e2e-docker.sh index c7cfed36a7..785c18fb94 100755 --- a/Tests/IntegrationTests/e2e-docker.sh +++ b/Tests/IntegrationTests/e2e-docker.sh @@ -58,12 +58,16 @@ dc exec -T php bash <<-'BASH' ./flow cr:setup --content-repository onedimension ./flow cr:import --content-repository onedimension --path ./DistributionPackages/Neos.Test.OneDimension/Resources/Private/Content + # Hack to create Neos workspace metadata for the live workspace (Failed to load metadata for workspace "live"). + ./flow migrateevents:migrateWorkspaceMetadataToWorkspaceService --content-repository onedimension # Connect to a Neos site, todo the nodeTypeName parameter is obsolete but necessary ./flow site:create neos-test-onedimension Neos.Test.OneDimension Neos.TestNodeTypes:Document.HomePage ./flow domain:add neos-test-onedimension onedimension.localhost --port 8081 ./flow cr:setup --content-repository twodimensions ./flow cr:import --content-repository twodimensions --path ./DistributionPackages/Neos.Test.TwoDimensions/Resources/Private/Content + # Hack to create Neos workspace metadata for the live workspace (Failed to load metadata for workspace "live"). + ./flow migrateevents:migrateWorkspaceMetadataToWorkspaceService --content-repository onedimension # Connect to a Neos site, todo the nodeTypeName parameter is obsolete but necessary ./flow site:create neos-test-twodimensions Neos.Test.TwoDimensions Neos.TestNodeTypes:Document.HomePage ./flow domain:add neos-test-twodimensions twodimensions.localhost --port 8081 diff --git a/Tests/IntegrationTests/e2e.sh b/Tests/IntegrationTests/e2e.sh index 491915f5f2..e78c71986e 100755 --- a/Tests/IntegrationTests/e2e.sh +++ b/Tests/IntegrationTests/e2e.sh @@ -80,12 +80,16 @@ function run_tests() { ./flow cr:setup --content-repository onedimension ./flow cr:import --content-repository onedimension --path ./DistributionPackages/Neos.Test.OneDimension/Resources/Private/Content + # Hack to create Neos workspace metadata for the live workspace (Failed to load metadata for workspace "live"). + ./flow migrateevents:migrateWorkspaceMetadataToWorkspaceService --content-repository onedimension # Connect to a Neos site, todo the nodeTypeName parameter is obsolete but necessary ./flow site:create neos-test-onedimension Neos.Test.OneDimension Neos.TestNodeTypes:Document.HomePage ./flow domain:add neos-test-onedimension onedimension.localhost --port 8081 ./flow cr:setup --content-repository twodimensions ./flow cr:import --content-repository twodimensions --path ./DistributionPackages/Neos.Test.TwoDimensions/Resources/Private/Content + # Hack to create Neos workspace metadata for the live workspace (Failed to load metadata for workspace "live"). + ./flow migrateevents:migrateWorkspaceMetadataToWorkspaceService --content-repository onedimension # Connect to a Neos site, todo the nodeTypeName parameter is obsolete but necessary ./flow site:create neos-test-twodimensions Neos.Test.TwoDimensions Neos.TestNodeTypes:Document.HomePage ./flow domain:add neos-test-twodimensions twodimensions.localhost --port 8081 From 3b752f6356a86eacf421af7e0fa78d22bde38482 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Wed, 9 Oct 2024 08:46:49 +0200 Subject: [PATCH 10/11] TASK: Remove `migrateWorkspaceMetadataToWorkspaceService` hack from e2e tests and use `assignrole` --- Tests/IntegrationTests/e2e-docker.sh | 12 ++++++------ Tests/IntegrationTests/e2e.sh | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Tests/IntegrationTests/e2e-docker.sh b/Tests/IntegrationTests/e2e-docker.sh index 785c18fb94..6fe682e282 100755 --- a/Tests/IntegrationTests/e2e-docker.sh +++ b/Tests/IntegrationTests/e2e-docker.sh @@ -58,17 +58,17 @@ dc exec -T php bash <<-'BASH' ./flow cr:setup --content-repository onedimension ./flow cr:import --content-repository onedimension --path ./DistributionPackages/Neos.Test.OneDimension/Resources/Private/Content - # Hack to create Neos workspace metadata for the live workspace (Failed to load metadata for workspace "live"). - ./flow migrateevents:migrateWorkspaceMetadataToWorkspaceService --content-repository onedimension - # Connect to a Neos site, todo the nodeTypeName parameter is obsolete but necessary + # TODO: Fix when part of importer: Add Neos workspace role for the live workspace + ./flow workspace:assignrole live Neos.Neos:LivePublisher collaborator --content-repository onedimension + # TODO: Fix when part of importer: Connect to a Neos site, todo the nodeTypeName parameter is obsolete but necessary ./flow site:create neos-test-onedimension Neos.Test.OneDimension Neos.TestNodeTypes:Document.HomePage ./flow domain:add neos-test-onedimension onedimension.localhost --port 8081 ./flow cr:setup --content-repository twodimensions ./flow cr:import --content-repository twodimensions --path ./DistributionPackages/Neos.Test.TwoDimensions/Resources/Private/Content - # Hack to create Neos workspace metadata for the live workspace (Failed to load metadata for workspace "live"). - ./flow migrateevents:migrateWorkspaceMetadataToWorkspaceService --content-repository onedimension - # Connect to a Neos site, todo the nodeTypeName parameter is obsolete but necessary + # TODO: Fix when part of importer: Add Neos workspace role for the live workspace + ./flow workspace:assignrole live Neos.Neos:LivePublisher collaborator --content-repository twodimensions + # TODO: Fix when part of importer: Connect to a Neos site, todo the nodeTypeName parameter is obsolete but necessary ./flow site:create neos-test-twodimensions Neos.Test.TwoDimensions Neos.TestNodeTypes:Document.HomePage ./flow domain:add neos-test-twodimensions twodimensions.localhost --port 8081 diff --git a/Tests/IntegrationTests/e2e.sh b/Tests/IntegrationTests/e2e.sh index e78c71986e..9446741e5d 100755 --- a/Tests/IntegrationTests/e2e.sh +++ b/Tests/IntegrationTests/e2e.sh @@ -80,17 +80,17 @@ function run_tests() { ./flow cr:setup --content-repository onedimension ./flow cr:import --content-repository onedimension --path ./DistributionPackages/Neos.Test.OneDimension/Resources/Private/Content - # Hack to create Neos workspace metadata for the live workspace (Failed to load metadata for workspace "live"). - ./flow migrateevents:migrateWorkspaceMetadataToWorkspaceService --content-repository onedimension - # Connect to a Neos site, todo the nodeTypeName parameter is obsolete but necessary + # TODO: Fix when part of importer: Add Neos workspace role for the live workspace + ./flow workspace:assignrole live Neos.Neos:LivePublisher collaborator --content-repository onedimension + # TODO: Fix when part of importer: Connect to a Neos site, todo the nodeTypeName parameter is obsolete but necessary ./flow site:create neos-test-onedimension Neos.Test.OneDimension Neos.TestNodeTypes:Document.HomePage ./flow domain:add neos-test-onedimension onedimension.localhost --port 8081 ./flow cr:setup --content-repository twodimensions ./flow cr:import --content-repository twodimensions --path ./DistributionPackages/Neos.Test.TwoDimensions/Resources/Private/Content - # Hack to create Neos workspace metadata for the live workspace (Failed to load metadata for workspace "live"). - ./flow migrateevents:migrateWorkspaceMetadataToWorkspaceService --content-repository onedimension - # Connect to a Neos site, todo the nodeTypeName parameter is obsolete but necessary + # TODO: Fix when part of importer: Add Neos workspace role for the live workspace + ./flow workspace:assignrole live Neos.Neos:LivePublisher collaborator --content-repository twodimensions + # TODO: Fix when part of importer: Connect to a Neos site, todo the nodeTypeName parameter is obsolete but necessary ./flow site:create neos-test-twodimensions Neos.Test.TwoDimensions Neos.TestNodeTypes:Document.HomePage ./flow domain:add neos-test-twodimensions twodimensions.localhost --port 8081 From 5d70a0b727dd743e30e9bc3dc8f52e6ec8c80464 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Wed, 9 Oct 2024 11:18:19 +0200 Subject: [PATCH 11/11] Revert "WIP REVERT ME, PATCH E2E" This reverts commit 3aefeb2315e2af3d8d02d2e3d32013dbf07b5601. --- Tests/IntegrationTests/TestDistribution/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/IntegrationTests/TestDistribution/composer.json b/Tests/IntegrationTests/TestDistribution/composer.json index d3cb3c304c..24e9fb8c31 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": "feature/4726-extract-workspace-metadata-and-user-assignment-to-neos-dev as 9.0", + "neos/neos-development-collection": "9.0.x-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",