From 60705cea253aae33c788acadf363626675f313de Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Fri, 16 Feb 2024 10:05:44 +0100 Subject: [PATCH] WIP: Migrate `showAction` to NodeIdentity --- .../Classes/SharedModel/Node/NodeIdentity.php | 14 +++++ .../Classes/NodeSerializer.php | 5 +- .../Controller/Frontend/NodeController.php | 62 +++++++++---------- .../FrontendRouting/NodeShortcutResolver.php | 32 +++++----- 4 files changed, 61 insertions(+), 52 deletions(-) diff --git a/Neos.ContentRepository.Core/Classes/SharedModel/Node/NodeIdentity.php b/Neos.ContentRepository.Core/Classes/SharedModel/Node/NodeIdentity.php index bb4ddfc7f64..5071bbe157d 100644 --- a/Neos.ContentRepository.Core/Classes/SharedModel/Node/NodeIdentity.php +++ b/Neos.ContentRepository.Core/Classes/SharedModel/Node/NodeIdentity.php @@ -63,6 +63,20 @@ public static function fromJsonString(string $jsonString): self return self::fromArray(\json_decode($jsonString, true, JSON_THROW_ON_ERROR)); } + public function withNodeAggregateId(NodeAggregateId $nodeAggregateId): self + { + return new self($this->contentRepositoryId, $this->workspaceName, $this->dimensionSpacePoint, $nodeAggregateId); + } + + public function equals(self $other): bool + { + return $this->contentRepositoryId->equals($other->contentRepositoryId) + && $this->workspaceName->equals($other->workspaceName) + && $this->dimensionSpacePoint->equals($other->dimensionSpacePoint) + && $this->nodeAggregateId->equals($other->nodeAggregateId); + + } + public function toJson(): string { return json_encode($this, JSON_THROW_ON_ERROR); diff --git a/Neos.ContentRepositoryRegistry/Classes/NodeSerializer.php b/Neos.ContentRepositoryRegistry/Classes/NodeSerializer.php index c4d565049bf..5fc12b98fa4 100644 --- a/Neos.ContentRepositoryRegistry/Classes/NodeSerializer.php +++ b/Neos.ContentRepositoryRegistry/Classes/NodeSerializer.php @@ -22,7 +22,7 @@ public function __construct( ) { } - public function findNodeByIdentity(NodeIdentity $identity): Node + public function findNodeByIdentity(NodeIdentity $identity, VisibilityConstraints $visibilityConstraints): Node { $contentRepository = $this->contentRepositoryRegistry->get($identity->contentRepositoryId); $workspace = $contentRepository->getWorkspaceFinder()->findOneByName($identity->workspaceName); @@ -32,8 +32,7 @@ public function findNodeByIdentity(NodeIdentity $identity): Node $subgraph = $contentRepository->getContentGraph()->getSubgraph( $workspace->currentContentStreamId, $identity->dimensionSpacePoint, - // todo policy? Or what to prevent from accidentally showing unwanted nodes. - VisibilityConstraints::withoutRestrictions() + $visibilityConstraints ); $node = $subgraph->findNodeById($identity->nodeAggregateId); if (!$node) { diff --git a/Neos.Neos/Classes/Controller/Frontend/NodeController.php b/Neos.Neos/Classes/Controller/Frontend/NodeController.php index f5ae466fd07..52cf8592be7 100644 --- a/Neos.Neos/Classes/Controller/Frontend/NodeController.php +++ b/Neos.Neos/Classes/Controller/Frontend/NodeController.php @@ -25,7 +25,9 @@ use Neos\ContentRepository\Core\Projection\ContentGraph\Subtree; use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; +use Neos\ContentRepository\Core\SharedModel\Node\NodeIdentity; use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry; +use Neos\ContentRepositoryRegistry\NodeSerializer; use Neos\Flow\Annotations as Flow; use Neos\Flow\Mvc\Controller\ActionController; use Neos\Flow\Mvc\Exception\NoMatchingRouteException; @@ -43,6 +45,8 @@ use Neos\Neos\FrontendRouting\NodeAddressFactory; use Neos\Neos\FrontendRouting\NodeShortcutResolver; use Neos\Neos\FrontendRouting\NodeUriBuilder; +use Neos\Neos\FrontendRouting\NodeUriBuilderFactory; +use Neos\Neos\FrontendRouting\NodeUriSpecification; use Neos\Neos\FrontendRouting\SiteDetection\SiteDetectionResult; use Neos\Neos\Utility\NodeTypeWithFallbackProvider; use Neos\Neos\View\FusionView; @@ -106,6 +110,12 @@ class NodeController extends ActionController #[Flow\InjectConfiguration(path: "frontend.shortcutRedirectHttpStatusCode", package: "Neos.Neos")] protected int $shortcutRedirectHttpStatusCode; + #[Flow\Inject] + protected NodeUriBuilderFactory $nodeUriBuilderFactory; + + #[Flow\Inject] + protected NodeSerializer $nodeSerializer; + /** * @param string $node * @throws NodeNotFoundException @@ -193,41 +203,31 @@ public function previewAction(string $node): void */ public function showAction(string $node): void { - $siteDetectionResult = SiteDetectionResult::fromRequest($this->request->getHttpRequest()); - $contentRepository = $this->contentRepositoryRegistry->get($siteDetectionResult->contentRepositoryId); + $nodeIdentity = NodeIdentity::fromJsonString($node); + unset($node); - $nodeAddress = NodeAddressFactory::create($contentRepository)->createFromUriString($node); - if (!$nodeAddress->isInLiveWorkspace()) { + if (!$nodeIdentity->workspaceName->isLive()) { throw new NodeNotFoundException('The requested node isn\'t accessible to the current user', 1430218623); } - $subgraph = $contentRepository->getContentGraph()->getSubgraph( - $nodeAddress->contentStreamId, - $nodeAddress->dimensionSpacePoint, - VisibilityConstraints::frontend() - ); + $node = $this->nodeSerializer->findNodeByIdentity($nodeIdentity, VisibilityConstraints::frontend()); + $subgraph = $this->contentRepositoryRegistry->subgraphForNode($node); - $site = $subgraph->findClosestNode($nodeAddress->nodeAggregateId, FindClosestNodeFilter::create(nodeTypes: NodeTypeNameFactory::NAME_SITE)); + $site = $subgraph->findClosestNode($node->nodeAggregateId, FindClosestNodeFilter::create(nodeTypes: NodeTypeNameFactory::NAME_SITE)); if ($site === null) { - throw new NodeNotFoundException("TODO: SITE NOT FOUND; should not happen (for address " . $nodeAddress); + throw new NodeNotFoundException("TODO: SITE NOT FOUND; should not happen (for address " . $nodeIdentity->toJson()); } - $this->fillCacheWithContentNodes($nodeAddress->nodeAggregateId, $subgraph); - - $nodeInstance = $subgraph->findNodeById($nodeAddress->nodeAggregateId); + $this->fillCacheWithContentNodes($node->nodeAggregateId, $subgraph); - if ($nodeInstance === null) { - throw new NodeNotFoundException('The requested node does not exist', 1596191460); - } - - if ($this->getNodeType($nodeInstance)->isOfType(NodeTypeNameFactory::NAME_SHORTCUT)) { - $this->handleShortcutNode($nodeAddress, $contentRepository); + if ($this->getNodeType($node)->isOfType(NodeTypeNameFactory::NAME_SHORTCUT)) { + $this->handleShortcutNode($nodeIdentity); } $this->view->setOption('renderingModeName', RenderingMode::FRONTEND); $this->view->assignMultiple([ - 'value' => $nodeInstance, + 'value' => $node, 'site' => $site, ]); } @@ -269,31 +269,31 @@ protected function overrideViewVariablesFromInternalArguments() /** * Handles redirects to shortcut targets of nodes in the live workspace. * - * @param NodeAddress $nodeAddress * @throws NodeNotFoundException * @throws \Neos\Flow\Mvc\Exception\StopActionException */ - protected function handleShortcutNode(NodeAddress $nodeAddress, ContentRepository $contentRepository): void + protected function handleShortcutNode(NodeIdentity $nodeIdentity): void { try { - $resolvedTarget = $this->nodeShortcutResolver->resolveShortcutTarget($nodeAddress, $contentRepository); + $resolvedTarget = $this->nodeShortcutResolver->resolveShortcutTarget($nodeIdentity); } catch (InvalidShortcutException $e) { throw new NodeNotFoundException(sprintf( - 'The shortcut node target of node "%s" could not be resolved: %s', - $nodeAddress, + 'The shortcut node target of node %s could not be resolved: %s', + $nodeIdentity->toJson(), $e->getMessage() ), 1430218730, $e); } - if ($resolvedTarget instanceof NodeAddress) { - if ($resolvedTarget === $nodeAddress) { + if ($resolvedTarget instanceof NodeIdentity) { + if ($nodeIdentity->equals($resolvedTarget)) { return; } try { - $resolvedUri = NodeUriBuilder::fromRequest($this->request)->uriFor($nodeAddress); + $resolvedUri = $this->nodeUriBuilderFactory->forRequest($this->request->getHttpRequest()) + ->uriFor(NodeUriSpecification::create($nodeIdentity)); } catch (NoMatchingRouteException $e) { throw new NodeNotFoundException(sprintf( - 'The shortcut node target of node "%s" could not be resolved: %s', - $nodeAddress, + 'The shortcut node target of node %s could not be resolved: %s', + $nodeIdentity->toJson(), $e->getMessage() ), 1599670695, $e); } diff --git a/Neos.Neos/Classes/FrontendRouting/NodeShortcutResolver.php b/Neos.Neos/Classes/FrontendRouting/NodeShortcutResolver.php index a63351f3569..2ad10bced9f 100644 --- a/Neos.Neos/Classes/FrontendRouting/NodeShortcutResolver.php +++ b/Neos.Neos/Classes/FrontendRouting/NodeShortcutResolver.php @@ -17,7 +17,8 @@ use GuzzleHttp\Psr7\Uri; use Neos\ContentRepository\Core\ContentRepository; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; -use Neos\Neos\FrontendRouting\NodeAddress; +use Neos\ContentRepository\Core\SharedModel\Node\NodeIdentity; +use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry; use Neos\Neos\FrontendRouting\Exception\InvalidShortcutException; use Neos\Neos\FrontendRouting\Exception\NodeNotFoundException; use Neos\Neos\FrontendRouting\Projection\DocumentNodeInfo; @@ -38,16 +39,11 @@ */ class NodeShortcutResolver { - private AssetRepository $assetRepository; - - private ResourceManager $resourceManager; - public function __construct( - AssetRepository $assetRepository, - ResourceManager $resourceManager + private readonly AssetRepository $assetRepository, + private readonly ResourceManager $resourceManager, + private readonly ContentRepositoryRegistry $contentRepositoryRegistry ) { - $this->assetRepository = $assetRepository; - $this->resourceManager = $resourceManager; } /** @@ -55,31 +51,31 @@ public function __construct( * Note: The ContentStreamId is not required for this service, * because it is only covering the live workspace * - * @param NodeAddress $nodeAddress - * @return NodeAddress|UriInterface NodeAddress is returned if we want to link to another node + * @return NodeIdentity|UriInterface NodeIdentity is returned if we want to link to another node * (i.e. node is NOT a shortcut node; or target is a node); * or UriInterface for links to fixed URLs (Asset URLs or external URLs) * @throws \Neos\Neos\FrontendRouting\Exception\InvalidShortcutException * @throws NodeNotFoundException */ - public function resolveShortcutTarget(NodeAddress $nodeAddress, ContentRepository $contentRepository) + public function resolveShortcutTarget(NodeIdentity $nodeIdentity) { - if (!$nodeAddress->workspaceName->isLive()) { - throw new \RuntimeException(sprintf('Cannot resolve shortcut target for node-address %s in workspace %s because the DocumentUriPathProjection only handles the live workspace.', $nodeAddress->nodeAggregateId->value, $nodeAddress->workspaceName->value), 1707208650); + if (!$nodeIdentity->workspaceName->isLive()) { + throw new \RuntimeException(sprintf('Cannot resolve shortcut target for node-address %s in workspace %s because the DocumentUriPathProjection only handles the live workspace.', $nodeIdentity->nodeAggregateId->value, $nodeIdentity->workspaceName->value), 1707208650); } + $contentRepository = $this->contentRepositoryRegistry->get($nodeIdentity->contentRepositoryId); $documentUriPathFinder = $contentRepository->projectionState(DocumentUriPathFinder::class); $documentNodeInfo = $documentUriPathFinder->getByIdAndDimensionSpacePointHash( - $nodeAddress->nodeAggregateId, - $nodeAddress->dimensionSpacePoint->hash + $nodeIdentity->nodeAggregateId, + $nodeIdentity->dimensionSpacePoint->hash ); $resolvedTarget = $this->resolveNode($documentNodeInfo, $contentRepository); if ($resolvedTarget instanceof UriInterface) { return $resolvedTarget; } if ($resolvedTarget === $documentNodeInfo) { - return $nodeAddress; + return $nodeIdentity; } - return $nodeAddress->withNodeAggregateId($documentNodeInfo->getNodeAggregateId()); + return $nodeIdentity->withNodeAggregateId($documentNodeInfo->getNodeAggregateId()); } /**