Skip to content

Commit 9819aec

Browse files
committed
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
1 parent 4b6366d commit 9819aec

File tree

3 files changed

+134
-31
lines changed

3 files changed

+134
-31
lines changed

Classes/Domain/Model/Changes/MoveAfter.php

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,13 @@
1313
*/
1414

1515
use InvalidArgumentException;
16+
use Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePoint;
17+
use Neos\ContentRepository\Core\Feature\NodeDuplication\Command\CopyNodesRecursively;
1618
use Neos\ContentRepository\Core\Feature\NodeMove\Command\MoveNodeAggregate;
1719
use Neos\ContentRepository\Core\Feature\NodeMove\Dto\RelationDistributionStrategy;
18-
use Neos\Neos\Ui\Domain\Model\Feedback\Operations\RemoveNode;
20+
use Neos\ContentRepository\Core\Feature\NodeRemoval\Command\RemoveNodeAggregate;
21+
use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints;
22+
use Neos\ContentRepository\Core\SharedModel\Node\NodeVariantSelectionStrategy;
1923
use Neos\Neos\Ui\Domain\Model\Feedback\Operations\UpdateNodeInfo;
2024

2125
/**
@@ -71,19 +75,49 @@ public function apply(): void
7175
$hasEqualParentNode = $parentNode->aggregateId
7276
->equals($parentNodeOfPreviousSibling->aggregateId);
7377

74-
7578
$contentRepository = $this->contentRepositoryRegistry->get($subject->contentRepositoryId);
7679

77-
$command = MoveNodeAggregate::create(
78-
$subject->workspaceName,
79-
$subject->dimensionSpacePoint,
80-
$subject->aggregateId,
81-
RelationDistributionStrategy::STRATEGY_GATHER_ALL,
82-
$hasEqualParentNode ? null : $parentNodeOfPreviousSibling->aggregateId,
83-
$precedingSibling->aggregateId,
84-
$succeedingSibling?->aggregateId,
85-
);
86-
$contentRepository->handle($command);
80+
if (!$precedingSibling->dimensionSpacePoint->equals($subject->dimensionSpacePoint)) {
81+
// WORKAROUND for MOVE ACROSS DIMENSIONS:
82+
// - we want it to work like a copy/paste, followed by an original delete.
83+
// - This is to ensure the user can use it as expected from text editors, where context
84+
// is not preserved during cut/paste.
85+
// - LATERON, we need to expose CreateNodeVariant (which creates connected variants) in the UI as well.
86+
$command = CopyNodesRecursively::createFromSubgraphAndStartNode(
87+
$contentRepository->getContentGraph($subject->workspaceName)->getSubgraph(
88+
$subject->dimensionSpacePoint,
89+
VisibilityConstraints::withoutRestrictions()
90+
),
91+
$subject->workspaceName,
92+
$subject,
93+
// NOTE: in order to be able to copy/paste across dimensions, we need to use
94+
// the TARGET NODE's DimensionSpacePoint to create the node in the target dimension.
95+
OriginDimensionSpacePoint::fromDimensionSpacePoint($precedingSibling->dimensionSpacePoint),
96+
$parentNodeOfPreviousSibling->aggregateId,
97+
$succeedingSibling?->aggregateId
98+
);
99+
100+
$contentRepository->handle($command);
101+
102+
$command = RemoveNodeAggregate::create(
103+
$subject->workspaceName,
104+
$subject->aggregateId,
105+
$subject->dimensionSpacePoint,
106+
NodeVariantSelectionStrategy::STRATEGY_ALL_SPECIALIZATIONS,
107+
);
108+
$contentRepository->handle($command);
109+
} else {
110+
$command = MoveNodeAggregate::create(
111+
$subject->workspaceName,
112+
$subject->dimensionSpacePoint,
113+
$subject->aggregateId,
114+
RelationDistributionStrategy::STRATEGY_GATHER_ALL,
115+
$hasEqualParentNode ? null : $parentNodeOfPreviousSibling->aggregateId,
116+
$precedingSibling->aggregateId,
117+
$succeedingSibling?->aggregateId,
118+
);
119+
$contentRepository->handle($command);
120+
}
87121

88122
$updateParentNodeInfo = new UpdateNodeInfo();
89123
$updateParentNodeInfo->setNode($parentNodeOfPreviousSibling);

Classes/Domain/Model/Changes/MoveBefore.php

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,13 @@
1212
* source code.
1313
*/
1414

