From cb3f15eab03c0be3fa1860fdc60499d1a061a769 Mon Sep 17 00:00:00 2001 From: bwaidelich Date: Sun, 24 Sep 2023 10:25:58 +0200 Subject: [PATCH] WIP: FEATURE: Node Attributes Resolves: #4550 Related: #3732 --- ...ectionIntegrityViolationDetectionTrait.php | 6 +- .../RestrictionIntegrityIsProvided.feature | 11 +- ...strictionsArePropagatedRecursively.feature | 11 +- .../DoctrineDbalContentGraphProjection.php | 189 ++++--------- .../DoctrineDbalContentGraphSchemaBuilder.php | 10 +- ...estrictionRelations.php => Attributes.php} | 46 ++-- .../Projection/Feature/NodeDisabling.php | 27 +- .../ProjectionIntegrityViolationDetector.php | 48 ++-- .../src/Domain/Repository/ContentGraph.php | 51 ++-- .../src/Domain/Repository/ContentSubgraph.php | 50 ++-- .../Projection/Feature/NodeDisabling.php | 8 +- .../Projection/HypergraphProjection.php | 16 +- ...ableNodeAggregate_ConstraintChecks.feature | 67 ++--- ...bleNodeAggregate_WithoutDimensions.feature | 3 +- ...isableNodeAggregate_WithDimensions.feature | 6 +- ...bleNodeAggregate_WithoutDimensions.feature | 83 +++--- ...EnableNodeAggregate_WithDimensions.feature | 6 +- ...eringDisableStateWithoutDimensions.feature | 253 ++++++++++-------- .../Features/NodeTraversal/Timestamps.feature | 2 +- .../Classes/EventStore/EventNormalizer.php | 26 +- .../Command/AddNodeAggregateAttribute.php | 107 ++++++++ .../Command/RemoveNodeAggregateAttribute.php | 107 ++++++++ .../Feature/NodeAttributes/Dto/Attribute.php | 39 +++ .../Feature/NodeAttributes/Dto/Attributes.php | 66 +++++ .../Event/NodeAggregateAttributeWasAdded.php | 78 ++++++ .../NodeAggregateAttributeWasRemoved.php | 78 ++++++ .../Event/NodeAggregateWasDisabled.php | 2 +- .../Event/NodeAggregateWasEnabled.php | 2 +- .../Feature/NodeDisabling/NodeDisabling.php | 11 +- ...ctionIntegrityViolationDetectionRunner.php | 4 +- ...ionIntegrityViolationDetectorInterface.php | 20 +- .../ContentGraph/VisibilityConstraints.php | 8 + .../NodeHiddenStateProjection.php | 16 +- .../Classes/NodeDataToEventsProcessor.php | 49 ++-- .../Bootstrap/Features/NodeDisabling.php | 13 +- .../CatchUpHook/RouterCacheHook.php | 30 +-- .../Projection/DocumentUriPathProjection.php | 20 +- .../ChangeProjection.php | 16 +- .../FrontendRouting/DisableNodes.feature | 19 +- 39 files changed, 1026 insertions(+), 578 deletions(-) rename Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Projection/Feature/{RestrictionRelations.php => Attributes.php} (79%) create mode 100644 Neos.ContentRepository.Core/Classes/Feature/NodeAttributes/Command/AddNodeAggregateAttribute.php create mode 100644 Neos.ContentRepository.Core/Classes/Feature/NodeAttributes/Command/RemoveNodeAggregateAttribute.php create mode 100644 Neos.ContentRepository.Core/Classes/Feature/NodeAttributes/Dto/Attribute.php create mode 100644 Neos.ContentRepository.Core/Classes/Feature/NodeAttributes/Dto/Attributes.php create mode 100644 Neos.ContentRepository.Core/Classes/Feature/NodeAttributes/Event/NodeAggregateAttributeWasAdded.php create mode 100644 Neos.ContentRepository.Core/Classes/Feature/NodeAttributes/Event/NodeAggregateAttributeWasRemoved.php diff --git a/Neos.ContentGraph.DoctrineDbalAdapter/Tests/Behavior/Features/Bootstrap/ProjectionIntegrityViolationDetectionTrait.php b/Neos.ContentGraph.DoctrineDbalAdapter/Tests/Behavior/Features/Bootstrap/ProjectionIntegrityViolationDetectionTrait.php index a34cb7b2dad..646a41e058d 100644 --- a/Neos.ContentGraph.DoctrineDbalAdapter/Tests/Behavior/Features/Bootstrap/ProjectionIntegrityViolationDetectionTrait.php +++ b/Neos.ContentGraph.DoctrineDbalAdapter/Tests/Behavior/Features/Bootstrap/ProjectionIntegrityViolationDetectionTrait.php @@ -65,7 +65,7 @@ public function iRemoveTheFollowingRestrictionRelation(TableNode $payloadTable): $record = $this->transformDatasetToRestrictionRelationRecord($dataset); $this->dbalClient->getConnection()->delete( - $this->getTableNamePrefix() . '_restrictionrelation', + $this->getTableNamePrefix() . '_attribute', $record ); } @@ -80,7 +80,7 @@ public function iDetachTheFollowingRestrictionRelationFromItsOrigin(TableNode $p $dataset = $this->transformPayloadTableToDataset($payloadTable); $record = $this->transformDatasetToRestrictionRelationRecord($dataset); $this->dbalClient->getConnection()->update( - $this->getTableNamePrefix() . '_restrictionrelation', + $this->getTableNamePrefix() . '_attribute', [ 'originnodeaggregateid' => (string)TestingNodeAggregateId::nonExistent() ], @@ -98,7 +98,7 @@ public function iDetachTheFollowingRestrictionRelationFromItsTarget(TableNode $p $dataset = $this->transformPayloadTableToDataset($payloadTable); $record = $this->transformDatasetToRestrictionRelationRecord($dataset); $this->dbalClient->getConnection()->update( - $this->getTableNamePrefix() . '_restrictionrelation', + $this->getTableNamePrefix() . '_attribute', [ 'affectednodeaggregateid' => (string)TestingNodeAggregateId::nonExistent() ], diff --git a/Neos.ContentGraph.DoctrineDbalAdapter/Tests/Behavior/Features/Projection/ProjectionIntegrityViolationDetection/RestrictionIntegrityIsProvided.feature b/Neos.ContentGraph.DoctrineDbalAdapter/Tests/Behavior/Features/Projection/ProjectionIntegrityViolationDetection/RestrictionIntegrityIsProvided.feature index f73f315d2b0..df475ff402a 100644 --- a/Neos.ContentGraph.DoctrineDbalAdapter/Tests/Behavior/Features/Projection/ProjectionIntegrityViolationDetection/RestrictionIntegrityIsProvided.feature +++ b/Neos.ContentGraph.DoctrineDbalAdapter/Tests/Behavior/Features/Projection/ProjectionIntegrityViolationDetection/RestrictionIntegrityIsProvided.feature @@ -22,10 +22,10 @@ Feature: Run integrity violation detection regarding restriction relations | newContentStreamId | "cs-identifier" | And the graph projection is fully up to date And the command CreateRootNodeAggregateWithNode is executed with payload: - | Key | Value | - | contentStreamId | "cs-identifier" | - | nodeAggregateId | "lady-eleonode-rootford" | - | nodeTypeName | "Neos.ContentRepository:Root" | + | Key | Value | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "lady-eleonode-rootford" | + | nodeTypeName | "Neos.ContentRepository:Root" | And the event NodeAggregateWithNodeWasCreated was published with payload: | Key | Value | | contentStreamId | "cs-identifier" | @@ -46,11 +46,12 @@ Feature: Run integrity violation detection regarding restriction relations | parentNodeAggregateId | "sir-david-nodenborough" | | nodeName | "child-document" | | nodeAggregateClassification | "regular" | - And the event NodeAggregateWasDisabled was published with payload: + And the event NodeAggregateAttributeWasAdded was published with payload: | Key | Value | | contentStreamId | "cs-identifier" | | nodeAggregateId | "sir-david-nodenborough" | | affectedDimensionSpacePoints | [{"language":"de"},{"language":"gsw"},{"language":"fr"}] | + | attribute | "disabled" | And the graph projection is fully up to date Scenario: Detach a restriction relation from its origin diff --git a/Neos.ContentGraph.DoctrineDbalAdapter/Tests/Behavior/Features/Projection/ProjectionIntegrityViolationDetection/RestrictionsArePropagatedRecursively.feature b/Neos.ContentGraph.DoctrineDbalAdapter/Tests/Behavior/Features/Projection/ProjectionIntegrityViolationDetection/RestrictionsArePropagatedRecursively.feature index ab8b30e34ad..cc94c1dccc8 100644 --- a/Neos.ContentGraph.DoctrineDbalAdapter/Tests/Behavior/Features/Projection/ProjectionIntegrityViolationDetection/RestrictionsArePropagatedRecursively.feature +++ b/Neos.ContentGraph.DoctrineDbalAdapter/Tests/Behavior/Features/Projection/ProjectionIntegrityViolationDetection/RestrictionsArePropagatedRecursively.feature @@ -22,10 +22,10 @@ Feature: Run integrity violation detection regarding restriction relations | newContentStreamId | "cs-identifier" | And the graph projection is fully up to date And the command CreateRootNodeAggregateWithNode is executed with payload: - | Key | Value | - | contentStreamId | "cs-identifier" | - | nodeAggregateId | "lady-eleonode-rootford" | - | nodeTypeName | "Neos.ContentRepository:Root" | + | Key | Value | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "lady-eleonode-rootford" | + | nodeTypeName | "Neos.ContentRepository:Root" | And the graph projection is fully up to date Scenario: Create nodes, disable the topmost and remove some restriction edges manually @@ -59,11 +59,12 @@ Feature: Run integrity violation detection regarding restriction relations | parentNodeAggregateId | "sir-nodeward-nodington-iii" | | nodeName | "child-document" | | nodeAggregateClassification | "regular" | - And the event NodeAggregateWasDisabled was published with payload: + And the event NodeAggregateAttributeWasAdded was published with payload: | Key | Value | | contentStreamId | "cs-identifier" | | nodeAggregateId | "sir-david-nodenborough" | | affectedDimensionSpacePoints | [{"language":"de"},{"language":"gsw"},{"language":"fr"}] | + | attribute | "disabled" | And the graph projection is fully up to date And I remove the following restriction relation: | Key | Value | diff --git a/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphProjection.php b/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphProjection.php index be851203b64..5d83ca40cac 100644 --- a/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphProjection.php +++ b/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphProjection.php @@ -8,11 +8,11 @@ use Doctrine\DBAL\Schema\AbstractSchemaManager; use Doctrine\DBAL\Schema\Comparator; use Doctrine\DBAL\Types\Types; +use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Projection\Feature\Attributes; use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Projection\Feature\NodeDisabling; use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Projection\Feature\NodeMove; use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Projection\Feature\NodeRemoval; use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Projection\Feature\NodeVariation; -use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Projection\Feature\RestrictionRelations; use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Projection\HierarchyRelation; use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Projection\NodeRecord; use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Projection\NodeRelationAnchorPoint; @@ -28,8 +28,8 @@ use Neos\ContentRepository\Core\Feature\DimensionSpaceAdjustment\Event\DimensionShineThroughWasAdded; use Neos\ContentRepository\Core\Feature\DimensionSpaceAdjustment\Event\DimensionSpacePointWasMoved; use Neos\ContentRepository\Core\Feature\NodeCreation\Event\NodeAggregateWithNodeWasCreated; -use Neos\ContentRepository\Core\Feature\NodeDisabling\Event\NodeAggregateWasDisabled; -use Neos\ContentRepository\Core\Feature\NodeDisabling\Event\NodeAggregateWasEnabled; +use Neos\ContentRepository\Core\Feature\NodeAttributes\Event\NodeAggregateAttributeWasAdded; +use Neos\ContentRepository\Core\Feature\NodeAttributes\Event\NodeAggregateAttributeWasRemoved; use Neos\ContentRepository\Core\Feature\NodeModification\Dto\SerializedPropertyValues; use Neos\ContentRepository\Core\Feature\NodeModification\Event\NodePropertiesWereSet; use Neos\ContentRepository\Core\Feature\NodeMove\Event\NodeAggregateWasMoved; @@ -46,7 +46,6 @@ use Neos\ContentRepository\Core\Infrastructure\DbalClientInterface; use Neos\ContentRepository\Core\NodeType\NodeTypeManager; use Neos\ContentRepository\Core\NodeType\NodeTypeName; -use Neos\ContentRepository\Core\Projection\CatchUpHookFactoryInterface; use Neos\ContentRepository\Core\Projection\ContentGraph\Timestamps; use Neos\ContentRepository\Core\Projection\ProjectionInterface; use Neos\ContentRepository\Core\Projection\WithMarkStaleInterface; @@ -68,7 +67,7 @@ final class DoctrineDbalContentGraphProjection implements ProjectionInterface, W { use NodeVariation; use NodeDisabling; - use RestrictionRelations; + use Attributes; use NodeRemoval; use NodeMove; @@ -149,7 +148,7 @@ private function truncateDatabaseTables(): void $connection->executeQuery('TRUNCATE table ' . $this->tableNamePrefix . '_node'); $connection->executeQuery('TRUNCATE table ' . $this->tableNamePrefix . '_hierarchyrelation'); $connection->executeQuery('TRUNCATE table ' . $this->tableNamePrefix . '_referencerelation'); - $connection->executeQuery('TRUNCATE table ' . $this->tableNamePrefix . '_restrictionrelation'); + $connection->executeQuery('TRUNCATE table ' . $this->tableNamePrefix . '_attribute'); } public function canHandle(EventInterface $event): bool @@ -163,7 +162,7 @@ public function canHandle(EventInterface $event): bool ContentStreamWasRemoved::class, NodePropertiesWereSet::class, NodeReferencesWereSet::class, - NodeAggregateWasEnabled::class, + NodeAggregateAttributeWasRemoved::class, NodeAggregateTypeWasChanged::class, DimensionSpacePointWasMoved::class, DimensionShineThroughWasAdded::class, @@ -172,7 +171,7 @@ public function canHandle(EventInterface $event): bool NodeSpecializationVariantWasCreated::class, NodeGeneralizationVariantWasCreated::class, NodePeerVariantWasCreated::class, - NodeAggregateWasDisabled::class + NodeAggregateAttributeWasAdded::class ]); } @@ -187,7 +186,7 @@ public function apply(EventInterface $event, EventEnvelope $eventEnvelope): void ContentStreamWasRemoved::class => $this->whenContentStreamWasRemoved($event), NodePropertiesWereSet::class => $this->whenNodePropertiesWereSet($event, $eventEnvelope), NodeReferencesWereSet::class => $this->whenNodeReferencesWereSet($event, $eventEnvelope), - NodeAggregateWasEnabled::class => $this->whenNodeAggregateWasEnabled($event), + NodeAggregateAttributeWasRemoved::class => $this->whenNodeAggregateAttributeWasRemoved($event), NodeAggregateTypeWasChanged::class => $this->whenNodeAggregateTypeWasChanged($event, $eventEnvelope), DimensionSpacePointWasMoved::class => $this->whenDimensionSpacePointWasMoved($event), DimensionShineThroughWasAdded::class => $this->whenDimensionShineThroughWasAdded($event), @@ -196,7 +195,7 @@ public function apply(EventInterface $event, EventEnvelope $eventEnvelope): void NodeSpecializationVariantWasCreated::class => $this->whenNodeSpecializationVariantWasCreated($event, $eventEnvelope), NodeGeneralizationVariantWasCreated::class => $this->whenNodeGeneralizationVariantWasCreated($event, $eventEnvelope), NodePeerVariantWasCreated::class => $this->whenNodePeerVariantWasCreated($event, $eventEnvelope), - NodeAggregateWasDisabled::class => $this->whenNodeAggregateWasDisabled($event), + NodeAggregateAttributeWasAdded::class => $this->whenNodeAggregateAttributeWasAdded($event), default => throw new \InvalidArgumentException(sprintf('Unsupported event %s', get_debug_type($event))), }; } @@ -324,7 +323,7 @@ private function whenNodeAggregateWithNodeWasCreated(NodeAggregateWithNodeWasCre $eventEnvelope, ); - $this->connectRestrictionRelationsFromParentNodeToNewlyCreatedNode( + $this->connectAttributeRelationsFromParentNodeToNewlyCreatedNode( $event->contentStreamId, $event->parentNodeAggregateId, $event->nodeAggregateId, @@ -365,11 +364,11 @@ private function whenNodeAggregateNameWasChanged(NodeAggregateNameWasChanged $ev } /** - * Copy the restriction edges from the parent Node to the newly created child node; + * Copy the attribute edges from the parent Node to the newly created child node; * so that newly created nodes inherit the visibility constraints of the parent. * @throws \Doctrine\DBAL\DBALException */ - private function connectRestrictionRelationsFromParentNodeToNewlyCreatedNode( + private function connectAttributeRelationsFromParentNodeToNewlyCreatedNode( ContentStreamId $contentStreamId, NodeAggregateId $parentNodeAggregateId, NodeAggregateId $newlyCreatedNodeAggregateId, @@ -378,23 +377,25 @@ private function connectRestrictionRelationsFromParentNodeToNewlyCreatedNode( // TODO: still unsure why we need an "INSERT IGNORE" here; // normal "INSERT" can trigger a duplicate key constraint exception $this->getDatabaseConnection()->executeUpdate(' - INSERT IGNORE INTO ' . $this->tableNamePrefix . '_restrictionrelation ( + INSERT IGNORE INTO ' . $this->tableNamePrefix . '_attribute ( contentstreamid, dimensionspacepointhash, originnodeaggregateid, - affectednodeaggregateid + affectednodeaggregateid, + value ) SELECT - r.contentstreamid, - r.dimensionspacepointhash, - r.originnodeaggregateid, - "' . $newlyCreatedNodeAggregateId->value . '" as affectednodeaggregateid + a.contentstreamid, + a.dimensionspacepointhash, + a.originnodeaggregateid, + "' . $newlyCreatedNodeAggregateId->value . '" as affectednodeaggregateid, + a.value FROM - ' . $this->tableNamePrefix . '_restrictionrelation r + ' . $this->tableNamePrefix . '_attribute a WHERE - r.contentstreamid = :sourceContentStreamId - and r.dimensionspacepointhash IN (:visibleDimensionSpacePoints) - and r.affectednodeaggregateid = :parentNodeAggregateId + a.contentstreamid = :sourceContentStreamId + and a.dimensionspacepointhash IN (:visibleDimensionSpacePoints) + and a.affectednodeaggregateid = :parentNodeAggregateId ', [ 'sourceContentStreamId' => $contentStreamId->value, 'visibleDimensionSpacePoints' => $dimensionSpacePointsInWhichNewlyCreatedNodeAggregateIsVisible @@ -641,23 +642,25 @@ private function whenContentStreamWasForked(ContentStreamWasForked $event): void ]); // - // 2) copy Hidden Node information to second content stream + // 2) copy Node attributes to second content stream // $this->getDatabaseConnection()->executeUpdate(' - INSERT INTO ' . $this->tableNamePrefix . '_restrictionrelation ( + INSERT INTO ' . $this->tableNamePrefix . '_attribute ( contentstreamid, dimensionspacepointhash, originnodeaggregateid, - affectednodeaggregateid + affectednodeaggregateid, + value ) SELECT "' . $event->newContentStreamId->value . '" AS contentstreamid, - r.dimensionspacepointhash, - r.originnodeaggregateid, - r.affectednodeaggregateid + a.dimensionspacepointhash, + a.originnodeaggregateid, + a.affectednodeaggregateid, + a.value FROM - ' . $this->tableNamePrefix . '_restrictionrelation r - WHERE r.contentstreamid = :sourceContentStreamId + ' . $this->tableNamePrefix . '_attribute a + WHERE a.contentstreamid = :sourceContentStreamId ', [ 'sourceContentStreamId' => $event->sourceContentStreamId->value ]); @@ -702,9 +705,9 @@ private function whenContentStreamWasRemoved(ContentStreamWasRemoved $event): vo ) '); - // Drop restriction relations + // Drop attribute relations $this->getDatabaseConnection()->executeUpdate(' - DELETE FROM ' . $this->tableNamePrefix . '_restrictionrelation + DELETE FROM ' . $this->tableNamePrefix . '_attribute WHERE contentstreamid = :contentStreamId ', [ @@ -814,99 +817,13 @@ function (NodeRecord $node) use ($eventEnvelope) { }); } - private function cascadeRestrictionRelations( - ContentStreamId $contentStreamId, - NodeAggregateId $parentNodeAggregateId, - NodeAggregateId $entryNodeAggregateId, - DimensionSpacePointSet $affectedDimensionSpacePoints - ): void { - $this->getDatabaseConnection()->executeUpdate( - ' - -- GraphProjector::cascadeRestrictionRelations - INSERT INTO ' . $this->tableNamePrefix . '_restrictionrelation - ( - contentstreamid, - dimensionspacepointhash, - originnodeaggregateid, - affectednodeaggregateid - ) - -- we build a recursive tree - with recursive tree as ( - -- -------------------------------- - -- INITIAL query: select the nodes of the given entry node aggregate as roots of the tree - -- -------------------------------- - select - n.relationanchorpoint, - n.nodeaggregateid, - h.dimensionspacepointhash - from - ' . $this->tableNamePrefix . '_node n - -- we need to join with the hierarchy relation, because we need the dimensionspacepointhash. - inner join ' . $this->tableNamePrefix . '_hierarchyrelation h - on h.childnodeanchor = n.relationanchorpoint - where - n.nodeaggregateid = :entryNodeAggregateId - and h.contentstreamid = :contentStreamId - and h.dimensionspacepointhash in (:dimensionSpacePointHashes) - union - -- -------------------------------- - -- RECURSIVE query: do one "child" query step - -- -------------------------------- - select - c.relationanchorpoint, - c.nodeaggregateid, - h.dimensionspacepointhash - from - tree p - inner join ' . $this->tableNamePrefix . '_hierarchyrelation h - on h.parentnodeanchor = p.relationanchorpoint - inner join ' . $this->tableNamePrefix . '_node c - on h.childnodeanchor = c.relationanchorpoint - where - h.contentstreamid = :contentStreamId - and h.dimensionspacepointhash in (:dimensionSpacePointHashes) - ) - - -- -------------------------------- - -- create new restriction relations... - -- -------------------------------- - SELECT - "' . $contentStreamId->value . '" as contentstreamid, - tree.dimensionspacepointhash, - originnodeaggregateid, - tree.nodeaggregateid as affectednodeaggregateid - FROM tree - -- -------------------------------- - -- ...by joining the tree with all restriction relations ingoing to the given parent - -- -------------------------------- - INNER JOIN ( - SELECT originnodeaggregateid FROM ' . $this->tableNamePrefix . '_restrictionrelation - WHERE contentstreamid = :contentStreamId - AND affectednodeaggregateid = :parentNodeAggregateId - AND dimensionspacepointhash IN (:affectedDimensionSpacePointHashes) - ) AS joinedrestrictingancestors - ', - [ - 'contentStreamId' => $contentStreamId->value, - 'parentNodeAggregateId' => $parentNodeAggregateId->value, - 'entryNodeAggregateId' => $entryNodeAggregateId->value, - 'dimensionSpacePointHashes' => $affectedDimensionSpacePoints->getPointHashes(), - 'affectedDimensionSpacePointHashes' => $affectedDimensionSpacePoints->getPointHashes() - ], - [ - 'dimensionSpacePointHashes' => Connection::PARAM_STR_ARRAY, - 'affectedDimensionSpacePointHashes' => Connection::PARAM_STR_ARRAY - ] - ); - } - /** * @throws \Throwable */ - private function whenNodeAggregateWasEnabled(NodeAggregateWasEnabled $event): void + private function whenNodeAggregateAttributeWasRemoved(NodeAggregateAttributeWasRemoved $event): void { $this->transactional(function () use ($event) { - $this->removeOutgoingRestrictionRelationsOfNodeAggregateInDimensionSpacePoints( + $this->removeOutgoingAttributeRelationsOfNodeAggregateInDimensionSpacePoints( $event->contentStreamId, $event->nodeAggregateId, $event->affectedDimensionSpacePoints @@ -1142,15 +1059,15 @@ function (NodeRecord $nodeRecord) use ($event) { ] ); - // 3) restriction relations + // 3) attribute relations $this->getDatabaseConnection()->executeStatement( ' - UPDATE ' . $this->tableNamePrefix . '_restrictionrelation r + UPDATE ' . $this->tableNamePrefix . '_attribute a SET - r.dimensionspacepointhash = :newDimensionSpacePointHash + a.dimensionspacepointhash = :newDimensionSpacePointHash WHERE - r.dimensionspacepointhash = :originalDimensionSpacePointHash - AND r.contentstreamid = :contentStreamId + a.dimensionspacepointhash = :originalDimensionSpacePointHash + AND a.contentstreamid = :contentStreamId ', [ 'originalDimensionSpacePointHash' => $event->source->hash, @@ -1196,23 +1113,25 @@ private function whenDimensionShineThroughWasAdded(DimensionShineThroughWasAdded ] ); - // 2) restriction relations + // 2) attribute relations $this->getDatabaseConnection()->executeUpdate(' - INSERT INTO ' . $this->tableNamePrefix . '_restrictionrelation ( + INSERT INTO ' . $this->tableNamePrefix . '_attribute ( contentstreamid, dimensionspacepointhash, originnodeaggregateid, - affectednodeaggregateid + affectednodeaggregateid, + value ) SELECT - r.contentstreamid, + a.contentstreamid, :targetDimensionSpacePointHash, - r.originnodeaggregateid, - r.affectednodeaggregateid + a.originnodeaggregateid, + a.affectednodeaggregateid, + a.value FROM - ' . $this->tableNamePrefix . '_restrictionrelation r - WHERE r.contentstreamid = :contentStreamId - AND r.dimensionspacepointhash = :sourceDimensionSpacePointHash + ' . $this->tableNamePrefix . '_attribute a + WHERE a.contentstreamid = :contentStreamId + AND a.dimensionspacepointhash = :sourceDimensionSpacePointHash ', [ 'contentStreamId' => $event->contentStreamId->value, diff --git a/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphSchemaBuilder.php b/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphSchemaBuilder.php index 7a21d326589..c4e3f6cb9a5 100644 --- a/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphSchemaBuilder.php +++ b/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphSchemaBuilder.php @@ -22,7 +22,7 @@ public function buildSchema(): Schema $this->createNodeTable($schema); $this->createHierarchyRelationTable($schema); $this->createReferenceRelationTable($schema); - $this->createRestrictionRelationTable($schema); + $this->createAttributeTable($schema); return $schema; } @@ -118,9 +118,9 @@ private function createReferenceRelationTable(Schema $schema): void ->setPrimaryKey(['name', 'position', 'nodeanchorpoint']); } - private function createRestrictionRelationTable(Schema $schema): void + private function createAttributeTable(Schema $schema): void { - $table = $schema->createTable($this->tableNamePrefix . '_restrictionrelation'); + $table = $schema->createTable($this->tableNamePrefix . '_attribute'); $table->addColumn('contentstreamid', Types::STRING) ->setLength(40) ->setNotnull(true); @@ -133,6 +133,9 @@ private function createRestrictionRelationTable(Schema $schema): void $table->addColumn('affectednodeaggregateid', Types::STRING) ->setLength(64) ->setNotnull(true); + $table->addColumn('value', Types::STRING) + ->setLength(30) + ->setNotnull(true); $table ->setPrimaryKey([ @@ -141,5 +144,6 @@ private function createRestrictionRelationTable(Schema $schema): void 'originnodeaggregateid', 'affectednodeaggregateid' ]); + $table->addIndex(['value']); } } diff --git a/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Projection/Feature/RestrictionRelations.php b/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Projection/Feature/Attributes.php similarity index 79% rename from Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Projection/Feature/RestrictionRelations.php rename to Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Projection/Feature/Attributes.php index 1e3ebed5081..2a9135685de 100644 --- a/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Projection/Feature/RestrictionRelations.php +++ b/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Projection/Feature/Attributes.php @@ -13,7 +13,7 @@ /** * @internal */ -trait RestrictionRelations +trait Attributes { abstract protected function getProjectionContentGraph(): ProjectionContentGraph; @@ -25,20 +25,20 @@ abstract protected function getTableNamePrefix(): string; * @param DimensionSpacePointSet $affectedDimensionSpacePoints * @throws \Doctrine\DBAL\DBALException */ - private function removeOutgoingRestrictionRelationsOfNodeAggregateInDimensionSpacePoints( + private function removeOutgoingAttributeRelationsOfNodeAggregateInDimensionSpacePoints( ContentStreamId $contentStreamId, NodeAggregateId $originNodeAggregateId, DimensionSpacePointSet $affectedDimensionSpacePoints ): void { $this->getDatabaseConnection()->executeUpdate( ' --- GraphProjector::removeOutgoingRestrictionRelationsOfNodeAggregateInDimensionSpacePoints +-- GraphProjector::removeOutgoingAttributeRelationsOfNodeAggregateInDimensionSpacePoints -DELETE r.* -FROM ' . $this->getTableNamePrefix() . '_restrictionrelation r -WHERE r.contentstreamid = :contentStreamId -AND r.originnodeaggregateid = :originNodeAggregateId -AND r.dimensionspacepointhash in (:dimensionSpacePointHashes)', +DELETE a.* +FROM ' . $this->getTableNamePrefix() . '_attribute a +WHERE a.contentstreamid = :contentStreamId +AND a.originnodeaggregateid = :originNodeAggregateId +AND a.dimensionspacepointhash in (:dimensionSpacePointHashes)', [ 'contentStreamId' => $contentStreamId->value, 'originNodeAggregateId' => $originNodeAggregateId->value, @@ -53,16 +53,16 @@ private function removeOutgoingRestrictionRelationsOfNodeAggregateInDimensionSpa /** * @throws \Doctrine\DBAL\DBALException */ - private function removeAllRestrictionRelationsUnderneathNodeAggregate( + private function removeAllAttributeRelationsUnderneathNodeAggregate( ContentStreamId $contentStreamId, NodeAggregateId $nodeAggregateId ): void { $this->getDatabaseConnection()->executeUpdate( ' - -- GraphProjector::removeAllRestrictionRelationsUnderneathNodeAggregate + -- GraphProjector::removeAllAttributeRelationsUnderneathNodeAggregate - delete r.* from - ' . $this->getTableNamePrefix() . '_restrictionrelation r + delete a.* from + ' . $this->getTableNamePrefix() . '_attribute a join ( -- we build a recursive tree @@ -106,9 +106,9 @@ private function removeAllRestrictionRelationsUnderneathNodeAggregate( -- the "tree" CTE now contains a list of tuples (nodeAggregateId,dimensionSpacePointHash) -- which are *descendants* of the starting NodeAggregateId in ALL DimensionSpacePointHashes where - r.contentstreamid = :contentStreamId - and r.dimensionspacepointhash = tree.dimensionspacepointhash - and r.affectednodeaggregateid = tree.nodeaggregateid + a.contentstreamid = :contentStreamId + and a.dimensionspacepointhash = tree.dimensionspacepointhash + and a.affectednodeaggregateid = tree.nodeaggregateid ', [ 'entryNodeAggregateId' => $nodeAggregateId->value, @@ -120,7 +120,7 @@ private function removeAllRestrictionRelationsUnderneathNodeAggregate( /** * @throws \Doctrine\DBAL\DBALException */ - private function removeAllRestrictionRelationsInSubtreeImposedByAncestors( + private function removeAllAttributeRelationsInSubtreeImposedByAncestors( ContentStreamId $contentStreamId, NodeAggregateId $entryNodeAggregateId, DimensionSpacePointSet $affectedDimensionSpacePoints @@ -134,14 +134,14 @@ private function removeAllRestrictionRelationsInSubtreeImposedByAncestors( $this->getDatabaseConnection()->executeUpdate( ' - -- GraphProjector::removeAllRestrictionRelationsInSubtreeImposedByAncestors + -- GraphProjector::removeAllAttributeRelationsInSubtreeImposedByAncestors - DELETE r.* - FROM ' . $this->getTableNamePrefix() . '_restrictionrelation r - WHERE r.contentstreamid = :contentStreamId - AND r.originnodeaggregateid NOT IN (:descendantNodeAggregateIds) - AND r.affectednodeaggregateid IN (:descendantNodeAggregateIds) - AND r.dimensionspacepointhash IN (:affectedDimensionSpacePointHashes)', + DELETE a.* + FROM ' . $this->getTableNamePrefix() . '_attribute a + WHERE a.contentstreamid = :contentStreamId + AND a.originnodeaggregateid NOT IN (:descendantNodeAggregateIds) + AND a.affectednodeaggregateid IN (:descendantNodeAggregateIds) + AND a.dimensionspacepointhash IN (:affectedDimensionSpacePointHashes)', [ 'contentStreamId' => $contentStreamId->value, 'descendantNodeAggregateIds' => array_keys($descendantNodeAggregateIds), diff --git a/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Projection/Feature/NodeDisabling.php b/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Projection/Feature/NodeDisabling.php index e3ade9c7eb2..4a5810a88bc 100644 --- a/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Projection/Feature/NodeDisabling.php +++ b/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Projection/Feature/NodeDisabling.php @@ -5,13 +5,7 @@ namespace Neos\ContentGraph\DoctrineDbalAdapter\Domain\Projection\Feature; use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Types\Types; -use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Projection\NodeRecord; -use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePointSet; -use Neos\ContentRepository\Core\Feature\NodeDisabling\Event\NodeAggregateWasDisabled; -use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; -use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; -use Neos\EventStore\Model\EventEnvelope; +use Neos\ContentRepository\Core\Feature\NodeAttributes\Event\NodeAggregateAttributeWasAdded; /** * The NodeDisabling projection feature trait @@ -25,7 +19,7 @@ abstract protected function getTableNamePrefix(): string; /** * @throws \Throwable */ - private function whenNodeAggregateWasDisabled(NodeAggregateWasDisabled $event): void + private function whenNodeAggregateAttributeWasAdded(NodeAggregateAttributeWasAdded $event): void { $this->transactional(function () use ($event) { @@ -34,9 +28,9 @@ private function whenNodeAggregateWasDisabled(NodeAggregateWasDisabled $event): // normal "INSERT" can trigger a duplicate key constraint exception $this->getDatabaseConnection()->executeStatement( ' --- GraphProjector::whenNodeAggregateWasDisabled -insert ignore into ' . $this->getTableNamePrefix() . '_restrictionrelation - (contentstreamid, dimensionspacepointhash, originnodeaggregateid, affectednodeaggregateid) +-- GraphProjector::whenNodeAggregateAttributeWasAdded +insert ignore into ' . $this->getTableNamePrefix() . '_attribute + (contentstreamid, dimensionspacepointhash, originnodeaggregateid, affectednodeaggregateid, value) -- we build a recursive tree with recursive tree as ( @@ -76,16 +70,19 @@ private function whenNodeAggregateWasDisabled(NodeAggregateWasDisabled $event): ) select - "' . $event->contentStreamId->value . '" as contentstreamid, + :contentStreamId as contentstreamid, dimensionspacepointhash, - "' . $event->nodeAggregateId->value . '" as originnodeaggregateid, - nodeaggregateid as affectednodeaggregateid + :originNodeAggregateId as originnodeaggregateid, + nodeaggregateid as affectednodeaggregateid, + :attributeValue as value from tree ', [ 'entryNodeAggregateId' => $event->nodeAggregateId->value, 'contentStreamId' => $event->contentStreamId->value, - 'dimensionSpacePointHashes' => $event->affectedDimensionSpacePoints->getPointHashes() + 'dimensionSpacePointHashes' => $event->affectedDimensionSpacePoints->getPointHashes(), + 'originNodeAggregateId' => $event->nodeAggregateId->value, + 'attributeValue' => $event->attribute->value, ], [ 'dimensionSpacePointHashes' => Connection::PARAM_STR_ARRAY diff --git a/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Projection/ProjectionIntegrityViolationDetector.php b/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Projection/ProjectionIntegrityViolationDetector.php index 1ee0e3e106b..3bdf4ccfe1f 100644 --- a/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Projection/ProjectionIntegrityViolationDetector.php +++ b/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Projection/ProjectionIntegrityViolationDetector.php @@ -1,7 +1,7 @@ client->getConnection()->executeQuery( @@ -183,17 +183,17 @@ public function restrictionsArePropagatedRecursively(): Result FROM ' . $this->tableNamePrefix . '_hierarchyrelation h INNER JOIN ' . $this->tableNamePrefix . '_node p ON p.relationanchorpoint = h.parentnodeanchor - INNER JOIN ' . $this->tableNamePrefix . '_restrictionrelation pr - ON pr.affectednodeaggregateid = p.nodeaggregateid - AND pr.contentstreamid = h.contentstreamid - AND pr.dimensionspacepointhash = h.dimensionspacepointhash + INNER JOIN ' . $this->tableNamePrefix . '_attribute pa + ON pa.affectednodeaggregateid = p.nodeaggregateid + AND pa.contentstreamid = h.contentstreamid + AND pa.dimensionspacepointhash = h.dimensionspacepointhash INNER JOIN ' . $this->tableNamePrefix . '_node c ON c.relationanchorpoint = h.childnodeanchor - LEFT JOIN ' . $this->tableNamePrefix . '_restrictionrelation cr - ON cr.affectednodeaggregateid = c.nodeaggregateid - AND cr.contentstreamid = h.contentstreamid - AND cr.dimensionspacepointhash = h.dimensionspacepointhash - WHERE cr.affectednodeaggregateid IS NULL' + LEFT JOIN ' . $this->tableNamePrefix . '_attribute ca + ON ca.affectednodeaggregateid = c.nodeaggregateid + AND ca.contentstreamid = h.contentstreamid + AND ca.dimensionspacepointhash = h.dimensionspacepointhash + WHERE ca.affectednodeaggregateid IS NULL' )->fetchAllAssociative(); foreach ($nodeRecordsWithMissingRestrictions as $nodeRecord) { @@ -201,7 +201,7 @@ public function restrictionsArePropagatedRecursively(): Result 'Node aggregate ' . $nodeRecord['nodeaggregateid'] . ' misses a restriction relation in content stream ' . $nodeRecord['contentstreamid'] . ' and dimension space point ' . $nodeRecord['dimensionspacepoint'], - self::ERROR_CODE_NODE_HAS_MISSING_RESTRICTION + self::ERROR_CODE_NODE_HAS_MISSING_ATTRIBUTE )); } @@ -211,27 +211,27 @@ public function restrictionsArePropagatedRecursively(): Result /** * @inheritDoc */ - public function restrictionIntegrityIsProvided(): Result + public function attributeIntegrityIsProvided(): Result { $result = new Result(); $restrictionRelationRecordsWithoutOriginOrAffectedNode = $this->client->getConnection()->executeQuery( ' - SELECT r.* FROM ' . $this->tableNamePrefix . '_restrictionrelation r + SELECT a.* FROM ' . $this->tableNamePrefix . '_attribute a LEFT JOIN ( ' . $this->tableNamePrefix . '_node p INNER JOIN ' . $this->tableNamePrefix . '_hierarchyrelation ph ON p.relationanchorpoint = ph.childnodeanchor - ) ON p.nodeaggregateid = r.originnodeaggregateid - AND ph.contentstreamid = r.contentstreamid - AND ph.dimensionspacepointhash = r.dimensionspacepointhash + ) ON p.nodeaggregateid = a.originnodeaggregateid + AND ph.contentstreamid = a.contentstreamid + AND ph.dimensionspacepointhash = a.dimensionspacepointhash LEFT JOIN ( ' . $this->tableNamePrefix . '_node c INNER JOIN ' . $this->tableNamePrefix . '_hierarchyrelation ch ON c.relationanchorpoint = ch.childnodeanchor - ) ON c.nodeaggregateid = r.affectednodeaggregateid - AND ch.contentstreamid = r.contentstreamid - AND ch.dimensionspacepointhash = r.dimensionspacepointhash + ) ON c.nodeaggregateid = a.affectednodeaggregateid + AND ch.contentstreamid = a.contentstreamid + AND ch.dimensionspacepointhash = a.dimensionspacepointhash WHERE p.nodeaggregateid IS NULL OR c.nodeaggregateid IS NULL' )->fetchAllAssociative(); @@ -242,7 +242,7 @@ public function restrictionIntegrityIsProvided(): Result . ' -> ' . $relationRecord['affectednodeaggregateid'] . ' does not connect two nodes in content stream ' . $relationRecord['contentstreamid'] . ' and dimension space point ' . $relationRecord['dimensionspacepointhash'], - self::ERROR_CODE_RESTRICTION_INTEGRITY_IS_COMPROMISED + self::ERROR_CODE_ATTRIBUTE_INTEGRITY_IS_COMPROMISED )); } diff --git a/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Repository/ContentGraph.php b/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Repository/ContentGraph.php index 5be39df7d16..00301fbb10a 100644 --- a/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Repository/ContentGraph.php +++ b/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Repository/ContentGraph.php @@ -225,14 +225,14 @@ public function findNodeAggregateById( $query = 'SELECT n.*, h.name, h.contentstreamid, h.dimensionspacepoint AS covereddimensionspacepoint, - r.dimensionspacepointhash AS disableddimensionspacepointhash + a.dimensionspacepointhash AS disableddimensionspacepointhash FROM ' . $this->tableNamePrefix . '_hierarchyrelation h JOIN ' . $this->tableNamePrefix . '_node n ON n.relationanchorpoint = h.childnodeanchor - LEFT JOIN ' . $this->tableNamePrefix . '_restrictionrelation r - ON r.originnodeaggregateid = n.nodeaggregateid - AND r.contentstreamid = h.contentstreamid - AND r.affectednodeaggregateid = n.nodeaggregateid - AND r.dimensionspacepointhash = h.dimensionspacepointhash + LEFT JOIN ' . $this->tableNamePrefix . '_attribute a + ON a.originnodeaggregateid = n.nodeaggregateid + AND a.contentstreamid = h.contentstreamid + AND a.affectednodeaggregateid = n.nodeaggregateid + AND a.dimensionspacepointhash = h.dimensionspacepointhash WHERE n.nodeaggregateid = :nodeAggregateId AND h.contentstreamid = :contentStreamId'; $parameters = [ @@ -261,21 +261,22 @@ public function findParentNodeAggregates( $query = 'SELECT p.*, ph.name, ph.contentstreamid, ph.dimensionspacepoint AS covereddimensionspacepoint, - r.dimensionspacepointhash AS disableddimensionspacepointhash + a.dimensionspacepointhash AS disableddimensionspacepointhash FROM ' . $this->tableNamePrefix . '_node p JOIN ' . $this->tableNamePrefix . '_hierarchyrelation ph ON ph.childnodeanchor = p.relationanchorpoint JOIN ' . $this->tableNamePrefix . '_hierarchyrelation ch ON ch.parentnodeanchor = p.relationanchorpoint JOIN ' . $this->tableNamePrefix . '_node c ON ch.childnodeanchor = c.relationanchorpoint - LEFT JOIN ' . $this->tableNamePrefix . '_restrictionrelation r - ON r.originnodeaggregateid = p.nodeaggregateid - AND r.contentstreamid = ph.contentstreamid - AND r.affectednodeaggregateid = p.nodeaggregateid - AND r.dimensionspacepointhash = ph.dimensionspacepointhash + LEFT JOIN ' . $this->tableNamePrefix . '_attribute a + ON a.originnodeaggregateid = p.nodeaggregateid + AND a.contentstreamid = ph.contentstreamid + AND a.affectednodeaggregateid = p.nodeaggregateid + AND a.dimensionspacepointhash = ph.dimensionspacepointhash WHERE c.nodeaggregateid = :nodeAggregateId AND ph.contentstreamid = :contentStreamId - AND ch.contentstreamid = :contentStreamId'; + AND ch.contentstreamid = :contentStreamId + AND a.value = "disabled"'; $parameters = [ 'nodeAggregateId' => $childNodeAggregateId->value, 'contentStreamId' => $contentStreamId->value @@ -302,15 +303,15 @@ public function findParentNodeAggregateByChildOriginDimensionSpacePoint( $query = 'SELECT n.*, h.name, h.contentstreamid, h.dimensionspacepoint AS covereddimensionspacepoint, - r.dimensionspacepointhash AS disableddimensionspacepointhash + a.dimensionspacepointhash AS disableddimensionspacepointhash FROM ' . $this->tableNamePrefix . '_node n JOIN ' . $this->tableNamePrefix . '_hierarchyrelation h ON h.childnodeanchor = n.relationanchorpoint - LEFT JOIN ' . $this->tableNamePrefix . '_restrictionrelation r - ON r.originnodeaggregateid = n.nodeaggregateid - AND r.contentstreamid = h.contentstreamid - AND r.affectednodeaggregateid = n.nodeaggregateid - AND r.dimensionspacepointhash = h.dimensionspacepointhash + LEFT JOIN ' . $this->tableNamePrefix . '_attribute a + ON a.originnodeaggregateid = n.nodeaggregateid + AND a.contentstreamid = h.contentstreamid + AND a.affectednodeaggregateid = n.nodeaggregateid + AND a.dimensionspacepointhash = h.dimensionspacepointhash WHERE n.nodeaggregateid = ( SELECT p.nodeaggregateid FROM ' . $this->tableNamePrefix . '_node p INNER JOIN ' . $this->tableNamePrefix . '_hierarchyrelation ch @@ -422,7 +423,7 @@ private function createChildNodeAggregateQuery(): string { return 'SELECT c.*, ch.name, ch.contentstreamid, ch.dimensionspacepoint AS covereddimensionspacepoint, - r.dimensionspacepointhash AS disableddimensionspacepointhash + a.dimensionspacepointhash AS disableddimensionspacepointhash FROM ' . $this->tableNamePrefix . '_node p JOIN ' . $this->tableNamePrefix . '_hierarchyrelation ph ON ph.childnodeanchor = p.relationanchorpoint @@ -430,11 +431,11 @@ private function createChildNodeAggregateQuery(): string ON ch.parentnodeanchor = p.relationanchorpoint JOIN ' . $this->tableNamePrefix . '_node c ON ch.childnodeanchor = c.relationanchorpoint - LEFT JOIN ' . $this->tableNamePrefix . '_restrictionrelation r - ON r.originnodeaggregateid = p.nodeaggregateid - AND r.contentstreamid = ph.contentstreamid - AND r.affectednodeaggregateid = p.nodeaggregateid - AND r.dimensionspacepointhash = ph.dimensionspacepointhash + LEFT JOIN ' . $this->tableNamePrefix . '_attribute a + ON a.originnodeaggregateid = p.nodeaggregateid + AND a.contentstreamid = ph.contentstreamid + AND a.affectednodeaggregateid = p.nodeaggregateid + AND a.dimensionspacepointhash = ph.dimensionspacepointhash WHERE p.nodeaggregateid = :parentNodeAggregateId AND ph.contentstreamid = :contentStreamId AND ch.contentstreamid = :contentStreamId'; diff --git a/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Repository/ContentSubgraph.php b/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Repository/ContentSubgraph.php index ea068498bdb..56b277d2bbe 100644 --- a/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Repository/ContentSubgraph.php +++ b/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Repository/ContentSubgraph.php @@ -82,6 +82,7 @@ * - n -> node * - h -> hierarchy edge * - r -> reference + * - a -> attribute * * - if more than one node (parent-child) * - pn -> parent node @@ -163,7 +164,7 @@ public function findNodeById(NodeAggregateId $nodeAggregateId): ?Node ->where('n.nodeaggregateid = :nodeAggregateId')->setParameter('nodeAggregateId', $nodeAggregateId->value) ->andWhere('h.contentstreamid = :contentStreamId')->setParameter('contentStreamId', $this->contentStreamId->value) ->andWhere('h.dimensionspacepointhash = :dimensionSpacePointHash')->setParameter('dimensionSpacePointHash', $this->dimensionSpacePoint->hash); - $this->addRestrictionRelationConstraints($queryBuilder); + $this->addAttributeRelationConstraints($queryBuilder); return $this->fetchNode($queryBuilder); } @@ -178,7 +179,7 @@ public function findRootNodeByType(NodeTypeName $nodeTypeName): ?Node ->andWhere('h.dimensionspacepointhash = :dimensionSpacePointHash')->setParameter('dimensionSpacePointHash', $this->dimensionSpacePoint->hash) ->andWhere('n.classification = :nodeAggregateClassification') ->setParameter('nodeAggregateClassification', NodeAggregateClassification::CLASSIFICATION_ROOT->value); - $this->addRestrictionRelationConstraints($queryBuilder); + $this->addAttributeRelationConstraints($queryBuilder); return $this->fetchNode($queryBuilder); } @@ -195,7 +196,7 @@ public function findParentNode(NodeAggregateId $childNodeAggregateId): ?Node ->andWhere('ch.contentstreamid = :contentStreamId') ->andWhere('ph.dimensionspacepointhash = :dimensionSpacePointHash')->setParameter('dimensionSpacePointHash', $this->dimensionSpacePoint->hash) ->andWhere('ch.dimensionspacepointhash = :dimensionSpacePointHash'); - $this->addRestrictionRelationConstraints($queryBuilder, 'cn', 'ch'); + $this->addAttributeRelationConstraints($queryBuilder, 'cn', 'ch'); return $this->fetchNode($queryBuilder); } @@ -228,7 +229,7 @@ public function findChildNodeConnectedThroughEdgeName(NodeAggregateId $parentNod ->andWhere('h.contentstreamid = :contentStreamId')->setParameter('contentStreamId', $this->contentStreamId->value) ->andWhere('h.dimensionspacepointhash = :dimensionSpacePointHash')->setParameter('dimensionSpacePointHash', $this->dimensionSpacePoint->hash) ->andWhere('h.name = :edgeName')->setParameter('edgeName', $edgeName->value); - $this->addRestrictionRelationConstraints($queryBuilder, 'cn'); + $this->addAttributeRelationConstraints($queryBuilder, 'cn'); return $this->fetchNode($queryBuilder); } @@ -277,7 +278,7 @@ public function findSubtree(NodeAggregateId $entryNodeAggregateId, FindSubtreeFi ->where('h.contentstreamid = :contentStreamId') ->andWhere('h.dimensionspacepointhash = :dimensionSpacePointHash') ->andWhere('n.nodeaggregateid = :entryNodeAggregateId'); - $this->addRestrictionRelationConstraints($queryBuilderInitial); + $this->addAttributeRelationConstraints($queryBuilderInitial); $queryBuilderRecursive = $this->createQueryBuilder() ->select('c.*, h.name, h.contentstreamid, p.nodeaggregateid AS parentNodeAggregateId, p.level + 1 AS level, h.position') @@ -292,7 +293,7 @@ public function findSubtree(NodeAggregateId $entryNodeAggregateId, FindSubtreeFi if ($filter->nodeTypeConstraints !== null) { $this->addNodeTypeConstraints($queryBuilderRecursive, $filter->nodeTypeConstraints, 'c'); } - $this->addRestrictionRelationConstraints($queryBuilderRecursive, 'c'); + $this->addAttributeRelationConstraints($queryBuilderRecursive, 'c'); $queryBuilderCte = $this->createQueryBuilder() ->select('*') @@ -366,7 +367,7 @@ public function findClosestNode(NodeAggregateId $entryNodeAggregateId, FindClose ->andWhere('ph.contentstreamid = :contentStreamId') ->andWhere('ph.dimensionspacepointhash = :dimensionSpacePointHash') ->andWhere('n.nodeaggregateid = :entryNodeAggregateId'); - $this->addRestrictionRelationConstraints($queryBuilderInitial, 'n', 'ph'); + $this->addAttributeRelationConstraints($queryBuilderInitial, 'n', 'ph'); $queryBuilderRecursive = $this->createQueryBuilder() ->select('p.*, h.name, h.contentstreamid, h.parentnodeanchor') @@ -375,7 +376,7 @@ public function findClosestNode(NodeAggregateId $entryNodeAggregateId, FindClose ->innerJoin('p', $this->tableNamePrefix . '_hierarchyrelation', 'h', 'h.childnodeanchor = p.relationanchorpoint') ->where('h.contentstreamid = :contentStreamId') ->andWhere('h.dimensionspacepointhash = :dimensionSpacePointHash'); - $this->addRestrictionRelationConstraints($queryBuilderRecursive, 'p'); + $this->addAttributeRelationConstraints($queryBuilderRecursive, 'p'); $queryBuilderCte = $this->createQueryBuilder() ->select('*') @@ -476,22 +477,23 @@ private function createUniqueParameterName(): string return 'param_' . (++$this->dynamicParameterCount); } - private function addRestrictionRelationConstraints(QueryBuilder $queryBuilder, string $nodeTableAlias = 'n', string $hierarchyRelationTableAlias = 'h'): void + private function addAttributeRelationConstraints(QueryBuilder $queryBuilder, string $nodeTableAlias = 'n', string $hierarchyRelationTableAlias = 'h'): void { - if ($this->visibilityConstraints->isDisabledContentShown()) { + if ($this->visibilityConstraints->restrictedAttributes()->isEmpty()) { return; } $nodeTablePrefix = $nodeTableAlias === '' ? '' : $nodeTableAlias . '.'; $hierarchyRelationTablePrefix = $hierarchyRelationTableAlias === '' ? '' : $hierarchyRelationTableAlias . '.'; $subQueryBuilder = $this->createQueryBuilder() ->select('1') - ->from($this->tableNamePrefix . '_restrictionrelation', 'r') - ->where('r.contentstreamid = ' . $hierarchyRelationTablePrefix . 'contentstreamid') - ->andWhere('r.dimensionspacepointhash = ' . $hierarchyRelationTablePrefix . 'dimensionspacepointhash') - ->andWhere('r.affectednodeaggregateid = ' . $nodeTablePrefix . 'nodeaggregateid'); + ->from($this->tableNamePrefix . '_attribute', 'a') + ->where('a.contentstreamid = ' . $hierarchyRelationTablePrefix . 'contentstreamid') + ->andWhere('a.dimensionspacepointhash = ' . $hierarchyRelationTablePrefix . 'dimensionspacepointhash') + ->andWhere('a.affectednodeaggregateid = ' . $nodeTablePrefix . 'nodeaggregateid') + ->andWhere('a.value IN ("disabled")'); $queryBuilder->andWhere( 'NOT EXISTS (' . $subQueryBuilder->getSQL() . ')' - ); + )->setParameter('restrictedAttributes', $this->visibilityConstraints->restrictedAttributes()->toStringArray(), Connection::PARAM_STR_ARRAY); } private function addNodeTypeConstraints(QueryBuilder $queryBuilder, NodeTypeConstraints $nodeTypeConstraints, string $nodeTableAlias = 'n'): void @@ -605,7 +607,7 @@ private function buildChildNodesQuery(NodeAggregateId $parentNodeAggregateId, Fi if ($filter->propertyValue !== null) { $this->addPropertyValueConstraints($queryBuilder, $filter->propertyValue); } - $this->addRestrictionRelationConstraints($queryBuilder); + $this->addAttributeRelationConstraints($queryBuilder); return $queryBuilder; } @@ -625,8 +627,8 @@ private function buildReferencesQuery(bool $backReferences, NodeAggregateId $nod ->andWhere('sh.dimensionspacepointhash = :dimensionSpacePointHash') ->andWhere('dh.contentstreamid = :contentStreamId')->setParameter('contentStreamId', $this->contentStreamId->value) ->andWhere('sh.contentstreamid = :contentStreamId'); - $this->addRestrictionRelationConstraints($queryBuilder, 'dn', 'dh'); - $this->addRestrictionRelationConstraints($queryBuilder, 'sn', 'sh'); + $this->addAttributeRelationConstraints($queryBuilder, 'dn', 'dh'); + $this->addAttributeRelationConstraints($queryBuilder, 'sn', 'sh'); if ($filter->nodeTypeConstraints !== null) { $this->addNodeTypeConstraints($queryBuilder, $filter->nodeTypeConstraints, "{$destinationTablePrefix}n"); } @@ -692,7 +694,7 @@ private function buildSiblingsQuery(bool $preceding, NodeAggregateId $siblingNod ->andWhere('h.position ' . ($preceding ? '<' : '>') . ' (' . $siblingPositionSubQuery->getSQL() . ')') ->orderBy('h.position', $preceding ? 'DESC' : 'ASC'); - $this->addRestrictionRelationConstraints($queryBuilder); + $this->addAttributeRelationConstraints($queryBuilder); if ($filter->nodeTypeConstraints !== null) { $this->addNodeTypeConstraints($queryBuilder, $filter->nodeTypeConstraints); } @@ -725,8 +727,8 @@ private function buildAncestorNodesQueries(NodeAggregateId $entryNodeAggregateId ->andWhere('ph.contentstreamid = :contentStreamId') ->andWhere('ph.dimensionspacepointhash = :dimensionSpacePointHash') ->andWhere('c.nodeaggregateid = :entryNodeAggregateId'); - $this->addRestrictionRelationConstraints($queryBuilderInitial, 'n', 'ph'); - $this->addRestrictionRelationConstraints($queryBuilderInitial, 'c', 'ch'); + $this->addAttributeRelationConstraints($queryBuilderInitial, 'n', 'ph'); + $this->addAttributeRelationConstraints($queryBuilderInitial, 'c', 'ch'); $queryBuilderRecursive = $this->createQueryBuilder() ->select('p.*, h.name, h.contentstreamid, h.parentnodeanchor') @@ -735,7 +737,7 @@ private function buildAncestorNodesQueries(NodeAggregateId $entryNodeAggregateId ->innerJoin('p', $this->tableNamePrefix . '_hierarchyrelation', 'h', 'h.childnodeanchor = p.relationanchorpoint') ->where('h.contentstreamid = :contentStreamId') ->andWhere('h.dimensionspacepointhash = :dimensionSpacePointHash'); - $this->addRestrictionRelationConstraints($queryBuilderRecursive, 'p'); + $this->addAttributeRelationConstraints($queryBuilderRecursive, 'p'); $queryBuilderCte = $this->createQueryBuilder() ->select('*') @@ -767,7 +769,7 @@ private function buildDescendantNodesQueries(NodeAggregateId $entryNodeAggregate ->andWhere('ph.contentstreamid = :contentStreamId') ->andWhere('ph.dimensionspacepointhash = :dimensionSpacePointHash') ->andWhere('p.nodeaggregateid = :entryNodeAggregateId'); - $this->addRestrictionRelationConstraints($queryBuilderInitial); + $this->addAttributeRelationConstraints($queryBuilderInitial); $queryBuilderRecursive = $this->createQueryBuilder() ->select('c.*, h.name, h.contentstreamid, p.nodeaggregateid AS parentNodeAggregateId, p.level + 1 AS level, h.position') @@ -776,7 +778,7 @@ private function buildDescendantNodesQueries(NodeAggregateId $entryNodeAggregate ->innerJoin('p', $this->tableNamePrefix . '_node', 'c', 'c.relationanchorpoint = h.childnodeanchor') ->where('h.contentstreamid = :contentStreamId') ->andWhere('h.dimensionspacepointhash = :dimensionSpacePointHash'); - $this->addRestrictionRelationConstraints($queryBuilderRecursive, 'c'); + $this->addAttributeRelationConstraints($queryBuilderRecursive, 'c'); $queryBuilderCte = $this->createQueryBuilder() ->select('*') diff --git a/Neos.ContentGraph.PostgreSQLAdapter/src/Domain/Projection/Feature/NodeDisabling.php b/Neos.ContentGraph.PostgreSQLAdapter/src/Domain/Projection/Feature/NodeDisabling.php index f96b2535b29..d6f239cbf3a 100644 --- a/Neos.ContentGraph.PostgreSQLAdapter/src/Domain/Projection/Feature/NodeDisabling.php +++ b/Neos.ContentGraph.PostgreSQLAdapter/src/Domain/Projection/Feature/NodeDisabling.php @@ -17,8 +17,8 @@ use Doctrine\DBAL\Connection; use Neos\ContentGraph\PostgreSQLAdapter\Domain\Projection\ProjectionHypergraph; use Neos\ContentGraph\PostgreSQLAdapter\Domain\Projection\RestrictionHyperrelationRecord; -use Neos\ContentRepository\Core\Feature\NodeDisabling\Event\NodeAggregateWasDisabled; -use Neos\ContentRepository\Core\Feature\NodeDisabling\Event\NodeAggregateWasEnabled; +use Neos\ContentRepository\Core\Feature\NodeAttributes\Event\NodeAggregateAttributeWasAdded; +use Neos\ContentRepository\Core\Feature\NodeAttributes\Event\NodeAggregateAttributeWasRemoved; /** * The node disabling feature set for the hypergraph projector @@ -30,7 +30,7 @@ trait NodeDisabling /** * @throws \Throwable */ - private function whenNodeAggregateWasDisabled(NodeAggregateWasDisabled $event): void + private function whenNodeAggregateAttributeWasAdded(NodeAggregateAttributeWasAdded $event): void { $this->transactional(function () use ($event) { $descendantNodeAggregateIdsByAffectedDimensionSpacePoint @@ -58,7 +58,7 @@ private function whenNodeAggregateWasDisabled(NodeAggregateWasDisabled $event): /** * @throws \Throwable */ - private function whenNodeAggregateWasEnabled(NodeAggregateWasEnabled $event): void + private function whenNodeAggregateAttributeWasRemoved(NodeAggregateAttributeWasRemoved $event): void { $this->transactional(function () use ($event) { $restrictionRelations = $this->getProjectionHypergraph()->findOutgoingRestrictionRelations( diff --git a/Neos.ContentGraph.PostgreSQLAdapter/src/Domain/Projection/HypergraphProjection.php b/Neos.ContentGraph.PostgreSQLAdapter/src/Domain/Projection/HypergraphProjection.php index f9037c9453a..764aed1082c 100644 --- a/Neos.ContentGraph.PostgreSQLAdapter/src/Domain/Projection/HypergraphProjection.php +++ b/Neos.ContentGraph.PostgreSQLAdapter/src/Domain/Projection/HypergraphProjection.php @@ -33,8 +33,8 @@ use Neos\ContentRepository\Core\EventStore\EventInterface; use Neos\ContentRepository\Core\Feature\ContentStreamForking\Event\ContentStreamWasForked; use Neos\ContentRepository\Core\Feature\NodeCreation\Event\NodeAggregateWithNodeWasCreated; -use Neos\ContentRepository\Core\Feature\NodeDisabling\Event\NodeAggregateWasDisabled; -use Neos\ContentRepository\Core\Feature\NodeDisabling\Event\NodeAggregateWasEnabled; +use Neos\ContentRepository\Core\Feature\NodeAttributes\Event\NodeAggregateAttributeWasAdded; +use Neos\ContentRepository\Core\Feature\NodeAttributes\Event\NodeAggregateAttributeWasRemoved; use Neos\ContentRepository\Core\Feature\NodeModification\Event\NodePropertiesWereSet; use Neos\ContentRepository\Core\Feature\NodeReferencing\Event\NodeReferencesWereSet; use Neos\ContentRepository\Core\Feature\NodeRemoval\Event\NodeAggregateWasRemoved; @@ -156,9 +156,9 @@ public function canHandle(EventInterface $event): bool // NodeCreation RootNodeAggregateWithNodeWasCreated::class, NodeAggregateWithNodeWasCreated::class, - // NodeDisabling - NodeAggregateWasDisabled::class, - NodeAggregateWasEnabled::class, + // Node Tags + NodeAggregateAttributeWasAdded::class, + NodeAggregateAttributeWasRemoved::class, // NodeModification NodePropertiesWereSet::class, // NodeReferencing @@ -189,9 +189,9 @@ public function apply(EventInterface $event, EventEnvelope $eventEnvelope): void // NodeCreation RootNodeAggregateWithNodeWasCreated::class => $this->whenRootNodeAggregateWithNodeWasCreated($event), NodeAggregateWithNodeWasCreated::class => $this->whenNodeAggregateWithNodeWasCreated($event), - // NodeDisabling - NodeAggregateWasDisabled::class => $this->whenNodeAggregateWasDisabled($event), - NodeAggregateWasEnabled::class => $this->whenNodeAggregateWasEnabled($event), + // Node tags + NodeAggregateAttributeWasAdded::class => $this->whenNodeAggregateAttributeWasAdded($event), + NodeAggregateAttributeWasRemoved::class => $this->whenNodeAggregateAttributeWasRemoved($event), // NodeModification NodePropertiesWereSet::class => $this->whenNodePropertiesWereSet($event), // NodeReferencing diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/01-DisableNodeAggregate_ConstraintChecks.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/01-DisableNodeAggregate_ConstraintChecks.feature index 53b1435cd50..b43a18913cc 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/01-DisableNodeAggregate_ConstraintChecks.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/01-DisableNodeAggregate_ConstraintChecks.feature @@ -18,71 +18,72 @@ Feature: Constraint checks on node aggregate disabling And I am in content repository "default" And I am user identified by "initiating-user-identifier" And the command CreateRootWorkspace is executed with payload: - | Key | Value | - | workspaceName | "live" | - | workspaceTitle | "Live" | - | workspaceDescription | "The live workspace" | - | newContentStreamId | "cs-identifier" | + | Key | Value | + | workspaceName | "live" | + | workspaceTitle | "Live" | + | workspaceDescription | "The live workspace" | + | newContentStreamId | "cs-identifier" | And the graph projection is fully up to date And I am in content stream "cs-identifier" and dimension space point {"language":"de"} And the command CreateRootNodeAggregateWithNode is executed with payload: - | Key | Value | + | Key | Value | | nodeAggregateId | "lady-eleonode-rootford" | - | nodeTypeName | "Neos.ContentRepository:Root" | + | nodeTypeName | "Neos.ContentRepository:Root" | And the graph projection is fully up to date And the following CreateNodeAggregateWithNode commands are executed: - | nodeAggregateId | nodeTypeName | parentNodeAggregateId | nodeName | - | sir-david-nodenborough | Neos.ContentRepository.Testing:Document | lady-eleonode-rootford | document | + | nodeAggregateId | nodeTypeName | parentNodeAggregateId | nodeName | + | sir-david-nodenborough | Neos.ContentRepository.Testing:Document | lady-eleonode-rootford | document | Scenario: Try to disable a node aggregate in a non-existing content stream When the command DisableNodeAggregate is executed with payload and exceptions are caught: - | Key | Value | - | contentStreamId | "i-do-not-exist" | - | nodeAggregateId | "sir-david-nodenborough" | - | nodeVariantSelectionStrategy | "allVariants" | + | Key | Value | + | contentStreamId | "i-do-not-exist" | + | nodeAggregateId | "sir-david-nodenborough" | + | nodeVariantSelectionStrategy | "allVariants" | Then the last command should have thrown an exception of type "ContentStreamDoesNotExistYet" Scenario: Try to disable a non-existing node aggregate When the command DisableNodeAggregate is executed with payload and exceptions are caught: - | Key | Value | - | nodeAggregateId | "i-do-not-exist" | - | nodeVariantSelectionStrategy | "allVariants" | + | Key | Value | + | nodeAggregateId | "i-do-not-exist" | + | nodeVariantSelectionStrategy | "allVariants" | Then the last command should have thrown an exception of type "NodeAggregateCurrentlyDoesNotExist" Scenario: Try to disable an already disabled node aggregate Given the command DisableNodeAggregate is executed with payload: - | Key | Value | - | nodeAggregateId | "sir-david-nodenborough" | - | coveredDimensionSpacePoint | {"language": "de"} | - | nodeVariantSelectionStrategy | "allVariants" | + | Key | Value | + | nodeAggregateId | "sir-david-nodenborough" | + | coveredDimensionSpacePoint | {"language": "de"} | + | nodeVariantSelectionStrategy | "allVariants" | And the graph projection is fully up to date # Note: The behavior has been changed with https://github.com/neos/neos-development-collection/pull/4284 and the test was adjusted accordingly When the command DisableNodeAggregate is executed with payload: - | Key | Value | - | nodeAggregateId | "sir-david-nodenborough" | - | coveredDimensionSpacePoint | {"language": "de"} | - | nodeVariantSelectionStrategy | "allVariants" | + | Key | Value | + | nodeAggregateId | "sir-david-nodenborough" | + | coveredDimensionSpacePoint | {"language": "de"} | + | nodeVariantSelectionStrategy | "allVariants" | Then I expect exactly 4 events to be published on stream with prefix "ContentStream:cs-identifier" - And event at index 3 is of type "NodeAggregateWasDisabled" with payload: + And event at index 3 is of type "NodeAggregateAttributeWasAdded" with payload: | Key | Expected | | contentStreamId | "cs-identifier" | | nodeAggregateId | "sir-david-nodenborough" | | affectedDimensionSpacePoints | [{"language":"de"},{"language":"gsw"}] | + | attribute | "disabled" | Scenario: Try to disable a node aggregate in a non-existing dimension space point When the command DisableNodeAggregate is executed with payload and exceptions are caught: - | Key | Value | - | nodeAggregateId | "sir-david-nodenborough" | - | coveredDimensionSpacePoint | {"undeclared": "undefined"} | - | nodeVariantSelectionStrategy | "allVariants" | + | Key | Value | + | nodeAggregateId | "sir-david-nodenborough" | + | coveredDimensionSpacePoint | {"undeclared": "undefined"} | + | nodeVariantSelectionStrategy | "allVariants" | Then the last command should have thrown an exception of type "DimensionSpacePointNotFound" Scenario: Try to disable a node aggregate in a dimension space point it does not cover When the command DisableNodeAggregate is executed with payload and exceptions are caught: - | Key | Value | - | nodeAggregateId | "sir-david-nodenborough" | - | coveredDimensionSpacePoint | {"language": "en"} | - | nodeVariantSelectionStrategy | "allVariants" | + | Key | Value | + | nodeAggregateId | "sir-david-nodenborough" | + | coveredDimensionSpacePoint | {"language": "en"} | + | nodeVariantSelectionStrategy | "allVariants" | Then the last command should have thrown an exception of type "NodeAggregateDoesCurrentlyNotCoverDimensionSpacePoint" diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/02-DisableNodeAggregate_WithoutDimensions.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/02-DisableNodeAggregate_WithoutDimensions.feature index 479f978752d..93608042aaf 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/02-DisableNodeAggregate_WithoutDimensions.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/02-DisableNodeAggregate_WithoutDimensions.feature @@ -51,11 +51,12 @@ Feature: Disable a node aggregate | nodeVariantSelectionStrategy | "allVariants" | Then I expect exactly 8 events to be published on stream with prefix "ContentStream:cs-identifier" - And event at index 7 is of type "NodeAggregateWasDisabled" with payload: + And event at index 7 is of type "NodeAggregateAttributeWasAdded" with payload: | Key | Expected | | contentStreamId | "cs-identifier" | | nodeAggregateId | "sir-david-nodenborough" | | affectedDimensionSpacePoints | [[]] | + | attribute | "disabled" | When the graph projection is fully up to date And I am in content stream "cs-identifier" diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/03-DisableNodeAggregate_WithDimensions.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/03-DisableNodeAggregate_WithDimensions.feature index 9b72cef2dec..c984b2ea503 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/03-DisableNodeAggregate_WithDimensions.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/03-DisableNodeAggregate_WithDimensions.feature @@ -61,11 +61,12 @@ Feature: Disable a node aggregate | nodeVariantSelectionStrategy | "allSpecializations" | Then I expect exactly 9 events to be published on stream with prefix "ContentStream:cs-identifier" - And event at index 8 is of type "NodeAggregateWasDisabled" with payload: + And event at index 8 is of type "NodeAggregateAttributeWasAdded" with payload: | Key | Expected | | contentStreamId | "cs-identifier" | | nodeAggregateId | "sir-david-nodenborough" | | affectedDimensionSpacePoints | [{"language":"de"}, {"language":"ltz"}, {"language":"gsw"}] | + | attribute | "disabled" | When the graph projection is fully up to date And I am in content stream "cs-identifier" @@ -312,11 +313,12 @@ Feature: Disable a node aggregate | nodeVariantSelectionStrategy | "allVariants" | Then I expect exactly 9 events to be published on stream with prefix "ContentStream:cs-identifier" - And event at index 8 is of type "NodeAggregateWasDisabled" with payload: + And event at index 8 is of type "NodeAggregateAttributeWasAdded" with payload: | Key | Expected | | contentStreamId | "cs-identifier" | | nodeAggregateId | "sir-david-nodenborough" | | affectedDimensionSpacePoints | [{"language":"ltz"}, {"language":"mul"}, {"language":"de"}, {"language":"en"}, {"language":"gsw"}] | + | attribute | "disabled" | When the graph projection is fully up to date And I am in content stream "cs-identifier" diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/05-EnableNodeAggregate_WithoutDimensions.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/05-EnableNodeAggregate_WithoutDimensions.feature index ab94a4bb19c..0c81f517d55 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/05-EnableNodeAggregate_WithoutDimensions.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/05-EnableNodeAggregate_WithoutDimensions.feature @@ -19,49 +19,50 @@ Feature: Enable a node aggregate And I am in content repository "default" And I am user identified by "initiating-user-identifier" And the command CreateRootWorkspace is executed with payload: - | Key | Value | - | workspaceName | "live" | - | workspaceTitle | "Live" | - | workspaceDescription | "The live workspace" | - | newContentStreamId | "cs-identifier" | + | Key | Value | + | workspaceName | "live" | + | workspaceTitle | "Live" | + | workspaceDescription | "The live workspace" | + | newContentStreamId | "cs-identifier" | And the graph projection is fully up to date And I am in content stream "cs-identifier" and dimension space point {} And the command CreateRootNodeAggregateWithNode is executed with payload: - | Key | Value | + | Key | Value | | nodeAggregateId | "lady-eleonode-rootford" | - | nodeTypeName | "Neos.ContentRepository:Root" | + | nodeTypeName | "Neos.ContentRepository:Root" | And the graph projection is fully up to date And the following CreateNodeAggregateWithNode commands are executed: - | nodeAggregateId | nodeTypeName | parentNodeAggregateId | nodeName | - | preceding-nodenborough | Neos.ContentRepository.Testing:Document | lady-eleonode-rootford | preceding-document | - | sir-david-nodenborough | Neos.ContentRepository.Testing:Document | lady-eleonode-rootford | document | - | succeeding-nodenborough | Neos.ContentRepository.Testing:Document | lady-eleonode-rootford | succeeding-document | - | nody-mc-nodeface | Neos.ContentRepository.Testing:Document | sir-david-nodenborough | child-document | + | nodeAggregateId | nodeTypeName | parentNodeAggregateId | nodeName | + | preceding-nodenborough | Neos.ContentRepository.Testing:Document | lady-eleonode-rootford | preceding-document | + | sir-david-nodenborough | Neos.ContentRepository.Testing:Document | lady-eleonode-rootford | document | + | succeeding-nodenborough | Neos.ContentRepository.Testing:Document | lady-eleonode-rootford | succeeding-document | + | nody-mc-nodeface | Neos.ContentRepository.Testing:Document | sir-david-nodenborough | child-document | And the command SetNodeReferences is executed with payload: - | Key | Value | + | Key | Value | | sourceNodeAggregateId | "preceding-nodenborough" | - | referenceName | "references" | - | references | [{"target": "sir-david-nodenborough"}] | + | referenceName | "references" | + | references | [{"target": "sir-david-nodenborough"}] | And the graph projection is fully up to date Scenario: Enable a previously disabled node with arbitrary strategy since dimensions are not involved Given the command DisableNodeAggregate is executed with payload: | Key | Value | - | nodeAggregateId | "sir-david-nodenborough" | - | nodeVariantSelectionStrategy | "allVariants" | + | nodeAggregateId | "sir-david-nodenborough" | + | nodeVariantSelectionStrategy | "allVariants" | And the graph projection is fully up to date When the command EnableNodeAggregate is executed with payload: | Key | Value | - | nodeAggregateId | "sir-david-nodenborough" | + | nodeAggregateId | "sir-david-nodenborough" | | nodeVariantSelectionStrategy | "allVariants" | Then I expect exactly 9 events to be published on stream with prefix "ContentStream:cs-identifier" - And event at index 8 is of type "NodeAggregateWasEnabled" with payload: + And event at index 8 is of type "NodeAggregateAttributeWasRemoved" with payload: | Key | Expected | - | contentStreamId | "cs-identifier" | - | nodeAggregateId | "sir-david-nodenborough" | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "sir-david-nodenborough" | | affectedDimensionSpacePoints | [[]] | + | attribute | "disabled" | When the graph projection is fully up to date And I am in content stream "cs-identifier" @@ -84,7 +85,7 @@ Feature: Enable a node aggregate | document | cs-identifier;sir-david-nodenborough;{} | | succeeding-document | cs-identifier;succeeding-nodenborough;{} | And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 2 levels deep should be: - | Level | nodeAggregateId | + | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | | 1 | preceding-nodenborough | | 1 | sir-david-nodenborough | @@ -124,25 +125,26 @@ Feature: Enable a node aggregate Scenario: Enable a previously disabled node with explicitly disabled child nodes with arbitrary strategy since dimensions are not involved Given the command DisableNodeAggregate is executed with payload: | Key | Value | - | nodeAggregateId | "sir-david-nodenborough" | - | nodeVariantSelectionStrategy | "allVariants" | + | nodeAggregateId | "sir-david-nodenborough" | + | nodeVariantSelectionStrategy | "allVariants" | And the graph projection is fully up to date And the command DisableNodeAggregate is executed with payload: | Key | Value | - | nodeAggregateId | "nody-mc-nodeface" | - | nodeVariantSelectionStrategy | "allVariants" | + | nodeAggregateId | "nody-mc-nodeface" | + | nodeVariantSelectionStrategy | "allVariants" | And the graph projection is fully up to date When the command EnableNodeAggregate is executed with payload: | Key | Value | - | nodeAggregateId | "sir-david-nodenborough" | + | nodeAggregateId | "sir-david-nodenborough" | | nodeVariantSelectionStrategy | "allVariants" | Then I expect exactly 10 events to be published on stream with prefix "ContentStream:cs-identifier" - And event at index 9 is of type "NodeAggregateWasEnabled" with payload: + And event at index 9 is of type "NodeAggregateAttributeWasRemoved" with payload: | Key | Expected | - | contentStreamId | "cs-identifier" | - | nodeAggregateId | "sir-david-nodenborough" | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "sir-david-nodenborough" | | affectedDimensionSpacePoints | [[]] | + | attribute | "disabled" | When the graph projection is fully up to date And I am in content stream "cs-identifier" @@ -161,7 +163,7 @@ Feature: Enable a node aggregate | document | cs-identifier;sir-david-nodenborough;{} | | succeeding-document | cs-identifier;succeeding-nodenborough;{} | And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 2 levels deep should be: - | Level | nodeAggregateId | + | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | | 1 | preceding-nodenborough | | 1 | sir-david-nodenborough | @@ -201,25 +203,26 @@ Feature: Enable a node aggregate Scenario: Enable a previously disabled node with explicitly disabled parent node with arbitrary strategy since dimensions are not involved Given the command DisableNodeAggregate is executed with payload: | Key | Value | - | nodeAggregateId | "sir-david-nodenborough" | - | nodeVariantSelectionStrategy | "allVariants" | + | nodeAggregateId | "sir-david-nodenborough" | + | nodeVariantSelectionStrategy | "allVariants" | And the graph projection is fully up to date And the command DisableNodeAggregate is executed with payload: | Key | Value | - | nodeAggregateId | "nody-mc-nodeface" | - | nodeVariantSelectionStrategy | "allVariants" | + | nodeAggregateId | "nody-mc-nodeface" | + | nodeVariantSelectionStrategy | "allVariants" | And the graph projection is fully up to date When the command EnableNodeAggregate is executed with payload: | Key | Value | - | nodeAggregateId | "nody-mc-nodeface" | + | nodeAggregateId | "nody-mc-nodeface" | | nodeVariantSelectionStrategy | "allVariants" | Then I expect exactly 10 events to be published on stream with prefix "ContentStream:cs-identifier" - And event at index 9 is of type "NodeAggregateWasEnabled" with payload: + And event at index 9 is of type "NodeAggregateAttributeWasRemoved" with payload: | Key | Expected | - | contentStreamId | "cs-identifier" | - | nodeAggregateId | "nody-mc-nodeface" | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nody-mc-nodeface" | | affectedDimensionSpacePoints | [[]] | + | attribute | "disabled" | When the graph projection is fully up to date And I am in content stream "cs-identifier" @@ -237,7 +240,7 @@ Feature: Enable a node aggregate | preceding-document | cs-identifier;preceding-nodenborough;{} | | succeeding-document | cs-identifier;succeeding-nodenborough;{} | And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 2 levels deep should be: - | Level | nodeAggregateId | + | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | | 1 | preceding-nodenborough | | 1 | succeeding-nodenborough | diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/06-EnableNodeAggregate_WithDimensions.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/06-EnableNodeAggregate_WithDimensions.feature index 0296fbf8ab7..8ba5d1cf4c4 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/06-EnableNodeAggregate_WithDimensions.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/06-NodeDisabling/06-EnableNodeAggregate_WithDimensions.feature @@ -75,11 +75,12 @@ Feature: Enable a node aggregate | nodeVariantSelectionStrategy | "allSpecializations" | Then I expect exactly 12 events to be published on stream with prefix "ContentStream:cs-identifier" - And event at index 11 is of type "NodeAggregateWasEnabled" with payload: + And event at index 11 is of type "NodeAggregateAttributeWasRemoved" with payload: | Key | Expected | | contentStreamId | "cs-identifier" | | nodeAggregateId | "sir-david-nodenborough" | | affectedDimensionSpacePoints | [{"language":"de"},{"language":"ltz"},{"language":"gsw"}] | + | attribute | "disabled" | When the graph projection is fully up to date And I am in content stream "cs-identifier" @@ -370,11 +371,12 @@ Feature: Enable a node aggregate | nodeVariantSelectionStrategy | "allVariants" | Then I expect exactly 12 events to be published on stream with prefix "ContentStream:cs-identifier" - And event at index 11 is of type "NodeAggregateWasEnabled" with payload: + And event at index 11 is of type "NodeAggregateAttributeWasRemoved" with payload: | Key | Expected | | contentStreamId | "cs-identifier" | | nodeAggregateId | "sir-david-nodenborough" | | affectedDimensionSpacePoints | [{"language":"mul"},{"language":"de"},{"language":"en"},{"language":"gsw"},{"language":"ltz"}] | + | attribute | "disabled" | When the graph projection is fully up to date And I am in content stream "cs-identifier" diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeMove/MoveNodeAggregateConsideringDisableStateWithoutDimensions.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeMove/MoveNodeAggregateConsideringDisableStateWithoutDimensions.feature index bfcfcc68cbb..85349136d2f 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeMove/MoveNodeAggregateConsideringDisableStateWithoutDimensions.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeMove/MoveNodeAggregateConsideringDisableStateWithoutDimensions.feature @@ -24,72 +24,73 @@ Feature: Move a node aggregate considering disable state but without content dim 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" | + | Key | Value | + | workspaceName | "live" | + | workspaceTitle | "Live" | + | workspaceDescription | "The live workspace" | + | newContentStreamId | "cs-identifier" | And the graph projection is fully up to date And the command CreateRootNodeAggregateWithNode is executed with payload: - | Key | Value | - | contentStreamId | "cs-identifier" | - | nodeAggregateId | "lady-eleonode-rootford" | - | nodeTypeName | "Neos.ContentRepository:Root" | + | Key | Value | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "lady-eleonode-rootford" | + | nodeTypeName | "Neos.ContentRepository:Root" | And the event NodeAggregateWithNodeWasCreated was published with payload: - | Key | Value | - | contentStreamId | "cs-identifier" | - | nodeAggregateId | "sir-david-nodenborough" | - | nodeTypeName | "Neos.ContentRepository.Testing:Document" | - | originDimensionSpacePoint | {} | - | coveredDimensionSpacePoints | [{}] | - | parentNodeAggregateId | "lady-eleonode-rootford" | - | nodeName | "document" | - | nodeAggregateClassification | "regular" | + | Key | Value | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "sir-david-nodenborough" | + | nodeTypeName | "Neos.ContentRepository.Testing:Document" | + | originDimensionSpacePoint | {} | + | coveredDimensionSpacePoints | [{}] | + | parentNodeAggregateId | "lady-eleonode-rootford" | + | nodeName | "document" | + | nodeAggregateClassification | "regular" | And the event NodeAggregateWithNodeWasCreated was published with payload: - | Key | Value | - | contentStreamId | "cs-identifier" | - | nodeAggregateId | "nody-mc-nodeface" | - | nodeTypeName | "Neos.ContentRepository.Testing:Document" | - | originDimensionSpacePoint | {} | - | coveredDimensionSpacePoints | [{}] | - | parentNodeAggregateId | "sir-david-nodenborough" | - | nodeName | "child-document" | - | nodeAggregateClassification | "regular" | + | Key | Value | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nody-mc-nodeface" | + | nodeTypeName | "Neos.ContentRepository.Testing:Document" | + | originDimensionSpacePoint | {} | + | coveredDimensionSpacePoints | [{}] | + | parentNodeAggregateId | "sir-david-nodenborough" | + | nodeName | "child-document" | + | nodeAggregateClassification | "regular" | And the event NodeAggregateWithNodeWasCreated was published with payload: - | Key | Value | - | contentStreamId | "cs-identifier" | - | nodeAggregateId | "sir-nodeward-nodington-iii" | - | nodeTypeName | "Neos.ContentRepository.Testing:Document" | - | originDimensionSpacePoint | {} | - | coveredDimensionSpacePoints | [{}] | - | parentNodeAggregateId | "lady-eleonode-rootford" | - | nodeName | "esquire" | - | nodeAggregateClassification | "regular" | + | Key | Value | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "sir-nodeward-nodington-iii" | + | nodeTypeName | "Neos.ContentRepository.Testing:Document" | + | originDimensionSpacePoint | {} | + | coveredDimensionSpacePoints | [{}] | + | parentNodeAggregateId | "lady-eleonode-rootford" | + | nodeName | "esquire" | + | nodeAggregateClassification | "regular" | And the event NodeAggregateWithNodeWasCreated was published with payload: - | Key | Value | - | contentStreamId | "cs-identifier" | - | nodeAggregateId | "nodimus-prime" | - | nodeTypeName | "Neos.ContentRepository.Testing:Document" | - | originDimensionSpacePoint | {} | - | coveredDimensionSpacePoints | [{}] | - | parentNodeAggregateId | "sir-nodeward-nodington-iii" | - | nodeName | "esquire-child" | - | nodeAggregateClassification | "regular" | + | Key | Value | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nodimus-prime" | + | nodeTypeName | "Neos.ContentRepository.Testing:Document" | + | originDimensionSpacePoint | {} | + | coveredDimensionSpacePoints | [{}] | + | parentNodeAggregateId | "sir-nodeward-nodington-iii" | + | nodeName | "esquire-child" | + | nodeAggregateClassification | "regular" | And the graph projection is fully up to date Scenario: Move a node disabled by one of its ancestors to a new parent that is enabled - Given the event NodeAggregateWasDisabled was published with payload: + Given the event NodeAggregateAttributeWasAdded was published with payload: | Key | Value | - | contentStreamId | "cs-identifier" | - | nodeAggregateId | "sir-david-nodenborough" | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "sir-david-nodenborough" | | affectedDimensionSpacePoints | [{}] | + | attribute | "disabled" | And the graph projection is fully up to date When the command MoveNodeAggregate is executed with payload: - | Key | Value | + | Key | Value | | contentStreamId | "cs-identifier" | | nodeAggregateId | "nody-mc-nodeface" | - | dimensionSpacePoint | {} | + | dimensionSpacePoint | {} | | newParentNodeAggregateId | "sir-nodeward-nodington-iii" | | newSucceedingSiblingNodeAggregateId | null | And the graph projection is fully up to date @@ -102,18 +103,19 @@ Feature: Move a node aggregate considering disable state but without content dim And I expect this node to be a child of node cs-identifier;sir-nodeward-nodington-iii;{} Scenario: Move a node disabled by itself to a new parent that is enabled - Given the event NodeAggregateWasDisabled was published with payload: + Given the event NodeAggregateAttributeWasAdded was published with payload: | Key | Value | - | contentStreamId | "cs-identifier" | - | nodeAggregateId | "nody-mc-nodeface" | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nody-mc-nodeface" | | affectedDimensionSpacePoints | [{}] | + | attribute | "disabled" | And the graph projection is fully up to date When the command MoveNodeAggregate is executed with payload: - | Key | Value | + | Key | Value | | contentStreamId | "cs-identifier" | | nodeAggregateId | "nody-mc-nodeface" | - | dimensionSpacePoint | {} | + | dimensionSpacePoint | {} | | newParentNodeAggregateId | "sir-nodeward-nodington-iii" | | newSucceedingSiblingNodeAggregateId | null | And the graph projection is fully up to date @@ -123,18 +125,19 @@ Feature: Move a node aggregate considering disable state but without content dim Then I expect node aggregate identifier "nody-mc-nodeface" and node path "esquire/child-document" to lead to no node Scenario: Move a node that disables one of its descendants to a new parent that is enabled - Given the event NodeAggregateWasDisabled was published with payload: + Given the event NodeAggregateAttributeWasAdded was published with payload: | Key | Value | - | contentStreamId | "cs-identifier" | - | nodeAggregateId | "sir-david-nodenborough" | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "sir-david-nodenborough" | | affectedDimensionSpacePoints | [{}] | + | attribute | "disabled" | And the graph projection is fully up to date When the command MoveNodeAggregate is executed with payload: - | Key | Value | + | Key | Value | | contentStreamId | "cs-identifier" | | nodeAggregateId | "sir-david-nodenborough" | - | dimensionSpacePoint | {} | + | dimensionSpacePoint | {} | | newParentNodeAggregateId | "sir-nodeward-nodington-iii" | | newSucceedingSiblingNodeAggregateId | null | And the graph projection is fully up to date @@ -145,23 +148,25 @@ Feature: Move a node aggregate considering disable state but without content dim Then I expect node aggregate identifier "nody-mc-nodeface" and node path "esquire/document/child-document" to lead to no node Scenario: Move a node that is disabled by one of its ancestors to a new parent that disables itself - Given the event NodeAggregateWasDisabled was published with payload: + Given the event NodeAggregateAttributeWasAdded was published with payload: | Key | Value | - | contentStreamId | "cs-identifier" | - | nodeAggregateId | "sir-david-nodenborough" | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "sir-david-nodenborough" | | affectedDimensionSpacePoints | [{}] | - And the event NodeAggregateWasDisabled was published with payload: + | attribute | "disabled" | + And the event NodeAggregateAttributeWasAdded was published with payload: | Key | Value | - | contentStreamId | "cs-identifier" | - | nodeAggregateId | "sir-nodeward-nodington-iii" | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "sir-nodeward-nodington-iii" | | affectedDimensionSpacePoints | [{}] | + | attribute | "disabled" | And the graph projection is fully up to date When the command MoveNodeAggregate is executed with payload: - | Key | Value | + | Key | Value | | contentStreamId | "cs-identifier" | | nodeAggregateId | "nody-mc-nodeface" | - | dimensionSpacePoint | {} | + | dimensionSpacePoint | {} | | newParentNodeAggregateId | "sir-nodeward-nodington-iii" | | newSucceedingSiblingNodeAggregateId | null | And the graph projection is fully up to date @@ -171,22 +176,24 @@ Feature: Move a node aggregate considering disable state but without content dim Then I expect node aggregate identifier "nody-mc-nodeface" and node path "esquire/child-document" to lead to no node Scenario: Move a node that is disabled by itself to a new parent that disables itself - Given the event NodeAggregateWasDisabled was published with payload: + Given the event NodeAggregateAttributeWasAdded was published with payload: | Key | Value | - | contentStreamId | "cs-identifier" | - | nodeAggregateId | "sir-david-nodenborough" | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "sir-david-nodenborough" | | affectedDimensionSpacePoints | [{}] | - And the event NodeAggregateWasDisabled was published with payload: + | attribute | "disabled" | + And the event NodeAggregateAttributeWasAdded was published with payload: | Key | Value | - | contentStreamId | "cs-identifier" | - | nodeAggregateId | "sir-nodeward-nodington-iii" | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "sir-nodeward-nodington-iii" | | affectedDimensionSpacePoints | [{}] | + | attribute | "disabled" | When the command MoveNodeAggregate is executed with payload: - | Key | Value | + | Key | Value | | contentStreamId | "cs-identifier" | | nodeAggregateId | "sir-david-nodenborough" | - | dimensionSpacePoint | {} | + | dimensionSpacePoint | {} | | newParentNodeAggregateId | "sir-nodeward-nodington-iii" | | newSucceedingSiblingNodeAggregateId | null | And the graph projection is fully up to date @@ -196,17 +203,18 @@ Feature: Move a node aggregate considering disable state but without content dim Then I expect node aggregate identifier "sir-david-nodenborough" and node path "esquire/document" to lead to no node Scenario: Move a node that is enabled to a new parent that disables itself - Given the event NodeAggregateWasDisabled was published with payload: + Given the event NodeAggregateAttributeWasAdded was published with payload: | Key | Value | - | contentStreamId | "cs-identifier" | - | nodeAggregateId | "sir-nodeward-nodington-iii" | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "sir-nodeward-nodington-iii" | | affectedDimensionSpacePoints | [{}] | + | attribute | "disabled" | When the command MoveNodeAggregate is executed with payload: - | Key | Value | + | Key | Value | | contentStreamId | "cs-identifier" | | nodeAggregateId | "sir-david-nodenborough" | - | dimensionSpacePoint | {} | + | dimensionSpacePoint | {} | | newParentNodeAggregateId | "sir-nodeward-nodington-iii" | | newSucceedingSiblingNodeAggregateId | null | And the graph projection is fully up to date @@ -216,22 +224,24 @@ Feature: Move a node aggregate considering disable state but without content dim Then I expect node aggregate identifier "sir-david-nodenborough" and node path "esquire/document" to lead to no node Scenario: Move a node that disables any of its descendants to a new parent that disables itself - Given the event NodeAggregateWasDisabled was published with payload: + Given the event NodeAggregateAttributeWasAdded was published with payload: | Key | Value | - | contentStreamId | "cs-identifier" | - | nodeAggregateId | "sir-david-nodenborough" | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "sir-david-nodenborough" | | affectedDimensionSpacePoints | [{}] | - And the event NodeAggregateWasDisabled was published with payload: + | attribute | "disabled" | + And the event NodeAggregateAttributeWasAdded was published with payload: | Key | Value | - | contentStreamId | "cs-identifier" | - | nodeAggregateId | "sir-nodeward-nodington-iii" | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "sir-nodeward-nodington-iii" | | affectedDimensionSpacePoints | [{}] | + | attribute | "disabled" | When the command MoveNodeAggregate is executed with payload: - | Key | Value | + | Key | Value | | contentStreamId | "cs-identifier" | | nodeAggregateId | "sir-david-nodenborough" | - | dimensionSpacePoint | {} | + | dimensionSpacePoint | {} | | newParentNodeAggregateId | "sir-nodeward-nodington-iii" | | newSucceedingSiblingNodeAggregateId | null | And the graph projection is fully up to date @@ -242,22 +252,24 @@ Feature: Move a node aggregate considering disable state but without content dim Then I expect node aggregate identifier "nody-mc-nodeface" and node path "esquire/document/child-document" to lead to no node Scenario: Move a node that is disabled by one of its ancestors to a new parent that is disabled by one of its ancestors - Given the event NodeAggregateWasDisabled was published with payload: + Given the event NodeAggregateAttributeWasAdded was published with payload: | Key | Value | - | contentStreamId | "cs-identifier" | - | nodeAggregateId | "sir-david-nodenborough" | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "sir-david-nodenborough" | | affectedDimensionSpacePoints | [{}] | - And the event NodeAggregateWasDisabled was published with payload: + | attribute | "disabled" | + And the event NodeAggregateAttributeWasAdded was published with payload: | Key | Value | - | contentStreamId | "cs-identifier" | - | nodeAggregateId | "sir-nodeward-nodington-iii" | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "sir-nodeward-nodington-iii" | | affectedDimensionSpacePoints | [{}] | + | attribute | "disabled" | When the command MoveNodeAggregate is executed with payload: - | Key | Value | + | Key | Value | | contentStreamId | "cs-identifier" | | nodeAggregateId | "nody-mc-nodeface" | - | dimensionSpacePoint | {} | + | dimensionSpacePoint | {} | | newParentNodeAggregateId | "nodimus-prime" | | newSucceedingSiblingNodeAggregateId | null | And the graph projection is fully up to date @@ -267,22 +279,24 @@ Feature: Move a node aggregate considering disable state but without content dim Then I expect node aggregate identifier "nody-mc-nodeface" and node path "esquire/esquire-child/child-document" to lead to no node Scenario: Move a node that is disabled by itself to a new parent that is disabled by one of its ancestors - Given the event NodeAggregateWasDisabled was published with payload: + Given the event NodeAggregateAttributeWasAdded was published with payload: | Key | Value | - | contentStreamId | "cs-identifier" | - | nodeAggregateId | "sir-david-nodenborough" | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "sir-david-nodenborough" | | affectedDimensionSpacePoints | [{}] | - And the event NodeAggregateWasDisabled was published with payload: + | attribute | "disabled" | + And the event NodeAggregateAttributeWasAdded was published with payload: | Key | Value | - | contentStreamId | "cs-identifier" | - | nodeAggregateId | "sir-nodeward-nodington-iii" | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "sir-nodeward-nodington-iii" | | affectedDimensionSpacePoints | [{}] | + | attribute | "disabled" | When the command MoveNodeAggregate is executed with payload: - | Key | Value | + | Key | Value | | contentStreamId | "cs-identifier" | | nodeAggregateId | "sir-david-nodenborough" | - | dimensionSpacePoint | {} | + | dimensionSpacePoint | {} | | newParentNodeAggregateId | "nodimus-prime" | | newSucceedingSiblingNodeAggregateId | null | And the graph projection is fully up to date @@ -292,22 +306,24 @@ Feature: Move a node aggregate considering disable state but without content dim Then I expect node aggregate identifier "sir-david-nodenborough" and node path "esquire/esquire-child/document" to lead to no node Scenario: Move a node that disables any of its descendants to a new parent that is disabled by one of its ancestors - Given the event NodeAggregateWasDisabled was published with payload: + Given the event NodeAggregateAttributeWasAdded was published with payload: | Key | Value | - | contentStreamId | "cs-identifier" | - | nodeAggregateId | "sir-david-nodenborough" | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "sir-david-nodenborough" | | affectedDimensionSpacePoints | [{}] | - And the event NodeAggregateWasDisabled was published with payload: + | attribute | "disabled" | + And the event NodeAggregateAttributeWasAdded was published with payload: | Key | Value | - | contentStreamId | "cs-identifier" | - | nodeAggregateId | "sir-nodeward-nodington-iii" | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "sir-nodeward-nodington-iii" | | affectedDimensionSpacePoints | [{}] | + | attribute | "disabled" | When the command MoveNodeAggregate is executed with payload: - | Key | Value | + | Key | Value | | contentStreamId | "cs-identifier" | | nodeAggregateId | "sir-david-nodenborough" | - | dimensionSpacePoint | {} | + | dimensionSpacePoint | {} | | newParentNodeAggregateId | "nodimus-prime" | | newSucceedingSiblingNodeAggregateId | null | And the graph projection is fully up to date @@ -318,17 +334,18 @@ Feature: Move a node aggregate considering disable state but without content dim Then I expect node aggregate identifier "nody-mc-nodeface" and node path "esquire/esquire-child/document/child-document" to lead to no node Scenario: Move a node that is enabled to a new parent that is disabled by one of its ancestors - Given the event NodeAggregateWasDisabled was published with payload: + Given the event NodeAggregateAttributeWasAdded was published with payload: | Key | Value | - | contentStreamId | "cs-identifier" | - | nodeAggregateId | "sir-nodeward-nodington-iii" | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "sir-nodeward-nodington-iii" | | affectedDimensionSpacePoints | [{}] | + | attribute | "disabled" | When the command MoveNodeAggregate is executed with payload: - | Key | Value | + | Key | Value | | contentStreamId | "cs-identifier" | | nodeAggregateId | "sir-david-nodenborough" | - | dimensionSpacePoint | {} | + | dimensionSpacePoint | {} | | newParentNodeAggregateId | "nodimus-prime" | | newSucceedingSiblingNodeAggregateId | null | And the graph projection is fully up to date diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/Timestamps.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/Timestamps.feature index 1ddee7b3778..860259519f4 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/Timestamps.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeTraversal/Timestamps.feature @@ -250,7 +250,7 @@ Feature: Behavior of Node timestamp properties "created", "originalCreated", "la | created | originalCreated | lastModified | originalLastModified | | 2023-03-16 12:30:00 | 2023-03-16 12:30:00 | | | - Scenario: NodeAggregateWasEnabled and NodeAggregateWasDisabled events don't update last modified timestamps + Scenario: NodeAggregateAttributeWasAdded and NodeAggregateAttributeWasRemoved events don't update last modified timestamps When the current date and time is "2023-03-16T13:00:00+01:00" And the command DisableNodeAggregate is executed with payload: | Key | Value | diff --git a/Neos.ContentRepository.Core/Classes/EventStore/EventNormalizer.php b/Neos.ContentRepository.Core/Classes/EventStore/EventNormalizer.php index 020e0f23b80..ae2ddecb660 100644 --- a/Neos.ContentRepository.Core/Classes/EventStore/EventNormalizer.php +++ b/Neos.ContentRepository.Core/Classes/EventStore/EventNormalizer.php @@ -10,7 +10,10 @@ use Neos\ContentRepository\Core\Feature\ContentStreamRemoval\Event\ContentStreamWasRemoved; use Neos\ContentRepository\Core\Feature\DimensionSpaceAdjustment\Event\DimensionShineThroughWasAdded; use Neos\ContentRepository\Core\Feature\DimensionSpaceAdjustment\Event\DimensionSpacePointWasMoved; +use Neos\ContentRepository\Core\Feature\NodeAttributes\Dto\Attribute; use Neos\ContentRepository\Core\Feature\NodeCreation\Event\NodeAggregateWithNodeWasCreated; +use Neos\ContentRepository\Core\Feature\NodeAttributes\Event\NodeAggregateAttributeWasAdded; +use Neos\ContentRepository\Core\Feature\NodeAttributes\Event\NodeAggregateAttributeWasRemoved; use Neos\ContentRepository\Core\Feature\NodeDisabling\Event\NodeAggregateWasDisabled; use Neos\ContentRepository\Core\Feature\NodeDisabling\Event\NodeAggregateWasEnabled; use Neos\ContentRepository\Core\Feature\NodeModification\Event\NodePropertiesWereSet; @@ -26,19 +29,19 @@ use Neos\ContentRepository\Core\Feature\RootNodeCreation\Event\RootNodeAggregateWithNodeWasCreated; use Neos\ContentRepository\Core\Feature\WorkspaceCreation\Event\RootWorkspaceWasCreated; use Neos\ContentRepository\Core\Feature\WorkspaceCreation\Event\WorkspaceWasCreated; +use Neos\ContentRepository\Core\Feature\WorkspaceModification\Event\WorkspaceBaseWorkspaceWasChanged; +use Neos\ContentRepository\Core\Feature\WorkspaceModification\Event\WorkspaceOwnerWasChanged; +use Neos\ContentRepository\Core\Feature\WorkspaceModification\Event\WorkspaceWasRemoved; +use Neos\ContentRepository\Core\Feature\WorkspaceModification\Event\WorkspaceWasRenamed; use Neos\ContentRepository\Core\Feature\WorkspacePublication\Event\WorkspaceWasDiscarded; use Neos\ContentRepository\Core\Feature\WorkspacePublication\Event\WorkspaceWasPartiallyDiscarded; use Neos\ContentRepository\Core\Feature\WorkspacePublication\Event\WorkspaceWasPartiallyPublished; use Neos\ContentRepository\Core\Feature\WorkspacePublication\Event\WorkspaceWasPublished; use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Event\WorkspaceRebaseFailed; use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Event\WorkspaceWasRebased; -use Neos\ContentRepository\Core\Feature\WorkspaceModification\Event\WorkspaceWasRenamed; -use Neos\ContentRepository\Core\Feature\WorkspaceModification\Event\WorkspaceWasRemoved; -use Neos\ContentRepository\Core\Feature\WorkspaceModification\Event\WorkspaceOwnerWasChanged; -use Neos\EventStore\Model\Event\EventData; use Neos\EventStore\Model\Event; +use Neos\EventStore\Model\Event\EventData; use Neos\EventStore\Model\Event\EventType; -use Neos\ContentRepository\Core\Feature\WorkspaceModification\Event\WorkspaceBaseWorkspaceWasChanged; /** * Central authority to convert Content Repository domain events to Event Store EventData and EventType, vice versa. @@ -73,9 +76,11 @@ public function __construct() DimensionShineThroughWasAdded::class, DimensionSpacePointWasMoved::class, NodeAggregateNameWasChanged::class, - NodeAggregateTypeWasChanged::class, NodeAggregateWasDisabled::class, NodeAggregateWasEnabled::class, + NodeAggregateAttributeWasAdded::class, + NodeAggregateAttributeWasRemoved::class, + NodeAggregateTypeWasChanged::class, NodeAggregateWasMoved::class, NodeAggregateWasRemoved::class, NodeAggregateWithNodeWasCreated::class, @@ -147,6 +152,7 @@ public function getEventClassName(Event $event): string public function denormalize(Event $event): EventInterface { + /** @var class-string $eventClassName */ $eventClassName = $this->getEventClassName($event); try { $eventDataAsArray = json_decode($event->data->value, true, 512, JSON_THROW_ON_ERROR); @@ -157,6 +163,12 @@ public function denormalize(Event $event): EventInterface ); } assert(is_array($eventDataAsArray)); - return $eventClassName::fromArray($eventDataAsArray); + $eventInstance = $eventClassName::fromArray($eventDataAsArray); + return match ($eventInstance::class) { + // upcast disabled / enabled events to the corresponding attribute events + NodeAggregateWasDisabled::class => new NodeAggregateAttributeWasAdded($eventInstance->contentStreamId, $eventInstance->nodeAggregateId, $eventInstance->affectedDimensionSpacePoints, Attribute::fromString('disabled')), + NodeAggregateWasEnabled::class => new NodeAggregateAttributeWasRemoved($eventInstance->contentStreamId, $eventInstance->nodeAggregateId, $eventInstance->affectedDimensionSpacePoints, Attribute::fromString('disabled')), + default => $eventInstance, + }; } } diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeAttributes/Command/AddNodeAggregateAttribute.php b/Neos.ContentRepository.Core/Classes/Feature/NodeAttributes/Command/AddNodeAggregateAttribute.php new file mode 100644 index 00000000000..1030c7afdca --- /dev/null +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeAttributes/Command/AddNodeAggregateAttribute.php @@ -0,0 +1,107 @@ + $array + */ + public static function fromArray(array $array): self + { + return new self( + ContentStreamId::fromString($array['contentStreamId']), + NodeAggregateId::fromString($array['nodeAggregateId']), + DimensionSpacePoint::fromArray($array['coveredDimensionSpacePoint']), + NodeVariantSelectionStrategy::from($array['nodeVariantSelectionStrategy']), + Attribute::fromString($array['attribute']), + ); + } + + /** + * @return array + */ + public function jsonSerialize(): array + { + return get_object_vars($this); + } + + public function createCopyForContentStream(ContentStreamId $target): self + { + return new self( + $target, + $this->nodeAggregateId, + $this->coveredDimensionSpacePoint, + $this->nodeVariantSelectionStrategy, + $this->attribute, + ); + } + + public function matchesNodeId(NodeIdToPublishOrDiscard $nodeIdToPublish): bool + { + return ( + $this->contentStreamId === $nodeIdToPublish->contentStreamId + && $this->coveredDimensionSpacePoint === $nodeIdToPublish->dimensionSpacePoint + && $this->nodeAggregateId->equals($nodeIdToPublish->nodeAggregateId) + ); + } +} diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeAttributes/Command/RemoveNodeAggregateAttribute.php b/Neos.ContentRepository.Core/Classes/Feature/NodeAttributes/Command/RemoveNodeAggregateAttribute.php new file mode 100644 index 00000000000..d54007a069b --- /dev/null +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeAttributes/Command/RemoveNodeAggregateAttribute.php @@ -0,0 +1,107 @@ + $array + */ + public static function fromArray(array $array): self + { + return new self( + ContentStreamId::fromString($array['contentStreamId']), + NodeAggregateId::fromString($array['nodeAggregateId']), + DimensionSpacePoint::fromArray($array['coveredDimensionSpacePoint']), + NodeVariantSelectionStrategy::from($array['nodeVariantSelectionStrategy']), + Attribute::fromString($array['attribute']) + ); + } + + /** + * @return array + */ + public function jsonSerialize(): array + { + return get_object_vars($this); + } + + public function createCopyForContentStream(ContentStreamId $target): self + { + return new self( + $target, + $this->nodeAggregateId, + $this->coveredDimensionSpacePoint, + $this->nodeVariantSelectionStrategy, + $this->attribute, + ); + } + + public function matchesNodeId(NodeIdToPublishOrDiscard $nodeIdToPublish): bool + { + return ( + $this->contentStreamId === $nodeIdToPublish->contentStreamId + && $this->coveredDimensionSpacePoint === $nodeIdToPublish->dimensionSpacePoint + && $this->nodeAggregateId->equals($nodeIdToPublish->nodeAggregateId) + ); + } +} diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeAttributes/Dto/Attribute.php b/Neos.ContentRepository.Core/Classes/Feature/NodeAttributes/Dto/Attribute.php new file mode 100644 index 00000000000..00c52fca08b --- /dev/null +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeAttributes/Dto/Attribute.php @@ -0,0 +1,39 @@ +value; + } +} diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeAttributes/Dto/Attributes.php b/Neos.ContentRepository.Core/Classes/Feature/NodeAttributes/Dto/Attributes.php new file mode 100644 index 00000000000..323f4d2488e --- /dev/null +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeAttributes/Dto/Attributes.php @@ -0,0 +1,66 @@ + + */ +final readonly class Attributes implements \IteratorAggregate, \JsonSerializable +{ + + /** + * @var array + */ + private array $attributes; + + private function __construct(Attribute ...$attributes) + { + $this->attributes = $attributes; + } + + public static function createEmpty(): self + { + return new self(); + } + + public static function fromStringArray(array $array): self + { + return new self(...array_map(Attribute::fromString(...), $array)); + } + + public function isEmpty(): bool + { + return $this->attributes === []; + } + + /** + * @return array + */ + public function toStringArray(): array + { + return array_map(static fn (Attribute $attribute) => $attribute->value, $this->attributes); + } + + public function jsonSerialize(): array + { + return $this->attributes; + } + + public function getIterator(): \Traversable + { + return new \ArrayIterator($this->attributes); + } +} diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeAttributes/Event/NodeAggregateAttributeWasAdded.php b/Neos.ContentRepository.Core/Classes/Feature/NodeAttributes/Event/NodeAggregateAttributeWasAdded.php new file mode 100644 index 00000000000..2255c9f17c4 --- /dev/null +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeAttributes/Event/NodeAggregateAttributeWasAdded.php @@ -0,0 +1,78 @@ +contentStreamId; + } + + public function getNodeAggregateId(): NodeAggregateId + { + return $this->nodeAggregateId; + } + + public function createCopyForContentStream(ContentStreamId $targetContentStreamId): self + { + return new self( + $targetContentStreamId, + $this->nodeAggregateId, + $this->affectedDimensionSpacePoints, + $this->attribute, + ); + } + + public static function fromArray(array $values): EventInterface + { + return new self( + ContentStreamId::fromString($values['contentStreamId']), + NodeAggregateId::fromString($values['nodeAggregateId']), + DimensionSpacePointSet::fromArray($values['affectedDimensionSpacePoints']), + Attribute::fromString($values['attribute']), + ); + } + + public function jsonSerialize(): array + { + return get_object_vars($this); + } +} diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeAttributes/Event/NodeAggregateAttributeWasRemoved.php b/Neos.ContentRepository.Core/Classes/Feature/NodeAttributes/Event/NodeAggregateAttributeWasRemoved.php new file mode 100644 index 00000000000..27047103596 --- /dev/null +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeAttributes/Event/NodeAggregateAttributeWasRemoved.php @@ -0,0 +1,78 @@ +contentStreamId; + } + + public function getNodeAggregateId(): NodeAggregateId + { + return $this->nodeAggregateId; + } + + public function createCopyForContentStream(ContentStreamId $targetContentStreamId): self + { + return new self( + $targetContentStreamId, + $this->nodeAggregateId, + $this->affectedDimensionSpacePoints, + $this->attribute, + ); + } + + public static function fromArray(array $values): EventInterface + { + return new self( + ContentStreamId::fromString($values['contentStreamId']), + NodeAggregateId::fromString($values['nodeAggregateId']), + DimensionSpacePointSet::fromArray($values['affectedDimensionSpacePoints']), + Attribute::fromString($values['attribute']), + ); + } + + public function jsonSerialize(): array + { + return get_object_vars($this); + } +} diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeDisabling/Event/NodeAggregateWasDisabled.php b/Neos.ContentRepository.Core/Classes/Feature/NodeDisabling/Event/NodeAggregateWasDisabled.php index 60639d39f69..6b263714e22 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeDisabling/Event/NodeAggregateWasDisabled.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeDisabling/Event/NodeAggregateWasDisabled.php @@ -24,7 +24,7 @@ /** * A node aggregate was disabled * - * @api events are the persistence-API of the content repository + * @deprecated This event will never be emitted, it is up-casted to a corresponding {@see NodeAggregateAttributeWasAdded} event instead. This implementation is just kept for backwards-compatibility */ final class NodeAggregateWasDisabled implements EventInterface, diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeDisabling/Event/NodeAggregateWasEnabled.php b/Neos.ContentRepository.Core/Classes/Feature/NodeDisabling/Event/NodeAggregateWasEnabled.php index 2fe7344543b..47924e8e724 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeDisabling/Event/NodeAggregateWasEnabled.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeDisabling/Event/NodeAggregateWasEnabled.php @@ -24,7 +24,7 @@ /** * A node aggregate was enabled * - * @api events are the persistence-API of the content repository + * @deprecated This event will never be emitted, it is up-casted to a corresponding {@see NodeAggregateAttributeWasRemoved} event instead. This implementation is just kept for backwards-compatibility */ final class NodeAggregateWasEnabled implements EventInterface, diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeDisabling/NodeDisabling.php b/Neos.ContentRepository.Core/Classes/Feature/NodeDisabling/NodeDisabling.php index ea4f647c33b..7d8b02961eb 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeDisabling/NodeDisabling.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeDisabling/NodeDisabling.php @@ -20,11 +20,12 @@ use Neos\ContentRepository\Core\EventStore\Events; use Neos\ContentRepository\Core\EventStore\EventsToPublish; use Neos\ContentRepository\Core\Feature\ContentStreamEventStreamName; +use Neos\ContentRepository\Core\Feature\NodeAttributes\Dto\Attribute; +use Neos\ContentRepository\Core\Feature\NodeAttributes\Event\NodeAggregateAttributeWasAdded; +use Neos\ContentRepository\Core\Feature\NodeAttributes\Event\NodeAggregateAttributeWasRemoved; use Neos\ContentRepository\Core\SharedModel\Exception\ContentStreamDoesNotExistYet; use Neos\ContentRepository\Core\Feature\NodeDisabling\Command\DisableNodeAggregate; use Neos\ContentRepository\Core\Feature\NodeDisabling\Command\EnableNodeAggregate; -use Neos\ContentRepository\Core\Feature\NodeDisabling\Event\NodeAggregateWasDisabled; -use Neos\ContentRepository\Core\Feature\NodeDisabling\Event\NodeAggregateWasEnabled; use Neos\ContentRepository\Core\SharedModel\Exception\NodeAggregatesTypeIsAmbiguous; use Neos\ContentRepository\Core\SharedModel\Exception\NodeAggregateCurrentlyDoesNotExist; use Neos\ContentRepository\Core\Feature\Common\NodeAggregateEventPublisher; @@ -74,10 +75,11 @@ private function handleDisableNodeAggregate( ); $events = Events::with( - new NodeAggregateWasDisabled( + new NodeAggregateAttributeWasAdded( $command->contentStreamId, $command->nodeAggregateId, $affectedDimensionSpacePoints, + Attribute::fromString('disabled'), ), ); @@ -128,10 +130,11 @@ public function handleEnableNodeAggregate( ); $events = Events::with( - new NodeAggregateWasEnabled( + new NodeAggregateAttributeWasRemoved( $command->contentStreamId, $command->nodeAggregateId, $affectedDimensionSpacePoints, + Attribute::fromString('disabled'), ) ); diff --git a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/ProjectionIntegrityViolationDetectionRunner.php b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/ProjectionIntegrityViolationDetectionRunner.php index cfabeca608e..300cba07df8 100644 --- a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/ProjectionIntegrityViolationDetectionRunner.php +++ b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/ProjectionIntegrityViolationDetectionRunner.php @@ -45,8 +45,8 @@ public function run(): Result ->nodeAggregatesAreConsistentlyClassifiedPerContentStream()); $result->merge($this->projectionIntegrityViolationDetector->referenceIntegrityIsProvided()); $result->merge($this->projectionIntegrityViolationDetector->referencesAreDistinctlySorted()); - $result->merge($this->projectionIntegrityViolationDetector->restrictionIntegrityIsProvided()); - $result->merge($this->projectionIntegrityViolationDetector->restrictionsArePropagatedRecursively()); + $result->merge($this->projectionIntegrityViolationDetector->attributeIntegrityIsProvided()); + $result->merge($this->projectionIntegrityViolationDetector->attributesArePropagatedRecursively()); $result->merge($this->projectionIntegrityViolationDetector->siblingsAreDistinctlySorted()); return $result; diff --git a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/ProjectionIntegrityViolationDetectorInterface.php b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/ProjectionIntegrityViolationDetectorInterface.php index 416a8b62e75..b00bae32816 100644 --- a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/ProjectionIntegrityViolationDetectorInterface.php +++ b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/ProjectionIntegrityViolationDetectorInterface.php @@ -30,8 +30,8 @@ interface ProjectionIntegrityViolationDetectorInterface public const ERROR_CODE_NODE_AGGREGATE_IS_AMBIGUOUSLY_CLASSIFIED = 1597825384; public const ERROR_CODE_NODE_IS_DISCONNECTED_FROM_THE_ROOT = 1597754245; public const ERROR_CODE_NODE_DOES_NOT_COVER_ITS_ORIGIN = 1597828607; - public const ERROR_CODE_NODE_HAS_MISSING_RESTRICTION = 1597837797; - public const ERROR_CODE_RESTRICTION_INTEGRITY_IS_COMPROMISED = 1597846598; + public const ERROR_CODE_NODE_HAS_MISSING_ATTRIBUTE = 1597837797; + public const ERROR_CODE_ATTRIBUTE_INTEGRITY_IS_COMPROMISED = 1597846598; public const ERROR_CODE_HIERARCHY_INTEGRITY_IS_COMPROMISED = 1597909228; public const ERROR_CODE_SIBLINGS_ARE_AMBIGUOUSLY_SORTED = 1597910918; public const ERROR_CODE_REFERENCE_INTEGRITY_IS_COMPROMISED = 1597919585; @@ -89,8 +89,8 @@ public function siblingsAreDistinctlySorted(): Result; public function tetheredNodesAreNamed(): Result; /** - * A is marked as hidden, so B and C should have incoming restriction edges. - * This test should fail if e.g. in the example below, the restriction edge from A to C is missing. + * A gets tagged with an attribute, so B and C should have corresponding incoming attribute edges. + * This test should fail if e.g. in the example below, the attribute edge from A to C is missing. * * ┌─────┐ * │ A │━━┓ @@ -101,26 +101,26 @@ public function tetheredNodesAreNamed(): Result; * │ B │◀━┛ * └─────┘ ┃ * │ - * │ ┃ <-- this Restriction Edge is missing. + * │ ┃ <-- this Attribute Edge is missing. * ┌─────┐ * │ C │◀ ┛ * └─────┘ */ - public function restrictionsArePropagatedRecursively(): Result; + public function attributesArePropagatedRecursively(): Result; /** - * Checks that the restriction edges are connected at source (e.g. to "A") and at destination (e.g. to "B") + * Checks that the attribute edges are connected at source (e.g. to "A") and at destination (e.g. to "B") * * ┌─────┐ - * │ A │━━┓ <-- checks that A exists (for each restriction edge) + * │ A │━━┓ <-- checks that A exists (for each attribute edge) * └─────┘ ┃ * │ ┃ * │ ┃ * ┌─────┐ ┃ - * │ B │◀━┛ <-- checks that B exists (for each restriction edge) + * │ B │◀━┛ <-- checks that B exists (for each attribute edge) * └─────┘ */ - public function restrictionIntegrityIsProvided(): Result; + public function attributeIntegrityIsProvided(): Result; /** * Checks that the reference edges are connected at source (e.g. to "A") and at destination (e.g. to "B") diff --git a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/VisibilityConstraints.php b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/VisibilityConstraints.php index e8404c11b21..4e8ce386980 100644 --- a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/VisibilityConstraints.php +++ b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/VisibilityConstraints.php @@ -14,6 +14,9 @@ namespace Neos\ContentRepository\Core\Projection\ContentGraph; +use Neos\ContentRepository\Core\Feature\NodeAttributes\Dto\Attribute; +use Neos\ContentRepository\Core\Feature\NodeAttributes\Dto\Attributes; + /** * The context parameters value object * @@ -35,6 +38,11 @@ public function isDisabledContentShown(): bool return $this->disabledContentShown; } + public function restrictedAttributes(): Attributes + { + return $this->disabledContentShown ? Attributes::createEmpty() : Attributes::fromStringArray(['disabled']); + } + public function getHash(): string { return md5('disabled' . $this->disabledContentShown); diff --git a/Neos.ContentRepository.Core/Classes/Projection/NodeHiddenState/NodeHiddenStateProjection.php b/Neos.ContentRepository.Core/Classes/Projection/NodeHiddenState/NodeHiddenStateProjection.php index 3b83ff11b60..80097755e37 100644 --- a/Neos.ContentRepository.Core/Classes/Projection/NodeHiddenState/NodeHiddenStateProjection.php +++ b/Neos.ContentRepository.Core/Classes/Projection/NodeHiddenState/NodeHiddenStateProjection.php @@ -22,8 +22,8 @@ use Neos\ContentRepository\Core\EventStore\EventInterface; use Neos\ContentRepository\Core\Feature\ContentStreamForking\Event\ContentStreamWasForked; use Neos\ContentRepository\Core\Feature\DimensionSpaceAdjustment\Event\DimensionSpacePointWasMoved; -use Neos\ContentRepository\Core\Feature\NodeDisabling\Event\NodeAggregateWasDisabled; -use Neos\ContentRepository\Core\Feature\NodeDisabling\Event\NodeAggregateWasEnabled; +use Neos\ContentRepository\Core\Feature\NodeAttributes\Event\NodeAggregateAttributeWasAdded; +use Neos\ContentRepository\Core\Feature\NodeAttributes\Event\NodeAggregateAttributeWasRemoved; use Neos\ContentRepository\Core\Infrastructure\DbalClientInterface; use Neos\ContentRepository\Core\Projection\ProjectionInterface; use Neos\EventStore\CatchUp\CheckpointStorageInterface; @@ -102,8 +102,8 @@ public function reset(): void public function canHandle(EventInterface $event): bool { return in_array($event::class, [ - NodeAggregateWasDisabled::class, - NodeAggregateWasEnabled::class, + NodeAggregateAttributeWasAdded::class, + NodeAggregateAttributeWasRemoved::class, ContentStreamWasForked::class, DimensionSpacePointWasMoved::class ]); @@ -112,8 +112,8 @@ public function canHandle(EventInterface $event): bool public function apply(EventInterface $event, EventEnvelope $eventEnvelope): void { match ($event::class) { - NodeAggregateWasDisabled::class => $this->whenNodeAggregateWasDisabled($event), - NodeAggregateWasEnabled::class => $this->whenNodeAggregateWasEnabled($event), + NodeAggregateAttributeWasAdded::class => $this->whenNodeAggregateAttributeWasAdded($event), + NodeAggregateAttributeWasRemoved::class => $this->whenNodeAggregateAttributeWasRemoved($event), ContentStreamWasForked::class => $this->whenContentStreamWasForked($event), DimensionSpacePointWasMoved::class => $this->whenDimensionSpacePointWasMoved($event), default => throw new \InvalidArgumentException(sprintf('Unsupported event %s', get_debug_type($event))), @@ -137,7 +137,7 @@ public function getState(): NodeHiddenStateFinder } - private function whenNodeAggregateWasDisabled(NodeAggregateWasDisabled $event): void + private function whenNodeAggregateAttributeWasAdded(NodeAggregateAttributeWasAdded $event): void { $this->transactional(function () use ($event) { foreach ($event->affectedDimensionSpacePoints as $dimensionSpacePoint) { @@ -160,7 +160,7 @@ private function whenNodeAggregateWasDisabled(NodeAggregateWasDisabled $event): }); } - private function whenNodeAggregateWasEnabled(NodeAggregateWasEnabled $event): void + private function whenNodeAggregateAttributeWasRemoved(NodeAggregateAttributeWasRemoved $event): void { $this->getDatabaseConnection()->executeQuery( ' diff --git a/Neos.ContentRepository.LegacyNodeMigration/Classes/NodeDataToEventsProcessor.php b/Neos.ContentRepository.LegacyNodeMigration/Classes/NodeDataToEventsProcessor.php index 6e827a4eb69..fad4ecf198e 100644 --- a/Neos.ContentRepository.LegacyNodeMigration/Classes/NodeDataToEventsProcessor.php +++ b/Neos.ContentRepository.LegacyNodeMigration/Classes/NodeDataToEventsProcessor.php @@ -9,48 +9,47 @@ use League\Flysystem\FilesystemException; use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePointSet; use Neos\ContentRepository\Core\DimensionSpace\InterDimensionalVariationGraph; +use Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePoint; +use Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePointSet; use Neos\ContentRepository\Core\DimensionSpace\VariantType; use Neos\ContentRepository\Core\EventStore\EventInterface; use Neos\ContentRepository\Core\EventStore\EventNormalizer; -use Neos\ContentRepository\Core\Feature\NodeMove\Dto\CoverageNodeMoveMapping; -use Neos\ContentRepository\Core\Feature\NodeMove\Dto\CoverageNodeMoveMappings; -use Neos\ContentRepository\Core\SharedModel\Node\ReferenceName; -use Neos\ContentRepository\Export\Event\ValueObject\ExportedEvent; -use Neos\ContentRepository\Export\ProcessorInterface; -use Neos\ContentRepository\Export\ProcessorResult; -use Neos\ContentRepository\Core\Feature\NodeModification\Dto\PropertyValuesToWrite; -use Neos\ContentRepository\Core\Feature\NodeReferencing\Dto\SerializedNodeReferences; use Neos\ContentRepository\Core\Feature\NodeCreation\Event\NodeAggregateWithNodeWasCreated; -use Neos\ContentRepository\Core\Feature\NodeDisabling\Event\NodeAggregateWasDisabled; +use Neos\ContentRepository\Core\Feature\NodeAttributes\Event\NodeAggregateAttributeWasAdded; +use Neos\ContentRepository\Core\Feature\NodeAttributes\Dto\Attribute; +use Neos\ContentRepository\Core\Feature\NodeModification\Dto\PropertyValuesToWrite; use Neos\ContentRepository\Core\Feature\NodeModification\Event\NodePropertiesWereSet; -use Neos\ContentRepository\Core\Feature\NodeMove\Event\NodeAggregateWasMoved; +use Neos\ContentRepository\Core\Feature\NodeMove\Dto\CoverageNodeMoveMapping; +use Neos\ContentRepository\Core\Feature\NodeMove\Dto\CoverageNodeMoveMappings; use Neos\ContentRepository\Core\Feature\NodeMove\Dto\OriginNodeMoveMapping; use Neos\ContentRepository\Core\Feature\NodeMove\Dto\OriginNodeMoveMappings; use Neos\ContentRepository\Core\Feature\NodeMove\Dto\SucceedingSiblingNodeMoveDestination; +use Neos\ContentRepository\Core\Feature\NodeMove\Event\NodeAggregateWasMoved; +use Neos\ContentRepository\Core\Feature\NodeReferencing\Dto\SerializedNodeReferences; use Neos\ContentRepository\Core\Feature\NodeReferencing\Event\NodeReferencesWereSet; use Neos\ContentRepository\Core\Feature\NodeVariation\Event\NodeGeneralizationVariantWasCreated; use Neos\ContentRepository\Core\Feature\NodeVariation\Event\NodePeerVariantWasCreated; use Neos\ContentRepository\Core\Feature\NodeVariation\Event\NodeSpecializationVariantWasCreated; use Neos\ContentRepository\Core\Feature\RootNodeCreation\Event\RootNodeAggregateWithNodeWasCreated; use Neos\ContentRepository\Core\Infrastructure\Property\PropertyConverter; -use Neos\ContentRepository\Export\Severity; -use Neos\ContentRepository\LegacyNodeMigration\Exception\MigrationException; -use Neos\ContentRepository\LegacyNodeMigration\Helpers\SerializedPropertyValuesAndReferences; -use Neos\ContentRepository\LegacyNodeMigration\Helpers\VisitedNodeAggregate; -use Neos\ContentRepository\LegacyNodeMigration\Helpers\VisitedNodeAggregates; +use Neos\ContentRepository\Core\NodeType\NodeType; +use Neos\ContentRepository\Core\NodeType\NodeTypeManager; +use Neos\ContentRepository\Core\NodeType\NodeTypeName; +use Neos\ContentRepository\Core\Projection\ContentGraph\NodePath; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateClassification; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateIds; use Neos\ContentRepository\Core\SharedModel\Node\NodeName; -use Neos\ContentRepository\Core\Projection\ContentGraph\NodePath; -use Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePoint; -use Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePointSet; -use Neos\ContentRepository\Core\SharedModel\Node\PropertyName; -use Neos\ContentRepository\Core\NodeType\NodeType; -use Neos\ContentRepository\Core\NodeType\NodeTypeManager; -use Neos\ContentRepository\Core\NodeType\NodeTypeName; -use Neos\ContentRepository\Core\SharedModel\User\UserId; +use Neos\ContentRepository\Core\SharedModel\Node\ReferenceName; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; +use Neos\ContentRepository\Export\Event\ValueObject\ExportedEvent; +use Neos\ContentRepository\Export\ProcessorInterface; +use Neos\ContentRepository\Export\ProcessorResult; +use Neos\ContentRepository\Export\Severity; +use Neos\ContentRepository\LegacyNodeMigration\Exception\MigrationException; +use Neos\ContentRepository\LegacyNodeMigration\Helpers\SerializedPropertyValuesAndReferences; +use Neos\ContentRepository\LegacyNodeMigration\Helpers\VisitedNodeAggregate; +use Neos\ContentRepository\LegacyNodeMigration\Helpers\VisitedNodeAggregates; use Neos\Flow\Persistence\Doctrine\DataTypes\JsonArrayType; use Neos\Flow\Property\PropertyMapper; use Ramsey\Uuid\Uuid; @@ -255,9 +254,9 @@ public function processNodeDataWithoutFallbackToEmptyDimension(NodeAggregateId $ // create node aggregate $this->exportEvent(new NodeAggregateWithNodeWasCreated($this->contentStreamId, $nodeAggregateId, $nodeTypeName, $originDimensionSpacePoint, $this->interDimensionalVariationGraph->getSpecializationSet($originDimensionSpacePoint->toDimensionSpacePoint()), $parentNodeAggregate->nodeAggregateId, $nodeName, $serializedPropertyValuesAndReferences->serializedPropertyValues, NodeAggregateClassification::CLASSIFICATION_REGULAR, null)); } - // nodes are hidden via NodeAggregateWasDisabled event + // nodes are hidden via NodeAggregateAttributeWasAdded event if ($nodeDataRow['hidden']) { - $this->exportEvent(new NodeAggregateWasDisabled($this->contentStreamId, $nodeAggregateId, $this->interDimensionalVariationGraph->getSpecializationSet($originDimensionSpacePoint->toDimensionSpacePoint(), true, $this->visitedNodes->alreadyVisitedOriginDimensionSpacePoints($nodeAggregateId)->toDimensionSpacePointSet()))); + $this->exportEvent(new NodeAggregateAttributeWasAdded($this->contentStreamId, $nodeAggregateId, $this->interDimensionalVariationGraph->getSpecializationSet($originDimensionSpacePoint->toDimensionSpacePoint(), true, $this->visitedNodes->alreadyVisitedOriginDimensionSpacePoints($nodeAggregateId)->toDimensionSpacePointSet()), Attribute::fromString('disabled'))); } foreach ($serializedPropertyValuesAndReferences->references as $referencePropertyName => $destinationNodeAggregateIds) { $this->nodeReferencesWereSetEvents[] = new NodeReferencesWereSet($this->contentStreamId, $nodeAggregateId, new OriginDimensionSpacePointSet([$originDimensionSpacePoint]), ReferenceName::fromString($referencePropertyName), SerializedNodeReferences::fromNodeAggregateIds($destinationNodeAggregateIds)); diff --git a/Neos.ContentRepository.TestSuite/Classes/Behavior/Features/Bootstrap/Features/NodeDisabling.php b/Neos.ContentRepository.TestSuite/Classes/Behavior/Features/Bootstrap/Features/NodeDisabling.php index a794a506c34..febebfff2f4 100644 --- a/Neos.ContentRepository.TestSuite/Classes/Behavior/Features/Bootstrap/Features/NodeDisabling.php +++ b/Neos.ContentRepository.TestSuite/Classes/Behavior/Features/Bootstrap/Features/NodeDisabling.php @@ -75,11 +75,11 @@ public function theCommandDisableNodeAggregateIsExecutedWithPayloadAndExceptions } /** - * @Given /^the event NodeAggregateWasDisabled was published with payload:$/ + * @Given /^the event NodeAggregateAttributeWasAdded was published with payload:$/ * @param TableNode $payloadTable * @throws \Exception */ - public function theEventNodeAggregateWasDisabledWasPublishedWithPayload(TableNode $payloadTable) + public function theEventNodeAggregateAttributeWasAddedWasPublishedWithPayload(TableNode $payloadTable) { $eventPayload = $this->readPayloadTable($payloadTable); $streamName = ContentStreamEventStreamName::fromContentStreamId( @@ -88,16 +88,15 @@ public function theEventNodeAggregateWasDisabledWasPublishedWithPayload(TableNod : $this->currentContentStreamId ); - $this->publishEvent('NodeAggregateWasDisabled', $streamName->getEventStreamName(), $eventPayload); + $this->publishEvent('NodeAggregateAttributeWasAdded', $streamName->getEventStreamName(), $eventPayload); } - /** - * @Given /^the event NodeAggregateWasEnabled was published with payload:$/ + * @Given /^the event NodeAggregateAttributeWasRemoved was published with payload:$/ * @param TableNode $payloadTable * @throws \Exception */ - public function theEventNodeAggregateWasEnabledWasPublishedWithPayload(TableNode $payloadTable) + public function theEventNodeAggregateAttributeWasRemovedWasPublishedWithPayload(TableNode $payloadTable) { $eventPayload = $this->readPayloadTable($payloadTable); $streamName = ContentStreamEventStreamName::fromContentStreamId( @@ -106,7 +105,7 @@ public function theEventNodeAggregateWasEnabledWasPublishedWithPayload(TableNode : $this->currentContentStreamId ); - $this->publishEvent('NodeAggregateWasEnabled', $streamName->getEventStreamName(), $eventPayload); + $this->publishEvent('NodeAggregateAttributeWasRemoved', $streamName->getEventStreamName(), $eventPayload); } diff --git a/Neos.Neos/Classes/FrontendRouting/CatchUpHook/RouterCacheHook.php b/Neos.Neos/Classes/FrontendRouting/CatchUpHook/RouterCacheHook.php index 8477bb071e7..96bd8deb285 100644 --- a/Neos.Neos/Classes/FrontendRouting/CatchUpHook/RouterCacheHook.php +++ b/Neos.Neos/Classes/FrontendRouting/CatchUpHook/RouterCacheHook.php @@ -2,21 +2,21 @@ namespace Neos\Neos\FrontendRouting\CatchUpHook; -use Neos\ContentRepository\Core\Projection\CatchUpHookInterface; use Neos\ContentRepository\Core\ContentRepository; +use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint; use Neos\ContentRepository\Core\EventStore\EventInterface; -use Neos\EventStore\Model\EventEnvelope; +use Neos\ContentRepository\Core\Feature\NodeAttributes\Event\NodeAggregateAttributeWasAdded; use Neos\ContentRepository\Core\Feature\NodeModification\Event\NodePropertiesWereSet; +use Neos\ContentRepository\Core\Feature\NodeMove\Dto\CoverageNodeMoveMapping; use Neos\ContentRepository\Core\Feature\NodeMove\Event\NodeAggregateWasMoved; use Neos\ContentRepository\Core\Feature\NodeRemoval\Event\NodeAggregateWasRemoved; -use Neos\Neos\FrontendRouting\Projection\DocumentUriPathFinder; -use Neos\ContentRepository\Core\Feature\NodeMove\Dto\CoverageNodeMoveMapping; -use Neos\Neos\FrontendRouting\Projection\DocumentNodeInfo; -use Neos\Neos\FrontendRouting\Exception\NodeNotFoundException; -use Neos\ContentRepository\Core\Feature\NodeDisabling\Event\NodeAggregateWasDisabled; -use Neos\Flow\Mvc\Routing\RouterCachingService; +use Neos\ContentRepository\Core\Projection\CatchUpHookInterface; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; -use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint; +use Neos\EventStore\Model\EventEnvelope; +use Neos\Flow\Mvc\Routing\RouterCachingService; +use Neos\Neos\FrontendRouting\Exception\NodeNotFoundException; +use Neos\Neos\FrontendRouting\Projection\DocumentNodeInfo; +use Neos\Neos\FrontendRouting\Projection\DocumentUriPathFinder; final class RouterCacheHook implements CatchUpHookInterface { @@ -43,7 +43,7 @@ public function onBeforeEvent(EventInterface $eventInstance, EventEnvelope $even NodeAggregateWasRemoved::class => $this->onBeforeNodeAggregateWasRemoved($eventInstance), NodePropertiesWereSet::class => $this->onBeforeNodePropertiesWereSet($eventInstance), NodeAggregateWasMoved::class => $this->onBeforeNodeAggregateWasMoved($eventInstance), - NodeAggregateWasDisabled::class => $this->onBeforeNodeAggregateWasDisabled($eventInstance), + NodeAggregateAttributeWasAdded::class => $this->onBeforeNodeAggregateAttributeWasAdded($eventInstance), default => null }; } @@ -51,10 +51,8 @@ public function onBeforeEvent(EventInterface $eventInstance, EventEnvelope $even public function onAfterEvent(EventInterface $eventInstance, EventEnvelope $eventEnvelope): void { match ($eventInstance::class) { - NodeAggregateWasRemoved::class => $this->flushAllCollectedTags(), - NodePropertiesWereSet::class => $this->flushAllCollectedTags(), - NodeAggregateWasMoved::class => $this->flushAllCollectedTags(), - NodeAggregateWasDisabled::class => $this->flushAllCollectedTags(), + NodeAggregateWasRemoved::class, NodePropertiesWereSet::class, NodeAggregateWasMoved::class => $this->flushAllCollectedTags(), + NodeAggregateAttributeWasAdded::class => $eventInstance->attribute->value === 'disabled' ? $this->flushAllCollectedTags() : null, default => null }; } @@ -69,9 +67,9 @@ public function onAfterCatchUp(): void // Nothing to do here } - private function onBeforeNodeAggregateWasDisabled(NodeAggregateWasDisabled $event): void + private function onBeforeNodeAggregateAttributeWasAdded(NodeAggregateAttributeWasAdded $event): void { - if (!$this->getState()->isLiveContentStream($event->contentStreamId)) { + if ($event->attribute->value !== 'disabled' || !$this->getState()->isLiveContentStream($event->contentStreamId)) { return; } diff --git a/Neos.Neos/Classes/FrontendRouting/Projection/DocumentUriPathProjection.php b/Neos.Neos/Classes/FrontendRouting/Projection/DocumentUriPathProjection.php index f2150759fde..f22b9eea969 100644 --- a/Neos.Neos/Classes/FrontendRouting/Projection/DocumentUriPathProjection.php +++ b/Neos.Neos/Classes/FrontendRouting/Projection/DocumentUriPathProjection.php @@ -15,8 +15,8 @@ use Neos\ContentRepository\Core\Feature\DimensionSpaceAdjustment\Event\DimensionShineThroughWasAdded; use Neos\ContentRepository\Core\Feature\DimensionSpaceAdjustment\Event\DimensionSpacePointWasMoved; use Neos\ContentRepository\Core\Feature\NodeCreation\Event\NodeAggregateWithNodeWasCreated; -use Neos\ContentRepository\Core\Feature\NodeDisabling\Event\NodeAggregateWasDisabled; -use Neos\ContentRepository\Core\Feature\NodeDisabling\Event\NodeAggregateWasEnabled; +use Neos\ContentRepository\Core\Feature\NodeAttributes\Event\NodeAggregateAttributeWasAdded; +use Neos\ContentRepository\Core\Feature\NodeAttributes\Event\NodeAggregateAttributeWasRemoved; use Neos\ContentRepository\Core\Feature\NodeModification\Event\NodePropertiesWereSet; use Neos\ContentRepository\Core\Feature\NodeMove\Dto\CoverageNodeMoveMapping; use Neos\ContentRepository\Core\Feature\NodeMove\Dto\ParentNodeMoveDestination; @@ -121,8 +121,8 @@ public function canHandle(EventInterface $event): bool NodePeerVariantWasCreated::class, NodeGeneralizationVariantWasCreated::class, NodeSpecializationVariantWasCreated::class, - NodeAggregateWasDisabled::class, - NodeAggregateWasEnabled::class, + NodeAggregateAttributeWasAdded::class, + NodeAggregateAttributeWasRemoved::class, NodeAggregateWasRemoved::class, NodePropertiesWereSet::class, NodeAggregateWasMoved::class, @@ -142,8 +142,8 @@ public function apply(EventInterface $event, EventEnvelope $eventEnvelope): void NodePeerVariantWasCreated::class => $this->whenNodePeerVariantWasCreated($event), NodeGeneralizationVariantWasCreated::class => $this->whenNodeGeneralizationVariantWasCreated($event), NodeSpecializationVariantWasCreated::class => $this->whenNodeSpecializationVariantWasCreated($event), - NodeAggregateWasDisabled::class => $this->whenNodeAggregateWasDisabled($event), - NodeAggregateWasEnabled::class => $this->whenNodeAggregateWasEnabled($event), + NodeAggregateAttributeWasAdded::class => $this->whenNodeAggregateAttributeWasAdded($event), + NodeAggregateAttributeWasRemoved::class => $this->whenNodeAggregateAttributeWasRemoved($event), NodeAggregateWasRemoved::class => $this->whenNodeAggregateWasRemoved($event), NodePropertiesWereSet::class => $this->whenNodePropertiesWereSet($event, $eventEnvelope), NodeAggregateWasMoved::class => $this->whenNodeAggregateWasMoved($event), @@ -415,9 +415,9 @@ private function copyVariants( } } - private function whenNodeAggregateWasDisabled(NodeAggregateWasDisabled $event): void + private function whenNodeAggregateAttributeWasAdded(NodeAggregateAttributeWasAdded $event): void { - if (!$this->getState()->isLiveContentStream($event->contentStreamId)) { + if ($event->attribute->value !== 'disabled' || !$this->getState()->isLiveContentStream($event->contentStreamId)) { return; } foreach ($event->affectedDimensionSpacePoints as $dimensionSpacePoint) { @@ -446,9 +446,9 @@ private function whenNodeAggregateWasDisabled(NodeAggregateWasDisabled $event): } } - private function whenNodeAggregateWasEnabled(NodeAggregateWasEnabled $event): void + private function whenNodeAggregateAttributeWasRemoved(NodeAggregateAttributeWasRemoved $event): void { - if (!$this->getState()->isLiveContentStream($event->contentStreamId)) { + if ($event->attribute->value !== 'disabled' || !$this->getState()->isLiveContentStream($event->contentStreamId)) { return; } foreach ($event->affectedDimensionSpacePoints as $dimensionSpacePoint) { diff --git a/Neos.Neos/Classes/PendingChangesProjection/ChangeProjection.php b/Neos.Neos/Classes/PendingChangesProjection/ChangeProjection.php index 4f4a640b9b3..9a969cd3011 100644 --- a/Neos.Neos/Classes/PendingChangesProjection/ChangeProjection.php +++ b/Neos.Neos/Classes/PendingChangesProjection/ChangeProjection.php @@ -24,8 +24,8 @@ use Neos\ContentRepository\Core\EventStore\EventInterface; use Neos\ContentRepository\Core\Feature\DimensionSpaceAdjustment\Event\DimensionSpacePointWasMoved; use Neos\ContentRepository\Core\Feature\NodeCreation\Event\NodeAggregateWithNodeWasCreated; -use Neos\ContentRepository\Core\Feature\NodeDisabling\Event\NodeAggregateWasDisabled; -use Neos\ContentRepository\Core\Feature\NodeDisabling\Event\NodeAggregateWasEnabled; +use Neos\ContentRepository\Core\Feature\NodeAttributes\Event\NodeAggregateAttributeWasAdded; +use Neos\ContentRepository\Core\Feature\NodeAttributes\Event\NodeAggregateAttributeWasRemoved; use Neos\ContentRepository\Core\Feature\NodeModification\Event\NodePropertiesWereSet; use Neos\ContentRepository\Core\Feature\NodeMove\Event\NodeAggregateWasMoved; use Neos\ContentRepository\Core\Feature\NodeReferencing\Event\NodeReferencesWereSet; @@ -168,8 +168,8 @@ public function canHandle(EventInterface $event): bool NodePropertiesWereSet::class, NodeReferencesWereSet::class, NodeAggregateWithNodeWasCreated::class, - NodeAggregateWasDisabled::class, - NodeAggregateWasEnabled::class, + NodeAggregateAttributeWasAdded::class, + NodeAggregateAttributeWasRemoved::class, NodeAggregateWasRemoved::class, DimensionSpacePointWasMoved::class, NodeGeneralizationVariantWasCreated::class, @@ -186,8 +186,8 @@ public function apply(EventInterface $event, EventEnvelope $eventEnvelope): void NodePropertiesWereSet::class => $this->whenNodePropertiesWereSet($event), NodeReferencesWereSet::class => $this->whenNodeReferencesWereSet($event), NodeAggregateWithNodeWasCreated::class => $this->whenNodeAggregateWithNodeWasCreated($event), - NodeAggregateWasDisabled::class => $this->whenNodeAggregateWasDisabled($event), - NodeAggregateWasEnabled::class => $this->whenNodeAggregateWasEnabled($event), + NodeAggregateAttributeWasAdded::class => $this->whenNodeAggregateAttributeWasAdded($event), + NodeAggregateAttributeWasRemoved::class => $this->whenNodeAggregateAttributeWasRemoved($event), NodeAggregateWasRemoved::class => $this->whenNodeAggregateWasRemoved($event), DimensionSpacePointWasMoved::class => $this->whenDimensionSpacePointWasMoved($event), NodeSpecializationVariantWasCreated::class => $this->whenNodeSpecializationVariantWasCreated($event), @@ -272,7 +272,7 @@ private function whenNodeAggregateWithNodeWasCreated(NodeAggregateWithNodeWasCre ); } - private function whenNodeAggregateWasDisabled(NodeAggregateWasDisabled $event): void + private function whenNodeAggregateAttributeWasAdded(NodeAggregateAttributeWasAdded $event): void { foreach ($event->affectedDimensionSpacePoints as $dimensionSpacePoint) { $this->markAsChanged( @@ -283,7 +283,7 @@ private function whenNodeAggregateWasDisabled(NodeAggregateWasDisabled $event): } } - private function whenNodeAggregateWasEnabled(NodeAggregateWasEnabled $event): void + private function whenNodeAggregateAttributeWasRemoved(NodeAggregateAttributeWasRemoved $event): void { foreach ($event->affectedDimensionSpacePoints as $dimensionSpacePoint) { $this->markAsChanged( diff --git a/Neos.Neos/Tests/Behavior/Features/FrontendRouting/DisableNodes.feature b/Neos.Neos/Tests/Behavior/Features/FrontendRouting/DisableNodes.feature index 41b44abc148..9d5ab463e04 100644 --- a/Neos.Neos/Tests/Behavior/Features/FrontendRouting/DisableNodes.feature +++ b/Neos.Neos/Tests/Behavior/Features/FrontendRouting/DisableNodes.feature @@ -32,10 +32,10 @@ Feature: Routing behavior of removed, disabled and re-enabled nodes | workspaceName | "live" | | newContentStreamId | "cs-identifier" | And the command CreateRootNodeAggregateWithNode is executed with payload: - | Key | Value | - | contentStreamId | "cs-identifier" | - | nodeAggregateId | "lady-eleonode-rootford" | - | nodeTypeName | "Neos.Neos:Sites" | + | Key | Value | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "lady-eleonode-rootford" | + | nodeTypeName | "Neos.Neos:Sites" | And the graph projection is fully up to date # lady-eleonode-rootford @@ -48,7 +48,7 @@ Feature: Routing behavior of removed, disabled and re-enabled nodes # And I am in content stream "cs-identifier" and dimension space point {} And the following CreateNodeAggregateWithNode commands are executed: - | nodeAggregateId | parentNodeAggregateId | nodeTypeName | initialPropertyValues | nodeName | + | nodeAggregateId | parentNodeAggregateId | nodeTypeName | initialPropertyValues | nodeName | | shernode-homes | lady-eleonode-rootford | Neos.Neos:Test.Routing.Page | {"uriPathSegment": "ignore-me"} | node1 | | sir-david-nodenborough | shernode-homes | Neos.Neos:Test.Routing.Page | {"uriPathSegment": "david-nodenborough"} | node2 | | duke-of-contentshire | sir-david-nodenborough | Neos.Neos:Test.Routing.Content | {"uriPathSegment": "ignore-me"} | node3 | @@ -166,11 +166,12 @@ Feature: Routing behavior of removed, disabled and re-enabled nodes | nodeAggregateId | "sir-david-nodenborough" | | coveredDimensionSpacePoint | {} | | nodeVariantSelectionStrategy | "allVariants" | - And the event NodeAggregateWasDisabled was published with payload: + And the event NodeAggregateAttributeWasAdded was published with payload: | Key | Value | | contentStreamId | "cs-identifier" | | nodeAggregateId | "sir-david-nodenborough" | | affectedDimensionSpacePoints | [{}] | + | attribute | "disabled" | And the graph projection is fully up to date And The documenturipath projection is up to date Then No node should match URL "/david-nodenborough" @@ -197,11 +198,12 @@ Feature: Routing behavior of removed, disabled and re-enabled nodes | nodeAggregateId | "sir-david-nodenborough" | | coveredDimensionSpacePoint | {} | | nodeVariantSelectionStrategy | "allVariants" | - And the event NodeAggregateWasDisabled was published with payload: + And the event NodeAggregateAttributeWasAdded was published with payload: | Key | Value | | contentStreamId | "cs-identifier" | | nodeAggregateId | "earl-o-documentbourgh" | | affectedDimensionSpacePoints | [{}] | + | attribute | "disabled" | And the graph projection is fully up to date And The documenturipath projection is up to date Then No node should match URL "/david-nodenborough" @@ -214,11 +216,12 @@ Feature: Routing behavior of removed, disabled and re-enabled nodes | nodeAggregateId | "sir-david-nodenborough" | | coveredDimensionSpacePoint | {} | | nodeVariantSelectionStrategy | "allVariants" | - And the event NodeAggregateWasEnabled was published with payload: + And the event NodeAggregateAttributeWasRemoved was published with payload: | Key | Value | | contentStreamId | "cs-identifier" | | nodeAggregateId | "sir-david-nodenborough" | | affectedDimensionSpacePoints | [{}] | + | tag | "disabled" | And the graph projection is fully up to date And The documenturipath projection is up to date When I am on URL "/david-nodenborough"