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

WIP: Restore LinkingService #4886

Draft
wants to merge 3 commits into
base: 9.0
Choose a base branch
from
Draft
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
72 changes: 65 additions & 7 deletions Neos.Neos/Classes/Fusion/NodeUriImplementation.php
Expand Up @@ -18,11 +18,18 @@
use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Log\Utility\LogEnvironment;
use Neos\Flow\Mvc\ActionRequest;
use Neos\Flow\Mvc\ActionResponse;
use Neos\Flow\Mvc\Controller\Arguments;
use Neos\Flow\Mvc\Controller\ControllerContext;
use Neos\Flow\Mvc\Exception\NoMatchingRouteException;
use Neos\Flow\Mvc\Routing\UriBuilder;
use Neos\Fusion\Exception;
use Neos\Fusion\FusionObjects\AbstractFusionObject;
use Neos\Neos\Exception as NeosException;
use Neos\Neos\FrontendRouting\NodeAddressFactory;
use Neos\Neos\FrontendRouting\NodeUriBuilder;
use Neos\Neos\Service\LinkingService;
use Psr\Log\LoggerInterface;

/**
Expand All @@ -42,6 +49,12 @@
*/
protected $systemLogger;

/**
* @Flow\Inject
* @var LinkingService
*/
protected $linkingService;

/**
* A node object or a string node path or NULL to resolve the current document node
*/
Expand Down Expand Up @@ -120,13 +133,8 @@
return $this->fusionValue('baseNodeName');
}

/**
* Render the Uri.
*
* @return string The rendered URI or NULL if no URI could be resolved for the given node
* @throws \Neos\Flow\Mvc\Routing\Exception\MissingActionNameException
*/
public function evaluate()

public function evaluate2()

Check failure on line 137 in Neos.Neos/Classes/Fusion/NodeUriImplementation.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 Test linting-unit-functionaltests-mysql (deps: highest)

Method Neos\Neos\Fusion\NodeUriImplementation::evaluate2() has no return type specified.

Check failure on line 137 in Neos.Neos/Classes/Fusion/NodeUriImplementation.php

View workflow job for this annotation

GitHub Actions / PHP 8.3 Test linting-unit-functionaltests-mysql (deps: highest)

Method Neos\Neos\Fusion\NodeUriImplementation::evaluate2() has no return type specified.
{
$baseNode = null;
$baseNodeName = $this->getBaseNodeName() ?: 'documentNode';
Expand Down Expand Up @@ -175,4 +183,54 @@
}
return '';
}

/**
* Render the Uri.
*
* @return string The rendered URI or NULL if no URI could be resolved for the given node
*/
public function evaluate()
{
$baseNode = null;
$baseNodeName = $this->getBaseNodeName() ?: 'documentNode';
$currentContext = $this->runtime->getCurrentContext();
if (isset($currentContext[$baseNodeName])) {
$baseNode = $currentContext[$baseNodeName];
} else {
throw new NeosException(sprintf('Could not find a node instance in Fusion context with name "%s" and no node instance was given to the node argument. Set a node instance in the Fusion context or pass a node object to resolve the URI.', $baseNodeName), 1373100400);
}

$actionRequest = $this->getRuntime()->fusionGlobals->get('request');
if (!$actionRequest instanceof ActionRequest) {
throw new \Neos\Flow\Exception('The request is expected to be an ActionRequest.', 1707728033);
}

$uriBuilder = new UriBuilder();
$uriBuilder->setRequest($actionRequest);

$fakeControllerContext = new ControllerContext(
$actionRequest,
new ActionResponse(),
new Arguments(),
$uriBuilder
);

$node = $this->getNode();
try {
return $this->linkingService->createNodeUri(
$fakeControllerContext,
$node,
$baseNode,
$this->getFormat(),
$this->isAbsolute(),
$this->getAdditionalParams(),
$this->getSection(),
$this->getAddQueryString(),
$this->getArgumentsToBeExcludedFromQueryString()
);
} catch (NoMatchingRouteException) {
$this->systemLogger->warning(sprintf('Could not resolve "%s" to a node uri. Arguments: %s', $node instanceof Node ? $node->nodeAggregateId->value : $node, json_encode($uriBuilder->getLastArguments())), LogEnvironment::fromMethodName(__METHOD__));
return '';
}
}
}
34 changes: 10 additions & 24 deletions Neos.Neos/Classes/Service/LinkingService.php
Expand Up @@ -14,6 +14,7 @@