15+
use Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePoint;
16+
use Neos\ContentRepository\Core\Feature\NodeDuplication\Command\CopyNodesRecursively;
1517
use Neos\ContentRepository\Core\Feature\NodeMove\Command\MoveNodeAggregate;
1618
use Neos\ContentRepository\Core\Feature\NodeMove\Dto\RelationDistributionStrategy;
17-
use Neos\Neos\Ui\Domain\Model\Feedback\Operations\RemoveNode;
19+
use Neos\ContentRepository\Core\Feature\NodeRemoval\Command\RemoveNodeAggregate;
20+
use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints;
21+
use Neos\ContentRepository\Core\SharedModel\Node\NodeVariantSelectionStrategy;
1822
use Neos\Neos\Ui\Domain\Model\Feedback\Operations\UpdateNodeInfo;
1923

2024
/**
@@ -69,19 +73,49 @@ public function apply(): void
6973

7074
$contentRepository = $this->contentRepositoryRegistry->get($subject->contentRepositoryId);
7175

72-
$contentRepository->handle(
73-
MoveNodeAggregate::create(
76+
if (!$succeedingSibling->dimensionSpacePoint->equals($subject->dimensionSpacePoint)) {
77+
// WORKAROUND for MOVE ACROSS DIMENSIONS:
78+
// - we want it to work like a copy/paste, followed by an original delete.
79+
// - This is to ensure the user can use it as expected from text editors, where context
80+
// is not preserved during cut/paste.
81+
// - LATERON, we need to expose CreateNodeVariant (which creates connected variants) in the UI as well.
82+
$command = CopyNodesRecursively::createFromSubgraphAndStartNode(
83+
$contentRepository->getContentGraph($subject->workspaceName)->getSubgraph(
84+
$subject->dimensionSpacePoint,
85+
VisibilityConstraints::withoutRestrictions()
86+
),
87+
$subject->workspaceName,
88+
$subject,
89+
// NOTE: in order to be able to copy/paste across dimensions, we need to use
90+
// the TARGET NODE's DimensionSpacePoint to create the node in the target dimension.
91+
OriginDimensionSpacePoint::fromDimensionSpacePoint($precedingSibling->dimensionSpacePoint),
92+
$succeedingSiblingParent->aggregateId,
93+
$succeedingSibling?->aggregateId
94+
);
95+
$contentRepository->handle($command);
96+
97+
$command = RemoveNodeAggregate::create(
7498
$subject->workspaceName,
75-
$subject->dimensionSpacePoint,
7699
$subject->aggregateId,
77-
RelationDistributionStrategy::STRATEGY_GATHER_ALL,
78-
$hasEqualParentNode
79-
? null
80-
: $succeedingSiblingParent->aggregateId,
81-
$precedingSibling?->aggregateId,
82-
$succeedingSibling->aggregateId,
83-
)
84-
);
100+
$subject->dimensionSpacePoint,
101+
NodeVariantSelectionStrategy::STRATEGY_ALL_SPECIALIZATIONS,
102+
);
103+
$contentRepository->handle($command);
104+
} else {
105+
$contentRepository->handle(
106+
MoveNodeAggregate::create(
107+
$subject->workspaceName,
108+
$subject->dimensionSpacePoint,
109+
$subject->aggregateId,
110+
RelationDistributionStrategy::STRATEGY_GATHER_ALL,
111+
$hasEqualParentNode
112+
? null
113+
: $succeedingSiblingParent->aggregateId,
114+
$precedingSibling?->aggregateId,
115+
$succeedingSibling->aggregateId,
116+
)
117+
);
118+
}
85119

86120
$updateParentNodeInfo = new UpdateNodeInfo();
87121
$updateParentNodeInfo->setNode($succeedingSiblingParent);

Classes/Domain/Model/Changes/MoveInto.php

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,14 @@
1212
* source code.
1313
*/
1414

