From bf7bb9061b168437ec6f4566c7690906121c2cfd Mon Sep 17 00:00:00 2001 From: Simon Schaufelberger <simonschaufi+typo3@gmail.com> Date: Fri, 2 Feb 2024 19:40:16 +0100 Subject: [PATCH] [BUGFIX] Undefined array index for TCA without ctrl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A new test case is added to cover the case when a TCA definition exists without `ctrl`. The test case has been added to the test extension `blog_example` which is not part of the public extension. Resolves: #103025 Releases: main, 12.4, 11.5 Change-Id: Iccd4428ad544fd6a94ed48ec97264b2945463d04 Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/82790 Tested-by: Stefan Bürk <stefan@buerk.tech> Reviewed-by: Stefan Bürk <stefan@buerk.tech> Tested-by: core-ci <typo3@b13.com> --- .../Generic/Storage/Typo3DbQueryParser.php | 37 +++++++++--------- .../Classes/Domain/Model/RegistryEntry.php | 38 +++++++++++++++++++ .../Repository/RegistryEntryRepository.php | 27 +++++++++++++ ...blogexample_domain_model_registryentry.php | 14 +++++++ .../Private/Language/locallang_db.xlf | 3 ++ .../Extensions/blog_example/ext_tables.sql | 7 ++++ .../Storage/Typo3DbQueryParserTest.php | 22 +++++++++++ 7 files changed, 130 insertions(+), 18 deletions(-) create mode 100644 typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Classes/Domain/Model/RegistryEntry.php create mode 100644 typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Classes/Domain/Repository/RegistryEntryRepository.php create mode 100644 typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Configuration/TCA/tx_blogexample_domain_model_registryentry.php diff --git a/typo3/sysext/extbase/Classes/Persistence/Generic/Storage/Typo3DbQueryParser.php b/typo3/sysext/extbase/Classes/Persistence/Generic/Storage/Typo3DbQueryParser.php index 95936c076cc9..0e4564bdcca9 100644 --- a/typo3/sysext/extbase/Classes/Persistence/Generic/Storage/Typo3DbQueryParser.php +++ b/typo3/sysext/extbase/Classes/Persistence/Generic/Storage/Typo3DbQueryParser.php @@ -674,23 +674,24 @@ class Typo3DbQueryParser */ protected function getVisibilityConstraintStatement(QuerySettingsInterface $querySettings, $tableName, $tableAlias) { - $statement = ''; - if (is_array($GLOBALS['TCA'][$tableName]['ctrl'] ?? null)) { - $ignoreEnableFields = $querySettings->getIgnoreEnableFields(); - $enableFieldsToBeIgnored = $querySettings->getEnableFieldsToBeIgnored(); - $includeDeleted = $querySettings->getIncludeDeleted(); - if (($GLOBALS['TYPO3_REQUEST'] ?? null) instanceof ServerRequestInterface - && ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isFrontend() - ) { - $statement .= $this->getFrontendConstraintStatement($tableName, $ignoreEnableFields, $enableFieldsToBeIgnored, $includeDeleted); - } else { - // applicationType backend - $statement .= $this->getBackendConstraintStatement($tableName, $ignoreEnableFields, $includeDeleted); - } - if (!empty($statement)) { - $statement = $this->replaceTableNameWithAlias($statement, $tableName, $tableAlias); - $statement = strtolower(substr($statement, 1, 3)) === 'and' ? substr($statement, 5) : $statement; - } + if (!is_array($GLOBALS['TCA'][$tableName]['ctrl'] ?? null)) { + return ''; + } + + $ignoreEnableFields = $querySettings->getIgnoreEnableFields(); + $enableFieldsToBeIgnored = $querySettings->getEnableFieldsToBeIgnored(); + $includeDeleted = $querySettings->getIncludeDeleted(); + if (($GLOBALS['TYPO3_REQUEST'] ?? null) instanceof ServerRequestInterface + && ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isFrontend() + ) { + $statement = $this->getFrontendConstraintStatement($tableName, $ignoreEnableFields, $enableFieldsToBeIgnored, $includeDeleted); + } else { + // applicationType backend + $statement = $this->getBackendConstraintStatement($tableName, $ignoreEnableFields, $includeDeleted); + } + if (!empty($statement)) { + $statement = $this->replaceTableNameWithAlias($statement, $tableName, $tableAlias); + $statement = strtolower(substr($statement, 1, 3)) === 'and' ? substr($statement, 5) : $statement; } return $statement; } @@ -853,7 +854,7 @@ class Typo3DbQueryParser */ protected function getPageIdStatement($tableName, $tableAlias, array $storagePageIds) { - if (!is_array($GLOBALS['TCA'][$tableName]['ctrl'])) { + if (!is_array($GLOBALS['TCA'][$tableName]['ctrl'] ?? null)) { return ''; } diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Classes/Domain/Model/RegistryEntry.php b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Classes/Domain/Model/RegistryEntry.php new file mode 100644 index 000000000000..7f21607f4b41 --- /dev/null +++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Classes/Domain/Model/RegistryEntry.php @@ -0,0 +1,38 @@ +<?php + +declare(strict_types=1); + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +namespace TYPO3Tests\BlogExample\Domain\Model; + +use TYPO3\CMS\Extbase\DomainObject\AbstractValueObject; + +/** + * A registry entry + */ +class RegistryEntry extends AbstractValueObject +{ + protected string $name = ''; + + public function getName(): string + { + return $this->name; + } + + public function setName(string $name): void + { + $this->name = $name; + } +} diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Classes/Domain/Repository/RegistryEntryRepository.php b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Classes/Domain/Repository/RegistryEntryRepository.php new file mode 100644 index 000000000000..7442fed9e20d --- /dev/null +++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Classes/Domain/Repository/RegistryEntryRepository.php @@ -0,0 +1,27 @@ +<?php + +declare(strict_types=1); + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +namespace TYPO3Tests\BlogExample\Domain\Repository; + +use TYPO3\CMS\Extbase\Persistence\Repository; +use TYPO3Tests\BlogExample\Domain\Model\RegistryEntry; + +/** + * A repository for registry entries + * @extends Repository<RegistryEntry> + */ +class RegistryEntryRepository extends Repository {} diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Configuration/TCA/tx_blogexample_domain_model_registryentry.php b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Configuration/TCA/tx_blogexample_domain_model_registryentry.php new file mode 100644 index 000000000000..2d57b61e7b36 --- /dev/null +++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Configuration/TCA/tx_blogexample_domain_model_registryentry.php @@ -0,0 +1,14 @@ +<?php + +declare(strict_types=1); + +return [ + 'columns' => [ + 'name' => [ + 'label' => 'LLL:EXT:blog_example/Resources/Private/Language/locallang_db.xlf:tx_blogexample_domain_model_registryentry.name', + 'config' => [ + 'type' => 'input', + ], + ], + ], +]; diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Resources/Private/Language/locallang_db.xlf b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Resources/Private/Language/locallang_db.xlf index 39e9964776ad..ee401cfdab1b 100644 --- a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Resources/Private/Language/locallang_db.xlf +++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Resources/Private/Language/locallang_db.xlf @@ -111,6 +111,9 @@ <trans-unit id="tx_blogexample_domain_model_comment.content" resname="tx_blogexample_domain_model_comment.content"> <source>Content</source> </trans-unit> + <trans-unit id="tx_blogexample_domain_model_registryentry.name" resname="tx_blogexample_domain_model_registryentry.name"> + <source>Registry Entry Key</source> + </trans-unit> <trans-unit id="tx_blogexample_domain_model_tag" resname="tx_blogexample_domain_model_tag"> <source>Tag</source> </trans-unit> diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/ext_tables.sql b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/ext_tables.sql index f105a7eb0c58..6af59f31c715 100644 --- a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/ext_tables.sql +++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/ext_tables.sql @@ -53,6 +53,13 @@ CREATE TABLE tx_blogexample_domain_model_person ( tags_special int(11) unsigned DEFAULT '0' NOT NULL ); +# +# Table structure for table 'tx_blogexample_domain_model_registryentry' +# +CREATE TABLE tx_blogexample_domain_model_registryentry ( + name varchar(255) DEFAULT '' NOT NULL +); + # # Table structure for table 'tx_blogexample_domain_model_tag' # diff --git a/typo3/sysext/extbase/Tests/Functional/Persistence/Generic/Storage/Typo3DbQueryParserTest.php b/typo3/sysext/extbase/Tests/Functional/Persistence/Generic/Storage/Typo3DbQueryParserTest.php index cf5d6a1f05c9..8cc41b332748 100644 --- a/typo3/sysext/extbase/Tests/Functional/Persistence/Generic/Storage/Typo3DbQueryParserTest.php +++ b/typo3/sysext/extbase/Tests/Functional/Persistence/Generic/Storage/Typo3DbQueryParserTest.php @@ -36,6 +36,7 @@ use TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings; use TYPO3\CMS\Extbase\Persistence\QueryInterface; use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase; use TYPO3Tests\BlogExample\Domain\Repository\BlogRepository; +use TYPO3Tests\BlogExample\Domain\Repository\RegistryEntryRepository; final class Typo3DbQueryParserTest extends FunctionalTestCase { @@ -799,4 +800,25 @@ final class Typo3DbQueryParserTest extends FunctionalTestCase $compositeExpression = $queryBuilder->getQueryPart('where'); self::assertMatchesRegularExpression($expectedSql, (string)$compositeExpression); } + + /** + * @test + */ + public function tcaWithoutCtrlCreatesAValidSQLStatement(): void + { + $GLOBALS['TYPO3_REQUEST'] = (new ServerRequest()) + ->withAttribute('applicationType', SystemEnvironmentBuilder::REQUESTTYPE_BE); + $registryEntryRepository = $this->get(RegistryEntryRepository::class); + $querySettings = new Typo3QuerySettings(new Context(), $this->get(ConfigurationManagerInterface::class)); + + $query = $registryEntryRepository->createQuery(); + $query->setQuerySettings($querySettings); + + $typo3DbQueryParser = $this->get(Typo3DbQueryParser::class); + $queryBuilder = $typo3DbQueryParser->convertQueryToDoctrineQueryBuilder($query); + + $compositeExpression = $queryBuilder->getQueryPart('where'); + self::assertStringNotContainsString('hidden', (string)$compositeExpression); + self::assertStringNotContainsString('deleted', (string)$compositeExpression); + } } -- GitLab