Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/8.2' into 8.3
Browse files Browse the repository at this point in the history
  • Loading branch information
mhsdesign committed Feb 11, 2024
2 parents 3b6fea3 + ebbe0bf commit 4b71161
Show file tree
Hide file tree
Showing 18 changed files with 562 additions and 149 deletions.
18 changes: 14 additions & 4 deletions Neos.Fusion/Classes/Core/Cache/ParserCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,12 @@ public function cacheForFusionFile(?string $contextPathAndFilename, \Closure $ge
if (str_contains($contextPathAndFilename, 'nodetypes://')) {
$contextPathAndFilename = $this->getAbsolutePathForNodeTypesUri($contextPathAndFilename);
}
$identifier = $this->getCacheIdentifierForFile($contextPathAndFilename);
$fusionFileRealPath = realpath($contextPathAndFilename);
if ($fusionFileRealPath === false) {
// should not happen as the file would not been able to be read in the first place.
throw new \RuntimeException("Couldn't resolve realpath for: '$contextPathAndFilename'", 1705409467);
}
$identifier = $this->getCacheIdentifierForAbsoluteUnixStyleFilePathWithoutDirectoryTraversal($fusionFileRealPath);
return $this->cacheForIdentifier($identifier, $generateValueToCache);
}

Expand All @@ -78,11 +83,16 @@ public function cacheForDsl(string $identifier, string $code, \Closure $generate

private function cacheForIdentifier(string $identifier, \Closure $generateValueToCache): mixed
{
if ($this->parsePartialsCache->has($identifier)) {
return $this->parsePartialsCache->get($identifier);
$value = $this->parsePartialsCache->get($identifier);
if ($value !== false) {
return $value;
}
$value = $generateValueToCache();
$this->parsePartialsCache->set($identifier, $value);
if ($value !== false) {
// in the rare edge-case of a fusion dsl returning `false` we cannot cache it,
// as the above get would be ignored. This is an acceptable compromise.
$this->parsePartialsCache->set($identifier, $value);
}
return $value;
}

Expand Down
5 changes: 4 additions & 1 deletion Neos.Fusion/Classes/Core/Cache/ParserCacheFlusher.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ public function flushPartialCacheOnFileChanges($fileMonitorIdentifier, array $ch

$identifiersToFlush = [];
foreach ($changedFiles as $changedFile => $status) {
$identifiersToFlush[] = $this->getCacheIdentifierForFile($changedFile);
// flow already returns absolute file paths from the file monitor, so we don't have to call realpath.
// attempting to use realpath can even result in an error as the file might be removed and thus no realpath can be resolved via file system.
// https://github.com/neos/neos-development-collection/pull/4509
$identifiersToFlush[] = $this->getCacheIdentifierForAbsoluteUnixStyleFilePathWithoutDirectoryTraversal($changedFile);
}

if ($identifiersToFlush !== []) {
Expand Down
30 changes: 18 additions & 12 deletions Neos.Fusion/Classes/Core/Cache/ParserCacheIdentifierTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,32 @@
trait ParserCacheIdentifierTrait
{
/**
* creates a comparable hash of the dsl type and content to be used as cache identifier
* Creates a comparable hash of the dsl type and content to be used as cache identifier
*/
private function getCacheIdentifierForDslCode(string $identifier, string $code): string
{
return 'dsl_' . $identifier . '_' . md5($code);
}

/**
* creates a comparable hash of the absolute, resolved $fusionFileName
* Creates a comparable hash of the absolute-unix-style-file-path-without-directory-traversal
*
* @throws \InvalidArgumentException
* something like
* - /Users/marc/Code/neos-project/Packages/Neos
*
* its crucial that the path
* - is absolute
* - the path separator is in unix style: forward-slash /
* - doesn't contain directory traversal /../ or /./
*
* to be absolutely sure the path matches the criteria, {@see realpath} can be used.
*
* if the path does not match the criteria, a different hash as expected will be generated and caching will break.
*/
private function getCacheIdentifierForFile(string $fusionFileName): string
{
$realPath = realpath($fusionFileName);
if ($realPath === false) {
throw new \InvalidArgumentException("Couldn't resolve realpath for: '$fusionFileName'");
}

$realFusionFilePathWithoutRoot = str_replace(FLOW_PATH_ROOT, '', $realPath);
return 'file_' . md5($realFusionFilePathWithoutRoot);
private function getCacheIdentifierForAbsoluteUnixStyleFilePathWithoutDirectoryTraversal(
string $absoluteUnixStyleFilePathWithoutDirectoryTraversal
): string {
$filePathWithoutRoot = str_replace(FLOW_PATH_ROOT, '', $absoluteUnixStyleFilePathWithoutDirectoryTraversal);
return 'file_' . md5($filePathWithoutRoot);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,11 @@ public function linePrint(int $offset = 0): string

public function char(int $index = 0): string
{
if ($index < 0) {
return mb_substr($this->linePart, $index, 1);
if ($index < 0 && mb_strlen($this->linePart) < abs($index)) {
// prevent mb_substr returning first char if out of bounds
return '';
}
return mb_substr($this->linePart, $index, $index + 1);
return mb_substr($this->linePart, $index, 1);
}

public function charPrint(int $index = 0): string
Expand Down
10 changes: 6 additions & 4 deletions Neos.Fusion/Classes/Core/ObjectTreeParser/Lexer.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,14 @@ class Lexer
Token::MULTILINE_COMMENT => <<<'REGEX'
`^
/\* # start of a comment '/*'
[^*]* # match everything until special case '*'
[^*]* # consume until special case '*'
\*+ # consume all '*'
(?:
\*[^/] # if after the '*' there is a '/' break, else continue
[^*]* # until the special case '*' is encountered - unrolled loop following Jeffrey Friedl
[^/] # break if its the end: '/'
[^*]* # unrolled loop following Jeffrey E.F. Friedl
\*+
)*
\*/ # the end of a comment.
/ # the end of a comment.
`x
REGEX,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,17 @@ comment with // ane more comment
Here comes some comment with # and /* and // in it
*/

/**
* php doc style comment
*/

/***
comment with multiple stars uneven
***/

// another edge-case mentioned in NEOS-864
/**
comment with multiple stars even
**/

// another edge-case mentioned in NEOS-864 (no new line at the end)
#include: Pages/**/*.fusion
28 changes: 28 additions & 0 deletions Neos.Fusion/Tests/Unit/Core/Parser/ParserExceptionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
* source code.
*/

use Neos\Fusion\Core\ObjectTreeParser\ExceptionMessage\MessageLinePart;
use Neos\Fusion\Core\Parser;
use Neos\Fusion\Core\Cache\ParserCache;
use Neos\Fusion\Core\ObjectTreeParser\Exception\ParserException;
Expand Down Expand Up @@ -234,6 +235,11 @@ public function unclosedStatements(): \Generator
'Unclosed comment.'
];

yield 'unclosed multiline comment with multiple stars' => [
'/***',
'Unclosed comment.'
];

yield 'unclosed eel expression' => [
'a = ${',
'Unclosed eel expression.'
Expand Down Expand Up @@ -326,4 +332,26 @@ public function itMatchesThePartialExceptionMessage($fusion, $expectedMessage):
self::assertSame($expectedMessage, $e->getHelperMessagePart());
}
}

/**
* @test
*/
public function messageLinePartWorks()
{
$part = new MessageLinePart('abcd');

self::assertSame('', $part->char(-5));
self::assertSame('a', $part->char(-4));
self::assertSame('b', $part->char(-3));
self::assertSame('c', $part->char(-2));
self::assertSame('d', $part->char(-1));
self::assertSame('a', $part->char());
self::assertSame('a', $part->char(0));
self::assertSame('b', $part->char(1));
self::assertSame('c', $part->char(2));
self::assertSame('d', $part->char(3));
self::assertSame('', $part->char(4));
self::assertSame('abcd', $part->line());
self::assertSame('bcd', $part->line(1));
}
}
18 changes: 10 additions & 8 deletions Neos.Media.Browser/Resources/Private/Templates/Asset/Edit.html
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,16 @@ <h2>{neos:backend.translate(id: 'connectionError', package: 'Neos.Media.Browser'
</label>
</f:for>
</f:if>
<f:if condition="{assetCollections}">
<label>{neos:backend.translate(id: 'collections', package: 'Neos.Media.Browser')}</label>
<f:for each="{assetCollections}" as="assetCollection">
<label class="neos-checkbox neos-inline">
<m:form.checkbox property="assetCollections" multiple="TRUE" value="{assetCollection}" /><span></span> {assetCollection.title}
</label>
</f:for>
</f:if>
<f:security.ifAccess privilegeTarget="Neos.Media.Browser:ManageAssetCollections">
<f:if condition="{assetCollections}">
<label>{neos:backend.translate(id: 'collections', package: 'Neos.Media.Browser')}</label>
<f:for each="{assetCollections}" as="assetCollection">
<label class="neos-checkbox neos-inline">
<m:form.checkbox property="assetCollections" multiple="TRUE" value="{assetCollection}" /><span></span> {assetCollection.title}
</label>
</f:for>
</f:if>
</f:security.ifAccess>
</fieldset>
<fieldset>
<legend>{neos:backend.translate(id: 'metadata', package: 'Neos.Media.Browser')}</legend>
Expand Down
90 changes: 62 additions & 28 deletions Neos.Neos/Classes/Controller/Backend/MenuHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,16 @@
* source code.
*/

use Neos\ContentRepository\Domain\Factory\NodeFactory;
use Neos\ContentRepository\Domain\Repository\NodeDataRepository;
use Neos\ContentRepository\Domain\Repository\WorkspaceRepository;
use Neos\ContentRepository\Domain\Utility\NodePaths;
use Neos\ContentRepository\Security\Authorization\Privilege\Node\NodePrivilegeSubject;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Http\Exception;
use Neos\Flow\Mvc\Controller\ControllerContext;
use Neos\Flow\Mvc\Routing\Exception\MissingActionNameException;
use Neos\Flow\Security\Authorization\PrivilegeManagerInterface;
use Neos\Neos\Domain\Service\ContentContextFactory;
use Neos\Neos\Domain\Service\SiteService;
use Neos\Neos\Security\Authorization\Privilege\ModulePrivilege;
use Neos\Neos\Security\Authorization\Privilege\ModulePrivilegeSubject;
Expand Down Expand Up @@ -65,9 +68,21 @@ class MenuHelper

/**
* @Flow\Inject
* @var ContentContextFactory
* @var WorkspaceRepository
*/
protected $contextFactory;
protected $workspaceRepository;

/**
* @Flow\Inject
* @var NodeDataRepository
*/
protected $nodeDataRepository;

/**
* @Flow\Inject
* @var NodeFactory
*/
protected $nodeFactory;

/**
* @param array $settings
Expand All @@ -93,38 +108,57 @@ public function buildSiteList(ControllerContext $controllerContext): array
return [];
}

$context = $this->contextFactory->create();
$liveWorkspace = $this->workspaceRepository->findByIdentifier('live');

$domainsFound = false;
$sites = [];
foreach ($this->siteRepository->findOnline() as $site) {
$node = $context->getNode(\Neos\ContentRepository\Domain\Utility\NodePaths::addNodePathSegment(SiteService::SITES_ROOT_PATH, $site->getNodeName()));
if ($this->privilegeManager->isGranted(NodeTreePrivilege::class, new NodePrivilegeSubject($node))) {
$uri = null;
$active = false;
/** @var $site Site */
if ($site->hasActiveDomains()) {
$activeHostPatterns = $site->getActiveDomains()->map(static function ($domain) {
return $domain->getHostname();
})->toArray();

$active = in_array($requestUriHost, $activeHostPatterns, true);

if ($active) {
$uri = $contentModule['uri'];
} else {
$uri = $controllerContext->getUriBuilder()->reset()->uriFor('switchSite', ['site' => $site], 'Backend\Backend', 'Neos.Neos');
}
$granted = false;

$siteNodePath = NodePaths::addNodePathSegment(SiteService::SITES_ROOT_PATH, $site->getNodeName());
$siteNodesInAllDimensions = $this->nodeDataRepository->findByPathWithoutReduce($siteNodePath, $liveWorkspace);

$domainsFound = true;
foreach ($siteNodesInAllDimensions as $siteNodeData) {
$siteNodeContext = $this->nodeFactory->createContextMatchingNodeData($siteNodeData);
$siteNode = $this->nodeFactory->createFromNodeData($siteNodeData, $siteNodeContext);

// if the node exists, check if the user is granted access to this node
if ($this->privilegeManager->isGranted(NodeTreePrivilege::class, new NodePrivilegeSubject($siteNode))) {
$granted = true;
break;
}
}

// if no siteNode is accessible ignore this site
if (!$granted) {
continue;
}

$uri = null;
$active = false;
/** @var $site Site */
if ($site->hasActiveDomains()) {
$activeHostPatterns = $site->getActiveDomains()->map(static function ($domain) {
return $domain->getHostname();
})->toArray();

$active = in_array($requestUriHost, $activeHostPatterns, true);

if ($active) {
$uri = $contentModule['uri'];
} else {
$uri = $controllerContext->getUriBuilder()->reset()->uriFor('switchSite', ['site' => $site], 'Backend\Backend', 'Neos.Neos');
}

$sites[] = [
'name' => $site->getName(),
'nodeName' => $site->getNodeName(),
'uri' => $uri,
'active' => $active
];
$domainsFound = true;
}

$sites[] = [
'name' => $site->getName(),
'nodeName' => $site->getNodeName(),
'uri' => $uri,
'active' => $active
];
}

if ($domainsFound === false) {
Expand Down

0 comments on commit 4b71161

Please sign in to comment.