15+
use Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePoint;
16+
use Neos\ContentRepository\Core\Feature\NodeDuplication\Command\CopyNodesRecursively;
1517
use Neos\ContentRepository\Core\Feature\NodeMove\Command\MoveNodeAggregate;
1618
use Neos\ContentRepository\Core\Feature\NodeMove\Dto\RelationDistributionStrategy;
19+
use Neos\ContentRepository\Core\Feature\NodeRemoval\Command\RemoveNodeAggregate;
1720
use Neos\ContentRepository\Core\Projection\ContentGraph\Node;
21+
use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints;
22+
use Neos\ContentRepository\Core\SharedModel\Node\NodeVariantSelectionStrategy;
1823
use Neos\Neos\Ui\Domain\Model\Feedback\Operations\UpdateNodeInfo;
1924

2025
/**
@@ -78,15 +83,45 @@ public function apply(): void
7883
->equals($parentNode->aggregateId);
7984

8085
$contentRepository = $this->contentRepositoryRegistry->get($subject->contentRepositoryId);
81-
$contentRepository->handle(
82-
MoveNodeAggregate::create(
86+
if (!$parentNode->dimensionSpacePoint->equals($subject->dimensionSpacePoint)) {
87+
// WORKAROUND for MOVE ACROSS DIMENSIONS:
88+
// - we want it to work like a copy/paste, followed by an original delete.
89+
// - This is to ensure the user can use it as expected from text editors, where context
90+
// is not preserved during cut/paste.
91+
// - LATERON, we need to expose CreateNodeVariant (which creates connected variants) in the UI as well.
92+
$command = CopyNodesRecursively::createFromSubgraphAndStartNode(
93+
$contentRepository->getContentGraph($subject->workspaceName)->getSubgraph(
94+
$subject->dimensionSpacePoint,
95+
VisibilityConstraints::withoutRestrictions()
96+
),
97+
$subject->workspaceName,
98+
$subject,
99+
// NOTE: in order to be able to copy/paste across dimensions, we need to use
100+
// the TARGET NODE's DimensionSpacePoint to create the node in the target dimension.
101+
OriginDimensionSpacePoint::fromDimensionSpacePoint($parentNode->dimensionSpacePoint),
102+
$parentNode->aggregateId,
103+
null
104+
);
105+
$contentRepository->handle($command);
106+
107+
$command = RemoveNodeAggregate::create(
83108
$subject->workspaceName,
84-
$subject->dimensionSpacePoint,
85109
$subject->aggregateId,
86-
RelationDistributionStrategy::STRATEGY_GATHER_ALL,
87-
$hasEqualParentNode ? null : $parentNode->aggregateId,
88-
)
89-
);
110+
$subject->dimensionSpacePoint,
111+
NodeVariantSelectionStrategy::STRATEGY_ALL_SPECIALIZATIONS,
112+
);
113+
$contentRepository->handle($command);
114+
} else {
115+
$contentRepository->handle(
116+
MoveNodeAggregate::create(
117+
$subject->workspaceName,
118+
$subject->dimensionSpacePoint,
119+
$subject->aggregateId,
120+
RelationDistributionStrategy::STRATEGY_GATHER_ALL,
121+
$hasEqualParentNode ? null : $parentNode->aggregateId,
122+
)
123+
);
124+
}
90125

91126
$updateParentNodeInfo = new UpdateNodeInfo();
92127
$updateParentNodeInfo->setNode($parentNode);

0 commit comments

Comments
 (0)