diff --git a/Neos.ContentRepository.Core/Classes/Factory/ContentRepositoryFactory.php b/Neos.ContentRepository.Core/Classes/Factory/ContentRepositoryFactory.php index fde8bdd29a..9f7ef31dcb 100644 --- a/Neos.ContentRepository.Core/Classes/Factory/ContentRepositoryFactory.php +++ b/Neos.ContentRepository.Core/Classes/Factory/ContentRepositoryFactory.php @@ -24,7 +24,6 @@ use Neos\ContentRepository\Core\EventStore\EventNormalizer; use Neos\ContentRepository\Core\Feature\DimensionSpaceAdjustment\DimensionSpaceCommandHandler; use Neos\ContentRepository\Core\Feature\NodeAggregateCommandHandler; -use Neos\ContentRepository\Core\Feature\NodeDuplication\NodeDuplicationCommandHandler; use Neos\ContentRepository\Core\Feature\WorkspaceCommandHandler; use Neos\ContentRepository\Core\Infrastructure\Property\PropertyConverter; use Neos\ContentRepository\Core\NodeType\NodeTypeManager; @@ -158,11 +157,6 @@ public function getOrBuild(): ContentRepository new DimensionSpaceCommandHandler( $this->contentDimensionZookeeper, $this->interDimensionalVariationGraph, - ), - new NodeDuplicationCommandHandler( - $this->nodeTypeManager, - $this->contentDimensionZookeeper, - $this->interDimensionalVariationGraph, ) ); diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeDuplication/Command/CopyNodesRecursively.php b/Neos.ContentRepository.Core/Classes/Feature/NodeDuplication/Command/CopyNodesRecursively.php deleted file mode 100644 index ef175733c4..0000000000 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeDuplication/Command/CopyNodesRecursively.php +++ /dev/null @@ -1,166 +0,0 @@ - - */ - public function jsonSerialize(): array - { - return get_object_vars($this); - } - - public function withNodeAggregateIdMapping( - NodeAggregateIdMapping $nodeAggregateIdMapping - ): self { - return new self( - $this->workspaceName, - $this->nodeTreeToInsert, - $this->targetDimensionSpacePoint, - $this->targetParentNodeAggregateId, - $this->targetSucceedingSiblingNodeAggregateId, - $this->targetNodeName, - $nodeAggregateIdMapping - ); - } - - /** - * The target node's optional name. - * - * @deprecated the concept regarding node-names for non-tethered nodes is outdated. - */ - public function withTargetNodeName(NodeName $targetNodeName): self - { - return new self( - $this->workspaceName, - $this->nodeTreeToInsert, - $this->targetDimensionSpacePoint, - $this->targetParentNodeAggregateId, - $this->targetSucceedingSiblingNodeAggregateId, - $targetNodeName, - $this->nodeAggregateIdMapping - ); - } - - public function createCopyForWorkspace( - WorkspaceName $targetWorkspaceName, - ): self { - return new self( - $targetWorkspaceName, - $this->nodeTreeToInsert, - $this->targetDimensionSpacePoint, - $this->targetParentNodeAggregateId, - $this->targetSucceedingSiblingNodeAggregateId, - $this->targetNodeName, - $this->nodeAggregateIdMapping - ); - } -} diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeDuplication/Dto/NodeSubtreeSnapshot.php b/Neos.ContentRepository.Core/Classes/Feature/NodeDuplication/Dto/NodeSubtreeSnapshot.php deleted file mode 100644 index 1a724f5dbe..0000000000 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeDuplication/Dto/NodeSubtreeSnapshot.php +++ /dev/null @@ -1,135 +0,0 @@ -findChildNodes($sourceNode->aggregateId, FindChildNodesFilter::create()) as $sourceChildNode - ) { - $childNodes[] = self::fromSubgraphAndStartNode($subgraph, $sourceChildNode); - } - $properties = $sourceNode->properties; - - return new self( - $sourceNode->aggregateId, - $sourceNode->nodeTypeName, - $sourceNode->name, - $sourceNode->classification, - $properties->serialized(), - self::serializeProjectedReferences( - $subgraph->findReferences($sourceNode->aggregateId, FindReferencesFilter::create()) - ), - $childNodes - ); - } - - /** - * @return array - */ - public function jsonSerialize(): array - { - return [ - 'nodeAggregateId' => $this->nodeAggregateId, - 'nodeTypeName' => $this->nodeTypeName, - 'nodeName' => $this->nodeName, - 'nodeAggregateClassification' => $this->nodeAggregateClassification, - 'propertyValues' => $this->propertyValues, - 'nodeReferences' => $this->nodeReferences, - 'childNodes' => $this->childNodes, - ]; - } - - public function walk(\Closure $forEachElementFn): void - { - $forEachElementFn($this); - foreach ($this->childNodes as $childNode) { - $childNode->walk($forEachElementFn); - } - } - - /** - * @param array $array - */ - public static function fromArray(array $array): self - { - $childNodes = []; - foreach ($array['childNodes'] as $childNode) { - $childNodes[] = self::fromArray($childNode); - } - - return new self( - NodeAggregateId::fromString($array['nodeAggregateId']), - NodeTypeName::fromString($array['nodeTypeName']), - isset($array['nodeName']) ? NodeName::fromString($array['nodeName']) : null, - NodeAggregateClassification::from($array['nodeAggregateClassification']), - SerializedPropertyValues::fromArray($array['propertyValues']), - SerializedNodeReferences::fromArray($array['nodeReferences']), - $childNodes - ); - } - - private static function serializeProjectedReferences(References $references): SerializedNodeReferences - { - $serializedReferences = []; - $serializedReferencesByName = []; - foreach ($references as $reference) { - if (!isset($serializedReferencesByName[$reference->name->value])) { - $serializedReferencesByName[$reference->name->value] = []; - } - $serializedReferencesByName[$reference->name->value][] = SerializedNodeReference::fromTargetAndProperties($reference->node->aggregateId, $reference->properties ? $reference->properties->serialized() : SerializedPropertyValues::createEmpty()); - } - - foreach ($serializedReferencesByName as $name => $referenceObjects) { - $serializedReferences[] = SerializedNodeReferencesForName::fromSerializedReferences(ReferenceName::fromString($name), $referenceObjects); - } - - return SerializedNodeReferences::fromArray($serializedReferences); - } -} diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeDuplication/NodeDuplicationCommandHandler.php b/Neos.ContentRepository.Core/Classes/Feature/NodeDuplication/NodeDuplicationCommandHandler.php deleted file mode 100644 index d102d58e26..0000000000 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeDuplication/NodeDuplicationCommandHandler.php +++ /dev/null @@ -1,238 +0,0 @@ -nodeTypeManager; - } - - protected function getAllowedDimensionSubspace(): DimensionSpacePointSet - { - return $this->contentDimensionZookeeper->getAllowedDimensionSubspace(); - } - - public function canHandle(CommandInterface|RebasableToOtherWorkspaceInterface $command): bool - { - return method_exists($this, 'handle' . (new \ReflectionClass($command))->getShortName()); - } - - public function handle(CommandInterface|RebasableToOtherWorkspaceInterface $command, CommandHandlingDependencies $commandHandlingDependencies): EventsToPublish - { - /** @phpstan-ignore-next-line */ - return match ($command::class) { - CopyNodesRecursively::class => $this->handleCopyNodesRecursively($command, $commandHandlingDependencies), - }; - } - - /** - * @throws NodeConstraintException - */ - private function handleCopyNodesRecursively( - CopyNodesRecursively $command, - CommandHandlingDependencies $commandHandlingDependencies - ): EventsToPublish { - // Basic constraints (Content Stream / Dimension Space Point / Node Type of to-be-inserted root node) - $contentGraph = $commandHandlingDependencies->getContentGraph($command->workspaceName); - $expectedVersion = $this->getExpectedVersionOfContentStream($contentGraph->getContentStreamId(), $commandHandlingDependencies); - $this->requireDimensionSpacePointToExist( - $command->targetDimensionSpacePoint->toDimensionSpacePoint() - ); - $nodeType = $this->requireNodeType($command->nodeTreeToInsert->nodeTypeName); - $this->requireNodeTypeToNotBeOfTypeRoot($nodeType); - - // Constraint: Does the target parent node allow nodes of this type? - // NOTE: we only check this for the *root* node of the to-be-inserted structure; and not for its - // children (as we want to create the structure as-is; assuming it was already valid beforehand). - $this->requireConstraintsImposedByAncestorsAreMet( - $contentGraph, - $nodeType, - [$command->targetParentNodeAggregateId] - ); - - // Constraint: The new nodeAggregateIds are not allowed to exist yet. - $this->requireNewNodeAggregateIdsToNotExist( - $contentGraph, - $command->nodeAggregateIdMapping - ); - - // Constraint: the parent node must exist in the command's DimensionSpacePoint as well - $parentNodeAggregate = $this->requireProjectedNodeAggregate( - $contentGraph, - $command->targetParentNodeAggregateId - ); - if ($command->targetSucceedingSiblingNodeAggregateId) { - $this->requireProjectedNodeAggregate( - $contentGraph, - $command->targetSucceedingSiblingNodeAggregateId - ); - } - $this->requireNodeAggregateToCoverDimensionSpacePoint( - $parentNodeAggregate, - $command->targetDimensionSpacePoint->toDimensionSpacePoint() - ); - - // Calculate Covered Dimension Space Points: All points being specializations of the - // given DSP, where the parent also exists. - $specializations = $this->interDimensionalVariationGraph->getSpecializationSet( - $command->targetDimensionSpacePoint->toDimensionSpacePoint() - ); - $coveredDimensionSpacePoints = $specializations->getIntersection( - $parentNodeAggregate->coveredDimensionSpacePoints - ); - - // Constraint: The node name must be free for a new child of the parent node aggregate - if ($command->targetNodeName) { - $this->requireNodeNameToBeUncovered( - $contentGraph, - $command->targetNodeName, - $command->targetParentNodeAggregateId, - ); - } - - // Now, we can start creating the recursive structure. - $events = []; - $this->createEventsForNodeToInsert( - $contentGraph, - $command->targetDimensionSpacePoint, - $coveredDimensionSpacePoints, - $command->targetParentNodeAggregateId, - $command->targetSucceedingSiblingNodeAggregateId, - $command->targetNodeName, - $command->nodeTreeToInsert, - $command->nodeAggregateIdMapping, - $events - ); - - return new EventsToPublish( - ContentStreamEventStreamName::fromContentStreamId( - $contentGraph->getContentStreamId() - )->getEventStreamName(), - RebaseableCommand::enrichWithCommand( - $command, - Events::fromArray($events) - ), - $expectedVersion - ); - } - - private function requireNewNodeAggregateIdsToNotExist( - ContentGraphInterface $contentGraph, - \Neos\Neos\Domain\Service\NodeDuplication\NodeAggregateIdMapping $nodeAggregateIdMapping - ): void { - foreach ($nodeAggregateIdMapping->getAllNewNodeAggregateIds() as $nodeAggregateId) { - $this->requireProjectedNodeAggregateToNotExist( - $contentGraph, - $nodeAggregateId - ); - } - } - - /** - * @param array $events - */ - private function createEventsForNodeToInsert( - ContentGraphInterface $contentGraph, - OriginDimensionSpacePoint $originDimensionSpacePoint, - DimensionSpacePointSet $coveredDimensionSpacePoints, - NodeAggregateId $targetParentNodeAggregateId, - ?NodeAggregateId $targetSucceedingSiblingNodeAggregateId, - ?NodeName $targetNodeName, - NodeSubtreeSnapshot $nodeToInsert, - \Neos\Neos\Domain\Service\NodeDuplication\NodeAggregateIdMapping $nodeAggregateIdMapping, - array &$events, - ): void { - $events[] = new NodeAggregateWithNodeWasCreated( - $contentGraph->getWorkspaceName(), - $contentGraph->getContentStreamId(), - $nodeAggregateIdMapping->getNewNodeAggregateId( - $nodeToInsert->nodeAggregateId - ) ?: NodeAggregateId::create(), - $nodeToInsert->nodeTypeName, - $originDimensionSpacePoint, - $targetSucceedingSiblingNodeAggregateId - ? $this->resolveInterdimensionalSiblingsForCreation( - $contentGraph, - $targetSucceedingSiblingNodeAggregateId, - $originDimensionSpacePoint, - $coveredDimensionSpacePoints - ) - : InterdimensionalSiblings::fromDimensionSpacePointSetWithoutSucceedingSiblings($coveredDimensionSpacePoints), - $targetParentNodeAggregateId, - $targetNodeName, - $nodeToInsert->propertyValues, - $nodeToInsert->nodeAggregateClassification, - $nodeToInsert->nodeReferences, - ); - - foreach ($nodeToInsert->childNodes as $childNodeToInsert) { - $this->createEventsForNodeToInsert( - $contentGraph, - $originDimensionSpacePoint, - $coveredDimensionSpacePoints, - // the just-inserted node becomes the new parent node ID - $nodeAggregateIdMapping->getNewNodeAggregateId( - $nodeToInsert->nodeAggregateId - ) ?: NodeAggregateId::create(), - // $childNodesToInsert is already in the correct order; so appending only is fine. - null, - $childNodeToInsert->nodeName, - $childNodeToInsert, - $nodeAggregateIdMapping, - $events - ); - } - } -} diff --git a/Neos.ContentRepository.Core/Classes/Feature/RebaseableCommands.php b/Neos.ContentRepository.Core/Classes/Feature/RebaseableCommands.php index 4d5e6764b4..7fe372c869 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/RebaseableCommands.php +++ b/Neos.ContentRepository.Core/Classes/Feature/RebaseableCommands.php @@ -11,7 +11,6 @@ use Neos\ContentRepository\Core\Feature\NodeCreation\Command\CreateNodeAggregateWithNodeAndSerializedProperties; use Neos\ContentRepository\Core\Feature\NodeDisabling\Command\DisableNodeAggregate; use Neos\ContentRepository\Core\Feature\NodeDisabling\Command\EnableNodeAggregate; -use Neos\ContentRepository\Core\Feature\NodeDuplication\Command\CopyNodesRecursively; use Neos\ContentRepository\Core\Feature\NodeModification\Command\SetSerializedNodeProperties; use Neos\ContentRepository\Core\Feature\NodeMove\Command\MoveNodeAggregate; use Neos\ContentRepository\Core\Feature\NodeReferencing\Command\SetSerializedNodeReferences; @@ -47,7 +46,12 @@ public static function extractFromEventStream(EventStreamInterface $eventStream) { $commands = []; foreach ($eventStream as $eventEnvelope) { - if ($eventEnvelope->event->metadata && isset($eventEnvelope->event->metadata?->value['commandClass'])) { + if (isset($eventEnvelope->event->metadata?->value['commandClass'])) { + if ($eventEnvelope->event->metadata->value['commandClass'] === 'Neos\\ContentRepository\\Core\\Feature\\NodeDuplication\\Command\\CopyNodesRecursively') { + // The original draft of the CopyNodesRecursively command was removed with Beta 16. In case events that are created via that command are attempted to be + // normally rebased, published, or discarded we instead silently drop these events as announced. + continue; + } $commands[] = RebaseableCommand::extractFromEventEnvelope($eventEnvelope); } } @@ -105,9 +109,6 @@ private static function commandMatchesAtLeastOneNode( UntagSubtree::class, UpdateRootNodeAggregateDimensions::class, => $command->nodeAggregateId->equals($nodeId), - CopyNodesRecursively::class => $command->nodeAggregateIdMapping->getNewNodeAggregateId( - $command->nodeTreeToInsert->nodeAggregateId - )?->equals($nodeId), SetSerializedNodeReferences::class => $command->sourceNodeAggregateId->equals($nodeId), // for non node-aggregate-changes we return false, so they are kept as remainder: AddDimensionShineThrough::class, diff --git a/Neos.ContentRepositoryRegistry/Classes/Service/EventMigrationService.php b/Neos.ContentRepositoryRegistry/Classes/Service/EventMigrationService.php index e1b3b4dae8..444ef7f792 100644 --- a/Neos.ContentRepositoryRegistry/Classes/Service/EventMigrationService.php +++ b/Neos.ContentRepositoryRegistry/Classes/Service/EventMigrationService.php @@ -13,7 +13,6 @@ use Neos\ContentRepository\Core\Feature\NodeCreation\Command\CreateNodeAggregateWithNodeAndSerializedProperties; use Neos\ContentRepository\Core\Feature\NodeDisabling\Command\DisableNodeAggregate; use Neos\ContentRepository\Core\Feature\NodeDisabling\Command\EnableNodeAggregate; -use Neos\ContentRepository\Core\Feature\NodeDuplication\Command\CopyNodesRecursively; use Neos\ContentRepository\Core\Feature\NodeModification\Command\SetSerializedNodeProperties; use Neos\ContentRepository\Core\Feature\NodeMove\Command\MoveNodeAggregate; use Neos\ContentRepository\Core\Feature\NodeReferencing\Command\SetSerializedNodeReferences; @@ -223,7 +222,7 @@ public function migratePropertiesToUnset(\Closure $outputFn): void $outputRewriteNotice(sprintf('Metadata: Removed %d $initialPropertyValues', $propertiesWithNullValues)); $this->updateEventMetaData($eventEnvelope->sequenceNumber, $eventMetaData); } - } elseif ($eventMetaData['commandClass'] === CopyNodesRecursively::class) { + } elseif ($eventMetaData['commandClass'] === 'Neos\\ContentRepository\\Core\\Feature\\NodeDuplication\\Command\\CopyNodesRecursively') { // nodes can be also created on copy, and in $nodeTreeToInsert, we have to also omit null values. // NodeDuplicationCommandHandler::createEventsForNodeToInsert @@ -411,7 +410,7 @@ public function migrateMetaDataToWorkspaceName(\Closure $outputFn): void CreateNodeAggregateWithNodeAndSerializedProperties::class, DisableNodeAggregate::class, EnableNodeAggregate::class, - CopyNodesRecursively::class, + 'Neos\\ContentRepository\\Core\\Feature\\NodeDuplication\\Command\\CopyNodesRecursively', SetSerializedNodeProperties::class, MoveNodeAggregate::class, SetSerializedNodeReferences::class, @@ -863,7 +862,7 @@ public function migrateCopyTetheredNode(\Closure $outputFn): void $eventMetaData = $eventEnvelope->event->metadata?->value; // a copy is basically a NodeAggregateWithNodeWasCreated with CopyNodesRecursively command, so we skip others: - if (!$eventMetaData || ($eventMetaData['commandClass'] ?? null) !== CopyNodesRecursively::class) { + if (!$eventMetaData || ($eventMetaData['commandClass'] ?? null) !== 'Neos\\ContentRepository\\Core\\Feature\\NodeDuplication\\Command\\CopyNodesRecursively') { continue; } @@ -900,7 +899,7 @@ public function copyNodesStatus(\Closure $outputFn): void foreach ($eventStream as $eventEnvelope) { $eventMetaData = $eventEnvelope->event->metadata?->value; // a copy is basically a NodeAggregateWithNodeWasCreated with CopyNodesRecursively command, so we skip others: - if (!$eventMetaData || ($eventMetaData['commandClass'] ?? null) !== CopyNodesRecursively::class) { + if (!$eventMetaData || ($eventMetaData['commandClass'] ?? null) !== 'Neos\\ContentRepository\\Core\\Feature\\NodeDuplication\\Command\\CopyNodesRecursively') { continue; } diff --git a/Neos.Neos/Classes/Domain/Service/NodeDuplication/NodeAggregateIdMapping.php b/Neos.Neos/Classes/Domain/Service/NodeDuplication/NodeAggregateIdMapping.php index 88cac7635a..01eea26a48 100644 --- a/Neos.Neos/Classes/Domain/Service/NodeDuplication/NodeAggregateIdMapping.php +++ b/Neos.Neos/Classes/Domain/Service/NodeDuplication/NodeAggregateIdMapping.php @@ -14,7 +14,6 @@ * source code. */ -use Neos\ContentRepository\Core\Feature\NodeDuplication\Dto\NodeSubtreeSnapshot; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; /** @@ -63,23 +62,6 @@ public function withNewNodeAggregateId(NodeAggregateId $oldNodeAggregateId, Node return new self($nodeAggregateIds); } - /** - * Create a new id mapping, *GENERATING* new ids. - */ - public static function generateForNodeSubtreeSnapshot(NodeSubtreeSnapshot $nodeSubtreeSnapshot): self - { - $nodeAggregateIdMapping = []; - /** @phpstan-ignore neos.cr.internal */ - $nodeSubtreeSnapshot->walk( - function (NodeSubtreeSnapshot $nodeSubtreeSnapshot) use (&$nodeAggregateIdMapping) { - // here, we create new random NodeAggregateIds. - $nodeAggregateIdMapping[$nodeSubtreeSnapshot->nodeAggregateId->value] = NodeAggregateId::create(); - } - ); - - return new self($nodeAggregateIdMapping); - } - /** * @param array $array */ @@ -106,12 +88,4 @@ public function jsonSerialize(): array { return $this->nodeAggregateIds; } - - /** - * @return array - */ - public function getAllNewNodeAggregateIds(): array - { - return array_values($this->nodeAggregateIds); - } }