Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TASK: Adjust to changes in Neos removing internal node properties #3735

Merged
merged 5 commits into from May 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
326 changes: 182 additions & 144 deletions Classes/Domain/Model/Changes/Property.php
Expand Up @@ -12,6 +12,7 @@
* source code.
*/

use Neos\ContentRepository\Core\CommandHandler\CommandResult;
use Neos\ContentRepository\Core\DimensionSpace\Exception\DimensionSpacePointNotFound;
use Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePoint;
use Neos\ContentRepository\Core\Feature\NodeDisabling\Command\DisableNodeAggregate;
Expand All @@ -24,6 +25,8 @@
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\Projection\Workspace\Workspace;
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 +72,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 +133,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,163 +148,197 @@ 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->subgraphIdentity->contentRepositoryId);

$workspace = $this->contentRepositoryRegistry->get($this->subject->subgraphIdentity->contentRepositoryId)
->getWorkspaceFinder()->findOneByCurrentContentStreamId($subject->subgraphIdentity->contentStreamId);
if (!$workspace) {
throw new \Exception(
'Could not find workspace for content stream "' . $subject->subgraphIdentity->contentStreamId->value . '"',
1699008140
);
}
if (is_null($subject) || is_null($propertyName) || $this->canApply() === false) {
return;
}

$workspace = $this->contentRepositoryRegistry->get($this->subject->subgraphIdentity->contentRepositoryId)
->getWorkspaceFinder()->findOneByCurrentContentStreamId($subject->subgraphIdentity->contentStreamId);
if (!$workspace) {
throw new \Exception(
'Could not find workspace for content stream "' . $subject->subgraphIdentity->contentStreamId->value . '"',
1699008140
);
}

$commandResult = match (true) {
$this->getNodeType($subject)->hasReference($propertyName) => $this->handleNodeReferenceChange($workspace, $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($workspace, $subject, $propertyName),
$propertyName === '_hidden' => $this->handleHiddenPropertyChange($workspace, $subject, $propertyName),
mhsdesign marked this conversation as resolved.
Show resolved Hide resolved
default => $this->handlePropertyChange($workspace, $subject, $propertyName)
};

$commandResult->block();
$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
);
}

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(
$workspace->workspaceName,
$subject->nodeAggregateId,
$subject->originDimensionSpacePoint,
ReferenceName::fromString($propertyName),
NodeReferencesToWrite::fromNodeAggregateIds(NodeAggregateIds::fromArray($destinationNodeAggregateIds))
)
);
$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->subgraphIdentity->dimensionSpacePoint->equals($originDimensionSpacePoint)) {
$originDimensionSpacePoint = OriginDimensionSpacePoint::fromDimensionSpacePoint($subject->subgraphIdentity->dimensionSpacePoint);
// if origin dimension space point != current DSP -> translate transparently (matching old behavior)
$contentRepository->handle(
CreateNodeVariant::create(
$workspace->workspaceName,
$subject->nodeAggregateId,
$subject->originDimensionSpacePoint,
$originDimensionSpacePoint
)
)->block();
}
$commandResult = $contentRepository->handle(
SetNodeProperties::create(
$workspace->workspaceName,
$subject->nodeAggregateId,
$originDimensionSpacePoint,
PropertyValuesToWrite::fromArray(
[
$propertyName => $value
]
)
)
);
} else {
// property starts with "_"
if ($propertyName === '_nodeType') {
$commandResult = $contentRepository->handle(
ChangeNodeAggregateType::create(
$workspace->workspaceName,
$subject->nodeAggregateId,
NodeTypeName::fromString($value),
NodeAggregateTypeChangeChildConstraintConflictResolutionStrategy::STRATEGY_DELETE
)
);
} elseif ($propertyName === '_hidden') {
if ($value === true) {
$commandResult = $contentRepository->handle(
DisableNodeAggregate::create(
$workspace->workspaceName,
$subject->nodeAggregateId,
$subject->originDimensionSpacePoint->toDimensionSpacePoint(),
NodeVariantSelectionStrategy::STRATEGY_ALL_SPECIALIZATIONS
)
);
} else {
// unhide
$commandResult = $contentRepository->handle(
EnableNodeAggregate::create(
$workspace->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(Workspace $workspace, Node $subject, string $propertyName): CommandResult
{
$contentRepository = $this->contentRepositoryRegistry->get($subject->subgraphIdentity->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);

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

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

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

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

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

return $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(Workspace $workspace, Node $subject, string $propertyName): CommandResult
{
$contentRepository = $this->contentRepositoryRegistry->get($subject->subgraphIdentity->contentRepositoryId);
$value = $this->nodePropertyConversionService->convert(
$this->getNodeType($subject)->getPropertyType($propertyName),
$this->getValue()
);

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

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

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

return $contentRepository->handle(
SetNodeProperties::create(
$workspace->workspaceName,
$subject->nodeAggregateId,
$originDimensionSpacePoint,
PropertyValuesToWrite::fromArray(
[
$propertyName => $value
]
)
)
);
}
}
7 changes: 1 addition & 6 deletions Classes/Domain/Service/NodePropertyConverterService.php
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