From 2de97778ea5f1675ee2ccfa1da94167a4f6d08c5 Mon Sep 17 00:00:00 2001 From: Frank Naegler <frank.naegler@typo3.org> Date: Thu, 29 Jul 2021 12:34:50 +0200 Subject: [PATCH] [FEATURE] Access site configuration in foreign_table_where This patch adds the possibility to access site configuration with a placeholder in TCA's `foreign_table_where` query. To access a configuration value the following syntax is available: * ###SITE:rootPageId### * ###SITE:foo.bar.baz### (array path notation) Resolves: #94662 Releases: master Change-Id: I2047085d20c914960bbcd88df5b4c7a7f8d53282 Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/70126 Tested-by: core-ci <typo3@b13.com> Tested-by: Andreas Fernandez <a.fernandez@scripting-base.de> Tested-by: Benni Mack <benni@typo3.org> Reviewed-by: Andreas Fernandez <a.fernandez@scripting-base.de> Reviewed-by: Wouter Wolters <typo3@wouterwolters.nl> Reviewed-by: Benni Mack <benni@typo3.org> --- .../FormDataProvider/AbstractItemProvider.php | 56 ++++++++++++++++++ .../FormDataProvider/TcaSelectItemsTest.php | 58 +++++++++++++++++++ ...orSiteConfigurationInForeignTableWhere.rst | 32 ++++++++++ 3 files changed, 146 insertions(+) create mode 100644 typo3/sysext/core/Documentation/Changelog/master/Feature-94662-AddPlaceholderForSiteConfigurationInForeignTableWhere.rst diff --git a/typo3/sysext/backend/Classes/Form/FormDataProvider/AbstractItemProvider.php b/typo3/sysext/backend/Classes/Form/FormDataProvider/AbstractItemProvider.php index dff5629905ad..ffd50efafaf4 100644 --- a/typo3/sysext/backend/Classes/Form/FormDataProvider/AbstractItemProvider.php +++ b/typo3/sysext/backend/Classes/Form/FormDataProvider/AbstractItemProvider.php @@ -19,12 +19,14 @@ use Doctrine\DBAL\Exception as DBALException; use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; use TYPO3\CMS\Core\Configuration\Features; +use TYPO3\CMS\Core\Database\Connection; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Database\Query\QueryBuilder; use TYPO3\CMS\Core\Database\Query\QueryHelper; use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction; use TYPO3\CMS\Core\Database\Query\Restriction\WorkspaceRestriction; use TYPO3\CMS\Core\Database\RelationHandler; +use TYPO3\CMS\Core\Exception\SiteNotFoundException; use TYPO3\CMS\Core\Hooks\TcaItemsProcessorFunctions; use TYPO3\CMS\Core\Imaging\IconFactory; use TYPO3\CMS\Core\Localization\LanguageService; @@ -33,8 +35,11 @@ use TYPO3\CMS\Core\Messaging\FlashMessageQueue; use TYPO3\CMS\Core\Messaging\FlashMessageService; use TYPO3\CMS\Core\Resource\FileRepository; use TYPO3\CMS\Core\Resource\ResourceStorage; +use TYPO3\CMS\Core\Site\Entity\Site; +use TYPO3\CMS\Core\Site\SiteFinder; use TYPO3\CMS\Core\Type\Bitmask\Permission; use TYPO3\CMS\Core\Utility\ArrayUtility; +use TYPO3\CMS\Core\Utility\Exception\MissingArrayPathException; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\MathUtility; use TYPO3\CMS\Core\Versioning\VersionState; @@ -836,6 +841,8 @@ abstract class AbstractItemProvider ], $foreignTableClause ); + + $foreignTableClause = $this->parseSiteConfiguration($connection, $siteRootUid, $foreignTableClause); } // Split the clause into an array with keys WHERE, GROUPBY, ORDERBY, LIMIT @@ -871,6 +878,55 @@ abstract class AbstractItemProvider return $foreignTableClauseArray; } + protected function parseSiteConfiguration(Connection $connection, int $siteRootUid, string $foreignTableClause): string + { + if ($siteRootUid === 0) { + return $foreignTableClause; + } + + $siteClausesRegEx = '/###SITE:([^#]+)###/m'; + preg_match_all($siteClausesRegEx, $foreignTableClause, $matches, PREG_SET_ORDER); + + if (empty($matches)) { + return $foreignTableClause; + } + + try { + $site = GeneralUtility::makeInstance(SiteFinder::class)->getSiteByRootPageId($siteRootUid); + $replacements = []; + $configuration = $site->getConfiguration(); + array_walk($matches, static function ($match) use ($connection, &$replacements, &$configuration) { + $key = $match[1]; + try { + $value = ArrayUtility::getValueByPath($configuration, $key, '.'); + } catch (MissingArrayPathException $exception) { + $value = ''; + } + + if (is_string($value)) { + $value = $connection->quote($value); + } elseif (is_array($value)) { + $value = implode(',', array_map(static function ($item) use ($connection) { + return $connection->quote($item); + }, $value)); + } elseif (is_bool($value)) { + $value = (int)$value; + } + + $replacements[$match[0]] = $value; + }); + $foreignTableClause = str_replace( + array_keys($replacements), + array_values($replacements), + $foreignTableClause + ); + } catch (SiteNotFoundException $exception) { + // No site found, means also no site marker to replace + return $foreignTableClause; + } + return $foreignTableClause; + } + /** * Convert the current database values into an array * diff --git a/typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/TcaSelectItemsTest.php b/typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/TcaSelectItemsTest.php index 3ff837d7434b..82c12baf7bf3 100644 --- a/typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/TcaSelectItemsTest.php +++ b/typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/TcaSelectItemsTest.php @@ -38,6 +38,8 @@ use TYPO3\CMS\Core\Messaging\FlashMessageQueue; use TYPO3\CMS\Core\Messaging\FlashMessageService; use TYPO3\CMS\Core\Resource\FileRepository; use TYPO3\CMS\Core\Resource\ResourceStorage; +use TYPO3\CMS\Core\Site\Entity\Site; +use TYPO3\CMS\Core\Site\SiteFinder; use TYPO3\CMS\Core\Utility\ArrayUtility; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\StringUtility; @@ -903,6 +905,52 @@ class TcaSelectItemsTest extends UnitTestCase ], ], ], + 'replace SITE:rootPageId' => [ + 'AND fTable.uid = ###SITE:rootPageId###', + [ + ['fTable.uid = 1'], + [' 1=1'], + ['`pages.uid` = `fTable.pid`'], + ], + [] + ], + 'replace SITE:mySetting.foobar' => [ + 'AND fTable.foo = ###SITE:mySetting.foobar###', + [ + ['fTable.foo = 4711'], + [' 1=1'], + ['`pages.uid` = `fTable.pid`'], + ], + [] + ], + 'replace SITE:mySetting.doesNotExist' => [ + 'AND fTable.foo = ###SITE:mySetting.doesNotExist###', + [ + ['fTable.foo = \'\''], + [' 1=1'], + ['`pages.uid` = `fTable.pid`'], + ], + [] + ], + 'replace replace SITE:rootPageId, SITE:mySetting.foobar and PAGE_TSCONFIG_IDLIST' => [ + 'AND fTable.uid = ###SITE:rootPageId### AND fTable.foo = ###SITE:mySetting.foobar### AND fTable.bar IN (###PAGE_TSCONFIG_IDLIST###)', + [ + ['fTable.uid = 1 AND fTable.foo = 4711 AND fTable.bar IN (471,481)'], + [' 1=1'], + ['`pages.uid` = `fTable.pid`'], + ], + [ + 'pageTsConfig' => [ + 'TCEFORM.' => [ + 'aTable.' => [ + 'aField.' => [ + 'PAGE_TSCONFIG_IDLIST' => 'a, 471, b, 481, c', + ], + ], + ], + ], + ], + ], ]; } @@ -952,6 +1000,16 @@ class TcaSelectItemsTest extends UnitTestCase $GLOBALS['TCA']['fTable'] = []; + $siteProphecy = $this->prophesize(Site::class); + $siteProphecy->getConfiguration()->willReturn([ + 'rootPageId' => 1, + 'mySetting' => [ + 'foobar' => 4711, + ], + ]); + $siteFinderProphecy = $this->prophesize(SiteFinder::class); + $siteFinderProphecy->getSiteByRootPageId(Argument::any())->willReturn($siteProphecy->reveal()); + GeneralUtility::addInstance(SiteFinder::class, $siteFinderProphecy->reveal()); $fileRepositoryProphecy = $this->prophesize(FileRepository::class); $fileRepositoryProphecy->findByRelation(Argument::cetera())->shouldNotBeCalled(); GeneralUtility::setSingletonInstance(FileRepository::class, $fileRepositoryProphecy->reveal()); diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-94662-AddPlaceholderForSiteConfigurationInForeignTableWhere.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-94662-AddPlaceholderForSiteConfigurationInForeignTableWhere.rst new file mode 100644 index 000000000000..928559fe48b8 --- /dev/null +++ b/typo3/sysext/core/Documentation/Changelog/master/Feature-94662-AddPlaceholderForSiteConfigurationInForeignTableWhere.rst @@ -0,0 +1,32 @@ +.. include:: ../../Includes.txt + +=============================================================================== +Feature: #94662 - Add placeholder for site configuration in foreign_table_where +=============================================================================== + +See :issue:`94662` + +Description +=========== + +The `foreign_table_where` setting in TCA allows some old marker-based +placeholder to customize the query. The best place to define site-dependent +settings is the site configuration, which now can be used within +`foreign_table_where`. + +To access a configuration value the following syntax is available: + +* `###SITE:<KEY>###` - <KEY> is your setting name from site config e.g. `###SITE:rootPageId###` +* `###SITE:<KEY>.<SUBKEY>###` - an array path notation is possible. e.g. `###SITE:mySetting.categoryPid###` + +Example: +-------- +.. code-block:: php + + ... + 'fieldConfiguration' => [ + 'foreign_table_where' => ' AND ({#sys_category}.uid = ###SITE:rootPageId### OR {#sys_category}.pid = ###SITE:mySetting.categoryPid###) ORDER BY sys_category.title ASC', + ], + ... + +.. index:: Backend, FlexForm, TCA, NotScanned, ext:backend -- GitLab