namespace Neos\Neos\Service;

use Neos\ContentRepository\Core\Projection\ContentGraph\AbsoluteNodePath;
use Neos\ContentRepository\Core\Projection\ContentGraph\Node;
use Neos\ContentRepository\Core\Projection\NodeHiddenState\NodeHiddenStateFinder;
use Neos\ContentRepository\Core\Projection\NodeHiddenState\NodeHiddenStateProjection;
Expand All @@ -35,7 +36,6 @@
use Neos\Neos\Domain\Model\Site;
use Neos\Neos\Domain\Repository\SiteRepository;
use Neos\Neos\Exception as NeosException;
use Neos\Neos\FrontendRouting\NodeShortcutResolver;
use Neos\Neos\FrontendRouting\SiteDetection\SiteDetectionResult;
use Psr\Http\Message\UriInterface;
use Psr\Log\LoggerInterface;
Expand All @@ -46,7 +46,9 @@
* The target node can be provided as string or as a Node object; if not specified
* at all, the generated URI will refer to the current document node inside the Fusion context.
*
* When specifying the ``node`` argument as string, the following conventions apply:
* When specifying the `node` argument as string, the following conventions apply:
*
*
*
* *``node`` starts with ``/``:*
* The given path is an absolute node path and is treated as such.
Expand All @@ -56,8 +58,6 @@
* The given path is treated as a path relative to the current node.
* Examples: given that the current node is ``/sites/acmecom/products/``,
* ``stapler`` results in ``/sites/acmecom/products/stapler``,
* ``../about`` results in ``/sites/acmecom/about/``,
* ``./neos/info`` results in ``/sites/acmecom/products/neos/info``.
*
* *``node`` starts with a tilde character (``~``):*
* The given path is treated as a path relative to the current site node.
Expand All @@ -71,6 +71,7 @@
{
/**
* Pattern to match supported URIs.
* Todo doestn work with other node ids ....
*
* @var string
*/
Expand All @@ -89,12 +90,6 @@
*/
protected $resourceManager;

/**
* @Flow\Inject
* @var NodeShortcutResolver
*/
protected $nodeShortcutResolver;

/**
* @Flow\Inject
* @var PropertyMapper
Expand Down Expand Up @@ -183,7 +178,7 @@
return null;
}

return $this->createNodeUri($controllerContext, $targetObject, null, null, $absolute);

Check failure on line 181 in Neos.Neos/Classes/Service/LinkingService.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 Test linting-unit-functionaltests-mysql (deps: highest)

Parameter #2 $node of method Neos\Neos\Service\LinkingService::createNodeUri() expects Neos\ContentRepository\Core\Projection\ContentGraph\AbsoluteNodePath|Neos\ContentRepository\Core\Projection\ContentGraph\Node|string|null, Neos\ContentRepository\Core\Projection\ContentGraph\Node|Neos\Media\Domain\Model\AssetInterface given.

Check failure on line 181 in Neos.Neos/Classes/Service/LinkingService.php

View workflow job for this annotation

GitHub Actions / PHP 8.3 Test linting-unit-functionaltests-mysql (deps: highest)

Parameter #2 $node of method Neos\Neos\Service\LinkingService::createNodeUri() expects Neos\ContentRepository\Core\Projection\ContentGraph\AbsoluteNodePath|Neos\ContentRepository\Core\Projection\ContentGraph\Node|string|null, Neos\ContentRepository\Core\Projection\ContentGraph\Node|Neos\Media\Domain\Model\AssetInterface given.
}

/**
Expand Down Expand Up @@ -253,8 +248,8 @@
* Renders the URI to a given node instance or -path.
*
* @param ControllerContext $controllerContext
* @param mixed $node A node object or a string node path,
* if a relative path is provided the baseNode argument is required
* @param Node|AbsoluteNodePath|string $node A node object or a string node path,
* if a relative path is provided the baseNode argument is required
* @param Node $baseNode
* @param string $format Format to use for the URL, for example "html" or "json"
* @param boolean $absolute If set, an absolute URI is rendered
Expand All @@ -264,8 +259,6 @@
* @param boolean $addQueryString If set, the current query parameters will be kept in the URI
* @param array<int,string> $argumentsToBeExcludedFromQueryString arguments to be removed from the URI.
* Only active if $addQueryString = true
* @param boolean $resolveShortcuts @deprecated With Neos 7.0 this argument is no longer evaluated
* and log a message if set to FALSE
* @return string The rendered URI
* @throws NeosException if no URI could be resolved for the given node
* @throws \Neos\Flow\Mvc\Routing\Exception\MissingActionNameException
Expand All @@ -276,24 +269,16 @@
*/
public function createNodeUri(
ControllerContext $controllerContext,
$node = null,
Node|string|AbsoluteNodePath $node = null,
Node $baseNode = null,
$format = null,
$absolute = false,
array $arguments = [],
$section = '',
$addQueryString = false,
array $argumentsToBeExcludedFromQueryString = [],
$resolveShortcuts = true
array $argumentsToBeExcludedFromQueryString = []
): string {
$this->lastLinkedNode = null;
if ($resolveShortcuts === false) {
$this->systemLogger->info(sprintf(
'%s() was called with the "resolveShortCuts" argument set to FALSE.'
. ' This is no longer supported, the argument was ignored',
__METHOD__
));
}
if (!($node instanceof Node || is_string($node) || $baseNode instanceof Node)) {
throw new \InvalidArgumentException(
'Expected an instance of Node or a string for the node argument,'
Expand Down Expand Up @@ -367,6 +352,7 @@
$request = $controllerContext->getRequest()->getMainRequest();
$uriBuilder = clone $controllerContext->getUriBuilder();
$uriBuilder->setRequest($request);
// hiddne good one .
$action = $workspace && $workspace->isPublicWorkspace() && !$hiddenState->isHidden ? 'show' : 'preview';

return $uriBuilder
Expand Down
71 changes: 71 additions & 0 deletions Neos.Neos/Classes/Utility/LegacyNodePathNormalizer.php
@@ -0,0 +1,71 @@
<?php

declare(strict_types=1);

namespace Neos\Neos\Utility;

use Neos\ContentRepository\Core\Projection\ContentGraph\AbsoluteNodePath;
use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindClosestNodeFilter;
use Neos\ContentRepository\Core\Projection\ContentGraph\Node;
use Neos\ContentRepository\Core\Projection\ContentGraph\NodePath;
use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry;
use Neos\Flow\Annotations as Flow;
use Neos\Neos\Domain\Service\NodeTypeNameFactory;

/**
* @deprecated please use the new {@see AbsoluteNodePath} syntax instead.
*/
final class LegacyNodePathNormalizer
{
#[Flow\Inject]
protected ContentRepositoryRegistry $contentRepositoryRegistry;

/**
* Converts legacy paths like "/absolute/path", "~/site-relative/path" and "~" to the corresponding
* AbsoluteNodePath depending on the passed base node.
*
* The following syntax is not implemented and handled here:
*
* - node://
* - /<Neos.Neos:Sites>/my-site/main
* - some/relative/path
*
* Also while legacy and previously allowed, node path traversal like ./neos/info or ../foo/../../bar is not handled.
*/
public function resolveLegacyPathSyntaxToAbsoluteNodePath(
string $path,
Node $baseNode
): ?AbsoluteNodePath {
if (str_contains($path, '../') || str_contains($path, './')) {
throw new \InvalidArgumentException(sprintf('NodePath traversal via /../ is not allowed. Got: "%s"', $path), 1707732065);
}

if (!str_starts_with($path, '~') && !str_starts_with($path, '/')) {
return null;
}

$subgraph = $this->contentRepositoryRegistry->subgraphForNode($baseNode);

$siteNode = $subgraph->findClosestNode($baseNode->nodeAggregateId, FindClosestNodeFilter::create(nodeTypes: NodeTypeNameFactory::NAME_SITE));
if ($siteNode === null) {
throw new \RuntimeException(sprintf(
'Failed to determine site node for aggregate node "%s" and subgraph "%s"',
$baseNode->nodeAggregateId->value,
json_encode($subgraph, JSON_PARTIAL_OUTPUT_ON_ERROR)
), 1601366598);
}
if ($path === '~') {
return AbsoluteNodePath::fromRootNodeTypeNameAndRelativePath(
NodeTypeNameFactory::forSites(),
NodePath::fromNodeNames($siteNode->nodeName)

Check failure on line 60 in Neos.Neos/Classes/Utility/LegacyNodePathNormalizer.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 Test linting-unit-functionaltests-mysql (deps: highest)

Parameter #1 ...$nodeNames of static method Neos\ContentRepository\Core\Projection\ContentGraph\NodePath::fromNodeNames() expects Neos\ContentRepository\Core\SharedModel\Node\NodeName, Neos\ContentRepository\Core\SharedModel\Node\NodeName|null given.

Check failure on line 60 in Neos.Neos/Classes/Utility/LegacyNodePathNormalizer.php

View workflow job for this annotation

GitHub Actions / PHP 8.3 Test linting-unit-functionaltests-mysql (deps: highest)

Parameter #1 ...$nodeNames of static method Neos\ContentRepository\Core\Projection\ContentGraph\NodePath::fromNodeNames() expects Neos\ContentRepository\Core\SharedModel\Node\NodeName, Neos\ContentRepository\Core\SharedModel\Node\NodeName|null given.
);
} else {
return AbsoluteNodePath::fromRootNodeTypeNameAndRelativePath(
NodeTypeNameFactory::forSites(),
NodePath::fromPathSegments(
[$siteNode->nodeName->value, ...explode('/', substr($path, 1))]

Check failure on line 66 in Neos.Neos/Classes/Utility/LegacyNodePathNormalizer.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 Test linting-unit-functionaltests-mysql (deps: highest)

Cannot access property $value on Neos\ContentRepository\Core\SharedModel\Node\NodeName|null.

Check failure on line 66 in Neos.Neos/Classes/Utility/LegacyNodePathNormalizer.php

View workflow job for this annotation

GitHub Actions / PHP 8.3 Test linting-unit-functionaltests-mysql (deps: highest)

Cannot access property $value on Neos\ContentRepository\Core\SharedModel\Node\NodeName|null.
)
);
}
}
}
9 changes: 7 additions & 2 deletions Neos.Neos/Classes/ViewHelpers/Uri/NodeViewHelper.php
Expand Up @@ -33,6 +33,7 @@
use Neos\FluidAdaptor\Core\ViewHelper\Exception as ViewHelperException;
use Neos\Fusion\ViewHelpers\FusionContextTrait;
use Neos\Neos\FrontendRouting\NodeUriBuilder;
use Neos\Neos\Utility\LegacyNodePathNormalizer;

/**
* A view helper for creating URIs pointing to nodes.
Expand All @@ -50,8 +51,6 @@
* The given path is treated as a path relative to the current node.
* Examples: given that the current node is ``/sites/acmecom/products/``,
* ``stapler`` results in ``/sites/acmecom/products/stapler``,
* ``../about`` results in ``/sites/acmecom/about/``,
* ``./neos/info`` results in ``/sites/acmecom/products/neos/info``.
*
* *``node`` starts with a tilde character (``~``):*
* The given path is treated as a path relative to the current site node.
Expand Down Expand Up @@ -112,6 +111,12 @@ class NodeViewHelper extends AbstractViewHelper
*/
protected $contentRepositoryRegistry;

/**
* @Flow\Inject
* @var LegacyNodePathNormalizer
*/
protected $legacyNodePathNormalizer;

/**
* @Flow\Inject
* @var ThrowableStorageInterface
Expand Down