diff --git a/Neos.Fusion/Classes/Core/Cache/ParserCache.php b/Neos.Fusion/Classes/Core/Cache/ParserCache.php index 0ceb1f67f3f..16866957658 100644 --- a/Neos.Fusion/Classes/Core/Cache/ParserCache.php +++ b/Neos.Fusion/Classes/Core/Cache/ParserCache.php @@ -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); } @@ -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; } diff --git a/Neos.Fusion/Classes/Core/Cache/ParserCacheFlusher.php b/Neos.Fusion/Classes/Core/Cache/ParserCacheFlusher.php index abee7a14a46..99ce00d0d2b 100644 --- a/Neos.Fusion/Classes/Core/Cache/ParserCacheFlusher.php +++ b/Neos.Fusion/Classes/Core/Cache/ParserCacheFlusher.php @@ -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 !== []) { diff --git a/Neos.Fusion/Classes/Core/Cache/ParserCacheIdentifierTrait.php b/Neos.Fusion/Classes/Core/Cache/ParserCacheIdentifierTrait.php index 86fcf9d6a0a..23fd8a76b36 100644 --- a/Neos.Fusion/Classes/Core/Cache/ParserCacheIdentifierTrait.php +++ b/Neos.Fusion/Classes/Core/Cache/ParserCacheIdentifierTrait.php @@ -19,7 +19,7 @@ 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 { @@ -27,18 +27,24 @@ private function getCacheIdentifierForDslCode(string $identifier, string $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); } } diff --git a/Neos.Fusion/Classes/Core/ObjectTreeParser/ExceptionMessage/MessageLinePart.php b/Neos.Fusion/Classes/Core/ObjectTreeParser/ExceptionMessage/MessageLinePart.php index 26907f82676..10bbed5a31a 100644 --- a/Neos.Fusion/Classes/Core/ObjectTreeParser/ExceptionMessage/MessageLinePart.php +++ b/Neos.Fusion/Classes/Core/ObjectTreeParser/ExceptionMessage/MessageLinePart.php @@ -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 diff --git a/Neos.Fusion/Classes/Core/ObjectTreeParser/Lexer.php b/Neos.Fusion/Classes/Core/ObjectTreeParser/Lexer.php index 77453b29d90..565e670500c 100644 --- a/Neos.Fusion/Classes/Core/ObjectTreeParser/Lexer.php +++ b/Neos.Fusion/Classes/Core/ObjectTreeParser/Lexer.php @@ -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, diff --git a/Neos.Fusion/Tests/Unit/Core/Fixtures/ParserTestFusionComments01.fusion b/Neos.Fusion/Tests/Unit/Core/Fixtures/ParserTestFusionComments01.fusion index 50831893170..d5067e395d9 100644 --- a/Neos.Fusion/Tests/Unit/Core/Fixtures/ParserTestFusionComments01.fusion +++ b/Neos.Fusion/Tests/Unit/Core/Fixtures/ParserTestFusionComments01.fusion @@ -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 \ No newline at end of file diff --git a/Neos.Fusion/Tests/Unit/Core/Parser/ParserExceptionTest.php b/Neos.Fusion/Tests/Unit/Core/Parser/ParserExceptionTest.php index 790a3605d13..63a4648c72f 100644 --- a/Neos.Fusion/Tests/Unit/Core/Parser/ParserExceptionTest.php +++ b/Neos.Fusion/Tests/Unit/Core/Parser/ParserExceptionTest.php @@ -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; @@ -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.' @@ -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)); + } } diff --git a/Neos.Media.Browser/Resources/Private/Templates/Asset/Edit.html b/Neos.Media.Browser/Resources/Private/Templates/Asset/Edit.html index 06ec12fa5d7..fc7edbcaf03 100644 --- a/Neos.Media.Browser/Resources/Private/Templates/Asset/Edit.html +++ b/Neos.Media.Browser/Resources/Private/Templates/Asset/Edit.html @@ -57,14 +57,16 @@

