Skip to content

Commit

Permalink
Merge pull request #3218 from bwaidelich/feature/3020-stabilize-front…
Browse files Browse the repository at this point in the history
…endnoderouteparthandler

FEATURE: Stabilize and extend Frontend Routing
  • Loading branch information
jonnitto authored Dec 4, 2020
2 parents f36e4b3 + bb177ce commit 7559b90
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 25 deletions.
39 changes: 24 additions & 15 deletions Neos.Neos/Classes/Routing/FrontendNodeRoutePartHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,9 @@ protected function matchValue($requestPath)

return false;
}
if (!$this->nodeTypeIsAllowed($node)) {
return false;
}
if ($this->onlyMatchSiteNodes() && $node !== $node->getContext()->getCurrentSiteNode()) {
return false;
}
Expand Down Expand Up @@ -255,31 +258,27 @@ protected function resolveValue($node)
$contentContext = $node->getContext();
}

if (!$node->getNodeType()->isOfType('Neos.Neos:Document')) {
if (!$this->nodeTypeIsAllowed($node)) {
return false;
}

$siteNode = $contentContext->getCurrentSiteNode();
if ($this->onlyMatchSiteNodes() && $node !== $siteNode) {
return false;
}
$resolvedNode = $this->nodeShortcutResolver->resolveShortcutTarget($node);
if ($resolvedNode === null) {
throw new NeosException(sprintf('Could not resolve shortcut target for node "%s"', $node->getPath()), 1414771137);
}

$node = $this->resolveShortcutNode($node);
if ($node instanceof UriInterface) {
return new ResolveResult('', UriConstraints::fromUri($node), null);
$nodeOrUri = $this->resolveShortcutNode($node);
if ($nodeOrUri instanceof UriInterface) {
return new ResolveResult('', UriConstraints::fromUri($nodeOrUri), null);
}

try {
$uriConstraints = $this->buildUriConstraintsForResolvedNode($node);
$uriConstraints = $this->buildUriConstraintsForResolvedNode($nodeOrUri);
} catch (Exception\NoSiteException $exception) {
$this->systemLogger->debug('FrontendNodeRoutePartHandler resolveValue(): ' . $exception->getMessage());
return false;
}
return new ResolveResult('', $uriConstraints);
$uriPath = $this->resolveRoutePathForNode($nodeOrUri);
return new ResolveResult($uriPath, $uriConstraints);
}

/**
Expand Down Expand Up @@ -321,7 +320,6 @@ protected function resolveShortcutNode(NodeInterface $node)

/**
* Builds UriConstraints for the given $node with:
* * a path constraint equal to the resolved URI path
* * domain specific constraints for nodes in a different Neos site
* * a path suffix corresponding to the configured "uriPathSuffix"
*
Expand All @@ -331,8 +329,7 @@ protected function resolveShortcutNode(NodeInterface $node)
*/
protected function buildUriConstraintsForResolvedNode(NodeInterface $node): UriConstraints
{
$uriPath = $this->resolveRoutePathForNode($node);
$uriConstraints = UriConstraints::create()->withPath($uriPath);
$uriConstraints = UriConstraints::create();
$requestSite = $this->getCurrentSite();
if (!NodePaths::isSubPathOf(SiteService::SITES_ROOT_PATH, $node->getPath())) {
throw new Exception\NoSiteException(sprintf('The node at path "%s" is not located underneath the sites root path "%s"', $node->getPath(), SiteService::SITES_ROOT_PATH), 1604922914);
Expand All @@ -341,7 +338,7 @@ protected function buildUriConstraintsForResolvedNode(NodeInterface $node): UriC
if ($resolvedSiteNodeName !== $requestSite->getNodeName()) {
$resolvedSite = $this->siteRepository->findOneByNodeName($resolvedSiteNodeName);
if ($resolvedSite === null || $resolvedSite->isOffline()) {
throw new Exception\NoSiteException(sprintf('No online site found for request path "%s" and resolved site node name of "%s"', $uriPath, $resolvedSiteNodeName), 1604505599);
throw new Exception\NoSiteException(sprintf('No online site found for node "%s" and resolved site node name of "%s"', $node->getIdentifier(), $resolvedSiteNodeName), 1604505599);
}
$uriConstraints = $this->applyDomainToUriConstraints($uriConstraints, $resolvedSite->getPrimaryDomain());
}
Expand Down Expand Up @@ -471,6 +468,18 @@ protected function onlyMatchSiteNodes()
return isset($this->options['onlyMatchSiteNodes']) && $this->options['onlyMatchSiteNodes'] === true;
}

/**
* Whether the given $node is allowed according to the "nodeType" option
*
* @param NodeInterface $node
* @return bool
*/
protected function nodeTypeIsAllowed(NodeInterface $node): bool
{
$allowedNodeType = !empty($this->options['nodeType']) ? $this->options['nodeType'] : 'Neos.Neos:Document';
return $node->getNodeType()->isOfType($allowedNodeType);
}

/**
* Resolves the request path, also known as route path, identifying the given node.
*
Expand Down
72 changes: 62 additions & 10 deletions Neos.Neos/Tests/Unit/Routing/FrontendNodeRoutePartHandlerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -156,13 +156,13 @@ public function setUp(): void
public function contextPathsAndRequestPathsDataProvider()
{
return [
['/sites/examplecom@live;language=en_US', '/', true],
['/sites/examplecom@live;language=en_US', '/en_global', false],
['/sites/examplecom@user-robert;language=de_DE,en_US', '/de_global', false],
['/sites/examplecom/features@user-robert;language=de_DE,en_US', '/de_global/features', false],
['/sites/examplecom/features@user-robert;language=en_US', '/en_global/features', false],
['/sites/examplecom/features@user-robert;language=de_DE,en_US&country=global', '/de_global/features', false],
['/sites/examplecom/features@user-robert;country=de', '/en_de/features', false]
['/sites/examplecom@live;language=en_US', '', true],
['/sites/examplecom@live;language=en_US', 'en_global', false],
['/sites/examplecom@user-robert;language=de_DE,en_US', 'de_global', false],
['/sites/examplecom/features@user-robert;language=de_DE,en_US', 'de_global/features', false],
['/sites/examplecom/features@user-robert;language=en_US', 'en_global/features', false],
['/sites/examplecom/features@user-robert;language=de_DE,en_US&country=global', 'de_global/features', false],
['/sites/examplecom/features@user-robert;country=de', 'en_de/features', false]
];
}

Expand Down Expand Up @@ -303,6 +303,31 @@ public function matchWithParametersReturnsFalseOnNotMatchingSiteNodes()
}


/**
* @test
*/
public function matchWithParametersRespectsTheSpecifiedNodeTypeOption()
{
$mockContext = $this->buildMockContext(['workspaceName' => 'live']);
$mockContext->mockSite = $this->getMockBuilder(Site::class)->disableOriginalConstructor()->getMock();
$mockContext->mockSiteNode = $this->buildSiteNode($mockContext, '/sites/examplecom');

$mockSubNode = $this->buildSubNode($mockContext->mockSiteNode, 'features');
$mockSubNode->mockProperties['uriPathSegment'] = 'features';

$routePath = 'features';
self::assertInstanceOf(MatchResult::class, $this->matchForHost($routePath, 'localhost'));

$this->routePartHandler->setOptions(['nodeType' => 'Some.Package:Some.Node.Type']);

$mockNodeType = $this->getMockBuilder(NodeType::class)->disableOriginalConstructor()->getMock();
$mockNodeType->method('isOfType')->willReturn(false);
$mockSubNode->method('getNodeType')->willReturn($mockNodeType);

$routePath = 'features';
self::assertFalse($this->matchForHost($routePath, 'localhost'));
}

/**
* @test
*/
Expand Down Expand Up @@ -462,7 +487,7 @@ public function resolveSetsValueToContextPathIfPassedNodeCouldBeResolved()
$routeValues = ['node' => $mockSubSubNode];
$resolveResult = $this->resolveForHost($routeValues, 'localhost');
self::assertInstanceOf(ResolveResult::class, $resolveResult);
self::assertSame('/home/coffee-brands@user-robert', (string)$resolveResult->getUriConstraints()->toUri());
self::assertSame('home/coffee-brands@user-robert', $resolveResult->getResolvedValue());
}

/**
Expand All @@ -488,7 +513,7 @@ public function resolveSetsValueToContextPathIfPassedNodeCouldBeResolvedButIsInA
$routeValues = ['node' => $mockSubSubNode];
$resolveResult = $this->resolveForHost($routeValues, 'localhost');
self::assertInstanceOf(ResolveResult::class, $resolveResult);
self::assertSame('/home/coffee-brands@user-robert', (string)$resolveResult->getUriConstraints()->toUri());
self::assertSame('home/coffee-brands@user-robert', (string)$resolveResult->getResolvedValue());
}

/**
Expand Down Expand Up @@ -646,6 +671,33 @@ public function resolveReturnsFalseIfOnlyMatchSiteNodesOptionIsSetAndResolvedNod
self::assertFalse($this->resolveForHost($routeValues, 'localhost'));
}

/**
* @test
*/
public function resolveRespectsTheSpecifiedNodeTypeOption()
{
$mockContext = $this->buildMockContext(['workspaceName' => 'live']);
$mockContext->mockSite = $this->getMockBuilder(Site::class)->disableOriginalConstructor()->getMock();
$mockContext->mockSiteNode = $this->buildSiteNode($mockContext, '/sites/examplecom');

$mockSubNode = $this->buildSubNode($mockContext->mockSiteNode, 'features');
$mockSubNode->mockProperties['uriPathSegment'] = 'features';

$mockContext->expects(self::any())->method('getNode')->will(self::returnCallback(function ($nodePath) use ($mockSubNode) {
return ($nodePath === '/sites/examplecom/features') ? $mockSubNode : null;
}));

$routeValues = ['node' => $mockSubNode];

$this->routePartHandler->setOptions(['nodeType' => 'Some.Package:Some.Node.Type']);

$mockNodeType = $this->getMockBuilder(NodeType::class)->disableOriginalConstructor()->getMock();
$mockNodeType->method('isOfType')->willReturn(false);
$mockSubNode->method('getNodeType')->willReturn($mockNodeType);

self::assertFalse($this->resolveForHost($routeValues, 'localhost'));
}

/**
* @dataProvider contextPathsAndRequestPathsDataProvider
* @test
Expand Down Expand Up @@ -717,7 +769,7 @@ public function resolveConsidersDimensionValuesPassedViaTheContextPathForRenderi
$routeValues = ['node' => $contextPath];
$this->inject($this->routePartHandler, 'supportEmptySegmentForDimensions', $supportEmptySegmentForDimensions);
$resolveResult = $this->resolveForHost($routeValues, 'localhost');
self::assertSame($expectedUriPath, (string)$resolveResult->getUriConstraints()->toUri());
self::assertSame($expectedUriPath, $resolveResult->getResolvedValue());
}

/**
Expand Down

0 comments on commit 7559b90

Please sign in to comment.