diff --git a/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Repository/ContentGraph.php b/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Repository/ContentGraph.php index 43957bd5767..59910cf07ac 100644 --- a/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Repository/ContentGraph.php +++ b/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Repository/ContentGraph.php @@ -166,7 +166,7 @@ public function findNodeAggregateById( return $this->nodeFactory->mapNodeRowsToNodeAggregate( $this->fetchRows($queryBuilder), $this->workspaceName, - VisibilityConstraints::withoutRestrictions() + VisibilityConstraints::createEmpty() ); } @@ -274,7 +274,7 @@ public function findParentNodeAggregateByChildOriginDimensionSpacePoint(NodeAggr return $this->nodeFactory->mapNodeRowsToNodeAggregate( $this->fetchRows($queryBuilder), $this->workspaceName, - VisibilityConstraints::withoutRestrictions() + VisibilityConstraints::createEmpty() ); } @@ -368,7 +368,7 @@ private function mapQueryBuilderToNodeAggregate(QueryBuilder $queryBuilder): ?No return $this->nodeFactory->mapNodeRowsToNodeAggregate( $this->fetchRows($queryBuilder), $this->workspaceName, - VisibilityConstraints::withoutRestrictions() + VisibilityConstraints::createEmpty() ); } @@ -381,7 +381,7 @@ private function mapQueryBuilderToNodeAggregates(QueryBuilder $queryBuilder): No return $this->nodeFactory->mapNodeRowsToNodeAggregates( $this->fetchRows($queryBuilder), $this->workspaceName, - VisibilityConstraints::withoutRestrictions() + VisibilityConstraints::createEmpty() ); } diff --git a/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Repository/ContentSubgraph.php b/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Repository/ContentSubgraph.php index 3068a9e32cf..da250151c48 100644 --- a/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Repository/ContentSubgraph.php +++ b/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Repository/ContentSubgraph.php @@ -475,7 +475,7 @@ private function addSubtreeTagConstraints(QueryBuilder $queryBuilder, string $hi { $hierarchyRelationTablePrefix = $hierarchyRelationTableAlias === '' ? '' : $hierarchyRelationTableAlias . '.'; $i = 0; - foreach ($this->visibilityConstraints->tagConstraints as $excludedTag) { + foreach ($this->visibilityConstraints->excludedSubtreeTags as $excludedTag) { $queryBuilder->andWhere('NOT JSON_CONTAINS_PATH(' . $hierarchyRelationTablePrefix . 'subtreetags, \'one\', :tagPath' . $i . ')')->setParameter('tagPath' . $i, '$.' . $excludedTag->value); $i++; } diff --git a/Neos.ContentGraph.PostgreSQLAdapter/src/Domain/Repository/ContentHypergraph.php b/Neos.ContentGraph.PostgreSQLAdapter/src/Domain/Repository/ContentHypergraph.php index b1b6671bf20..a35e690a3ab 100644 --- a/Neos.ContentGraph.PostgreSQLAdapter/src/Domain/Repository/ContentHypergraph.php +++ b/Neos.ContentGraph.PostgreSQLAdapter/src/Domain/Repository/ContentHypergraph.php @@ -129,7 +129,7 @@ public function findNodeAggregateById( return $this->nodeFactory->mapNodeRowsToNodeAggregate( $nodeRows, - VisibilityConstraints::withoutRestrictions() + VisibilityConstraints::createEmpty() ); } @@ -173,7 +173,7 @@ public function findParentNodeAggregateByChildOriginDimensionSpacePoint( return $this->nodeFactory->mapNodeRowsToNodeAggregate( $nodeRows, - VisibilityConstraints::withoutRestrictions() + VisibilityConstraints::createEmpty() ); } @@ -187,7 +187,7 @@ public function findParentNodeAggregates( return $this->nodeFactory->mapNodeRowsToNodeAggregates( $nodeRows, - VisibilityConstraints::withoutRestrictions() + VisibilityConstraints::createEmpty() ); } @@ -217,7 +217,7 @@ public function findChildNodeAggregates( return $this->nodeFactory->mapNodeRowsToNodeAggregates( $nodeRows, - VisibilityConstraints::withoutRestrictions() + VisibilityConstraints::createEmpty() ); } @@ -236,7 +236,7 @@ public function findChildNodeAggregateByName( return $this->nodeFactory->mapNodeRowsToNodeAggregate( $nodeRows, - VisibilityConstraints::withoutRestrictions() + VisibilityConstraints::createEmpty() ); } @@ -252,7 +252,7 @@ public function findTetheredChildNodeAggregates( $nodeRows = $query->execute($this->dbal)->fetchAllAssociative(); - return $this->nodeFactory->mapNodeRowsToNodeAggregates($nodeRows, VisibilityConstraints::withoutRestrictions()); + return $this->nodeFactory->mapNodeRowsToNodeAggregates($nodeRows, VisibilityConstraints::createEmpty()); } public function getDimensionSpacePointsOccupiedByChildNodeName( diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/01-RemoveNodeAggregate_ConstraintChecks.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/01-RemoveNodeAggregate_ConstraintChecks.feature index e7e35cb4a5a..17e21b781ba 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/01-RemoveNodeAggregate_ConstraintChecks.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/01-RemoveNodeAggregate_ConstraintChecks.feature @@ -84,12 +84,3 @@ Feature: Remove NodeAggregate | coveredDimensionSpacePoint | {"language": "en"} | | nodeVariantSelectionStrategy | "allVariants" | Then the last command should have thrown an exception of type "NodeAggregateDoesCurrentlyNotCoverDimensionSpacePoint" - - Scenario: Try to remove a node aggregate using a non-existent removalAttachmentPoint - When the command RemoveNodeAggregate is executed with payload and exceptions are caught: - | Key | Value | - | nodeAggregateId | "sir-david-nodenborough" | - | nodeVariantSelectionStrategy | "allVariants" | - | coveredDimensionSpacePoint | {"language":"de"} | - | removalAttachmentPoint | "i-do-not-exist" | - Then the last command should have thrown an exception of type "NodeAggregateCurrentlyDoesNotExist" diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/02-RemoveNodeAggregate_WithoutDimensions.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/02-RemoveNodeAggregate_WithoutDimensions.feature index 9f00f7d69fc..6ce918428eb 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/02-RemoveNodeAggregate_WithoutDimensions.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/02-RemoveNodeAggregate_WithoutDimensions.feature @@ -48,7 +48,6 @@ Feature: Remove NodeAggregate | nodeAggregateId | "nodingers-cat" | | affectedOccupiedDimensionSpacePoints | [[]] | | affectedCoveredDimensionSpacePoints | [[]] | - | removalAttachmentPoint | null | Then I expect the graph projection to consist of exactly 2 nodes And I expect a node identified by cs-identifier;lady-eleonode-rootford;{} to exist in the content graph And I expect a node identified by cs-identifier;sir-david-nodenborough;{} to exist in the content graph diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/03-RemoveNodeAggregate_WithDimensions.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/03-RemoveNodeAggregate_WithDimensions.feature index 27be85fbf2b..250296eefff 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/03-RemoveNodeAggregate_WithDimensions.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/03-RemoveNodeAggregate_WithDimensions.feature @@ -51,7 +51,6 @@ Feature: Remove NodeAggregate | nodeAggregateId | "nodingers-cat" | | affectedOccupiedDimensionSpacePoints | [{"language":"en"}] | | affectedCoveredDimensionSpacePoints | [{"language":"de"},{"language":"gsw"}] | - | removalAttachmentPoint | null | Then I expect the graph projection to consist of exactly 4 nodes And I expect a node identified by cs-identifier;lady-eleonode-rootford;{} to exist in the content graph And I expect a node identified by cs-identifier;sir-david-nodenborough;{"language":"en"} to exist in the content graph @@ -198,7 +197,6 @@ Feature: Remove NodeAggregate | nodeAggregateId | "nodingers-cat" | | affectedOccupiedDimensionSpacePoints | [{"language":"de"},{"language":"en"}] | | affectedCoveredDimensionSpacePoints | [{"language":"de"},{"language":"en"},{"language":"gsw"},{"language":"fr"}] | - | removalAttachmentPoint | null | Then I expect the graph projection to consist of exactly 2 nodes And I expect a node identified by cs-identifier;lady-eleonode-rootford;{} to exist in the content graph And I expect a node identified by cs-identifier;sir-david-nodenborough;{"language":"en"} to exist in the content graph diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/11-NodeTypeChange/02-ChangeNodeAggregateType_DeleteStrategy.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/11-NodeTypeChange/02-ChangeNodeAggregateType_DeleteStrategy.feature index f73a3a112b7..aef9a6974fb 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/11-NodeTypeChange/02-ChangeNodeAggregateType_DeleteStrategy.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/11-NodeTypeChange/02-ChangeNodeAggregateType_DeleteStrategy.feature @@ -307,7 +307,6 @@ Feature: Change node aggregate type - behavior of DELETE strategy | nodeAggregateId | "nodingers-cat" | | affectedOccupiedDimensionSpacePoints | [{"language":"de"},{"language":"gsw"}] | | affectedCoveredDimensionSpacePoints | [{"language":"de"},{"language":"gsw"}] | - | removalAttachmentPoint | null | And event at index 14 is of type "NodeAggregateTypeWasChanged" with payload: | Key | Expected | | workspaceName | "live" | diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Parallel/ParallelWritingInWorkspaces/ParallelWritingInWorkspacesTest.php b/Neos.ContentRepository.BehavioralTests/Tests/Parallel/ParallelWritingInWorkspaces/ParallelWritingInWorkspacesTest.php index 06b508c094e..d72bfc27be5 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Parallel/ParallelWritingInWorkspaces/ParallelWritingInWorkspacesTest.php +++ b/Neos.ContentRepository.BehavioralTests/Tests/Parallel/ParallelWritingInWorkspaces/ParallelWritingInWorkspacesTest.php @@ -178,7 +178,7 @@ public function whileANodesArWrittenOnLive(): void $this->log('1. writing finished'); Assert::assertTrue(true, 'No exception was thrown ;)'); - $subgraph = $this->contentRepository->getContentGraph(WorkspaceName::forLive())->getSubgraph(DimensionSpacePoint::createWithoutDimensions(), VisibilityConstraints::withoutRestrictions()); + $subgraph = $this->contentRepository->getContentGraph(WorkspaceName::forLive())->getSubgraph(DimensionSpacePoint::createWithoutDimensions(), VisibilityConstraints::createEmpty()); $node = $subgraph->findNodeById(NodeAggregateId::fromString('nody-mc-nodeface-100')); Assert::assertNotNull($node); } @@ -220,7 +220,7 @@ public function thenConcurrentPublishLeadsToException(): void Assert::assertTrue(true, 'No exception was thrown ;)'); - $subgraph = $this->contentRepository->getContentGraph(WorkspaceName::fromString('user-test'))->getSubgraph(DimensionSpacePoint::createWithoutDimensions(), VisibilityConstraints::withoutRestrictions()); + $subgraph = $this->contentRepository->getContentGraph(WorkspaceName::fromString('user-test'))->getSubgraph(DimensionSpacePoint::createWithoutDimensions(), VisibilityConstraints::createEmpty()); $node = $subgraph->findNodeById(NodeAggregateId::fromString('user-nody-mc-nodeface-100')); Assert::assertNotNull($node); } diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Parallel/WorkspacePublicationDuringWriting/WorkspacePublicationDuringWritingTest.php b/Neos.ContentRepository.BehavioralTests/Tests/Parallel/WorkspacePublicationDuringWriting/WorkspacePublicationDuringWritingTest.php index 3c62dcc5149..72428ad7860 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Parallel/WorkspacePublicationDuringWriting/WorkspacePublicationDuringWritingTest.php +++ b/Neos.ContentRepository.BehavioralTests/Tests/Parallel/WorkspacePublicationDuringWriting/WorkspacePublicationDuringWritingTest.php @@ -169,7 +169,7 @@ public function whileANodesArWrittenOnLive(): void $this->log('writing finished'); Assert::assertTrue(true, 'No exception was thrown ;)'); - $subgraph = $this->contentRepository->getContentGraph(WorkspaceName::forLive())->getSubgraph(DimensionSpacePoint::createWithoutDimensions(), VisibilityConstraints::withoutRestrictions()); + $subgraph = $this->contentRepository->getContentGraph(WorkspaceName::forLive())->getSubgraph(DimensionSpacePoint::createWithoutDimensions(), VisibilityConstraints::createEmpty()); $node = $subgraph->findNodeById(NodeAggregateId::fromString('nody-mc-nodeface')); Assert::assertNotNull($node); Assert::assertSame($node->getProperty('title'), 'changed-title-50'); @@ -253,7 +253,7 @@ public function thenConcurrentPublishLeadsToException(): void } $node = $this->contentRepository->getContentGraph(WorkspaceName::fromString('user-test')) - ->getSubgraph(DimensionSpacePoint::createWithoutDimensions(), VisibilityConstraints::withoutRestrictions()) + ->getSubgraph(DimensionSpacePoint::createWithoutDimensions(), VisibilityConstraints::createEmpty()) ->findNodeById(NodeAggregateId::fromString('nody-mc-nodeface')); Assert::assertSame('written-after-failed-publish', $node?->getProperty('title')); diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Parallel/WorkspaceWritingDuringRebase/WorkspaceWritingDuringRebaseTest.php b/Neos.ContentRepository.BehavioralTests/Tests/Parallel/WorkspaceWritingDuringRebase/WorkspaceWritingDuringRebaseTest.php index 566a86c9bc9..47f8c5693b3 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Parallel/WorkspaceWritingDuringRebase/WorkspaceWritingDuringRebaseTest.php +++ b/Neos.ContentRepository.BehavioralTests/Tests/Parallel/WorkspaceWritingDuringRebase/WorkspaceWritingDuringRebaseTest.php @@ -209,7 +209,7 @@ public function thenConcurrentCommandsLeadToAnException(): void $this->log('write finished'); $node = $this->contentRepository->getContentGraph(WorkspaceName::fromString('user-test')) - ->getSubgraph(DimensionSpacePoint::createWithoutDimensions(), VisibilityConstraints::withoutRestrictions()) + ->getSubgraph(DimensionSpacePoint::createWithoutDimensions(), VisibilityConstraints::createEmpty()) ->findNodeById(NodeAggregateId::fromString('nody-mc-nodeface')); if ($actualException === null) { diff --git a/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php b/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php index 2ac1f100577..4705f81ffb8 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php +++ b/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php @@ -551,7 +551,7 @@ protected function requireNodeAggregateToBeSibling( ): void { $succeedingSiblings = $contentGraph->getSubgraph( $dimensionSpacePoint, - VisibilityConstraints::withoutRestrictions() + VisibilityConstraints::createEmpty() )->findSucceedingSiblingNodes($referenceNodeAggregateId, FindSucceedingSiblingNodesFilter::create()); if ($succeedingSiblings->toNodeAggregateIds()->contain($siblingNodeAggregateId)) { return; @@ -559,7 +559,7 @@ protected function requireNodeAggregateToBeSibling( $precedingSiblings = $contentGraph->getSubgraph( $dimensionSpacePoint, - VisibilityConstraints::withoutRestrictions() + VisibilityConstraints::createEmpty() )->findPrecedingSiblingNodes($referenceNodeAggregateId, FindPrecedingSiblingNodesFilter::create()); if ($precedingSiblings->toNodeAggregateIds()->contain($siblingNodeAggregateId)) { return; @@ -583,7 +583,7 @@ protected function requireNodeAggregateToBeChild( ): void { $childNodes = $contentGraph->getSubgraph( $dimensionSpacePoint, - VisibilityConstraints::withoutRestrictions() + VisibilityConstraints::createEmpty() )->findChildNodes($parentNodeAggregateId, FindChildNodesFilter::create()); if ($childNodes->toNodeAggregateIds()->contain($childNodeAggregateId)) { return; diff --git a/Neos.ContentRepository.Core/Classes/Feature/Common/NodeCreationInternals.php b/Neos.ContentRepository.Core/Classes/Feature/Common/NodeCreationInternals.php index 5a7e2ff42e4..d99baaf6b29 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/Common/NodeCreationInternals.php +++ b/Neos.ContentRepository.Core/Classes/Feature/Common/NodeCreationInternals.php @@ -45,7 +45,7 @@ private function resolveInterdimensionalSiblingsForCreation( OriginDimensionSpacePoint $sourceOrigin, DimensionSpacePointSet $coveredDimensionSpacePoints, ): InterdimensionalSiblings { - $subGraph = $contentGraph->getSubgraph($sourceOrigin->toDimensionSpacePoint(), VisibilityConstraints::withoutRestrictions()); + $subGraph = $contentGraph->getSubgraph($sourceOrigin->toDimensionSpacePoint(), VisibilityConstraints::createEmpty()); $originAlternativeSucceedingSiblings = $subGraph->findSucceedingSiblingNodes( $requestedSucceedingSiblingNodeAggregateId, FindSucceedingSiblingNodesFilter::create() @@ -53,7 +53,7 @@ private function resolveInterdimensionalSiblingsForCreation( $interdimensionalSiblings = []; foreach ($coveredDimensionSpacePoints as $coveredDimensionSpacePoint) { - $subGraph = $contentGraph->getSubgraph($coveredDimensionSpacePoint, VisibilityConstraints::withoutRestrictions()); + $subGraph = $contentGraph->getSubgraph($coveredDimensionSpacePoint, VisibilityConstraints::createEmpty()); $variantSucceedingSibling = $subGraph->findNodeById($requestedSucceedingSiblingNodeAggregateId); if ($variantSucceedingSibling) { // a) happy path, the explicitly requested succeeding sibling also exists in this dimension space point diff --git a/Neos.ContentRepository.Core/Classes/Feature/Common/NodeTypeChangeInternals.php b/Neos.ContentRepository.Core/Classes/Feature/Common/NodeTypeChangeInternals.php index abcb07b5bff..c95225339a6 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/Common/NodeTypeChangeInternals.php +++ b/Neos.ContentRepository.Core/Classes/Feature/Common/NodeTypeChangeInternals.php @@ -194,7 +194,7 @@ private function findDimensionSpacePointsConnectingParentAndChildAggregate( ): DimensionSpacePointSet { $points = []; foreach ($childNodeAggregate->coveredDimensionSpacePoints as $coveredDimensionSpacePoint) { - $parentNode = $contentGraph->getSubgraph($coveredDimensionSpacePoint, VisibilityConstraints::withoutRestrictions())->findParentNode( + $parentNode = $contentGraph->getSubgraph($coveredDimensionSpacePoint, VisibilityConstraints::createEmpty())->findParentNode( $childNodeAggregate->nodeAggregateId ); if ( diff --git a/Neos.ContentRepository.Core/Classes/Feature/Common/NodeVariationInternals.php b/Neos.ContentRepository.Core/Classes/Feature/Common/NodeVariationInternals.php index 349de0d4ced..a23556782f1 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/Common/NodeVariationInternals.php +++ b/Neos.ContentRepository.Core/Classes/Feature/Common/NodeVariationInternals.php @@ -276,14 +276,14 @@ private function resolveInterdimensionalSiblings( DimensionSpacePointSet $variantCoverage, ): InterdimensionalSiblings { $originSiblings = $contentGraph - ->getSubgraph($sourceOrigin->toDimensionSpacePoint(), VisibilityConstraints::withoutRestrictions()) + ->getSubgraph($sourceOrigin->toDimensionSpacePoint(), VisibilityConstraints::createEmpty()) ->findSucceedingSiblingNodes($varyingNodeAggregateId, FindSucceedingSiblingNodesFilter::create()); $interdimensionalSiblings = []; foreach ($variantCoverage as $variantDimensionSpacePoint) { // check the siblings succeeding in the origin dimension space point foreach ($originSiblings as $originSibling) { - $variantSibling = $contentGraph->getSubgraph($variantDimensionSpacePoint, VisibilityConstraints::withoutRestrictions())->findNodeById($originSibling->aggregateId); + $variantSibling = $contentGraph->getSubgraph($variantDimensionSpacePoint, VisibilityConstraints::createEmpty())->findNodeById($originSibling->aggregateId); if (!$variantSibling) { continue; } diff --git a/Neos.ContentRepository.Core/Classes/Feature/DimensionSpaceAdjustment/DimensionSpaceCommandHandler.php b/Neos.ContentRepository.Core/Classes/Feature/DimensionSpaceAdjustment/DimensionSpaceCommandHandler.php index 72695af98c7..c92bb724118 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/DimensionSpaceAdjustment/DimensionSpaceCommandHandler.php +++ b/Neos.ContentRepository.Core/Classes/Feature/DimensionSpaceAdjustment/DimensionSpaceCommandHandler.php @@ -142,7 +142,7 @@ private static function requireDimensionSpacePointToBeEmptyInContentStream( ContentGraphInterface $contentGraph, DimensionSpacePoint $dimensionSpacePoint ): void { - $hasNodes = $contentGraph->getSubgraph($dimensionSpacePoint, VisibilityConstraints::withoutRestrictions())->countNodes(); + $hasNodes = $contentGraph->getSubgraph($dimensionSpacePoint, VisibilityConstraints::createEmpty())->countNodes(); if ($hasNodes > 0) { throw new DimensionSpacePointAlreadyExists(sprintf( 'the content stream %s already contained nodes in dimension space point %s - this is not allowed.', diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeMove/NodeMove.php b/Neos.ContentRepository.Core/Classes/Feature/NodeMove/NodeMove.php index b062a80ecbc..06aa0a5519b 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeMove/NodeMove.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeMove/NodeMove.php @@ -249,7 +249,7 @@ private function resolveInterdimensionalSiblingsForMove( ): InterdimensionalSiblings { $selectedSubgraph = $contentGraph->getSubgraph( $selectedDimensionSpacePoint, - VisibilityConstraints::withoutRestrictions() + VisibilityConstraints::createEmpty() ); $alternativeSucceedingSiblingIds = $succeedingSiblingId ? $selectedSubgraph->findSucceedingSiblingNodes( @@ -268,7 +268,7 @@ private function resolveInterdimensionalSiblingsForMove( foreach ($affectedDimensionSpacePoints as $dimensionSpacePoint) { $variantSubgraph = $contentGraph->getSubgraph( $dimensionSpacePoint, - VisibilityConstraints::withoutRestrictions() + VisibilityConstraints::createEmpty() ); if ($succeedingSiblingId) { $variantSucceedingSibling = $variantSubgraph->findNodeById($succeedingSiblingId); diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeRemoval/Command/RemoveNodeAggregate.php b/Neos.ContentRepository.Core/Classes/Feature/NodeRemoval/Command/RemoveNodeAggregate.php index 806e24f191a..9809b67f71c 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeRemoval/Command/RemoveNodeAggregate.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeRemoval/Command/RemoveNodeAggregate.php @@ -31,20 +31,25 @@ \JsonSerializable, RebasableToOtherWorkspaceInterface { + /** + * @deprecated with Neos 9 Beta 19. Must not be specified any longer. Might get removed at any point. Only part of the command to handle rebasing legacy events. + */ + public ?NodeAggregateId $removalAttachmentPoint; + /** * @param WorkspaceName $workspaceName The workspace in which the remove operation is to be performed * @param NodeAggregateId $nodeAggregateId The identifier of the node aggregate to remove * @param DimensionSpacePoint $coveredDimensionSpacePoint One of the dimension space points covered by the node aggregate in which the user intends to remove it * @param NodeVariantSelectionStrategy $nodeVariantSelectionStrategy The strategy the user chose to determine which specialization variants will also be removed - * @param NodeAggregateId|null $removalAttachmentPoint Internal. It stores the document node id of the removed node, as that is what the UI needs later on for the change display. {@see self::withRemovalAttachmentPoint()} */ private function __construct( public WorkspaceName $workspaceName, public NodeAggregateId $nodeAggregateId, public DimensionSpacePoint $coveredDimensionSpacePoint, public NodeVariantSelectionStrategy $nodeVariantSelectionStrategy, - public ?NodeAggregateId $removalAttachmentPoint + ?NodeAggregateId $removalAttachmentPoint ) { + $this->removalAttachmentPoint = $removalAttachmentPoint; } /** @@ -71,23 +76,6 @@ public static function fromArray(array $array): self ); } - /** - * This adds usually the NodeAggregateId of the parent document node of the deleted node. - * It is needed for instance in the Neos UI for the following scenario: - * - when removing a node, you still need to be able to publish the removal. - * - For this to work, the Neos UI needs to know the id of the removed Node, **on the page where the removal happened** - * (so that the user can decide to publish a single page INCLUDING the removal on the page) - * - Because this command will *remove* the edge, - * we cannot know the position in the tree after doing the removal anymore. - * - * @param NodeAggregateId $removalAttachmentPoint - * @internal - */ - public function withRemovalAttachmentPoint(NodeAggregateId $removalAttachmentPoint): self - { - return new self($this->workspaceName, $this->nodeAggregateId, $this->coveredDimensionSpacePoint, $this->nodeVariantSelectionStrategy, $removalAttachmentPoint); - } - /** * @return array */ @@ -104,7 +92,7 @@ public function createCopyForWorkspace( $this->nodeAggregateId, $this->coveredDimensionSpacePoint, $this->nodeVariantSelectionStrategy, - $this->removalAttachmentPoint, + $this->removalAttachmentPoint ); } } diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeRemoval/Event/NodeAggregateWasRemoved.php b/Neos.ContentRepository.Core/Classes/Feature/NodeRemoval/Event/NodeAggregateWasRemoved.php index b3a82f88b2d..d1a70900f37 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeRemoval/Event/NodeAggregateWasRemoved.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeRemoval/Event/NodeAggregateWasRemoved.php @@ -21,7 +21,6 @@ use Neos\ContentRepository\Core\Feature\Common\EmbedsNodeAggregateId; use Neos\ContentRepository\Core\Feature\Common\EmbedsWorkspaceName; use Neos\ContentRepository\Core\Feature\Common\PublishableToWorkspaceInterface; -use Neos\ContentRepository\Core\Feature\NodeRemoval\Command\RemoveNodeAggregate; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; @@ -36,15 +35,20 @@ EmbedsNodeAggregateId, EmbedsWorkspaceName { + /** + * @deprecated with Neos 9 Beta 19. Must not be specified any longer. Might get removed at any point. + */ + public ?NodeAggregateId $removalAttachmentPoint; + public function __construct( public WorkspaceName $workspaceName, public ContentStreamId $contentStreamId, public NodeAggregateId $nodeAggregateId, public OriginDimensionSpacePointSet $affectedOccupiedDimensionSpacePoints, public DimensionSpacePointSet $affectedCoveredDimensionSpacePoints, - /** {@see RemoveNodeAggregate::$removalAttachmentPoint} for detailed docs what this is used for. */ - public ?NodeAggregateId $removalAttachmentPoint = null + ?NodeAggregateId $removalAttachmentPoint = null ) { + $this->removalAttachmentPoint = $removalAttachmentPoint; } public function getContentStreamId(): ContentStreamId @@ -64,7 +68,7 @@ public function getWorkspaceName(): WorkspaceName public function withWorkspaceNameAndContentStreamId(WorkspaceName $targetWorkspaceName, ContentStreamId $contentStreamId): self { - return new NodeAggregateWasRemoved( + return new self( $targetWorkspaceName, $contentStreamId, $this->nodeAggregateId, diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeRemoval/NodeRemoval.php b/Neos.ContentRepository.Core/Classes/Feature/NodeRemoval/NodeRemoval.php index 5f4f0e10078..30a53a6cc7f 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeRemoval/NodeRemoval.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeRemoval/NodeRemoval.php @@ -26,7 +26,6 @@ use Neos\ContentRepository\Core\Projection\ContentGraph\NodeAggregate; use Neos\ContentRepository\Core\SharedModel\Exception\ContentStreamDoesNotExistYet; use Neos\ContentRepository\Core\SharedModel\Exception\TetheredNodeAggregateCannotBeRemoved; -use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; /** * @internal implementation detail of Command Handlers @@ -60,12 +59,6 @@ private function handleRemoveNodeAggregate( $nodeAggregate, $command->coveredDimensionSpacePoint ); - if ($command->removalAttachmentPoint instanceof NodeAggregateId) { - $this->requireProjectedNodeAggregate( - $contentGraph, - $command->removalAttachmentPoint - ); - } $events = Events::with( new NodeAggregateWasRemoved( diff --git a/Neos.ContentRepository.Core/Classes/Feature/Security/StaticAuthProvider.php b/Neos.ContentRepository.Core/Classes/Feature/Security/StaticAuthProvider.php index 2d44bcf63fe..2aa741f5990 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/Security/StaticAuthProvider.php +++ b/Neos.ContentRepository.Core/Classes/Feature/Security/StaticAuthProvider.php @@ -7,6 +7,8 @@ use Neos\ContentRepository\Core\CommandHandler\CommandInterface; use Neos\ContentRepository\Core\Feature\Security\Dto\Privilege; use Neos\ContentRepository\Core\Feature\Security\Dto\UserId; +use Neos\ContentRepository\Core\Feature\SubtreeTagging\Dto\SubtreeTag; +use Neos\ContentRepository\Core\Feature\SubtreeTagging\Dto\SubtreeTags; use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; @@ -29,7 +31,7 @@ public function getAuthenticatedUserId(): UserId public function getVisibilityConstraints(WorkspaceName $workspaceName): VisibilityConstraints { - return VisibilityConstraints::default(); + return VisibilityConstraints::excludeSubtreeTags(SubtreeTags::create(SubtreeTag::disabled())); } public function canReadNodesFromWorkspace(WorkspaceName $workspaceName): Privilege diff --git a/Neos.ContentRepository.Core/Classes/Feature/SubtreeTagging/Dto/SubtreeTags.php b/Neos.ContentRepository.Core/Classes/Feature/SubtreeTagging/Dto/SubtreeTags.php index f016944baa8..ef01e7c3ea0 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/SubtreeTagging/Dto/SubtreeTags.php +++ b/Neos.ContentRepository.Core/Classes/Feature/SubtreeTagging/Dto/SubtreeTags.php @@ -41,6 +41,11 @@ public static function createEmpty(): self return new self(); } + public static function create(SubtreeTag ...$tags): self + { + return new self(...$tags); + } + /** * @param array $tags */ diff --git a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/VisibilityConstraints.php b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/VisibilityConstraints.php index f1c4f3b93ae..2f1cae0aae3 100644 --- a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/VisibilityConstraints.php +++ b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/VisibilityConstraints.php @@ -14,56 +14,107 @@ namespace Neos\ContentRepository\Core\Projection\ContentGraph; +use Neos\ContentRepository\Core\ContentRepository; +use Neos\ContentRepository\Core\Feature\Security\AuthProviderInterface; use Neos\ContentRepository\Core\Feature\SubtreeTagging\Dto\SubtreeTag; use Neos\ContentRepository\Core\Feature\SubtreeTagging\Dto\SubtreeTags; /** - * The visibility constraints define a context in which the content graph is accessed. + * The visibility constraints define how nodes in the content subgraph are accessed. * - * For example: In the `frontend` context, nodes with the `disabled` tag are excluded. In the `backend` context {@see self::withoutRestrictions()} they are included + * For this the constraints need to be provided to {@see ContentGraphInterface::getSubgraph()}. + * Alternatively {@see ContentRepository::getContentSubgraph()} uses the implemented {@see AuthProviderInterface} to determine + * the visibility constraint the current applications state via {@see AuthProviderInterface::getVisibilityConstraints()} + * + * To have nodes for example with the tag `my-disabled` excluded use: + * + * VisibilityConstraints::excludeSubtreeTags(SubtreeTags::create( + * SubtreeTag::fromString("my-disabled") + * )); + * + * But to access them no constraints can be used includes those: + * + * VisibilityConstraints::createEmpty(); * * @api */ final readonly class VisibilityConstraints implements \JsonSerializable { /** - * @param SubtreeTags $tagConstraints A set of {@see SubtreeTag} instances that will be _excluded_ from the results of any content graph query + * @param SubtreeTags $excludedSubtreeTags A set of {@see SubtreeTag} instances that will be _excluded_ from the results of any content graph query */ private function __construct( - public SubtreeTags $tagConstraints, + public SubtreeTags $excludedSubtreeTags, ) { } /** - * @param SubtreeTags $tagConstraints A set of {@see SubtreeTag} instances that will be _excluded_ from the results of any content graph query + * A subgraph without constraints for finding all nodes without filtering + * + * Nodes for example with tag disabled will be findable */ - public static function fromTagConstraints(SubtreeTags $tagConstraints): self + public static function createEmpty(): self + { + return new self(SubtreeTags::createEmpty()); + } + + /** + * @param SubtreeTags $subtreeTags A set of {@see SubtreeTag} instances that will be _excluded_ from the results of any content graph query + */ + public static function excludeSubtreeTags(SubtreeTags $subtreeTags): self { - return new self($tagConstraints); + return new self($subtreeTags); } public function getHash(): string { - return md5(implode('|', $this->tagConstraints->toStringArray())); + return md5(implode('|', $this->excludedSubtreeTags->toStringArray())); + } + + public function merge(VisibilityConstraints $other): self + { + return new self($this->excludedSubtreeTags->merge($other->excludedSubtreeTags)); } /** - * A non restricted subgraph can find all nodes without filtering. - * Disabled nodes are this way also findable. + * @deprecated with Neos 9 beta 19 please use {@see VisibilityConstraints::excludeSubtreeTags} instead. */ - public static function withoutRestrictions(): self + public static function fromTagConstraints(SubtreeTags $tagConstraints): self { - return new self(SubtreeTags::createEmpty()); + return self::excludeSubtreeTags($tagConstraints); } + /** + * Legacy, only for Neos.Neos context!, for standalone use please use {@see self::excludeSubtreeTags()} + * + * Please look into {@see \Neos\Neos\Domain\Service\NeosVisibilityConstraints()} instead. + * + * @deprecated with Neos 9 beta 19 + */ public static function default(): VisibilityConstraints { - return new self(SubtreeTags::fromStrings('disabled')); + return new self(SubtreeTags::create(SubtreeTag::disabled(), SubtreeTag::fromString('removed'))); } - public function withAddedSubtreeTag(SubtreeTag $subtreeTag): self + /** + * Legacy, only for Neos.Neos context!, for standalone use please use {@see self::createEmpty()} + * + * A subgraph without constraints for finding all nodes without filtering + * + * Nodes for example with tag disabled will be findable but not soft removed nodes + * + * For backwards compatibility to previous betas this method does not do what it promises. + * The classic Neos backend use-case previously used this method to be able to find also disabled nodes. + * Now with the introduction of soft removed tags, empty constraints will cause nodes + * to show up that were previously non-existent. Thus, this factory restricts 'removed' after all. + * + * Please use {@see \Neos\Neos\Domain\Service\NeosVisibilityConstraints::excludeRemoved()} instead. + * + * @deprecated with Neos 9 beta 19 + */ + public static function withoutRestrictions(): self { - return new self($this->tagConstraints->merge(SubtreeTags::fromArray([$subtreeTag]))); + return new self(SubtreeTags::fromStrings('removed')); } /** diff --git a/Neos.ContentRepository.NodeAccess/Classes/FlowQueryOperations/ContextOperation.php b/Neos.ContentRepository.NodeAccess/Classes/FlowQueryOperations/ContextOperation.php index 73c1cc3be25..9c7909aad0b 100644 --- a/Neos.ContentRepository.NodeAccess/Classes/FlowQueryOperations/ContextOperation.php +++ b/Neos.ContentRepository.NodeAccess/Classes/FlowQueryOperations/ContextOperation.php @@ -114,11 +114,11 @@ public function evaluate(FlowQuery $flowQuery, array $arguments) $newWorkspaceName ?? $contextNode->workspaceName )->getSubgraph( $newDimensions ?? $contextNode->dimensionSpacePoint, - VisibilityConstraints::fromTagConstraints( + VisibilityConstraints::excludeSubtreeTags( match ($newInvisibleContentShown) { - true => $contextNode->visibilityConstraints->tagConstraints->without(SubtreeTag::disabled()), - false => $contextNode->visibilityConstraints->tagConstraints->with(SubtreeTag::disabled()), - null => $contextNode->visibilityConstraints->tagConstraints + true => $contextNode->visibilityConstraints->excludedSubtreeTags->without(SubtreeTag::disabled()), + false => $contextNode->visibilityConstraints->excludedSubtreeTags->with(SubtreeTag::disabled()), + null => $contextNode->visibilityConstraints->excludedSubtreeTags } ) ); diff --git a/Neos.ContentRepository.StructureAdjustment/src/Adjustment/DisallowedChildNodeAdjustment.php b/Neos.ContentRepository.StructureAdjustment/src/Adjustment/DisallowedChildNodeAdjustment.php index 82a57da378c..2e1fe692750 100644 --- a/Neos.ContentRepository.StructureAdjustment/src/Adjustment/DisallowedChildNodeAdjustment.php +++ b/Neos.ContentRepository.StructureAdjustment/src/Adjustment/DisallowedChildNodeAdjustment.php @@ -54,7 +54,7 @@ public function findAdjustmentsForNodeType(NodeTypeName $nodeTypeName): \Generat foreach ($nodeAggregate->coveredDimensionSpacePoints as $coveredDimensionSpacePoint) { $subgraph = $this->contentGraph->getSubgraph( $coveredDimensionSpacePoint, - VisibilityConstraints::withoutRestrictions() + VisibilityConstraints::createEmpty() ); $parentNode = $subgraph->findParentNode($nodeAggregate->nodeAggregateId); diff --git a/Neos.ContentRepository.StructureAdjustment/src/Adjustment/TetheredNodeAdjustments.php b/Neos.ContentRepository.StructureAdjustment/src/Adjustment/TetheredNodeAdjustments.php index f4b806dd4bb..bd02a3d9446 100644 --- a/Neos.ContentRepository.StructureAdjustment/src/Adjustment/TetheredNodeAdjustments.php +++ b/Neos.ContentRepository.StructureAdjustment/src/Adjustment/TetheredNodeAdjustments.php @@ -67,7 +67,7 @@ public function findAdjustmentsForNodeType(NodeTypeName $nodeTypeName): \Generat foreach ($nodeType->tetheredNodeTypeDefinitions as $tetheredNodeTypeDefinition) { $tetheredNode = $this->contentGraph->getSubgraph( $originDimensionSpacePoint->toDimensionSpacePoint(), - VisibilityConstraints::withoutRestrictions() + VisibilityConstraints::createEmpty() )->findNodeByPath( $tetheredNodeTypeDefinition->name, $nodeAggregate->nodeAggregateId @@ -135,7 +135,7 @@ function () use ($tetheredNodeAggregate) { // find wrongly ordered tethered nodes if ($foundMissingOrDisallowedTetheredNodes === false) { foreach ($originDimensionSpacePoints as $originDimensionSpacePoint) { - $childNodes = $this->contentGraph->getSubgraph($originDimensionSpacePoint->toDimensionSpacePoint(), VisibilityConstraints::withoutRestrictions())->findChildNodes($nodeAggregate->nodeAggregateId, FindChildNodesFilter::create()); + $childNodes = $this->contentGraph->getSubgraph($originDimensionSpacePoint->toDimensionSpacePoint(), VisibilityConstraints::createEmpty())->findChildNodes($nodeAggregate->nodeAggregateId, FindChildNodesFilter::create()); /** is indexed by node name, and the value is the tethered node itself */ $actualTetheredChildNodes = []; diff --git a/Neos.ContentRepository.TestSuite/Classes/Fakes/FakeAuthProvider.php b/Neos.ContentRepository.TestSuite/Classes/Fakes/FakeAuthProvider.php index 7cf61436185..2a710e28754 100644 --- a/Neos.ContentRepository.TestSuite/Classes/Fakes/FakeAuthProvider.php +++ b/Neos.ContentRepository.TestSuite/Classes/Fakes/FakeAuthProvider.php @@ -52,7 +52,7 @@ public function getVisibilityConstraints(WorkspaceName $workspaceName): Visibili return self::$contentRepositoryAuthProvider->getVisibilityConstraints($workspaceName); } - return VisibilityConstraints::withoutRestrictions(); + return VisibilityConstraints::createEmpty(); } public function canReadNodesFromWorkspace(WorkspaceName $workspaceName): Privilege diff --git a/Neos.ContentRepository.TestSuite/Classes/Unit/NodeSubjectProvider.php b/Neos.ContentRepository.TestSuite/Classes/Unit/NodeSubjectProvider.php index 1799d01ad55..615b44a6418 100644 --- a/Neos.ContentRepository.TestSuite/Classes/Unit/NodeSubjectProvider.php +++ b/Neos.ContentRepository.TestSuite/Classes/Unit/NodeSubjectProvider.php @@ -104,7 +104,7 @@ public function createMinimalNodeOfType( new \DateTimeImmutable(), new \DateTimeImmutable() ), - VisibilityConstraints::withoutRestrictions(), + VisibilityConstraints::createEmpty(), ); } } diff --git a/Neos.ContentRepositoryRegistry/Classes/Command/ContentCommandController.php b/Neos.ContentRepositoryRegistry/Classes/Command/ContentCommandController.php index 09c44100e7d..db5118f4aca 100644 --- a/Neos.ContentRepositoryRegistry/Classes/Command/ContentCommandController.php +++ b/Neos.ContentRepositoryRegistry/Classes/Command/ContentCommandController.php @@ -63,7 +63,7 @@ public function createVariantsRecursivelyCommand(string $source, string $target, try { $sourceSubgraph = $contentRepositoryInstance->getContentGraph($workspaceName)->getSubgraph( $sourceSpacePoint, - VisibilityConstraints::withoutRestrictions() + VisibilityConstraints::createEmpty() ); } catch (WorkspaceDoesNotExist) { $this->outputLine('Workspace "%s" does not exist', [$workspaceName->value]); diff --git a/Neos.Media.Browser/Classes/Controller/UsageController.php b/Neos.Media.Browser/Classes/Controller/UsageController.php index e1ff71bd6ac..69ff9580fc5 100644 --- a/Neos.Media.Browser/Classes/Controller/UsageController.php +++ b/Neos.Media.Browser/Classes/Controller/UsageController.php @@ -142,7 +142,7 @@ public function relatedNodesAction(AssetInterface $asset) $subgraph = $contentRepository->getContentGraph($usage->getWorkspaceName())->getSubgraph( $usage->getOriginDimensionSpacePoint()->toDimensionSpacePoint(), - VisibilityConstraints::withoutRestrictions() + VisibilityConstraints::createEmpty() ); $node = $subgraph->findNodeById($usage->getNodeAggregateId()); diff --git a/Neos.Neos/Classes/AssetUsage/AssetUsageIndexingProcessor.php b/Neos.Neos/Classes/AssetUsage/AssetUsageIndexingProcessor.php index b4abe00f7ec..5a5dc27b4e8 100644 --- a/Neos.Neos/Classes/AssetUsage/AssetUsageIndexingProcessor.php +++ b/Neos.Neos/Classes/AssetUsage/AssetUsageIndexingProcessor.php @@ -58,7 +58,7 @@ public function buildIndex(ContentRepository $contentRepository, NodeTypeName $n foreach ($dimensionSpacePoints as $dimensionSpacePoint) { $this->dispatchMessage($callback, sprintf(' DimensionSpacePoint: %s', $dimensionSpacePoint->toJson())); - $subgraph = $contentGraph->getSubgraph($dimensionSpacePoint, VisibilityConstraints::withoutRestrictions()); + $subgraph = $contentGraph->getSubgraph($dimensionSpacePoint, VisibilityConstraints::createEmpty()); $childNodes = iterator_to_array($subgraph->findChildNodes($rootNodeAggregateId, FindChildNodesFilter::create())); while ($childNodes !== []) { diff --git a/Neos.Neos/Classes/AssetUsage/CatchUpHook/AssetUsageCatchUpHook.php b/Neos.Neos/Classes/AssetUsage/CatchUpHook/AssetUsageCatchUpHook.php index e84c82b9bfa..dffa9c3653e 100644 --- a/Neos.Neos/Classes/AssetUsage/CatchUpHook/AssetUsageCatchUpHook.php +++ b/Neos.Neos/Classes/AssetUsage/CatchUpHook/AssetUsageCatchUpHook.php @@ -100,7 +100,7 @@ public function onAfterCatchUp(): void private function updateNode(WorkspaceName $workspaceName, NodeAggregateId $nodeAggregateId, DimensionSpacePoint $dimensionSpacePoint): void { $contentGraph = $this->contentGraphReadModel->getContentGraph($workspaceName); - $node = $contentGraph->getSubgraph($dimensionSpacePoint, VisibilityConstraints::withoutRestrictions())->findNodeById($nodeAggregateId); + $node = $contentGraph->getSubgraph($dimensionSpacePoint, VisibilityConstraints::createEmpty())->findNodeById($nodeAggregateId); if ($node === null) { // Node not found, nothing to do here. @@ -125,7 +125,7 @@ private function removeNodes(WorkspaceName $workspaceName, NodeAggregateId $node $dimensionSpacePoint ); - $subgraph = $contentGraph->getSubgraph($dimensionSpacePoint, VisibilityConstraints::withoutRestrictions()); + $subgraph = $contentGraph->getSubgraph($dimensionSpacePoint, VisibilityConstraints::createEmpty()); $descendants = $subgraph->findDescendantNodes($nodeAggregateId, FindDescendantNodesFilter::create()); /** @var Node $descendant */ diff --git a/Neos.Neos/Classes/Controller/Backend/ContentController.php b/Neos.Neos/Classes/Controller/Backend/ContentController.php index f03dbbbd620..0b36192961a 100644 --- a/Neos.Neos/Classes/Controller/Backend/ContentController.php +++ b/Neos.Neos/Classes/Controller/Backend/ContentController.php @@ -148,11 +148,7 @@ public function uploadAssetAction(Asset $asset, string $metadata, string $node, $nodeAddress = NodeAddress::fromJsonString($node); $contentRepository = $this->contentRepositoryRegistry->get($nodeAddress->contentRepositoryId); - $node = $contentRepository->getContentGraph($nodeAddress->workspaceName) - ->getSubgraph( - $nodeAddress->dimensionSpacePoint, - VisibilityConstraints::withoutRestrictions() - ) + $node = $contentRepository->getContentSubgraph($nodeAddress->workspaceName, $nodeAddress->dimensionSpacePoint) ->findNodeById($nodeAddress->aggregateId); diff --git a/Neos.Neos/Classes/Controller/Frontend/NodeController.php b/Neos.Neos/Classes/Controller/Frontend/NodeController.php index 19300dea675..a990037439e 100644 --- a/Neos.Neos/Classes/Controller/Frontend/NodeController.php +++ b/Neos.Neos/Classes/Controller/Frontend/NodeController.php @@ -14,14 +14,12 @@ namespace Neos\Neos\Controller\Frontend; -use Neos\ContentRepository\Core\Feature\SubtreeTagging\Dto\SubtreeTag; use Neos\ContentRepository\Core\Projection\ContentGraph\ContentSubgraphInterface; use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindClosestNodeFilter; use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindSubtreeFilter; use Neos\ContentRepository\Core\Projection\ContentGraph\Node; use Neos\ContentRepository\Core\Projection\ContentGraph\Nodes; use Neos\ContentRepository\Core\Projection\ContentGraph\Subtree; -use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints; use Neos\ContentRepository\Core\SharedModel\Node\NodeAddress; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry; @@ -36,7 +34,7 @@ use Neos\Flow\Session\SessionInterface; use Neos\Flow\Utility\Now; use Neos\Neos\Domain\Model\RenderingMode; -use Neos\Neos\Domain\Model\User; +use Neos\Neos\Domain\Service\NeosVisibilityConstraints; use Neos\Neos\Domain\Service\NodeTypeNameFactory; use Neos\Neos\Domain\Service\RenderingModeService; use Neos\Neos\FrontendRouting\Exception\InvalidShortcutException; @@ -205,7 +203,7 @@ public function showAction(string $node): void // Neos backend users have access to the "disabled" SubtreeTag so that they can see/edit disabled nodes. // In this showAction (= "frontend") we have to explicitly remove those disabled nodes, even if the user was authenticated, // to ensure that disabled nodes are NEVER shown recursively. - $visibilityConstraints = $visibilityConstraints->withAddedSubtreeTag(SubtreeTag::disabled()); + $visibilityConstraints = $visibilityConstraints->merge(NeosVisibilityConstraints::excludeDisabled()); $uncachedSubgraph = $contentRepository->getContentGraph($nodeAddress->workspaceName)->getSubgraph($nodeAddress->dimensionSpacePoint, $visibilityConstraints); $subgraph = new ContentSubgraphWithRuntimeCaches($uncachedSubgraph, $this->subgraphCachePool); diff --git a/Neos.Neos/Classes/Controller/Service/NodesController.php b/Neos.Neos/Classes/Controller/Service/NodesController.php index 566b2070775..7be0a918e50 100644 --- a/Neos.Neos/Classes/Controller/Service/NodesController.php +++ b/Neos.Neos/Classes/Controller/Service/NodesController.php @@ -136,14 +136,14 @@ public function indexAction( unset($contextNode); if (is_null($nodeAddress)) { - $subgraph = $contentRepository->getContentGraph(WorkspaceName::fromString($workspaceName))->getSubgraph( - DimensionSpacePoint::fromLegacyDimensionArray($dimensions), - VisibilityConstraints::withoutRestrictions() // we are in a backend controller. + $subgraph = $contentRepository->getContentSubgraph( + WorkspaceName::fromString($workspaceName), + DimensionSpacePoint::fromLegacyDimensionArray($dimensions) ); } else { - $subgraph = $contentRepository->getContentGraph($nodeAddress->workspaceName)->getSubgraph( - $nodeAddress->dimensionSpacePoint, - VisibilityConstraints::withoutRestrictions() // we are in a backend controller. + $subgraph = $contentRepository->getContentSubgraph( + $nodeAddress->workspaceName, + $nodeAddress->dimensionSpacePoint ); } @@ -210,11 +210,7 @@ public function showAction(string $identifier, string $workspaceName = 'live', a $workspaceName = WorkspaceName::fromString($workspaceName); $dimensionSpacePoint = DimensionSpacePoint::fromLegacyDimensionArray($dimensions); - $subgraph = $contentRepository->getContentGraph($workspaceName) - ->getSubgraph( - $dimensionSpacePoint, - VisibilityConstraints::withoutRestrictions() - ); + $subgraph = $contentRepository->getContentSubgraph($workspaceName, $dimensionSpacePoint); $node = $subgraph->findNodeById($nodeAggregateId); @@ -276,19 +272,10 @@ public function createAction( $workspaceName = WorkspaceName::fromString($workspaceName); - $contentGraph = $contentRepository->getContentGraph($workspaceName); - $sourceSubgraph = $contentGraph - ->getSubgraph( - DimensionSpacePoint::fromLegacyDimensionArray($sourceDimensions), - VisibilityConstraints::withoutRestrictions() - ); + $sourceSubgraph = $contentRepository->getContentSubgraph($workspaceName, DimensionSpacePoint::fromLegacyDimensionArray($sourceDimensions)); $targetDimensionSpacePoint = DimensionSpacePoint::fromLegacyDimensionArray($dimensions); - $targetSubgraph = $contentGraph - ->getSubgraph( - $targetDimensionSpacePoint, - VisibilityConstraints::withoutRestrictions() - ); + $targetSubgraph = $contentRepository->getContentSubgraph($workspaceName, $targetDimensionSpacePoint); if ($mode === 'adoptFromAnotherDimension' || $mode === 'adoptFromAnotherDimensionAndCopyContent') { $this->adoptNodeAndParents( diff --git a/Neos.Neos/Classes/Domain/Service/NeosSubtreeTag.php b/Neos.Neos/Classes/Domain/Service/NeosSubtreeTag.php new file mode 100644 index 00000000000..70b7eb6b6cb --- /dev/null +++ b/Neos.Neos/Classes/Domain/Service/NeosSubtreeTag.php @@ -0,0 +1,64 @@ +handle(TagSubtree::create( + * $node->workspaceName, + * $node->aggregateId, + * $node->dimensionSpacePoint, + * NodeVariantSelectionStrategy::STRATEGY_ALL_SPECIALIZATIONS, + * NeosSubtreeTag::removed() + * )); + * + * Nodes tagged as removed will not show up in the frontend rendering nor in the backend by default. + * Only subtracting this removed tag from the {@see VisibilityConstraints} or operating on the node aggregates directly + * will make soft removed nodes available. + * + * @api + */ + public static function removed(): SubtreeTag + { + return SubtreeTag::fromString('removed'); + } +} diff --git a/Neos.Neos/Classes/Domain/Service/NeosVisibilityConstraints.php b/Neos.Neos/Classes/Domain/Service/NeosVisibilityConstraints.php new file mode 100644 index 00000000000..f6b6c3808f5 --- /dev/null +++ b/Neos.Neos/Classes/Domain/Service/NeosVisibilityConstraints.php @@ -0,0 +1,68 @@ +merge(NeosVisibilityConstraints::excludeDisabled()) + * + * @api + */ + public static function excludeDisabled(): VisibilityConstraints + { + return VisibilityConstraints::excludeSubtreeTags(SubtreeTags::create( + SubtreeTag::disabled() + )); + } + + /** + * Default constraints for the backend and cli, ensuring that disabled nodes are visible, but not soft removed nodes + * + * @api + */ + public static function excludeRemoved(): VisibilityConstraints + { + return VisibilityConstraints::excludeSubtreeTags(SubtreeTags::create( + NeosSubtreeTag::removed() + )); + } +} diff --git a/Neos.Neos/Classes/Domain/Service/NodeDuplicationService.php b/Neos.Neos/Classes/Domain/Service/NodeDuplicationService.php index dbd7d21460e..3b08338cf49 100644 --- a/Neos.Neos/Classes/Domain/Service/NodeDuplicationService.php +++ b/Neos.Neos/Classes/Domain/Service/NodeDuplicationService.php @@ -20,7 +20,6 @@ use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindSubtreeFilter; use Neos\ContentRepository\Core\Projection\ContentGraph\References; use Neos\ContentRepository\Core\Projection\ContentGraph\Subtree; -use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints; use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId; use Neos\ContentRepository\Core\SharedModel\Exception\NodeAggregateCurrentlyDoesNotExist; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; @@ -87,7 +86,7 @@ public function copyNodesRecursively( ): void { $contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId); - $subgraph = $contentRepository->getContentGraph($workspaceName)->getSubgraph($sourceDimensionSpacePoint, VisibilityConstraints::withoutRestrictions()); + $subgraph = $contentRepository->getContentGraph($workspaceName)->getSubgraph($sourceDimensionSpacePoint, NeosVisibilityConstraints::excludeRemoved()); $targetParentNode = $subgraph->findNodeById($targetParentNodeAggregateId); if ($targetParentNode === null) { diff --git a/Neos.Neos/Classes/Domain/Service/WorkspacePublishingService.php b/Neos.Neos/Classes/Domain/Service/WorkspacePublishingService.php index 64d31d69318..5b74a87a161 100644 --- a/Neos.Neos/Classes/Domain/Service/WorkspacePublishingService.php +++ b/Neos.Neos/Classes/Domain/Service/WorkspacePublishingService.php @@ -377,25 +377,16 @@ private function isChangePublishableWithinAncestorScope( NodeTypeName $ancestorNodeTypeName, NodeAggregateId $ancestorId ): bool { - // see method comment for `isChangeWithSelfReferencingRemovalAttachmentPoint` - // to get explanation for this condition - if ($this->isChangeWithSelfReferencingRemovalAttachmentPoint($change)) { - if ($ancestorNodeTypeName->equals(NodeTypeNameFactory::forSite())) { - return true; - } - } - if ($change->originDimensionSpacePoint) { $subgraph = $contentRepository->getContentGraph($workspaceName)->getSubgraph( $change->originDimensionSpacePoint->toDimensionSpacePoint(), - VisibilityConstraints::withoutRestrictions() + VisibilityConstraints::createEmpty() ); - // A Change is publishable if the respective node (or the respective - // removal attachment point) has a closest ancestor that matches our + // A Change is publishable if the respective node has a closest ancestor that matches our // current ancestor scope (Document/Site) $actualAncestorNode = $subgraph->findClosestNode( - $change->removalAttachmentPoint ?? $change->nodeAggregateId, + $change->getLegacyRemovalAttachmentPoint() ?? $change->nodeAggregateId, FindClosestNodeFilter::create(nodeTypes: $ancestorNodeTypeName->value) ); @@ -418,32 +409,4 @@ private function findAncestorAggregateIds(ContentGraphInterface $contentGraph, N return $nodeAggregateIds; } - - /** - * Before the introduction of the {@see WorkspacePublishingService}, the UI only ever - * referenced the closest document node as a removal attachment point. - * - * Removed document nodes therefore were referencing themselves. - * - * In order to enable publish/discard of removed documents, the removal - * attachment point of a document MUST refer to an ancestor. The UI now - * references the site node in those cases. - * - * Workspaces that were created before this change was introduced may - * contain removed documents, for which the site node can longer be - * located, because we have no reference to their respective site. - * - * Every document node that matches that description will be published - * or discarded by {@see WorkspacePublishingService::publishChangesInSite()}, regardless of what - * the current site is. - * - * @deprecated remove once we are sure this check is no longer needed due to - * * the UI sending proper commands - * * the ChangeFinder being refactored / rewritten - * (whatever happens first) - */ - private function isChangeWithSelfReferencingRemovalAttachmentPoint(Change $change): bool - { - return $change->removalAttachmentPoint?->equals($change->nodeAggregateId) ?? false; - } } diff --git a/Neos.Neos/Classes/PendingChangesProjection/Change.php b/Neos.Neos/Classes/PendingChangesProjection/Change.php index 798902f5517..8ec775878cd 100644 --- a/Neos.Neos/Classes/PendingChangesProjection/Change.php +++ b/Neos.Neos/Classes/PendingChangesProjection/Change.php @@ -18,6 +18,7 @@ use Doctrine\DBAL\Exception as DbalException; use Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePoint; use Neos\ContentRepository\Core\Feature\NodeRemoval\Command\RemoveNodeAggregate; +use Neos\ContentRepository\Core\Feature\NodeRemoval\Event\NodeAggregateWasRemoved; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; use Neos\Flow\Annotations as Flow; @@ -25,16 +26,13 @@ /** * Read model for pending changes * - * @internal !!! Still a bit unstable - might change in the future. + * @internal Only for consumption inside Neos. Not public api because the implementation will be refactored sooner or later: https://github.com/neos/neos-development-collection/issues/5493 * @Flow\Proxy(false) */ final class Change { public const AGGREGATE_DIMENSIONSPACEPOINT_HASH_PLACEHOLDER = 'AGGREGATE'; - /** - * @param NodeAggregateId|null $removalAttachmentPoint {@see RemoveNodeAggregate::$removalAttachmentPoint} for docs - */ public function __construct( public ContentStreamId $contentStreamId, public NodeAggregateId $nodeAggregateId, @@ -44,10 +42,26 @@ public function __construct( public bool $changed, public bool $moved, public bool $deleted, - public ?NodeAggregateId $removalAttachmentPoint = null + private ?NodeAggregateId $removalAttachmentPoint = null ) { } + /** + * Before soft removals the removalAttachmentPoint was metadata reached through from command to the final event. + * + * It stored the document node id of the removed node, as that was needed later for the change display and publication. + * + * See also https://github.com/neos/neos-development-collection/issues/4487 + * + * We continue to have {@see RemoveNodeAggregate::$removalAttachmentPoint} and {@see NodeAggregateWasRemoved::$removalAttachmentPoint} + * in the core to allow publishing and rebasing the legacy removals as in previous betas. + * + * @deprecated with Neos 9 Beta 19, obsolete via soft removals. Might be removed at any point. + */ + public function getLegacyRemovalAttachmentPoint(): ?NodeAggregateId + { + return $this->removalAttachmentPoint; + } /** * @param Connection $databaseConnection diff --git a/Neos.Neos/Classes/PendingChangesProjection/ChangeFinder.php b/Neos.Neos/Classes/PendingChangesProjection/ChangeFinder.php index f25e288f4e4..138b72a1316 100644 --- a/Neos.Neos/Classes/PendingChangesProjection/ChangeFinder.php +++ b/Neos.Neos/Classes/PendingChangesProjection/ChangeFinder.php @@ -22,8 +22,7 @@ /** * Finder for changes * - * !!! Still a bit unstable - might change in the future. - * + * @internal Only for consumption inside Neos. Not public api because the implementation will be refactored sooner or later: https://github.com/neos/neos-development-collection/issues/5493 * @Flow\Proxy(false) */ final class ChangeFinder implements ProjectionStateInterface diff --git a/Neos.Neos/Classes/PendingChangesProjection/ChangeProjection.php b/Neos.Neos/Classes/PendingChangesProjection/ChangeProjection.php index 746561294a7..01bad691b87 100644 --- a/Neos.Neos/Classes/PendingChangesProjection/ChangeProjection.php +++ b/Neos.Neos/Classes/PendingChangesProjection/ChangeProjection.php @@ -44,10 +44,10 @@ use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; use Neos\EventStore\Model\EventEnvelope; +use Neos\Neos\Domain\Service\NeosSubtreeTag; /** - * TODO: this class needs testing and probably a major refactoring! - * @internal + * @internal Only for consumption inside Neos. Not public api because the implementation will be refactored sooner or later: https://github.com/neos/neos-development-collection/issues/5493 * @implements ProjectionInterface */ class ChangeProjection implements ProjectionInterface @@ -231,6 +231,11 @@ private function whenSubtreeWasTagged(SubtreeWasTagged $event): void return; } foreach ($event->affectedDimensionSpacePoints as $dimensionSpacePoint) { + if ($event->tag->equals(NeosSubtreeTag::removed())) { + $this->markAsDeleted($event->contentStreamId, $event->nodeAggregateId, OriginDimensionSpacePoint::fromDimensionSpacePoint($dimensionSpacePoint)); + continue; + } + $this->markAsChanged( $event->contentStreamId, $event->nodeAggregateId, @@ -259,6 +264,7 @@ private function whenNodeAggregateWasRemoved(NodeAggregateWasRemoved $event): vo return; } + $this->dbal->executeStatement( 'DELETE FROM ' . $this->tableNamePrefix . ' WHERE @@ -299,6 +305,7 @@ private function whenNodeAggregateWasRemoved(NodeAggregateWasRemoved $event): vo 'nodeAggregateId' => $event->nodeAggregateId->value, 'originDimensionSpacePoint' => json_encode($occupiedDimensionSpacePoint), 'originDimensionSpacePointHash' => $occupiedDimensionSpacePoint->hash, + /** legacy information: {@see Change::getLegacyRemovalAttachmentPoint()} */ 'removalAttachmentPoint' => $event->removalAttachmentPoint?->value, ] ); @@ -449,6 +456,16 @@ static function (Change $change) { ); } + private function markAsDeleted( + ContentStreamId $contentStreamId, + NodeAggregateId $nodeAggregateId, + OriginDimensionSpacePoint $originDimensionSpacePoint, + ): void { + $this->modifyChange($contentStreamId, $nodeAggregateId, $originDimensionSpacePoint, static function (Change $change) { + $change->deleted = true; + }); + } + private function modifyChange( ContentStreamId $contentStreamId, NodeAggregateId $nodeAggregateId, diff --git a/Neos.Neos/Classes/PendingChangesProjection/Changes.php b/Neos.Neos/Classes/PendingChangesProjection/Changes.php index 9c23eaaf4f0..2b71d46b280 100644 --- a/Neos.Neos/Classes/PendingChangesProjection/Changes.php +++ b/Neos.Neos/Classes/PendingChangesProjection/Changes.php @@ -19,7 +19,7 @@ /** * Read model for a set of pending changes * - * @internal !!! Still a bit unstable - might change in the future. + * @internal Only for consumption inside Neos. Not public api because the implementation will be refactored sooner or later: https://github.com/neos/neos-development-collection/issues/5493 * @Flow\Proxy(false) * @implements \IteratorAggregate */ diff --git a/Neos.Neos/Classes/Security/Authorization/ContentRepositoryAuthorizationService.php b/Neos.Neos/Classes/Security/Authorization/ContentRepositoryAuthorizationService.php index b187e63f7a6..3d44d401b90 100644 --- a/Neos.Neos/Classes/Security/Authorization/ContentRepositoryAuthorizationService.php +++ b/Neos.Neos/Classes/Security/Authorization/ContentRepositoryAuthorizationService.php @@ -4,7 +4,6 @@ namespace Neos\Neos\Security\Authorization; -use Neos\ContentRepository\Core\Feature\SubtreeTagging\Dto\SubtreeTags; use Neos\ContentRepository\Core\Projection\ContentGraph\Node; use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints; use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId; @@ -21,6 +20,7 @@ use Neos\Neos\Domain\Model\WorkspaceRoleSubject; use Neos\Neos\Domain\Model\WorkspaceRoleSubjects; use Neos\Neos\Domain\Repository\WorkspaceMetadataAndRoleRepository; +use Neos\Neos\Domain\Service\NeosVisibilityConstraints; use Neos\Neos\Security\Authorization\Privilege\EditNodePrivilege; use Neos\Neos\Security\Authorization\Privilege\ReadNodePrivilege; use Neos\Neos\Security\Authorization\Privilege\SubtreeTagPrivilegeSubject; @@ -103,13 +103,14 @@ public function getNodePermissions(Node $node, array $roles): NodePermissions */ public function getVisibilityConstraints(ContentRepositoryId $contentRepositoryId, array $roles): VisibilityConstraints { - $restrictedSubtreeTags = SubtreeTags::createEmpty(); + // soft removals are never visible by default + $restrictedSubtreeTags = NeosVisibilityConstraints::excludeRemoved()->excludedSubtreeTags; /** @var ReadNodePrivilege $privilege */ foreach ($this->policyService->getAllPrivilegesByType(ReadNodePrivilege::class) as $privilege) { if (!$this->privilegeManager->isGrantedForRoles($roles, ReadNodePrivilege::class, new SubtreeTagPrivilegeSubject($privilege->getSubtreeTags(), $contentRepositoryId))) { $restrictedSubtreeTags = $restrictedSubtreeTags->merge($privilege->getSubtreeTags()); } } - return VisibilityConstraints::fromTagConstraints($restrictedSubtreeTags); + return VisibilityConstraints::excludeSubtreeTags($restrictedSubtreeTags); } } diff --git a/Neos.Neos/Classes/Security/ContentRepositoryAuthProvider/ContentRepositoryAuthProvider.php b/Neos.Neos/Classes/Security/ContentRepositoryAuthProvider/ContentRepositoryAuthProvider.php index 73c4bb6bf3f..41a5a369fcf 100644 --- a/Neos.Neos/Classes/Security/ContentRepositoryAuthProvider/ContentRepositoryAuthProvider.php +++ b/Neos.Neos/Classes/Security/ContentRepositoryAuthProvider/ContentRepositoryAuthProvider.php @@ -105,7 +105,7 @@ public function canExecuteCommand(CommandInterface $command): Privilege } $node = $this->contentGraphReadModel ->getContentGraph($nodeThatRequiresEditPrivilege->workspaceName) - ->getSubgraph($nodeThatRequiresEditPrivilege->dimensionSpacePoint, VisibilityConstraints::withoutRestrictions()) + ->getSubgraph($nodeThatRequiresEditPrivilege->dimensionSpacePoint, VisibilityConstraints::createEmpty()) ->findNodeById($nodeThatRequiresEditPrivilege->aggregateId); if ($node === null) { return Privilege::denied(sprintf('Failed to load node "%s" in workspace "%s"', $nodeThatRequiresEditPrivilege->aggregateId->value, $nodeThatRequiresEditPrivilege->workspaceName->value)); diff --git a/Neos.Neos/Configuration/Policy.yaml b/Neos.Neos/Configuration/Policy.yaml index 8b56990a8d7..325656d619e 100644 --- a/Neos.Neos/Configuration/Policy.yaml +++ b/Neos.Neos/Configuration/Policy.yaml @@ -128,7 +128,6 @@ privilegeTargets: # !!! matcher payload in this case is a ContentRepository SubtreeTag, # i.e. nodes with ths specified tag are only read if the user has the corresponding privilegeTarget assigned. matcher: 'disabled' - roles: 'Neos.Flow:Everybody': diff --git a/Neos.Neos/Tests/Behavior/Features/ContentRepository/NodeCopying/CopyNode_NoDimensions.feature b/Neos.Neos/Tests/Behavior/Features/ContentRepository/NodeCopying/CopyNode_NoDimensions.feature index f061b789a00..21830dd333d 100644 --- a/Neos.Neos/Tests/Behavior/Features/ContentRepository/NodeCopying/CopyNode_NoDimensions.feature +++ b/Neos.Neos/Tests/Behavior/Features/ContentRepository/NodeCopying/CopyNode_NoDimensions.feature @@ -177,6 +177,33 @@ Feature: Copy nodes (without dimensions) | Key | Type | Value | | title | string | "I am Node A1" | + Scenario: Soft removed nodes are not copied + When I am in workspace "live" and dimension space point {} + When the following CreateNodeAggregateWithNode commands are executed: + | nodeAggregateId | parentNodeAggregateId | nodeTypeName | initialPropertyValues | + | child-a | sir-nodeward-nodington-iii | Neos.ContentRepository.Testing:Document | {} | + | child-a1 | child-a | Neos.ContentRepository.Testing:Document | {"title": "I am Node A1"} | + | child-a2 | child-a | Neos.ContentRepository.Testing:Document | {} | + | child-b | sir-nodeward-nodington-iii | Neos.ContentRepository.Testing:Document | {} | + Given the command TagSubtree is executed with payload: + | Key | Value | + | nodeAggregateId | "child-a" | + | coveredDimensionSpacePoint | {} | + | nodeVariantSelectionStrategy | "allVariants" | + | tag | "removed" | + + When copy nodes recursively is executed with payload: + | Key | Value | + | sourceDimensionSpacePoint | {} | + | sourceNodeAggregateId | "sir-nodeward-nodington-iii" | + | targetDimensionSpacePoint | {} | + | targetParentNodeAggregateId | "nody-mc-nodeface" | + | targetSucceedingSiblingnodeAggregateId | null | + | nodeAggregateIdMapping | {"sir-nodeward-nodington-iii": "sir-nodeward-nodington-iii-copy", "child-b": "child-b-copy"} | + + And I expect the node aggregate "sir-nodeward-nodington-iii-copy" to exist + And I expect this node aggregate to have the child node aggregates ["child-b-copy"] + Scenario: References are copied for child nodes When I am in workspace "live" and dimension space point {} When the following CreateNodeAggregateWithNode commands are executed: diff --git a/Neos.Neos/Tests/Behavior/Features/PendingChanges/NodeSoftRemoval/01-SoftRemoveNodeAggregate_WithoutDimensions.feature b/Neos.Neos/Tests/Behavior/Features/PendingChanges/NodeSoftRemoval/01-SoftRemoveNodeAggregate_WithoutDimensions.feature new file mode 100644 index 00000000000..2bee22dcb38 --- /dev/null +++ b/Neos.Neos/Tests/Behavior/Features/PendingChanges/NodeSoftRemoval/01-SoftRemoveNodeAggregate_WithoutDimensions.feature @@ -0,0 +1,118 @@ +@contentrepository @adapters=DoctrineDBAL +@flowEntities +Feature: Soft remove node aggregate with node without dimensions + + Background: + Given using no content dimensions + And using the following node types: + """yaml + 'Neos.ContentRepository.Testing:Node': + properties: + text: + type: string + + """ + And using identifier "default", I define a content repository + And I am in content repository "default" + And the command CreateRootWorkspace is executed with payload: + | Key | Value | + | workspaceName | "live" | + | workspaceTitle | "Live" | + | workspaceDescription | "The live workspace" | + | newContentStreamId | "cs-identifier" | + + And I am in workspace "live" + And I am in dimension space point {} + + And I am user identified by "initiating-user-identifier" + And the command CreateRootNodeAggregateWithNode is executed with payload: + | Key | Value | + | nodeAggregateId | "lady-eleonode-rootford" | + | nodeTypeName | "Neos.ContentRepository:Root" | + + Then the following CreateNodeAggregateWithNode commands are executed: + | nodeAggregateId | nodeName | parentNodeAggregateId | nodeTypeName | initialPropertyValues | + | sir-david-nodenborough | node | lady-eleonode-rootford | Neos.ContentRepository.Testing:Node | {} | + | nody-mc-nodeface | child-node | sir-david-nodenborough | Neos.ContentRepository.Testing:Node | {"text": "This is a text about Nody Mc Nodeface"} | + | sir-nodeward-nodington-iii | esquire | lady-eleonode-rootford | Neos.ContentRepository.Testing:Node | {"text": "This is a text about Sir Nodeward Nodington III"} | + + Then the command CreateWorkspace is executed with payload: + | Key | Value | + | workspaceName | "user-workspace" | + | baseWorkspaceName | "live" | + | newContentStreamId | "user-cs-id" | + And I am in workspace "user-workspace" + And I am in dimension space point {} + + Scenario: Soft remove node aggregate in user-workspace + Given the command TagSubtree is executed with payload: + | Key | Value | + | workspaceName | "user-workspace" | + | nodeAggregateId | "nody-mc-nodeface" | + | coveredDimensionSpacePoint | {} | + | nodeVariantSelectionStrategy | "allSpecializations" | + | tag | "removed" | + + Then I expect the ChangeProjection to have the following changes in "user-cs-id": + | nodeAggregateId | created | changed | moved | deleted | originDimensionSpacePoint | + | nody-mc-nodeface | 0 | 0 | 0 | 1 | {} | + And I expect the ChangeProjection to have no changes in "cs-identifier" + + Scenario: Soft remove node aggregate in live workspace + Given the command TagSubtree is executed with payload: + | Key | Value | + | workspaceName | "live" | + | nodeAggregateId | "nody-mc-nodeface" | + | coveredDimensionSpacePoint | {} | + | nodeVariantSelectionStrategy | "allSpecializations" | + | tag | "removed" | + + Then I expect the ChangeProjection to have no changes in "cs-identifier" + And I expect the ChangeProjection to have no changes in "user-cs-id" + + Scenario: Soft remove node aggregate with children in user workspace + Given the command TagSubtree is executed with payload: + | Key | Value | + | workspaceName | "user-workspace" | + | nodeAggregateId | "sir-david-nodenborough" | + | coveredDimensionSpacePoint | {} | + | nodeVariantSelectionStrategy | "allSpecializations" | + | tag | "removed" | + + Then I expect the ChangeProjection to have the following changes in "user-cs-id": + | nodeAggregateId | created | changed | moved | deleted | originDimensionSpacePoint | + | sir-david-nodenborough | 0 | 0 | 0 | 1 | {} | + And I expect the ChangeProjection to have no changes in "cs-identifier" + + Scenario: Soft remove node aggregate with children in live workspace + Given the command TagSubtree is executed with payload: + | Key | Value | + | workspaceName | "live" | + | nodeAggregateId | "sir-david-nodenborough" | + | coveredDimensionSpacePoint | {} | + | nodeVariantSelectionStrategy | "allSpecializations" | + | tag | "removed" | + + Then I expect the ChangeProjection to have no changes in "cs-identifier" + And I expect the ChangeProjection to have no changes in "user-cs-id" + + Scenario: Soft remove node aggregate in user workspace which was already modified + Given the command SetNodeProperties is executed with payload: + | Key | Value | + | workspaceName | "user-workspace" | + | nodeAggregateId | "sir-david-nodenborough" | + | originDimensionSpacePoint | {} | + | propertyValues | {"text": "Other text"} | + + And the command TagSubtree is executed with payload: + | Key | Value | + | workspaceName | "user-workspace" | + | nodeAggregateId | "sir-david-nodenborough" | + | coveredDimensionSpacePoint | {} | + | nodeVariantSelectionStrategy | "allSpecializations" | + | tag | "removed" | + + Then I expect the ChangeProjection to have the following changes in "user-cs-id": + | nodeAggregateId | created | changed | moved | deleted | originDimensionSpacePoint | + | sir-david-nodenborough | 0 | 1 | 0 | 1 | {} | + And I expect the ChangeProjection to have no changes in "cs-identifier" diff --git a/Neos.Neos/Tests/Behavior/Features/PendingChanges/NodeSoftRemoval/02-SoftRemoveNodeAggregate_WithDimensions.feature b/Neos.Neos/Tests/Behavior/Features/PendingChanges/NodeSoftRemoval/02-SoftRemoveNodeAggregate_WithDimensions.feature new file mode 100644 index 00000000000..20ddb897feb --- /dev/null +++ b/Neos.Neos/Tests/Behavior/Features/PendingChanges/NodeSoftRemoval/02-SoftRemoveNodeAggregate_WithDimensions.feature @@ -0,0 +1,191 @@ +@contentrepository @adapters=DoctrineDBAL +@flowEntities +Feature: Soft remove node aggregate with node without dimensions + + Background: + Given using the following content dimensions: + | Identifier | Values | Generalizations | + | language | de,gsw,fr | gsw->de, fr | + And using the following node types: + """yaml + 'Neos.ContentRepository.Testing:Node': + properties: + text: + type: string + + """ + And using identifier "default", I define a content repository + And I am in content repository "default" + And the command CreateRootWorkspace is executed with payload: + | Key | Value | + | workspaceName | "live" | + | workspaceTitle | "Live" | + | workspaceDescription | "The live workspace" | + | newContentStreamId | "cs-identifier" | + + And I am in workspace "live" + And I am in dimension space point {"language": "de"} + + And I am user identified by "initiating-user-identifier" + And the command CreateRootNodeAggregateWithNode is executed with payload: + | Key | Value | + | nodeAggregateId | "lady-eleonode-rootford" | + | nodeTypeName | "Neos.ContentRepository:Root" | + + Then the following CreateNodeAggregateWithNode commands are executed: + | nodeAggregateId | nodeName | parentNodeAggregateId | nodeTypeName | initialPropertyValues | + | sir-david-nodenborough | node | lady-eleonode-rootford | Neos.ContentRepository.Testing:Node | {} | + | nody-mc-nodeface | child-node | sir-david-nodenborough | Neos.ContentRepository.Testing:Node | {"text": "This is a text about Nody Mc Nodeface"} | + | sir-nodeward-nodington-iii | esquire | lady-eleonode-rootford | Neos.ContentRepository.Testing:Node | {"text": "This is a text about Sir Nodeward Nodington III"} | + And the command CreateNodeVariant is executed with payload: + | Key | Value | + | nodeAggregateId | "sir-david-nodenborough" | + | sourceOrigin | {"language":"de"} | + | targetOrigin | {"language":"gsw"} | + + Then I am in dimension space point {"language": "fr"} + And the command CreateNodeVariant is executed with payload: + | Key | Value | + | nodeAggregateId | "sir-david-nodenborough" | + | sourceOrigin | {"language":"de"} | + | targetOrigin | {"language":"fr"} | + And the following CreateNodeAggregateWithNode commands are executed: + | nodeAggregateId | nodeName | parentNodeAggregateId | nodeTypeName | initialPropertyValues | + | sir-nodeward-nodington-iv | bukara | lady-eleonode-rootford | Neos.ContentRepository.Testing:Node | {"text": "This is a text about Sir Nodeward Nodington IV"} | + | sir-nodeward-nodington-v | tinquarto | lady-eleonode-rootford | Neos.ContentRepository.Testing:Node | {"text": "This is a text about Sir Nodeward Nodington V"} | + + Then the command CreateWorkspace is executed with payload: + | Key | Value | + | workspaceName | "user-workspace" | + | baseWorkspaceName | "live" | + | newContentStreamId | "user-cs-id" | + And I am in workspace "user-workspace" + And I am in dimension space point {"language": "de"} + + Scenario: Soft remove node aggregate in user-workspace + Given the command TagSubtree is executed with payload: + | Key | Value | + | workspaceName | "user-workspace" | + | nodeAggregateId | "nody-mc-nodeface" | + | coveredDimensionSpacePoint | {"language": "de"} | + | nodeVariantSelectionStrategy | "allSpecializations" | + | tag | "removed" | + + Then I expect the ChangeProjection to have the following changes in "user-cs-id": + | nodeAggregateId | created | changed | moved | deleted | originDimensionSpacePoint | + | nody-mc-nodeface | 0 | 0 | 0 | 1 | {"language": "de"} | + | nody-mc-nodeface | 0 | 0 | 0 | 1 | {"language": "gsw"} | + And I expect the ChangeProjection to have no changes in "cs-identifier" + + Scenario: Soft remove node aggregate in live workspace + Given the command TagSubtree is executed with payload: + | Key | Value | + | workspaceName | "live" | + | nodeAggregateId | "nody-mc-nodeface" | + | coveredDimensionSpacePoint | {"language": "de"} | + | nodeVariantSelectionStrategy | "allSpecializations" | + | tag | "removed" | + + Then I expect the ChangeProjection to have no changes in "cs-identifier" + And I expect the ChangeProjection to have no changes in "user-cs-id" + + Scenario: Soft remove node aggregate with children in user workspace with "allSpecializations" + Given the command TagSubtree is executed with payload: + | Key | Value | + | workspaceName | "user-workspace" | + | nodeAggregateId | "sir-david-nodenborough" | + | coveredDimensionSpacePoint | {"language": "de"} | + | nodeVariantSelectionStrategy | "allSpecializations" | + | tag | "removed" | + + Then I expect the ChangeProjection to have the following changes in "user-cs-id": + | nodeAggregateId | created | changed | moved | deleted | originDimensionSpacePoint | + | sir-david-nodenborough | 0 | 0 | 0 | 1 | {"language": "de"} | + | sir-david-nodenborough | 0 | 0 | 0 | 1 | {"language": "gsw"} | + And I expect the ChangeProjection to have no changes in "cs-identifier" + + Scenario: Soft remove node aggregate with children in user workspace with "allVariants" + Given the command TagSubtree is executed with payload: + | Key | Value | + | workspaceName | "user-workspace" | + | nodeAggregateId | "sir-david-nodenborough" | + | coveredDimensionSpacePoint | {"language": "de"} | + | nodeVariantSelectionStrategy | "allVariants" | + | tag | "removed" | + + Then I expect the ChangeProjection to have the following changes in "user-cs-id": + | nodeAggregateId | created | changed | moved | deleted | originDimensionSpacePoint | + | sir-david-nodenborough | 0 | 0 | 0 | 1 | {"language": "de"} | + | sir-david-nodenborough | 0 | 0 | 0 | 1 | {"language": "gsw"} | + | sir-david-nodenborough | 0 | 0 | 0 | 1 | {"language": "fr"} | + And I expect the ChangeProjection to have no changes in "cs-identifier" + + Scenario: Soft remove node aggregate with children in live workspace + Given the command TagSubtree is executed with payload: + | Key | Value | + | workspaceName | "live" | + | nodeAggregateId | "sir-david-nodenborough" | + | coveredDimensionSpacePoint | {"language": "de"} | + | nodeVariantSelectionStrategy | "allSpecializations" | + | tag | "removed" | + + Then I expect the ChangeProjection to have no changes in "cs-identifier" + And I expect the ChangeProjection to have no changes in "user-cs-id" + + Scenario: Soft remove node aggregate in user workspace which was already modified with "allSpecializations" + Given the command SetNodeProperties is executed with payload: + | Key | Value | + | workspaceName | "user-workspace" | + | nodeAggregateId | "sir-david-nodenborough" | + | originDimensionSpacePoint | {"language": "de"} | + | propertyValues | {"text": "Other text"} | + And the command SetNodeProperties is executed with payload: + | Key | Value | + | workspaceName | "user-workspace" | + | nodeAggregateId | "sir-david-nodenborough" | + | originDimensionSpacePoint | {"language": "fr"} | + | propertyValues | {"text": "Other text"} | + + And the command TagSubtree is executed with payload: + | Key | Value | + | workspaceName | "user-workspace" | + | nodeAggregateId | "sir-david-nodenborough" | + | coveredDimensionSpacePoint | {"language": "de"} | + | nodeVariantSelectionStrategy | "allSpecializations" | + | tag | "removed" | + + Then I expect the ChangeProjection to have the following changes in "user-cs-id": + | nodeAggregateId | created | changed | moved | deleted | originDimensionSpacePoint | + | sir-david-nodenborough | 0 | 1 | 0 | 1 | {"language": "de"} | + | sir-david-nodenborough | 0 | 0 | 0 | 1 | {"language": "gsw"} | + | sir-david-nodenborough | 0 | 1 | 0 | 0 | {"language": "fr"} | + And I expect the ChangeProjection to have no changes in "cs-identifier" + + Scenario: Soft remove node aggregate in user workspace which was already modified with "allVariants" + Given the command SetNodeProperties is executed with payload: + | Key | Value | + | workspaceName | "user-workspace" | + | nodeAggregateId | "sir-david-nodenborough" | + | originDimensionSpacePoint | {"language": "de"} | + | propertyValues | {"text": "Other text"} | + And the command SetNodeProperties is executed with payload: + | Key | Value | + | workspaceName | "user-workspace" | + | nodeAggregateId | "sir-david-nodenborough" | + | originDimensionSpacePoint | {"language": "fr"} | + | propertyValues | {"text": "Other text"} | + + And the command TagSubtree is executed with payload: + | Key | Value | + | workspaceName | "user-workspace" | + | nodeAggregateId | "sir-david-nodenborough" | + | coveredDimensionSpacePoint | {"language": "de"} | + | nodeVariantSelectionStrategy | "allVariants" | + | tag | "removed" | + + Then I expect the ChangeProjection to have the following changes in "user-cs-id": + | nodeAggregateId | created | changed | moved | deleted | originDimensionSpacePoint | + | sir-david-nodenborough | 0 | 1 | 0 | 1 | {"language": "de"} | + | sir-david-nodenborough | 0 | 0 | 0 | 1 | {"language": "gsw"} | + | sir-david-nodenborough | 0 | 1 | 0 | 1 | {"language": "fr"} | + And I expect the ChangeProjection to have no changes in "cs-identifier" diff --git a/Neos.Neos/Tests/Unit/Fusion/Helper/CachingHelperTest.php b/Neos.Neos/Tests/Unit/Fusion/Helper/CachingHelperTest.php index 019637ef4d3..03dbb22129e 100644 --- a/Neos.Neos/Tests/Unit/Fusion/Helper/CachingHelperTest.php +++ b/Neos.Neos/Tests/Unit/Fusion/Helper/CachingHelperTest.php @@ -257,7 +257,7 @@ private function createNode(NodeAggregateId $nodeAggregateId): Node null, NodeTags::createEmpty(), Timestamps::create($now, $now, null, null), - VisibilityConstraints::withoutRestrictions(), + VisibilityConstraints::createEmpty(), ); } } diff --git a/Neos.TimeableNodeVisibility/Classes/Service/TimeableNodeVisibilityService.php b/Neos.TimeableNodeVisibility/Classes/Service/TimeableNodeVisibilityService.php index 92561a17464..ac2e03ad39e 100644 --- a/Neos.TimeableNodeVisibility/Classes/Service/TimeableNodeVisibilityService.php +++ b/Neos.TimeableNodeVisibility/Classes/Service/TimeableNodeVisibilityService.php @@ -15,14 +15,13 @@ use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\PropertyValue\Criteria\OrCriteria; use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\PropertyValue\Criteria\PropertyValueLessThanOrEqual; use Neos\ContentRepository\Core\Projection\ContentGraph\Node; -use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints; -use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId; use Neos\ContentRepository\Core\SharedModel\Node\NodeVariantSelectionStrategy; use Neos\ContentRepository\Core\SharedModel\Node\PropertyName; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry; use Neos\Flow\Annotations as Flow; +use Neos\Neos\Domain\Service\NeosVisibilityConstraints; use Neos\TimeableNodeVisibility\Domain\ChangedVisibilities; use Neos\TimeableNodeVisibility\Domain\ChangedVisibility; use Psr\Log\LoggerInterface; @@ -92,10 +91,9 @@ private function getNodesWithExceededDates(ContentRepository $contentRepository, $contentGraph = $contentRepository->getContentGraph($workspaceName); - // We fetch without restriction to get also all disabled nodes $subgraph = $contentGraph->getSubgraph( $dimensionSpacePoint, - VisibilityConstraints::withoutRestrictions() + NeosVisibilityConstraints::excludeRemoved() ); $sitesNodeTypeName = NodeTypeName::fromString('Neos.Neos:Sites'); diff --git a/Neos.TimeableNodeVisibility/Tests/Behavior/Bootstrap/FeatureContext.php b/Neos.TimeableNodeVisibility/Tests/Behavior/Bootstrap/FeatureContext.php index 2656730cb90..3f23da5d49c 100644 --- a/Neos.TimeableNodeVisibility/Tests/Behavior/Bootstrap/FeatureContext.php +++ b/Neos.TimeableNodeVisibility/Tests/Behavior/Bootstrap/FeatureContext.php @@ -55,7 +55,7 @@ public function iExpectThisNodeToBeEnabled(): void Assert::assertNotNull($this->currentNode, 'No current node selected'); $subgraph = $this->currentContentRepository->getContentGraph($this->currentWorkspaceName)->getSubgraph( $this->currentDimensionSpacePoint, - VisibilityConstraints::withoutRestrictions(), + VisibilityConstraints::createEmpty(), ); $currentNode = $subgraph->findNodeById($this->currentNode->aggregateId); Assert::assertNotNull($currentNode, sprintf('Failed to find node with id "%s" in workspace %s and dimension %s', $this->currentNode->aggregateId->value, $subgraph->getWorkspaceName()->value, $subgraph->getDimensionSpacePoint()->toJson())); @@ -70,7 +70,7 @@ public function iExpectThisNodeToBeDisabled(): void Assert::assertNotNull($this->currentNode, 'No current node selected'); $subgraph = $this->currentContentRepository->getContentGraph($this->currentWorkspaceName)->getSubgraph( $this->currentDimensionSpacePoint, - VisibilityConstraints::withoutRestrictions(), + VisibilityConstraints::createEmpty(), ); $currentNode = $subgraph->findNodeById($this->currentNode->aggregateId); Assert::assertNotNull($currentNode, sprintf('Failed to find node with id "%s" in workspace %s and dimension %s', $this->currentNode->aggregateId->value, $subgraph->getWorkspaceName()->value, $subgraph->getDimensionSpacePoint()->toJson())); diff --git a/Neos.TimeableNodeVisibility/Tests/Behavior/Features/HandleExceeded.feature b/Neos.TimeableNodeVisibility/Tests/Behavior/Features/HandleExceeded.feature index f4b3234e78a..3db4da6ab63 100644 --- a/Neos.TimeableNodeVisibility/Tests/Behavior/Features/HandleExceeded.feature +++ b/Neos.TimeableNodeVisibility/Tests/Behavior/Features/HandleExceeded.feature @@ -236,6 +236,28 @@ Feature: Simple handling of nodes with exceeded enableAfter and disableAfter dat Then I expect this node to be enabled And I expect exactly 6 events to be published on stream "ContentStream:cs-identifier" + # <===========================|now|===========================> + # -----|Removed|++++++++++++++|---|+++++++++++++++++++++++++++ + Scenario: A disabled, soft removed node with enableAfter past will be ignored because its soft removed + When the following CreateNodeAggregateWithNode commands are executed: + | nodeAggregateId | parentNodeAggregateId | nodeTypeName | initialPropertyValues | + | shernode-homes | lady-eleonode-rootford | Some.Package:Homepage | {} | + | duke-of-contentshire | shernode-homes | Some.Package:Content | {"enableAfterDateTime": {"__type": "DateTimeImmutable", "value": "-10 days"}} | + Then I expect node aggregate identifier "duke-of-contentshire" to lead to node cs-identifier;duke-of-contentshire;{} + And the command DisableNodeAggregate is executed with payload: + | Key | Value | + | nodeAggregateId | "duke-of-contentshire" | + | nodeVariantSelectionStrategy | "allVariants" | + And the command TagSubtree is executed with payload: + | Key | Value | + | nodeAggregateId | "duke-of-contentshire" | + | nodeVariantSelectionStrategy | "allVariants" | + | tag | "removed" | + And I expect exactly 6 events to be published on stream "ContentStream:cs-identifier" + + Then I handle exceeded node dates + Then I expect this node to be disabled + And I expect exactly 6 events to be published on stream "ContentStream:cs-identifier" # <===========================|now|===========================> # ---------------------------|---|---|Enable|+++++|Disable|-- diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 3b7ab36631e..3ba6a0abfa4 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -829,19 +829,9 @@ protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepos $arbitraryDimensionSpacePoint = reset($dimensionSpacePoints); foreach ($changes as $change) { - $workspaceName = $selectedWorkspace->workspaceName; - if ($change->deleted) { - // If we deleted a node, there is no way for us to anymore find the deleted node in the ContentStream - // where the node was deleted. - // Thus, to figure out the rootline for display, we check the *base workspace* Content Stream. - // - // This is safe because the UI basically shows what would be removed once the deletion is published. - $baseWorkspace = $this->requireBaseWorkspace($selectedWorkspace, $contentRepository); - $workspaceName = $baseWorkspace->workspaceName; - } - $subgraph = $contentRepository->getContentGraph($workspaceName)->getSubgraph( + $subgraph = $contentRepository->getContentGraph($selectedWorkspace->workspaceName)->getSubgraph( $change->originDimensionSpacePoint?->toDimensionSpacePoint() ?? $arbitraryDimensionSpacePoint, - VisibilityConstraints::withoutRestrictions() + VisibilityConstraints::createEmpty() ); $node = $subgraph->findNodeById($change->nodeAggregateId); @@ -990,7 +980,7 @@ protected function getOriginalNode( ): ?Node { $baseSubgraph = $contentRepository->getContentGraph($baseWorkspaceName)->getSubgraph( $modifiedNode->dimensionSpacePoint, - VisibilityConstraints::withoutRestrictions() + VisibilityConstraints::createEmpty() ); return $baseSubgraph->findNodeById($modifiedNode->aggregateId); }