From 0448005971a2438e3767f87f89cbea4eac3ab60f Mon Sep 17 00:00:00 2001 From: Sebastian Kurfuerst Date: Mon, 21 Oct 2024 13:12:07 +0200 Subject: [PATCH 1/3] TASK: further fix e2e test cases for Neos 9 --- .../docker-compose.neos-dev-instance.yaml | 3 +++ Tests/IntegrationTests/pageModel.js | 2 +- Tests/IntegrationTests/start-neos-dev-instance.sh | 9 +++++++-- .../DimensionSwitcher/DimensionSelector.js | 2 +- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Tests/IntegrationTests/docker-compose.neos-dev-instance.yaml b/Tests/IntegrationTests/docker-compose.neos-dev-instance.yaml index b0dd656f7f..fa822f2292 100644 --- a/Tests/IntegrationTests/docker-compose.neos-dev-instance.yaml +++ b/Tests/IntegrationTests/docker-compose.neos-dev-instance.yaml @@ -14,12 +14,15 @@ services: # Enable GD PHP_EXTENSION_GD: 1 COMPOSER_CACHE_DIR: /home/circleci/.composer/cache + DB_HOST: db db: image: mariadb:10.11 environment: MYSQL_DATABASE: neos MYSQL_ROOT_PASSWORD: not_a_real_password + ports: + - 13309:3306 command: ['mysqld', '--character-set-server=utf8mb4', '--collation-server=utf8mb4_unicode_ci'] volumes: composer_cache: diff --git a/Tests/IntegrationTests/pageModel.js b/Tests/IntegrationTests/pageModel.js index 1a3c31e6a5..c018ad505f 100644 --- a/Tests/IntegrationTests/pageModel.js +++ b/Tests/IntegrationTests/pageModel.js @@ -59,7 +59,7 @@ export class PublishDropDown { static publishDropdownDiscardAll = ReactSelector('PublishDropDown ContextDropDownContents').find('button').withText('Discard all'); - static publishDropdownPublishAll = ReactSelector('PublishDropDown ShallowDropDownContents').find('button').withText('Publish all'); + static publishDropdownPublishAll = ReactSelector('PublishDropDown ContextDropDownContents').find('button').withText('Publish all'); static async discardAll() { const $discardAllBtn = Selector(this.publishDropdownDiscardAll); diff --git a/Tests/IntegrationTests/start-neos-dev-instance.sh b/Tests/IntegrationTests/start-neos-dev-instance.sh index c018e135a8..0733f3f266 100644 --- a/Tests/IntegrationTests/start-neos-dev-instance.sh +++ b/Tests/IntegrationTests/start-neos-dev-instance.sh @@ -41,6 +41,7 @@ dc exec -T php bash <<-'BASH' ./flow flow:cache:warmup ./flow doctrine:migrate ./flow user:create --username=admin --password=admin --first-name=John --last-name=Doe --roles=Administrator || true + ./flow user:create --username=editor --password=editor --first-name=Some --last-name=FooBarEditor --roles=Editor || true BASH echo "" @@ -68,7 +69,11 @@ dc exec -T php bash <<-BASH if ./flow site:list | grep -q 'Node name'; then ./flow site:prune '*' fi - ./flow site:import --package-key=Neos.TestSite + ./flow cr:setup + ./flow cr:setup --content-repository onedimension + ./flow cr:setup --content-repository twodimensions + ./flow cr:import --content-repository onedimension Packages/Sites/Neos.Test.OneDimension/Resources/Private/Content + ./flow site:create neos-test-onedimension Neos.Test.OneDimension Neos.TestNodeTypes:Document.HomePage ./flow resource:publish BASH @@ -85,5 +90,5 @@ dc exec -T php bash <<-'BASH' # enable changes of the Neos.TestNodeTypes outside of the container to appear in the container via sym link to mounted volume rm -rf /usr/src/app/TestDistribution/Packages/Application/Neos.TestNodeTypes - ln -s /usr/src/neos-ui/Tests/IntegrationTests/SharedNodeTypesPackage/ /usr/src/app/TestDistribution/Packages/Application/Neos.TestNodeTypes + ln -s /usr/src/neos-ui/Tests/IntegrationTests/TestDistribution/DistributionPackages/Neos.TestNodeTypes /usr/src/app/TestDistribution/Packages/Application/Neos.TestNodeTypes BASH diff --git a/packages/neos-ui/src/Containers/PrimaryToolbar/DimensionSwitcher/DimensionSelector.js b/packages/neos-ui/src/Containers/PrimaryToolbar/DimensionSwitcher/DimensionSelector.js index f2be3beb4a..6a2360ca40 100644 --- a/packages/neos-ui/src/Containers/PrimaryToolbar/DimensionSwitcher/DimensionSelector.js +++ b/packages/neos-ui/src/Containers/PrimaryToolbar/DimensionSwitcher/DimensionSelector.js @@ -16,7 +16,7 @@ const searchOptions = (searchTerm, processedSelectBoxOptions) => })) export default class DimensionSelector extends PureComponent { static propTypes = { - icon: PropTypes.string.isRequired, + icon: PropTypes.string, dimensionLabel: PropTypes.string.isRequired, presets: PropTypes.object.isRequired, activePreset: PropTypes.string.isRequired, From 4b6366d9c162493df16d324aaab4d49b71491a6f Mon Sep 17 00:00:00 2001 From: Sebastian Kurfuerst Date: Mon, 21 Oct 2024 13:25:12 +0200 Subject: [PATCH 2/3] BUGFIX: Copy/Paste across dimensions should not lead to error Resolves: #4614 --- Classes/Domain/Model/Changes/CopyAfter.php | 4 +++- Classes/Domain/Model/Changes/CopyBefore.php | 4 +++- Classes/Domain/Model/Changes/CopyInto.php | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Classes/Domain/Model/Changes/CopyAfter.php b/Classes/Domain/Model/Changes/CopyAfter.php index 30d3dfc638..9030f845e7 100644 --- a/Classes/Domain/Model/Changes/CopyAfter.php +++ b/Classes/Domain/Model/Changes/CopyAfter.php @@ -70,7 +70,9 @@ public function apply(): void ), $subject->workspaceName, $subject, - OriginDimensionSpacePoint::fromDimensionSpacePoint($subject->dimensionSpacePoint), + // NOTE: in order to be able to copy/paste across dimensions, we need to use + // the TARGET NODE's DimensionSpacePoint to create the node in the target dimension. + OriginDimensionSpacePoint::fromDimensionSpacePoint($previousSibling->dimensionSpacePoint), $parentNodeOfPreviousSibling->aggregateId, $succeedingSibling?->aggregateId ); diff --git a/Classes/Domain/Model/Changes/CopyBefore.php b/Classes/Domain/Model/Changes/CopyBefore.php index 87e0881b6d..b42fd16df8 100644 --- a/Classes/Domain/Model/Changes/CopyBefore.php +++ b/Classes/Domain/Model/Changes/CopyBefore.php @@ -65,7 +65,9 @@ public function apply(): void ), $subject->workspaceName, $subject, - OriginDimensionSpacePoint::fromDimensionSpacePoint($subject->dimensionSpacePoint), + // NOTE: in order to be able to copy/paste across dimensions, we need to use + // the TARGET NODE's DimensionSpacePoint to create the node in the target dimension. + OriginDimensionSpacePoint::fromDimensionSpacePoint($succeedingSibling->dimensionSpacePoint), $parentNodeOfSucceedingSibling->aggregateId, $succeedingSibling->aggregateId ); diff --git a/Classes/Domain/Model/Changes/CopyInto.php b/Classes/Domain/Model/Changes/CopyInto.php index bdd1188b7a..e8cd6f1a68 100644 --- a/Classes/Domain/Model/Changes/CopyInto.php +++ b/Classes/Domain/Model/Changes/CopyInto.php @@ -74,7 +74,9 @@ public function apply(): void ), $subject->workspaceName, $subject, - OriginDimensionSpacePoint::fromDimensionSpacePoint($subject->dimensionSpacePoint), + // NOTE: in order to be able to copy/paste across dimensions, we need to use + // the TARGET NODE's DimensionSpacePoint to create the node in the target dimension. + OriginDimensionSpacePoint::fromDimensionSpacePoint($parentNode->dimensionSpacePoint), $parentNode->aggregateId, null ); From 33df996450b5c11ab1e221b93bf99a86082e7c45 Mon Sep 17 00:00:00 2001 From: Sebastian Kurfuerst Date: Mon, 21 Oct 2024 13:42:40 +0200 Subject: [PATCH 3/3] BUGFIX: Cut/Paste across dimensions should not lead to error After thoroughly thinking this through, I decided that moving nodes across dimensions is effectively a copy followed by a delete, thus it CHANGES IDENTITIES of nodes then. This means it will work in all cases, even if the moved node already existed with the same ID in the target dimension. Lateron, we additionally need to expose CreateNodeVariant (which creates connected variants) in the UI as well. Resolves: #4614 --- Classes/Domain/Model/Changes/MoveAfter.php | 58 ++++++++++++++++----- Classes/Domain/Model/Changes/MoveBefore.php | 58 ++++++++++++++++----- Classes/Domain/Model/Changes/MoveInto.php | 49 ++++++++++++++--- 3 files changed, 134 insertions(+), 31 deletions(-) diff --git a/Classes/Domain/Model/Changes/MoveAfter.php b/Classes/Domain/Model/Changes/MoveAfter.php index 8f80c28472..846d7aeca8 100644 --- a/Classes/Domain/Model/Changes/MoveAfter.php +++ b/Classes/Domain/Model/Changes/MoveAfter.php @@ -13,9 +13,13 @@ */ use InvalidArgumentException; +use Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePoint; +use Neos\ContentRepository\Core\Feature\NodeDuplication\Command\CopyNodesRecursively; use Neos\ContentRepository\Core\Feature\NodeMove\Command\MoveNodeAggregate; use Neos\ContentRepository\Core\Feature\NodeMove\Dto\RelationDistributionStrategy; -use Neos\Neos\Ui\Domain\Model\Feedback\Operations\RemoveNode; +use Neos\ContentRepository\Core\Feature\NodeRemoval\Command\RemoveNodeAggregate; +use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints; +use Neos\ContentRepository\Core\SharedModel\Node\NodeVariantSelectionStrategy; use Neos\Neos\Ui\Domain\Model\Feedback\Operations\UpdateNodeInfo; /** @@ -71,19 +75,49 @@ public function apply(): void $hasEqualParentNode = $parentNode->aggregateId ->equals($parentNodeOfPreviousSibling->aggregateId); - $contentRepository = $this->contentRepositoryRegistry->get($subject->contentRepositoryId); - $command = MoveNodeAggregate::create( - $subject->workspaceName, - $subject->dimensionSpacePoint, - $subject->aggregateId, - RelationDistributionStrategy::STRATEGY_GATHER_ALL, - $hasEqualParentNode ? null : $parentNodeOfPreviousSibling->aggregateId, - $precedingSibling->aggregateId, - $succeedingSibling?->aggregateId, - ); - $contentRepository->handle($command); + if (!$precedingSibling->dimensionSpacePoint->equals($subject->dimensionSpacePoint)) { + // WORKAROUND for MOVE ACROSS DIMENSIONS: + // - we want it to work like a copy/paste, followed by an original delete. + // - This is to ensure the user can use it as expected from text editors, where context + // is not preserved during cut/paste. + // - LATERON, we need to expose CreateNodeVariant (which creates connected variants) in the UI as well. + $command = CopyNodesRecursively::createFromSubgraphAndStartNode( + $contentRepository->getContentGraph($subject->workspaceName)->getSubgraph( + $subject->dimensionSpacePoint, + VisibilityConstraints::withoutRestrictions() + ), + $subject->workspaceName, + $subject, + // NOTE: in order to be able to copy/paste across dimensions, we need to use + // the TARGET NODE's DimensionSpacePoint to create the node in the target dimension. + OriginDimensionSpacePoint::fromDimensionSpacePoint($precedingSibling->dimensionSpacePoint), + $parentNodeOfPreviousSibling->aggregateId, + $succeedingSibling?->aggregateId + ); + + $contentRepository->handle($command); + + $command = RemoveNodeAggregate::create( + $subject->workspaceName, + $subject->aggregateId, + $subject->dimensionSpacePoint, + NodeVariantSelectionStrategy::STRATEGY_ALL_SPECIALIZATIONS, + ); + $contentRepository->handle($command); + } else { + $command = MoveNodeAggregate::create( + $subject->workspaceName, + $subject->dimensionSpacePoint, + $subject->aggregateId, + RelationDistributionStrategy::STRATEGY_GATHER_ALL, + $hasEqualParentNode ? null : $parentNodeOfPreviousSibling->aggregateId, + $precedingSibling->aggregateId, + $succeedingSibling?->aggregateId, + ); + $contentRepository->handle($command); + } $updateParentNodeInfo = new UpdateNodeInfo(); $updateParentNodeInfo->setNode($parentNodeOfPreviousSibling); diff --git a/Classes/Domain/Model/Changes/MoveBefore.php b/Classes/Domain/Model/Changes/MoveBefore.php index 879c52fc35..e0e69add59 100644 --- a/Classes/Domain/Model/Changes/MoveBefore.php +++ b/Classes/Domain/Model/Changes/MoveBefore.php @@ -12,9 +12,13 @@ * source code. */ +use Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePoint; +use Neos\ContentRepository\Core\Feature\NodeDuplication\Command\CopyNodesRecursively; use Neos\ContentRepository\Core\Feature\NodeMove\Command\MoveNodeAggregate; use Neos\ContentRepository\Core\Feature\NodeMove\Dto\RelationDistributionStrategy; -use Neos\Neos\Ui\Domain\Model\Feedback\Operations\RemoveNode; +use Neos\ContentRepository\Core\Feature\NodeRemoval\Command\RemoveNodeAggregate; +use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints; +use Neos\ContentRepository\Core\SharedModel\Node\NodeVariantSelectionStrategy; use Neos\Neos\Ui\Domain\Model\Feedback\Operations\UpdateNodeInfo; /** @@ -69,19 +73,49 @@ public function apply(): void $contentRepository = $this->contentRepositoryRegistry->get($subject->contentRepositoryId); - $contentRepository->handle( - MoveNodeAggregate::create( + if (!$succeedingSibling->dimensionSpacePoint->equals($subject->dimensionSpacePoint)) { + // WORKAROUND for MOVE ACROSS DIMENSIONS: + // - we want it to work like a copy/paste, followed by an original delete. + // - This is to ensure the user can use it as expected from text editors, where context + // is not preserved during cut/paste. + // - LATERON, we need to expose CreateNodeVariant (which creates connected variants) in the UI as well. + $command = CopyNodesRecursively::createFromSubgraphAndStartNode( + $contentRepository->getContentGraph($subject->workspaceName)->getSubgraph( + $subject->dimensionSpacePoint, + VisibilityConstraints::withoutRestrictions() + ), + $subject->workspaceName, + $subject, + // NOTE: in order to be able to copy/paste across dimensions, we need to use + // the TARGET NODE's DimensionSpacePoint to create the node in the target dimension. + OriginDimensionSpacePoint::fromDimensionSpacePoint($succeedingSibling->dimensionSpacePoint), + $succeedingSiblingParent->aggregateId, + $succeedingSibling->aggregateId + ); + $contentRepository->handle($command); + + $command = RemoveNodeAggregate::create( $subject->workspaceName, - $subject->dimensionSpacePoint, $subject->aggregateId, - RelationDistributionStrategy::STRATEGY_GATHER_ALL, - $hasEqualParentNode - ? null - : $succeedingSiblingParent->aggregateId, - $precedingSibling?->aggregateId, - $succeedingSibling->aggregateId, - ) - ); + $subject->dimensionSpacePoint, + NodeVariantSelectionStrategy::STRATEGY_ALL_SPECIALIZATIONS, + ); + $contentRepository->handle($command); + } else { + $contentRepository->handle( + MoveNodeAggregate::create( + $subject->workspaceName, + $subject->dimensionSpacePoint, + $subject->aggregateId, + RelationDistributionStrategy::STRATEGY_GATHER_ALL, + $hasEqualParentNode + ? null + : $succeedingSiblingParent->aggregateId, + $precedingSibling?->aggregateId, + $succeedingSibling->aggregateId, + ) + ); + } $updateParentNodeInfo = new UpdateNodeInfo(); $updateParentNodeInfo->setNode($succeedingSiblingParent); diff --git a/Classes/Domain/Model/Changes/MoveInto.php b/Classes/Domain/Model/Changes/MoveInto.php index 75474d0563..2f37946271 100644 --- a/Classes/Domain/Model/Changes/MoveInto.php +++ b/Classes/Domain/Model/Changes/MoveInto.php @@ -12,9 +12,14 @@ * source code. */ +use Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePoint; +use Neos\ContentRepository\Core\Feature\NodeDuplication\Command\CopyNodesRecursively; use Neos\ContentRepository\Core\Feature\NodeMove\Command\MoveNodeAggregate; use Neos\ContentRepository\Core\Feature\NodeMove\Dto\RelationDistributionStrategy; +use Neos\ContentRepository\Core\Feature\NodeRemoval\Command\RemoveNodeAggregate; use Neos\ContentRepository\Core\Projection\ContentGraph\Node; +use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints; +use Neos\ContentRepository\Core\SharedModel\Node\NodeVariantSelectionStrategy; use Neos\Neos\Ui\Domain\Model\Feedback\Operations\UpdateNodeInfo; /** @@ -78,15 +83,45 @@ public function apply(): void ->equals($parentNode->aggregateId); $contentRepository = $this->contentRepositoryRegistry->get($subject->contentRepositoryId); - $contentRepository->handle( - MoveNodeAggregate::create( + if (!$parentNode->dimensionSpacePoint->equals($subject->dimensionSpacePoint)) { + // WORKAROUND for MOVE ACROSS DIMENSIONS: + // - we want it to work like a copy/paste, followed by an original delete. + // - This is to ensure the user can use it as expected from text editors, where context + // is not preserved during cut/paste. + // - LATERON, we need to expose CreateNodeVariant (which creates connected variants) in the UI as well. + $command = CopyNodesRecursively::createFromSubgraphAndStartNode( + $contentRepository->getContentGraph($subject->workspaceName)->getSubgraph( + $subject->dimensionSpacePoint, + VisibilityConstraints::withoutRestrictions() + ), + $subject->workspaceName, + $subject, + // NOTE: in order to be able to copy/paste across dimensions, we need to use + // the TARGET NODE's DimensionSpacePoint to create the node in the target dimension. + OriginDimensionSpacePoint::fromDimensionSpacePoint($parentNode->dimensionSpacePoint), + $parentNode->aggregateId, + null + ); + $contentRepository->handle($command); + + $command = RemoveNodeAggregate::create( $subject->workspaceName, - $subject->dimensionSpacePoint, $subject->aggregateId, - RelationDistributionStrategy::STRATEGY_GATHER_ALL, - $hasEqualParentNode ? null : $parentNode->aggregateId, - ) - ); + $subject->dimensionSpacePoint, + NodeVariantSelectionStrategy::STRATEGY_ALL_SPECIALIZATIONS, + ); + $contentRepository->handle($command); + } else { + $contentRepository->handle( + MoveNodeAggregate::create( + $subject->workspaceName, + $subject->dimensionSpacePoint, + $subject->aggregateId, + RelationDistributionStrategy::STRATEGY_GATHER_ALL, + $hasEqualParentNode ? null : $parentNode->aggregateId, + ) + ); + } $updateParentNodeInfo = new UpdateNodeInfo(); $updateParentNodeInfo->setNode($parentNode);