{neos:backend.translate(id: 'connectionError', package: 'Neos.Media.Browser' - - - - - - + + + + + + + +
{neos:backend.translate(id: 'metadata', package: 'Neos.Media.Browser')} diff --git a/Neos.Neos/Classes/Controller/Backend/MenuHelper.php b/Neos.Neos/Classes/Controller/Backend/MenuHelper.php index 779337503c1..54694d6f28c 100644 --- a/Neos.Neos/Classes/Controller/Backend/MenuHelper.php +++ b/Neos.Neos/Classes/Controller/Backend/MenuHelper.php @@ -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; @@ -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 @@ -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) { diff --git a/Neos.Neos/Classes/Controller/Module/Management/WorkspacesController.php b/Neos.Neos/Classes/Controller/Module/Management/WorkspacesController.php index d0aeeae4dc3..b348c160394 100644 --- a/Neos.Neos/Classes/Controller/Module/Management/WorkspacesController.php +++ b/Neos.Neos/Classes/Controller/Module/Management/WorkspacesController.php @@ -470,16 +470,18 @@ protected function computeChangesCount(Workspace $selectedWorkspace) { $changesCount = ['new' => 0, 'changed' => 0, 'removed' => 0, 'total' => 0]; foreach ($this->computeSiteChanges($selectedWorkspace) as $siteChanges) { - foreach ($siteChanges['documents'] as $documentChanges) { - foreach ($documentChanges['changes'] as $change) { - if ($change['node']->isRemoved()) { - $changesCount['removed']++; - } elseif ($change['isNew']) { - $changesCount['new']++; - } else { - $changesCount['changed']++; - }; - $changesCount['total']++; + foreach ($siteChanges['documents'] as $dimensions) { + foreach ($dimensions as $documentChanges) { + foreach ($documentChanges['changes'] as $change) { + if ($change['node']->isRemoved()) { + $changesCount['removed']++; + } elseif ($change['isNew']) { + $changesCount['new']++; + } else { + $changesCount['changed']++; + }; + $changesCount['total']++; + } } } } @@ -509,11 +511,15 @@ protected function computeSiteChanges(Workspace $selectedWorkspace) // $document will be null if we have a broken root line for this node. This actually should never happen, but currently can in some scenarios. if ($document !== null) { $documentPath = implode('/', array_slice(explode('/', $document->getPath()), 3)); + + $dimensionValues = $document->getDimensions(); + $documentDimension = Utility::sortDimensionValueArrayAndReturnDimensionsHash($dimensionValues); + $relativePath = str_replace(sprintf(SiteService::SITES_ROOT_PATH . '/%s/%s', $siteNodeName, $documentPath), '', $node->getPath()); if (!isset($siteChanges[$siteNodeName]['siteNode'])) { $siteChanges[$siteNodeName]['siteNode'] = $this->siteRepository->findOneByNodeName($siteNodeName); } - $siteChanges[$siteNodeName]['documents'][$documentPath]['documentNode'] = $document; + $siteChanges[$siteNodeName]['documents'][$documentDimension][$documentPath]['documentNode'] = $document; $change = [ 'node' => $node, @@ -522,7 +528,7 @@ protected function computeSiteChanges(Workspace $selectedWorkspace) if ($node->getNodeType()->isOfType('Neos.Neos:Node')) { $change['configuration'] = $node->getNodeType()->getFullConfiguration(); } - $siteChanges[$siteNodeName]['documents'][$documentPath]['changes'][$relativePath] = $change; + $siteChanges[$siteNodeName]['documents'][$documentDimension][$documentPath]['changes'][$relativePath] = $change; } } } @@ -534,17 +540,21 @@ protected function computeSiteChanges(Workspace $selectedWorkspace) ksort($siteChanges); foreach ($siteChanges as $siteKey => $site) { - foreach ($site['documents'] as $documentKey => $document) { - $liveDocumentNode = $liveContext->getNodeByIdentifier($document['documentNode']->getIdentifier()); - $siteChanges[$siteKey]['documents'][$documentKey]['isMoved'] = $liveDocumentNode && $document['documentNode']->getPath() !== $liveDocumentNode->getPath(); - $siteChanges[$siteKey]['documents'][$documentKey]['isNew'] = $liveDocumentNode === null; - foreach ($document['changes'] as $changeKey => $change) { - $liveNode = $liveContext->getNodeByIdentifier($change['node']->getIdentifier()); - $siteChanges[$siteKey]['documents'][$documentKey]['changes'][$changeKey]['isNew'] = is_null($liveNode); - $siteChanges[$siteKey]['documents'][$documentKey]['changes'][$changeKey]['isMoved'] = $liveNode && $change['node']->getPath() !== $liveNode->getPath(); + foreach ($site['documents'] as $documentDimension => $documentsPerDimension) { + foreach ($documentsPerDimension as $documentKey => $document) { + $liveDocumentNode = $liveContext->getNodeByIdentifier($document['documentNode']->getIdentifier()); + $siteChanges[$siteKey]['documents'][$documentDimension][$documentKey]['isMoved'] = $liveDocumentNode && $document['documentNode']->getPath() !== $liveDocumentNode->getPath(); + $siteChanges[$siteKey]['documents'][$documentDimension][$documentKey]['isNew'] = $liveDocumentNode === null; + foreach ($document['changes'] as $changeKey => $change) { + $liveNode = $liveContext->getNodeByIdentifier($change['node']->getIdentifier()); + $siteChanges[$siteKey]['documents'][$documentDimension][$documentKey]['changes'][$changeKey]['isNew'] = is_null($liveNode); + $siteChanges[$siteKey]['documents'][$documentDimension][$documentKey]['changes'][$changeKey]['isMoved'] = $liveNode && $change['node']->getPath() !== $liveNode->getPath(); + } } } - ksort($siteChanges[$siteKey]['documents']); + foreach ($siteChanges[$siteKey]['documents'] as $key => $document) { + ksort($siteChanges[$siteKey]['documents'][$key]); + } } return $siteChanges; } diff --git a/Neos.Neos/Documentation/Appendixes/ChangeLogs/7319.rst b/Neos.Neos/Documentation/Appendixes/ChangeLogs/7319.rst new file mode 100644 index 00000000000..711577afdc8 --- /dev/null +++ b/Neos.Neos/Documentation/Appendixes/ChangeLogs/7319.rst @@ -0,0 +1,57 @@ +`7.3.19 (2024-01-15) `_ +================================================================================================ + +Overview of merged pull requests +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`BUGFIX: Check SVG files for malicious code before providing original asset url links `_ +------------------------------------------------------------------------------------------------------------------------------------------------------- + +This adds a check in the preview of assets in the media module and checks for malicous content in svgs. If detected, the direct links to the original url get removed from the preview pages and a warning is shown. + +!`image `_ + +Fixes https://github.com/neos/flow-development-collection/issues/3248 + +* Packages: ``Neos`` ``Media.Browser`` + +`BUGFIX: Resolve StyleCI issues `_ +------------------------------------------------------------------------------------------------- + + + +* Packages: ``Neos`` ``Fusion`` + +`BUGFIX: node:repair fails with could not be converted to string `_ +---------------------------------------------------------------------------------------------------------------------------------- + +Fixes the following crash during node:repair + +```shell +./flow node:repair --dry-run --only removeBrokenEntityReferences +Dry run, not committing any changes. + +Checking for broken entity references ... +Object of class Neos\\Flow\\Persistence\\Doctrine\\Proxies\\__CG__\\Neos\\Media\\Domain\\Model\\ImageVariant could not be converted to string + + Type: Error + File: Data/Temporary/Development/SubContextWbWeb/Cache/Code/Flow_Object_Classes/Neos_ContentRepository_Command_NodeCommandControllerPlugin.php + Line: 836 +``` + +resolved `#4794 `_ + +**Upgrade instructions** + +- [x] Code follows the PSR-2 coding style +- ~~Tests have been created, run and adjusted as needed~~ + - There are not tests in place and I added none. +- [x] The PR is created against the `lowest maintained branch `_ -> 7.3 +- [ ] Reviewer - PR Title is brief but complete and starts with ``FEATURE|TASK|BUGFIX`` +- [ ] Reviewer - The first section explains the change briefly for change-logs +- [ ] Reviewer - Breaking Changes are marked with ``!!!`` and have upgrade-instructions + +* Packages: ``Neos`` ``ContentRepository`` + +`Detailed log `_ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/Neos.Neos/Documentation/Appendixes/ChangeLogs/8016.rst b/Neos.Neos/Documentation/Appendixes/ChangeLogs/8016.rst new file mode 100644 index 00000000000..ac20265320a --- /dev/null +++ b/Neos.Neos/Documentation/Appendixes/ChangeLogs/8016.rst @@ -0,0 +1,82 @@ +`8.0.16 (2024-01-15) `_ +================================================================================================ + +Overview of merged pull requests +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`BUGFIX: More precise selection of the DomNode with CSFR token `_ +-------------------------------------------------------------------------------------------------------------------------------- + +To prevent issues with selecting the wrong CSFR Token in the DOM, we now have a more precise selection of the DomNode with the CSFR token. + +fixes: `#4822 `_ + +**Review instructions** + +Install a version before 1.0.1 of the `Shel.Neos.WorkspaceModule `_ +and go to the user management module to impersonate a user. Then switch to the Workspace module and try to restore the original user. Without this patch, it should fail. With version 1.0.1 it will not fail btw. + + +* Packages: ``Media.Browser`` ``Neos`` + +`BUGFIX: Check SVG files for malicious code before providing original asset url links `_ +------------------------------------------------------------------------------------------------------------------------------------------------------- + +This adds a check in the preview of assets in the media module and checks for malicous content in svgs. If detected, the direct links to the original url get removed from the preview pages and a warning is shown. + +!`image `_ + +Fixes https://github.com/neos/flow-development-collection/issues/3248 + +* Packages: ``Neos`` ``Media.Browser`` + +`BUGFIX: Resolve StyleCI issues `_ +------------------------------------------------------------------------------------------------- + + + +* Packages: ``Neos`` ``Fusion`` + +`BUGFIX: node:repair fails with could not be converted to string `_ +---------------------------------------------------------------------------------------------------------------------------------- + +Fixes the following crash during node:repair + +```shell +./flow node:repair --dry-run --only removeBrokenEntityReferences +Dry run, not committing any changes. + +Checking for broken entity references ... +Object of class Neos\\Flow\\Persistence\\Doctrine\\Proxies\\__CG__\\Neos\\Media\\Domain\\Model\\ImageVariant could not be converted to string + + Type: Error + File: Data/Temporary/Development/SubContextWbWeb/Cache/Code/Flow_Object_Classes/Neos_ContentRepository_Command_NodeCommandControllerPlugin.php + Line: 836 +``` + +resolved `#4794 `_ + +**Upgrade instructions** + +- [x] Code follows the PSR-2 coding style +- ~~Tests have been created, run and adjusted as needed~~ + - There are not tests in place and I added none. +- [x] The PR is created against the `lowest maintained branch `_ -> 7.3 +- [ ] Reviewer - PR Title is brief but complete and starts with ``FEATURE|TASK|BUGFIX`` +- [ ] Reviewer - The first section explains the change briefly for change-logs +- [ ] Reviewer - Breaking Changes are marked with ``!!!`` and have upgrade-instructions + +* Packages: ``Neos`` ``ContentRepository`` + +`TASK: Removes neos/neos-setup `_ +------------------------------------------------------------------------------------------------ + +**Summary:** +When attempting to install Neos version 8.3 using the command ``composer create-project neos/neos-development-distribution neos-development 8.3.x-dev --keep-vcs``, the installation results in the neos-setup (version 1.x) being installed. The ``neos/cli-setup`` tool has been removed and the default command ``./flow welcome`` is still called. But the command is no longer available due to the absence of the CLI setup tool. Consequently, the setup process is not possible as the recommended command is missing. + +We remove the dependency from the development collection and adds the ``neos/neos-setup`` in the latest version to the `neos-development-distribution `_ + +* Packages: ``Neos`` + +`Detailed log `_ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/Neos.Neos/Documentation/Appendixes/ChangeLogs/8111.rst b/Neos.Neos/Documentation/Appendixes/ChangeLogs/8111.rst new file mode 100644 index 00000000000..42a6a42b9b0 --- /dev/null +++ b/Neos.Neos/Documentation/Appendixes/ChangeLogs/8111.rst @@ -0,0 +1,67 @@ +`8.1.11 (2024-01-15) `_ +================================================================================================ + +Overview of merged pull requests +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`BUGFIX: Check SVG files for malicious code before providing original asset url links `_ +------------------------------------------------------------------------------------------------------------------------------------------------------- + +This adds a check in the preview of assets in the media module and checks for malicous content in svgs. If detected, the direct links to the original url get removed from the preview pages and a warning is shown. + +!`image `_ + +Fixes https://github.com/neos/flow-development-collection/issues/3248 + +* Packages: ``Neos`` ``Media.Browser`` + +`BUGFIX: Resolve StyleCI issues `_ +------------------------------------------------------------------------------------------------- + + + +* Packages: ``Neos`` ``Fusion`` + +`BUGFIX: node:repair fails with could not be converted to string `_ +---------------------------------------------------------------------------------------------------------------------------------- + +Fixes the following crash during node:repair + +```shell +./flow node:repair --dry-run --only removeBrokenEntityReferences +Dry run, not committing any changes. + +Checking for broken entity references ... +Object of class Neos\\Flow\\Persistence\\Doctrine\\Proxies\\__CG__\\Neos\\Media\\Domain\\Model\\ImageVariant could not be converted to string + + Type: Error + File: Data/Temporary/Development/SubContextWbWeb/Cache/Code/Flow_Object_Classes/Neos_ContentRepository_Command_NodeCommandControllerPlugin.php + Line: 836 +``` + +resolved `#4794 `_ + +**Upgrade instructions** + +- [x] Code follows the PSR-2 coding style +- ~~Tests have been created, run and adjusted as needed~~ + - There are not tests in place and I added none. +- [x] The PR is created against the `lowest maintained branch `_ -> 7.3 +- [ ] Reviewer - PR Title is brief but complete and starts with ``FEATURE|TASK|BUGFIX`` +- [ ] Reviewer - The first section explains the change briefly for change-logs +- [ ] Reviewer - Breaking Changes are marked with ``!!!`` and have upgrade-instructions + +* Packages: ``Neos`` ``ContentRepository`` + +`TASK: Removes neos/neos-setup `_ +------------------------------------------------------------------------------------------------ + +**Summary:** +When attempting to install Neos version 8.3 using the command ``composer create-project neos/neos-development-distribution neos-development 8.3.x-dev --keep-vcs``, the installation results in the neos-setup (version 1.x) being installed. The ``neos/cli-setup`` tool has been removed and the default command ``./flow welcome`` is still called. But the command is no longer available due to the absence of the CLI setup tool. Consequently, the setup process is not possible as the recommended command is missing. + +We remove the dependency from the development collection and adds the ``neos/neos-setup`` in the latest version to the `neos-development-distribution `_ + +* Packages: ``Neos`` + +`Detailed log `_ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/Neos.Neos/Documentation/Appendixes/ChangeLogs/8211.rst b/Neos.Neos/Documentation/Appendixes/ChangeLogs/8211.rst new file mode 100644 index 00000000000..e282a5caa5b --- /dev/null +++ b/Neos.Neos/Documentation/Appendixes/ChangeLogs/8211.rst @@ -0,0 +1,69 @@ +`8.2.11 (2024-01-15) `_ +================================================================================================ + +Overview of merged pull requests +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`BUGFIX: Check SVG files for malicious code before providing original asset url links `_ +------------------------------------------------------------------------------------------------------------------------------------------------------- + +This adds a check in the preview of assets in the media module and checks for malicous content in svgs. If detected, the direct links to the original url get removed from the preview pages and a warning is shown. + +!`image `_ + +Fixes: +- https://github.com/neos/neos-development-collection/issues/4833 +- https://github.com/neos/flow-development-collection/issues/3248 + +* Packages: ``Neos`` ``Media.Browser`` + +`BUGFIX: Resolve StyleCI issues `_ +------------------------------------------------------------------------------------------------- + + + +* Packages: ``Neos`` ``Fusion`` + +`BUGFIX: node:repair fails with could not be converted to string `_ +---------------------------------------------------------------------------------------------------------------------------------- + +Fixes the following crash during node:repair + +```shell +./flow node:repair --dry-run --only removeBrokenEntityReferences +Dry run, not committing any changes. + +Checking for broken entity references ... +Object of class Neos\\Flow\\Persistence\\Doctrine\\Proxies\\__CG__\\Neos\\Media\\Domain\\Model\\ImageVariant could not be converted to string + + Type: Error + File: Data/Temporary/Development/SubContextWbWeb/Cache/Code/Flow_Object_Classes/Neos_ContentRepository_Command_NodeCommandControllerPlugin.php + Line: 836 +``` + +resolved `#4794 `_ + +**Upgrade instructions** + +- [x] Code follows the PSR-2 coding style +- ~~Tests have been created, run and adjusted as needed~~ + - There are not tests in place and I added none. +- [x] The PR is created against the `lowest maintained branch `_ -> 7.3 +- [ ] Reviewer - PR Title is brief but complete and starts with ``FEATURE|TASK|BUGFIX`` +- [ ] Reviewer - The first section explains the change briefly for change-logs +- [ ] Reviewer - Breaking Changes are marked with ``!!!`` and have upgrade-instructions + +* Packages: ``Neos`` + +`TASK: Removes neos/neos-setup `_ +------------------------------------------------------------------------------------------------ + +**Summary:** +When attempting to install Neos version 8.3 using the command ``composer create-project neos/neos-development-distribution neos-development 8.3.x-dev --keep-vcs``, the installation results in the neos-setup (version 1.x) being installed. The ``neos/cli-setup`` tool has been removed and the default command ``./flow welcome`` is still called. But the command is no longer available due to the absence of the CLI setup tool. Consequently, the setup process is not possible as the recommended command is missing. + +We remove the dependency from the development collection and adds the ``neos/neos-setup`` in the latest version to the `neos-development-distribution `_ + +* Packages: ``Neos`` + +`Detailed log `_ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/Neos.Neos/Resources/Private/Partials/Backend/UserMenu.html b/Neos.Neos/Resources/Private/Partials/Backend/UserMenu.html index b66b6a2225d..ec7c3b21842 100644 --- a/Neos.Neos/Resources/Private/Partials/Backend/UserMenu.html +++ b/Neos.Neos/Resources/Private/Partials/Backend/UserMenu.html @@ -1,5 +1,5 @@ {namespace neos=Neos\Neos\ViewHelpers} -
+
diff --git a/Neos.Neos/Resources/Private/Templates/Module/Management/Workspaces/Show.html b/Neos.Neos/Resources/Private/Templates/Module/Management/Workspaces/Show.html index 0742b1ecec5..9d0b570271e 100644 --- a/Neos.Neos/Resources/Private/Templates/Module/Management/Workspaces/Show.html +++ b/Neos.Neos/Resources/Private/Templates/Module/Management/Workspaces/Show.html @@ -15,31 +15,32 @@
- - - - - + + + + + - - - - - - + + + + - - - - - - - + + + - + + + + diff --git a/Neos.Neos/Resources/Public/JavaScript/Components/TopBar/UserMenu.js b/Neos.Neos/Resources/Public/JavaScript/Components/TopBar/UserMenu.js index c9d0da20b82..9e441601751 100644 --- a/Neos.Neos/Resources/Public/JavaScript/Components/TopBar/UserMenu.js +++ b/Neos.Neos/Resources/Public/JavaScript/Components/TopBar/UserMenu.js @@ -2,21 +2,35 @@ import { isNil } from '../../Helper' import {ApiService} from '../../Services' import { RestoreButton } from '../../Templates/RestoreButton' -const BASE_PATH = '/neos/impersonate/' export default class UserMenu { constructor(_root) { - const csfrTokenField = document.querySelector('[data-csrf-token]') - this._csrfToken = !isNil(csfrTokenField) - ? csfrTokenField.getAttribute('data-csrf-token') - : '' + const userMenuElement = document.querySelector('.neos-user-menu[data-csrf-token]') + this._csrfToken = isNil(userMenuElement) ? '' : userMenuElement.getAttribute('data-csrf-token') + this._baseModuleUri = this._getBaseModuleUri(userMenuElement) + this._basePath = this._getImpersonationBasePath() this._root = _root - this._apiService = new ApiService(BASE_PATH, this._csrfToken) + this._apiService = new ApiService(this._basePath, this._csrfToken) if (!isNil(_root)) { this._checkImpersonateStatus() } } + _getBaseModuleUri(element) { + let url = '/neos' + if (!isNil(element) && element.hasAttribute('data-module-basepath')) { + url = element.getAttribute('data-module-basepath') + } + return url + } + + _getImpersonationBasePath() { + let basePath = this._baseModuleUri + if (!basePath.endsWith('/')) { + basePath += '/' + } + return basePath + 'impersonate/' + } _renderRestoreButton(user) { const userMenuDropDown = this._root.querySelector('.neos-dropdown-menu') if (isNil(userMenuDropDown) || isNil(user)) { @@ -64,7 +78,7 @@ export default class UserMenu { const response = this._apiService.callRestore() response .then((data) => { - const { origin, impersonate, status } = data + const { origin, impersonate } = data const message = window.NeosCMS.I18n.translate( 'impersonate.success.restoreUser', 'Switched back from {0} to the orginal user {1}.', @@ -79,7 +93,7 @@ export default class UserMenu { // load default backend, so we don't need to care for the module permissions. // because in not every neos version the users have by default the content module or user module - window.location.pathname = '/neos' + window.location.href = this._baseModuleUri }) .catch(function (error) { if (window.NeosCMS) { diff --git a/Neos.Neos/Resources/Public/JavaScript/Module/Administration/UserManagement.js b/Neos.Neos/Resources/Public/JavaScript/Module/Administration/UserManagement.js index 80bb4641cd5..ef5a6c63f5a 100644 --- a/Neos.Neos/Resources/Public/JavaScript/Module/Administration/UserManagement.js +++ b/Neos.Neos/Resources/Public/JavaScript/Module/Administration/UserManagement.js @@ -2,13 +2,14 @@ import {isNil} from '../../Helper' import {ApiService} from '../../Services' import {ImpersonateButton} from '../../Templates/ImpersonateButton' -const BASE_PATH = '/neos/impersonate/' export default class UserManagement { constructor(_root) { - const csfrTokenField = document.querySelector('[data-csrf-token]') + const userMenuElement = document.querySelector('.neos-user-menu[data-csrf-token]') this._root = _root - this._csrfToken = !isNil(csfrTokenField) ? csfrTokenField.getAttribute('data-csrf-token') : '' - this._apiService = new ApiService(BASE_PATH, this._csrfToken) + this._csrfToken = isNil(userMenuElement) ? '' : userMenuElement.getAttribute('data-csrf-token') + this._baseModuleUri = this._getBaseModuleUri(userMenuElement) + this._basePath = this._getImpersonationBasePath() + this._apiService = new ApiService(this._basePath, this._csrfToken) if (!isNil(_root)) { this._initialize() @@ -27,6 +28,21 @@ export default class UserManagement { }); } + _getBaseModuleUri(element) { + let url = '/neos' + if (!isNil(element) && element.hasAttribute('data-module-basepath')) { + url = element.getAttribute('data-module-basepath') + } + return url + } + + _getImpersonationBasePath() { + let basePath = this._baseModuleUri + if (!basePath.endsWith('/')) { + basePath += '/' + } + return basePath + 'impersonate/' + } _renderImpersonateButtons() { const userTableActionButtons = Array.from(this._root.querySelectorAll('.neos-table .neos-action')) userTableActionButtons.forEach(_actionContainer => { @@ -61,14 +77,14 @@ export default class UserManagement { const response = this._apiService.callUserChange(identifier); response .then((data) => { - const {user, status} = data + const {user} = data const username = isNil(user) ? '' : user.accountIdentifier const message = window.NeosCMS.I18n.translate('impersonate.success.impersonateUser', 'Switched to the new user {0}.', 'Neos.Neos', 'Main', {0: username}) window.NeosCMS.Notification.ok(message) // load default backend, so we don't need to care for the module permissions. // because in not every neos version the users have by default the content module or user module - window.location.pathname = '/neos' + window.location.href = this._baseModuleUri }) .catch(function (error) { if (window.NeosCMS) {
- - {neos:backend.translate(id: 'workspaces.selectAllCurrentChanges', source: 'Modules', package: 'Neos.Neos')}
+ + {neos:backend.translate(id: 'workspaces.selectAllCurrentChanges', source: 'Modules', package: 'Neos.Neos')}
- - - - - - - + + +
+ + + + + + +
{neos:backend.translate(id: 'pathCaption', source: 'Main', package: 'Neos.Neos')}: @@ -54,40 +55,41 @@
-
- -
- - + + - - - -
+ +
+ + + + + + + +
- -
-
- - - + + + + + + +
- - -