Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/9.0' into bugfix/review-workspac…
Browse files Browse the repository at this point in the history
…e-changes
  • Loading branch information
mhsdesign committed Oct 17, 2023
2 parents 0f2dc77 + f04a914 commit fa38119
Show file tree
Hide file tree
Showing 73 changed files with 740 additions and 849 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,9 @@ protected function requireRootNodeTypeToBeUnoccupied(
protected function requireTetheredDescendantNodeTypesToExist(NodeType $nodeType): void
{
// this getter throws if any of the child nodeTypes doesnt exist!
$childNodeTypes = $nodeType->getAutoCreatedChildNodes();
foreach ($childNodeTypes as $childNodeType) {
$this->requireTetheredDescendantNodeTypesToExist($childNodeType);
$tetheredNodeTypes = $this->getNodeTypeManager()->getTetheredNodesConfigurationForNodeType($nodeType);
foreach ($tetheredNodeTypes as $tetheredNodeType) {
$this->requireTetheredDescendantNodeTypesToExist($tetheredNodeType);
}
}

Expand All @@ -176,7 +176,7 @@ protected function requireTetheredDescendantNodeTypesToExist(NodeType $nodeType)
*/
protected function requireTetheredDescendantNodeTypesToNotBeOfTypeRoot(NodeType $nodeType): void
{
foreach ($nodeType->getAutoCreatedChildNodes() as $tetheredChildNodeType) {
foreach ($this->getNodeTypeManager()->getTetheredNodesConfigurationForNodeType($nodeType) as $tetheredChildNodeType) {
if ($tetheredChildNodeType->isOfType(NodeTypeName::ROOT_NODE_TYPE_NAME)) {
throw new NodeTypeIsOfTypeRoot(
'Node type "' . $nodeType->name->value . '" for tethered descendant is of type root.',
Expand Down Expand Up @@ -301,12 +301,12 @@ protected function requireNodeTypeConstraintsImposedByParentToBeMet(
}
if (
$nodeName
&& $parentsNodeType->hasAutoCreatedChildNode($nodeName)
&& !$parentsNodeType->getTypeOfAutoCreatedChildNode($nodeName)?->name->equals($nodeType->name)
&& $parentsNodeType->hasTetheredNode($nodeName)
&& !$this->getNodeTypeManager()->getTypeOfTetheredNode($parentsNodeType, $nodeName)->name->equals($nodeType->name)
) {
throw new NodeConstraintException(
'Node type "' . $nodeType->name->value . '" does not match configured "'
. $parentsNodeType->getTypeOfAutoCreatedChildNode($nodeName)?->name->value
. $this->getNodeTypeManager()->getTypeOfTetheredNode($parentsNodeType, $nodeName)->name->value
. '" for auto created child nodes for parent type "' . $parentsNodeType->name->value
. '" with name "' . $nodeName->value . '"'
);
Expand All @@ -324,8 +324,8 @@ protected function areNodeTypeConstraintsImposedByParentValid(
}
if (
$nodeName
&& $parentsNodeType->hasAutoCreatedChildNode($nodeName)
&& !$parentsNodeType->getTypeOfAutoCreatedChildNode($nodeName)?->name->equals($nodeType->name)
&& $parentsNodeType->hasTetheredNode($nodeName)
&& !$this->getNodeTypeManager()->getTypeOfTetheredNode($parentsNodeType, $nodeName)->name->equals($nodeType->name)
) {
return false;
}
Expand Down Expand Up @@ -362,8 +362,8 @@ protected function areNodeTypeConstraintsImposedByGrandparentValid(
): bool {
if (
$parentNodeName
&& $grandParentsNodeType->hasAutoCreatedChildNode($parentNodeName)
&& !$grandParentsNodeType->allowsGrandchildNodeType($parentNodeName->value, $nodeType)
&& $grandParentsNodeType->hasTetheredNode($parentNodeName)
&& !$this->getNodeTypeManager()->isNodeTypeAllowedAsChildToTetheredNode($grandParentsNodeType, $parentNodeName, $nodeType)
) {
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,8 @@ protected function checkConstraintsImposedByAncestors(
}
if (
$nodeAggregate->nodeName
&& $parentsNodeType->hasAutoCreatedChildNode($nodeAggregate->nodeName)
&& $parentsNodeType->getTypeOfAutoCreatedChildNode($nodeAggregate->nodeName)?->name
&& $parentsNodeType->hasTetheredNode($nodeAggregate->nodeName)
&& $this->nodeTypeManager->getTypeOfTetheredNode($parentsNodeType, $nodeAggregate->nodeName)->name
!== $command->newNodeTypeName->value
) {
throw new NodeConstraintException(
Expand All @@ -232,9 +232,10 @@ protected function checkConstraintsImposedByAncestors(
);
if (
$parentAggregate->nodeName
&& $grandParentsNodeType->hasAutoCreatedChildNode($parentAggregate->nodeName)
&& !$grandParentsNodeType->allowsGrandchildNodeType(
$parentAggregate->nodeName->value,
&& $grandParentsNodeType->hasTetheredNode($parentAggregate->nodeName)
&& !$this->nodeTypeManager->isNodeTypeAllowedAsChildToTetheredNode(
$grandParentsNodeType,
$parentAggregate->nodeName,
$newNodeType
)
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
use Neos\ContentRepository\Core\Infrastructure\Property\PropertyConverter;
use Neos\ContentRepository\Core\Infrastructure\Property\PropertyType;
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\Exception\ContentStreamDoesNotExistYet;
Expand Down Expand Up @@ -61,6 +62,8 @@ abstract protected function requireNodeTypeToBeOfTypeRoot(NodeType $nodeType): v

abstract protected function getPropertyConverter(): PropertyConverter;

abstract protected function getNodeTypeManager(): NodeTypeManager;

private function handleCreateNodeAggregateWithNode(
CreateNodeAggregateWithNode $command,
ContentRepository $contentRepository
Expand Down Expand Up @@ -203,6 +206,7 @@ private function handleCreateNodeAggregateWithNodeAndSerializedProperties(
}
$descendantNodeAggregateIds = self::populateNodeAggregateIds(
$nodeType,
$this->getNodeTypeManager(),
$command->tetheredDescendantNodeAggregateIds
);
// Write the auto-created descendant node aggregate ids back to the command;
Expand Down Expand Up @@ -282,7 +286,7 @@ private function handleTetheredChildNodes(
ContentRepository $contentRepository,
): Events {
$events = [];
foreach ($nodeType->getAutoCreatedChildNodes() as $rawNodeName => $childNodeType) {
foreach ($this->getNodeTypeManager()->getTetheredNodesConfigurationForNodeType($nodeType) as $rawNodeName => $childNodeType) {
assert($childNodeType instanceof NodeType);
$nodeName = NodeName::fromString($rawNodeName);
$childNodePath = $nodePath
Expand Down Expand Up @@ -343,14 +347,15 @@ private function createTetheredWithNode(

protected static function populateNodeAggregateIds(
NodeType $nodeType,
NodeTypeManager $nodeTypeManager,
?NodeAggregateIdsByNodePaths $nodeAggregateIds,
NodePath $childPath = null
): NodeAggregateIdsByNodePaths {
if ($nodeAggregateIds === null) {
$nodeAggregateIds = NodeAggregateIdsByNodePaths::createEmpty();
}
// TODO: handle Multiple levels of autocreated child nodes
foreach ($nodeType->getAutoCreatedChildNodes() as $rawChildName => $childNodeType) {
foreach ($nodeTypeManager->getTetheredNodesConfigurationForNodeType($nodeType) as $rawChildName => $childNodeType) {
$childName = NodeName::fromString($rawChildName);
$childPath = $childPath
? $childPath->appendPathSegment($childName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
use Neos\ContentRepository\Core\Feature\NodeTypeChange\Command\ChangeNodeAggregateType;
use Neos\ContentRepository\Core\Feature\NodeTypeChange\Event\NodeAggregateTypeWasChanged;
use Neos\ContentRepository\Core\NodeType\NodeType;
use Neos\ContentRepository\Core\NodeType\NodeTypeManager;
use Neos\ContentRepository\Core\Projection\ContentGraph\Node;
use Neos\ContentRepository\Core\Projection\ContentGraph\NodeAggregate;
use Neos\ContentRepository\Core\Projection\ContentGraph\NodePath;
Expand All @@ -49,6 +50,8 @@
*/
trait NodeTypeChange
{
abstract protected function getNodeTypeManager(): NodeTypeManager;

abstract protected function requireProjectedNodeAggregate(
ContentStreamId $contentStreamId,
NodeAggregateId $nodeAggregateId,
Expand Down Expand Up @@ -89,6 +92,7 @@ abstract protected function areNodeTypeConstraintsImposedByGrandparentValid(

abstract protected static function populateNodeAggregateIds(
NodeType $nodeType,
NodeTypeManager $nodeTypeManager,
NodeAggregateIdsByNodePaths $nodeAggregateIds,
NodePath $childPath = null
): NodeAggregateIdsByNodePaths;
Expand Down Expand Up @@ -158,6 +162,7 @@ private function handleChangeNodeAggregateType(
**************/
$descendantNodeAggregateIds = static::populateNodeAggregateIds(
$newNodeType,
$this->getNodeTypeManager(),
$command->tetheredDescendantNodeAggregateIds
);
// Write the auto-created descendant node aggregate ids back to the command;
Expand Down Expand Up @@ -190,7 +195,7 @@ private function handleChangeNodeAggregateType(
}

// new tethered child nodes
$expectedTetheredNodes = $newNodeType->getAutoCreatedChildNodes();
$expectedTetheredNodes = $this->getNodeTypeManager()->getTetheredNodesConfigurationForNodeType($newNodeType);
foreach ($nodeAggregate->getNodes() as $node) {
assert($node instanceof Node);
foreach ($expectedTetheredNodes as $serializedTetheredNodeName => $expectedTetheredNodeType) {
Expand Down Expand Up @@ -371,7 +376,7 @@ private function deleteObsoleteTetheredNodesWhenChangingNodeType(
NodeType $newNodeType,
ContentRepository $contentRepository
): Events {
$expectedTetheredNodes = $newNodeType->getAutoCreatedChildNodes();
$expectedTetheredNodes = $this->getNodeTypeManager()->getTetheredNodesConfigurationForNodeType($newNodeType);

$events = [];
// find disallowed tethered nodes
Expand Down
137 changes: 137 additions & 0 deletions Neos.ContentRepository.Core/Classes/NodeType/ConstraintCheck.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
<?php

namespace Neos\ContentRepository\Core\NodeType;

/**
* Performs node type constraint checks against a given set of constraints
* @internal
*/
class ConstraintCheck
{
/**
* @param array<string,mixed> $constraints
*/
public function __construct(
private readonly array $constraints
) {
}

public function isNodeTypeAllowed(NodeType $nodeType): bool
{
$directConstraintsResult = $this->isNodeTypeAllowedByDirectConstraints($nodeType);
if ($directConstraintsResult !== null) {
return $directConstraintsResult;
}

$inheritanceConstraintsResult = $this->isNodeTypeAllowedByInheritanceConstraints($nodeType);
if ($inheritanceConstraintsResult !== null) {
return $inheritanceConstraintsResult;
}

if (isset($this->constraints['*'])) {
return (bool)$this->constraints['*'];
}

return false;
}

/**
* @return boolean|null true if the passed $nodeType is allowed by the $constraints, null if couldn't be decided
*/
protected function isNodeTypeAllowedByDirectConstraints(NodeType $nodeType): ?bool
{
if ($this->constraints === []) {
return true;
}

if (
array_key_exists($nodeType->name->value, $this->constraints)
&& $this->constraints[$nodeType->name->value] === true
) {
return true;
}

if (
array_key_exists($nodeType->name->value, $this->constraints)
&& $this->constraints[$nodeType->name->value] === false
) {
return false;
}

return null;
}

/**
* This method loops over the constraints and finds node types that the given node type inherits from. For all
* matched super types, their super types are traversed to find the closest super node with a constraint which
* is used to evaluated if the node type is allowed. It finds the closest results for true and false, and uses
* the distance to choose which one wins (lowest). If no result is found the node type is allowed.
*
* @return ?boolean (null if no constraint matched)
*/
protected function isNodeTypeAllowedByInheritanceConstraints(NodeType $nodeType): ?bool
{
$constraintDistanceForTrue = null;
$constraintDistanceForFalse = null;
foreach ($this->constraints as $superType => $constraint) {
if ($nodeType->isOfType($superType)) {
$distance = $this->traverseSuperTypes($nodeType, $superType, 0);

if (
$constraint === true
&& ($constraintDistanceForTrue === null || $constraintDistanceForTrue > $distance)
) {
$constraintDistanceForTrue = $distance;
}
if (
$constraint === false
&& ($constraintDistanceForFalse === null || $constraintDistanceForFalse > $distance)
) {
$constraintDistanceForFalse = $distance;
}
}
}

if ($constraintDistanceForTrue !== null && $constraintDistanceForFalse !== null) {
return $constraintDistanceForTrue < $constraintDistanceForFalse;
}

if ($constraintDistanceForFalse !== null) {
return false;
}

if ($constraintDistanceForTrue !== null) {
return true;
}

return null;
}

/**
* This method traverses the given node type to find the first super type that matches the constraint node type.
* In case the hierarchy has more than one way of finding a path to the node type it's not taken into account,
* since the first matched is returned. This is accepted on purpose for performance reasons and due to the fact
* that such hierarchies should be avoided.
*
* Returns null if no NodeType matched
*/
protected function traverseSuperTypes(
NodeType $currentNodeType,
string $constraintNodeTypeName,
int $distance
): ?int {
if ($currentNodeType->name->value === $constraintNodeTypeName) {
return $distance;
}

$distance++;
foreach ($currentNodeType->getDeclaredSuperTypes() as $superType) {
$result = $this->traverseSuperTypes($superType, $constraintNodeTypeName, $distance);
if ($result !== null) {
return $result;
}
}

return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Neos\ContentRepository\Core\NodeType\Exception;

/**
* @api Might be encountered when childNode information is requested for a child node which was never configured.
*/
class TetheredNodeNotConfigured extends \DomainException
{
}
Loading

0 comments on commit fa38119

Please sign in to comment.