Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/9.0' into task/refactorToNotUseC…
Browse files Browse the repository at this point in the history
…ontentSubgraphIdentity
  • Loading branch information
mhsdesign committed May 14, 2024
2 parents 90c62e4 + e721ed1 commit d3e38f6
Show file tree
Hide file tree
Showing 7 changed files with 183 additions and 148 deletions.
307 changes: 171 additions & 136 deletions Classes/Domain/Model/Changes/Property.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use Neos\ContentRepository\Core\Feature\NodeTypeChange\Dto\NodeAggregateTypeChangeChildConstraintConflictResolutionStrategy;
use Neos\ContentRepository\Core\Feature\NodeVariation\Command\CreateNodeVariant;
use Neos\ContentRepository\Core\NodeType\NodeTypeName;
use Neos\ContentRepository\Core\Projection\ContentGraph\Node;
use Neos\ContentRepository\Core\SharedModel\Exception\ContentStreamDoesNotExistYet;
use Neos\ContentRepository\Core\SharedModel\Exception\NodeAggregatesTypeIsAmbiguous;
use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateIds;
Expand Down Expand Up @@ -69,6 +70,7 @@ class Property extends AbstractChange

/**
* The value, the property will be set to
*
* @var string|array<int|string,mixed>|null
*/
protected string|array|null $value = null;
Expand Down Expand Up @@ -129,7 +131,7 @@ public function getIsInline(): bool
*/
public function canApply(): bool
{
if (is_null($this->subject)) {
if (is_null($this->subject) || is_null($this->subject->nodeType)) {
return false;
}
$nodeType = $this->subject->nodeType;
Expand All @@ -144,154 +146,187 @@ public function canApply(): bool
* @throws ContentStreamDoesNotExistYet
* @throws NodeAggregatesTypeIsAmbiguous
* @throws DimensionSpacePointNotFound
* @throws \Exception
*/
public function apply(): void
{
$subject = $this->subject;
$propertyName = $this->getPropertyName();
if ($this->canApply() && !is_null($subject) && !is_null($propertyName)) {
$contentRepository = $this->contentRepositoryRegistry->get($subject->contentRepositoryId);

if ($this->getNodeType($subject)->hasReference($propertyName)) {
// Use extra commands for reference handling
$value = $this->getValue();

$destinationNodeAggregateIds = [];

if (is_string($value) && !empty($value)) {
$destinationNodeAggregateIds = [$value];
} elseif (is_array($value)) {
$destinationNodeAggregateIds = $value;
}

$commandResult = $contentRepository->handle(
SetNodeReferences::create(
$subject->workspaceName,
$subject->nodeAggregateId,
$subject->originDimensionSpacePoint,
ReferenceName::fromString($propertyName),
NodeReferencesToWrite::fromNodeAggregateIds(NodeAggregateIds::fromArray($destinationNodeAggregateIds))
)
);
if (is_null($subject) || is_null($propertyName) || $this->canApply() === false) {
return;
}

match (true) {
$this->getNodeType($subject)->hasReference($propertyName) => $this->handleNodeReferenceChange($subject, $propertyName),
// todo create custom 'changes' for these special cases
// we continue to use the underscore logic in the Neos Ui code base as the JS-client code works this way
$propertyName === '_nodeType' => $this->handleNodeTypeChange($subject, $propertyName),
$propertyName === '_hidden' => $this->handleHiddenPropertyChange($subject, $propertyName),
default => $this->handlePropertyChange($subject, $propertyName)
};

$this->createFeedback($subject);
}

private function createFeedback(Node $subject): void
{
$propertyName = $this->getPropertyName();

// We have to refetch the Node after modifications because its a read-only model
// These 'Change' classes have been designed with mutable Neos < 9 Nodes and thus this might seem hacky
// When fully redesigning the Neos Ui php integration this will fixed
$subgraph = $this->contentRepositoryRegistry->subgraphForNode($subject);
$originalNodeAggregateId = $subject->nodeAggregateId;
$node = $subgraph->findNodeById($originalNodeAggregateId);
if (is_null($node)) {
throw new \InvalidArgumentException(
'Cannot apply Property on missing node ' . $originalNodeAggregateId->value,
1645560836
);
}

$this->updateWorkspaceInfo();
$parentNode = $subgraph->findParentNode($node->nodeAggregateId);

// This might be needed to update node label and other things that we can calculate only on the server
$updateNodeInfo = new UpdateNodeInfo();
$updateNodeInfo->setNode($node);
$this->feedbackCollection->add($updateNodeInfo);

$reloadIfChangedConfigurationPathForProperty = sprintf('properties.%s.ui.reloadIfChanged', $propertyName);
$reloadIfChangedConfigurationPathForReference = sprintf('references.%s.ui.reloadIfChanged', $propertyName);
if (!$this->getIsInline()
&& (
$this->getNodeType($node)->getConfiguration($reloadIfChangedConfigurationPathForProperty)
|| $this->getNodeType($node)->getConfiguration($reloadIfChangedConfigurationPathForReference)
)
) {
if ($this->getNodeDomAddress() && $this->getNodeDomAddress()->getFusionPath()
&& $parentNode
&& $this->getNodeType($parentNode)->isOfType('Neos.Neos:ContentCollection')) {
$reloadContentOutOfBand = new ReloadContentOutOfBand();
$reloadContentOutOfBand->setNode($node);
$reloadContentOutOfBand->setNodeDomAddress($this->getNodeDomAddress());
$this->feedbackCollection->add($reloadContentOutOfBand);
} else {
$value = $this->nodePropertyConversionService->convert(
$this->getNodeType($subject)->getPropertyType($propertyName),
$this->getValue()
);

if ($propertyName[0] !== '_' || $propertyName === '_hiddenInIndex') {
$originDimensionSpacePoint = $subject->originDimensionSpacePoint;
if (!$subject->dimensionSpacePoint->equals($originDimensionSpacePoint)) {
$originDimensionSpacePoint = OriginDimensionSpacePoint::fromDimensionSpacePoint($subject->dimensionSpacePoint);
// if origin dimension space point != current DSP -> translate transparently (matching old behavior)
$contentRepository->handle(
CreateNodeVariant::create(
$subject->workspaceName,
$subject->nodeAggregateId,
$subject->originDimensionSpacePoint,
$originDimensionSpacePoint
)
)->block();
}
$commandResult = $contentRepository->handle(
SetNodeProperties::create(
$subject->workspaceName,
$subject->nodeAggregateId,
$originDimensionSpacePoint,
PropertyValuesToWrite::fromArray(
[
$propertyName => $value
]
)
)
);
} else {
// property starts with "_"
if ($propertyName === '_nodeType') {
$commandResult = $contentRepository->handle(
ChangeNodeAggregateType::create(
$subject->workspaceName,
$subject->nodeAggregateId,
NodeTypeName::fromString($value),
NodeAggregateTypeChangeChildConstraintConflictResolutionStrategy::STRATEGY_DELETE
)
);
} elseif ($propertyName === '_hidden') {
if ($value === true) {
$commandResult = $contentRepository->handle(
DisableNodeAggregate::create(
$subject->workspaceName,
$subject->nodeAggregateId,
$subject->originDimensionSpacePoint->toDimensionSpacePoint(),
NodeVariantSelectionStrategy::STRATEGY_ALL_SPECIALIZATIONS
)
);
} else {
// unhide
$commandResult = $contentRepository->handle(
EnableNodeAggregate::create(
$subject->workspaceName,
$subject->nodeAggregateId,
$subject->originDimensionSpacePoint->toDimensionSpacePoint(),
NodeVariantSelectionStrategy::STRATEGY_ALL_SPECIALIZATIONS
)
);
}
} else {
throw new \Exception("TODO FIX");
}
}
$this->reloadDocument($node);
}
}

$commandResult->block();

// !!! REMEMBER: we are not allowed to use $node anymore,
// because it may have been modified by the commands above.
// Thus, we need to re-fetch it (as a workaround; until we do not need this anymore)
$subgraph = $this->contentRepositoryRegistry->subgraphForNode($subject);
$originalNodeAggregateId = $subject->nodeAggregateId;
$node = $subgraph->findNodeById($originalNodeAggregateId);
if (is_null($node)) {
throw new \InvalidArgumentException(
'Cannot apply Property on missing node ' . $originalNodeAggregateId->value,
1645560836
);
}
$reloadPageIfChangedConfigurationPathForProperty = sprintf('properties.%s.ui.reloadPageIfChanged', $propertyName);
$reloadPageIfChangedConfigurationPathForReference = sprintf('references.%s.ui.reloadPageIfChanged', $propertyName);
if (!$this->getIsInline()
&& (
$this->getNodeType($node)->getConfiguration($reloadPageIfChangedConfigurationPathForProperty)
|| $this->getNodeType($node)->getConfiguration($reloadPageIfChangedConfigurationPathForReference)
)
) {
$this->reloadDocument($node);
}
}

$this->updateWorkspaceInfo();
$parentNode = $subgraph->findParentNode($node->nodeAggregateId);
private function handleNodeReferenceChange(Node $subject, string $propertyName): void
{
$contentRepository = $this->contentRepositoryRegistry->get($subject->contentRepositoryId);
$value = $this->getValue();

// This might be needed to update node label and other things that we can calculate only on the server
$updateNodeInfo = new UpdateNodeInfo();
$updateNodeInfo->setNode($node);
$this->feedbackCollection->add($updateNodeInfo);
if (!is_array($value)) {
$value = [$value];
}

if (!$this->getIsInline()
&& (
$this->getNodeType($node)->hasConfiguration(sprintf('properties.%s.ui.reloadIfChanged', $propertyName))
|| $this->getNodeType($node)->hasConfiguration(sprintf('references.%s.ui.reloadIfChanged', $propertyName))
)
) {
if ($this->getNodeDomAddress() && $this->getNodeDomAddress()->getFusionPath()
&& $parentNode
&& $this->getNodeType($parentNode)->isOfType('Neos.Neos:ContentCollection')) {
$reloadContentOutOfBand = new ReloadContentOutOfBand();
$reloadContentOutOfBand->setNode($node);
$reloadContentOutOfBand->setNodeDomAddress($this->getNodeDomAddress());
$this->feedbackCollection->add($reloadContentOutOfBand);
} else {
$this->reloadDocument($node);
}
}
$value = array_filter($value, fn ($v) => is_string($v) && !empty($v));
$destinationNodeAggregateIds = array_values($value);

$contentRepository->handle(
SetNodeReferences::create(
$subject->workspaceName,
$subject->nodeAggregateId,
$subject->originDimensionSpacePoint,
ReferenceName::fromString($propertyName),
NodeReferencesToWrite::fromNodeAggregateIds(NodeAggregateIds::fromArray($destinationNodeAggregateIds))
)
);
}

private function handleHiddenPropertyChange(Node $subject, string $propertyName): void
{
$value = $this->nodePropertyConversionService->convert(
$this->getNodeType($subject)->getPropertyType($propertyName),
$this->getValue()
);

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

$command = EnableNodeAggregate::create(
$subject->workspaceName,
$subject->nodeAggregateId,
$subject->originDimensionSpacePoint->toDimensionSpacePoint(),
NodeVariantSelectionStrategy::STRATEGY_ALL_SPECIALIZATIONS
);

if ($value === true) {
$command = DisableNodeAggregate::create(
$subject->workspaceName,
$subject->nodeAggregateId,
$subject->originDimensionSpacePoint->toDimensionSpacePoint(),
NodeVariantSelectionStrategy::STRATEGY_ALL_SPECIALIZATIONS
);
}

$contentRepository->handle($command);
}

if (!$this->getIsInline()
&& (
$this->getNodeType($node)->getConfiguration(sprintf('properties.%s.ui.reloadPageIfChanged', $propertyName))
|| $this->getNodeType($node)->getConfiguration(sprintf('references.%s.ui.reloadPageIfChanged', $propertyName))
private function handleNodeTypeChange(Node $subject, string $propertyName): void
{
$contentRepository = $this->contentRepositoryRegistry->get($subject->contentRepositoryId);
$value = $this->nodePropertyConversionService->convert(
$this->getNodeType($subject)->getPropertyType($propertyName),
$this->getValue()
);

$contentRepository->handle(
ChangeNodeAggregateType::create(
$subject->workspaceName,
$subject->nodeAggregateId,
NodeTypeName::fromString($value),
NodeAggregateTypeChangeChildConstraintConflictResolutionStrategy::STRATEGY_DELETE
)
);
}

private function handlePropertyChange(Node $subject, string $propertyName): void
{
$contentRepository = $this->contentRepositoryRegistry->get($subject->contentRepositoryId);
$value = $this->nodePropertyConversionService->convert(
$this->getNodeType($subject)->getPropertyType($propertyName),
$this->getValue()
);

$originDimensionSpacePoint = $subject->originDimensionSpacePoint;
if (!$subject->dimensionSpacePoint->equals($originDimensionSpacePoint)) {
$originDimensionSpacePoint = OriginDimensionSpacePoint::fromDimensionSpacePoint($subject->dimensionSpacePoint);
// if origin dimension space point != current DSP -> translate transparently (matching old behavior)
$contentRepository->handle(
CreateNodeVariant::create(
$subject->workspaceName,
$subject->nodeAggregateId,
$subject->originDimensionSpacePoint,
$originDimensionSpacePoint
)
) {
$this->reloadDocument($node);
}
);
}

$contentRepository->handle(
SetNodeProperties::create(
$subject->workspaceName,
$subject->nodeAggregateId,
$originDimensionSpacePoint,
PropertyValuesToWrite::fromArray(
[
$propertyName => $value
]
)
)
);
}
}
7 changes: 1 addition & 6 deletions Classes/Domain/Service/NodePropertyConverterService.php
Original file line number Diff line number Diff line change
Expand Up @@ -138,12 +138,7 @@ private function getProperty(Node $node, string $propertyName): mixed
return $node->tags->contain(SubtreeTag::fromString('disabled'));
}

if ($propertyName[0] === '_' && $propertyName !== '_hiddenInIndex') {
$propertyValue = ObjectAccess::getProperty($node, ltrim($propertyName, '_'));
} else {
$propertyValue = $node->getProperty($propertyName);
}

$propertyValue = $node->getProperty($propertyName);
$propertyType = $this->getNodeType($node)->getPropertyType($propertyName);
try {
$convertedValue = $this->convertValue($propertyValue, $propertyType);
Expand Down
5 changes: 3 additions & 2 deletions Classes/Fusion/Helper/NodeInfoHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,9 @@ public function renderNodeWithMinimalPropertiesAndChildrenInformation(
$nodeInfo['properties'] = [
// if we are only rendering the tree state,
// ensure _isHidden is sent to hidden nodes are correctly shown in the tree.
'_hidden' => $node->tags->contain(SubtreeTag::fromString('disabled')),
'_hiddenInIndex' => $node->getProperty('_hiddenInIndex'),
// TODO: we should export this correctly named, but that needs changes throughout the JS code as well.
'_hidden' => $node->tags->withoutInherited()->contain(SubtreeTag::disabled()),
'_hiddenInIndex' => $node->getProperty('hiddenInMenu'),
'_hasTimeableNodeVisibility' =>
$node->getProperty('enableAfterDateTime') instanceof \DateTimeInterface
|| $node->getProperty('disableAfterDateTime') instanceof \DateTimeInterface,
Expand Down
5 changes: 5 additions & 0 deletions Resources/Private/Translations/es/Main.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -541,10 +541,15 @@
<trans-unit id="copyNodeTypeNameToClipboard" xml:space="preserve">
<source>Copy node type to clipboard</source>
<target state="translated">Copiar tipo de nodo al portapapeles</target>
</trans-unit>
<trans-unit id="syncPersonalWorkSpace" xml:space="preserve">
<source>Synchronize personal workspace</source>
<target state="translated">Sincronizar el espacio de trabajo personal</target>
</trans-unit>
<trans-unit id="copyNodeTypeNameToClipboard" xml:space="preserve">
<source>Copy node type to clipboard</source>
<target state="needs-translation">Copiar tipo de nodo al portapapeles</target>
</trans-unit>
</body>
</file>
</xliff>
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,6 @@ test(
await t.switchToMainWindow();
await t
.expect(Selector('[role="treeitem"] [role="button"][class*="isFocused"]').textContent)
.eql('MultiC');
.eql('MultiA');
}
);
Loading

0 comments on commit d3e38f6

Please sign in to comment.