From 36e461d18deb4cb21cc60e679717bf3c559191cb Mon Sep 17 00:00:00 2001 From: Morton Jonuschat <m.jonuschat@mojocode.de> Date: Fri, 30 Dec 2016 14:33:58 -0800 Subject: [PATCH] [TASK] Deprecate BackendUtility::getRecordsByField Deprecate BackendUtility::getRecordsByField() as it has a flawed design due to passing SQL fragments. This contradicts the goal of using named parameters for all queries in the core and requires passing the original QueryBuilder object in addition to the stringified constraint. Replace all calls to the method with direct usage of the QueryBuilder and deprecate the method. Resolves: #79122 Releases: master Change-Id: I8b040b98e20271aff84ef16fb89b59a406d54003 Reviewed-on: https://review.typo3.org/51078 Tested-by: TYPO3com <no-reply@typo3.com> Reviewed-by: Mona Muzaffar <mona.muzaffar@gmx.de> Tested-by: Mona Muzaffar <mona.muzaffar@gmx.de> Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch> Tested-by: Christian Kuhn <lolli@schwarzbu.ch> --- .../Classes/Controller/BackendController.php | 31 ++- .../Localization/LocalizationRepository.php | 36 +++- .../Classes/Utility/BackendUtility.php | 95 +++++---- .../backend/Classes/View/PageLayoutView.php | 70 ++++--- .../Functional/Utility/BackendUtilityTest.php | 48 +++++ .../Utility/Fixtures/sys_domain.xml | 21 ++ .../BackendUserAuthentication.php | 34 +++- .../core/Classes/DataHandling/DataHandler.php | 164 +++++++++++++--- .../Classes/Database/Query/QueryBuilder.php | 4 +- .../Classes/Database/SoftReferenceIndex.php | 22 ++- ...precateBackendUtilitygetRecordsByField.rst | 32 +++ .../Classes/LinkHandler/PageLinkHandler.php | 24 ++- .../Classes/Domain/Model/DeletedRecords.php | 183 ++++++++++-------- .../Classes/Service/WorkspaceService.php | 34 +++- 14 files changed, 590 insertions(+), 208 deletions(-) create mode 100644 typo3/sysext/backend/Tests/Functional/Utility/BackendUtilityTest.php create mode 100644 typo3/sysext/backend/Tests/Functional/Utility/Fixtures/sys_domain.xml create mode 100644 typo3/sysext/core/Documentation/Changelog/master/Deprecation-79122-DeprecateBackendUtilitygetRecordsByField.rst diff --git a/typo3/sysext/backend/Classes/Controller/BackendController.php b/typo3/sysext/backend/Classes/Controller/BackendController.php index 40d76e2350b9..bff93714611c 100644 --- a/typo3/sysext/backend/Classes/Controller/BackendController.php +++ b/typo3/sysext/backend/Classes/Controller/BackendController.php @@ -20,8 +20,12 @@ use TYPO3\CMS\Backend\Domain\Repository\Module\BackendModuleRepository; use TYPO3\CMS\Backend\Module\ModuleLoader; use TYPO3\CMS\Backend\Toolbar\ToolbarItemInterface; use TYPO3\CMS\Backend\Utility\BackendUtility; +use TYPO3\CMS\Core\Database\ConnectionPool; +use TYPO3\CMS\Core\Database\Query\Restriction\BackendWorkspaceRestriction; +use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction; use TYPO3\CMS\Core\Imaging\IconFactory; use TYPO3\CMS\Core\Page\PageRenderer; +use TYPO3\CMS\Core\Type\Bitmask\Permission; use TYPO3\CMS\Core\Type\File\ImageInfo; use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -720,16 +724,35 @@ class BackendController $beUser = $this->getBackendUser(); // EDIT page: $editId = preg_replace('/[^[:alnum:]_]/', '', GeneralUtility::_GET('edit')); - $editRecord = ''; if ($editId) { // Looking up the page to edit, checking permissions: $where = ' AND (' . $beUser->getPagePermsClause(2) . ' OR ' . $beUser->getPagePermsClause(16) . ')'; if (MathUtility::canBeInterpretedAsInteger($editId)) { $editRecord = BackendUtility::getRecordWSOL('pages', $editId, '*', $where); } else { - $records = BackendUtility::getRecordsByField('pages', 'alias', $editId, $where); - if (is_array($records)) { - $editRecord = reset($records); + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages'); + $queryBuilder->getRestrictions() + ->removeAll() + ->add(GeneralUtility::makeInstance(DeletedRestriction::class)) + ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class)); + + $editRecord = $queryBuilder->select('*') + ->from('pages') + ->where( + $queryBuilder->expr()->eq( + 'alias', + $queryBuilder->createNamedParameter($editId, \PDO::PARAM_STR) + ), + $queryBuilder->expr()->orX( + $beUser->getPagePermsClause(Permission::PAGE_EDIT), + $beUser->getPagePermsClause(Permission::CONTENT_EDIT) + ) + ) + ->setMaxResults(1) + ->execute() + ->fetch(); + + if ($editRecord !== false) { BackendUtility::workspaceOL('pages', $editRecord); } } diff --git a/typo3/sysext/backend/Classes/Domain/Repository/Localization/LocalizationRepository.php b/typo3/sysext/backend/Classes/Domain/Repository/Localization/LocalizationRepository.php index 10b16e762db4..dba66f8c4641 100644 --- a/typo3/sysext/backend/Classes/Domain/Repository/Localization/LocalizationRepository.php +++ b/typo3/sysext/backend/Classes/Domain/Repository/Localization/LocalizationRepository.php @@ -18,6 +18,7 @@ use TYPO3\CMS\Backend\Utility\BackendUtility; 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\BackendWorkspaceRestriction; use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -330,15 +331,32 @@ class LocalizationRepository $tcaCtrl = $GLOBALS['TCA'][$table]['ctrl']; if (isset($tcaCtrl['origUid'])) { - $recordLocalization = BackendUtility::getRecordsByField( - $table, - $tcaCtrl['origUid'], - $uid, - 'AND ' . $tcaCtrl['languageField'] . '=' . (int)$language . ($andWhereClause ? ' ' . $andWhereClause : ''), - '', - '', - '1' - ); + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable($table); + $queryBuilder->getRestrictions() + ->removeAll() + ->add(GeneralUtility::makeInstance(DeletedRestriction::class)) + ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class)); + + $queryBuilder->select('*') + ->from($table) + ->where( + $queryBuilder->expr()->eq( + $tcaCtrl['origUid'], + $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT) + ), + $queryBuilder->expr()->eq( + $tcaCtrl['languageField'], + $queryBuilder->createNamedParameter((int)$language, \PDO::PARAM_INT) + ) + ) + ->setMaxResults(1); + + if ($andWhereClause) { + $queryBuilder->andWhere(QueryHelper::stripLogicalOperatorPrefix($andWhereClause)); + } + + $recordLocalization = $queryBuilder->execute()->fetchAll(); } } return $recordLocalization; diff --git a/typo3/sysext/backend/Classes/Utility/BackendUtility.php b/typo3/sysext/backend/Classes/Utility/BackendUtility.php index 3d30e6867e3f..7308eda5aec6 100644 --- a/typo3/sysext/backend/Classes/Utility/BackendUtility.php +++ b/typo3/sysext/backend/Classes/Utility/BackendUtility.php @@ -221,6 +221,7 @@ class BackendUtility * @param bool $useDeleteClause Use the deleteClause to check if a record is deleted (default TRUE) * @param null|QueryBuilder $queryBuilder The queryBuilder must be provided, if the parameter $whereClause is given and the concept of prepared statement was used. Example within self::firstDomainRecord() * @return mixed Multidimensional array with selected records (if any is selected) + * @deprecated since TYPO3 v8, will be removed in TYPO3 v9 */ public static function getRecordsByField( $theTable, @@ -233,6 +234,7 @@ class BackendUtility $useDeleteClause = true, $queryBuilder = null ) { + GeneralUtility::logDeprecatedFunction(); if (is_array($GLOBALS['TCA'][$theTable])) { if (null === $queryBuilder) { $queryBuilder = static::getQueryBuilderForTable($theTable); @@ -394,27 +396,32 @@ class BackendUtility $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) ->getQueryBuilderForTable($table); + $queryBuilder->getRestrictions() + ->removeAll() + ->add(GeneralUtility::makeInstance(DeletedRestriction::class)) + ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class)); - $constraint = $queryBuilder->expr()->andX( - $queryBuilder->expr()->eq( - $tcaCtrl['languageField'], - $queryBuilder->createNamedParameter($language, \PDO::PARAM_INT) - ), - QueryHelper::stripLogicalOperatorPrefix($andWhereClause) - ); + $queryBuilder->select('*') + ->from($table) + ->where( + $queryBuilder->expr()->eq( + $tcaCtrl['transOrigPointerField'], + $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT) + ), + $queryBuilder->expr()->eq( + $tcaCtrl['languageField'], + $queryBuilder->createNamedParameter((int)$language, \PDO::PARAM_INT) + ) + ) + ->setMaxResults(1); - $recordLocalization = self::getRecordsByField( - $table, - $tcaCtrl['transOrigPointerField'], - $uid, - (string)$constraint, - '', - '', - 1, - true, - $queryBuilder - ); + if ($andWhereClause) { + $queryBuilder->andWhere(QueryHelper::stripLogicalOperatorPrefix($andWhereClause)); + } + + $recordLocalization = $queryBuilder->execute()->fetchAll(); } + return $recordLocalization; } @@ -3970,31 +3977,37 @@ class BackendUtility public static function firstDomainRecord($rootLine) { $queryBuilder = static::getQueryBuilderForTable('sys_domain'); - $constraint = $queryBuilder->expr()->andX( - $queryBuilder->expr()->eq( - 'redirectTo', - $queryBuilder->createNamedParameter('', \PDO::PARAM_STR) - ), - $queryBuilder->expr()->eq( - 'hidden', - $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT) + $queryBuilder->getRestrictions() + ->removeAll() + ->add(GeneralUtility::makeInstance(DeletedRestriction::class)) + ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class)); + + $queryBuilder->select('domainName') + ->from('sys_domain') + ->where( + $queryBuilder->expr()->eq( + 'pid', + $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT, ':pid') + ), + $queryBuilder->expr()->eq( + 'redirectTo', + $queryBuilder->createNamedParameter('', \PDO::PARAM_STR) + ), + $queryBuilder->expr()->eq( + 'hidden', + $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT) + ) ) - ); + ->setMaxResults(1) + ->orderBy('sorting'); + foreach ($rootLine as $row) { - $dRec = self::getRecordsByField( - 'sys_domain', - 'pid', - $row['uid'], - (string)$constraint, - '', - 'sorting', - '', - true, - $queryBuilder - ); - if (is_array($dRec)) { - $dRecord = reset($dRec); - return rtrim($dRecord['domainName'], '/'); + $domainName = $queryBuilder->setParameter('pid', $row['uid'], \PDO::PARAM_INT) + ->execute() + ->fetchColumn(0); + + if ($domainName) { + return rtrim($domainName, '/'); } } return null; diff --git a/typo3/sysext/backend/Classes/View/PageLayoutView.php b/typo3/sysext/backend/Classes/View/PageLayoutView.php index 09951be563a1..19fd03f16ce1 100644 --- a/typo3/sysext/backend/Classes/View/PageLayoutView.php +++ b/typo3/sysext/backend/Classes/View/PageLayoutView.php @@ -453,28 +453,32 @@ class PageLayoutView extends \TYPO3\CMS\Recordlist\RecordList\AbstractDatabaseRe $userCanEditPage = $this->getBackendUser()->check('tables_modify', 'pages_language_overlay'); } if ($userCanEditPage) { - $languageOverlayId = 0; $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) - ->getConnectionForTable('pages_language_overlay') - ->createQueryBuilder(); - $constraint = $queryBuilder->expr()->eq( - 'sys_language_uid', - $queryBuilder->createNamedParameter($this->tt_contentConfig['sys_language_uid'], \PDO::PARAM_INT) - ); - $pageOverlayRecord = BackendUtility::getRecordsByField( - 'pages_language_overlay', - 'pid', - (int)$this->id, - $constraint, - '', - '', - '', - true, - $queryBuilder - ); - if (!empty($pageOverlayRecord[0]['uid'])) { - $languageOverlayId = $pageOverlayRecord[0]['uid']; - } + ->getQueryBuilderForTable('pages_language_overlay'); + $queryBuilder->getRestrictions() + ->removeAll() + ->add(GeneralUtility::makeInstance(DeletedRestriction::class)) + ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class)); + + $queryBuilder->select('uid') + ->from('pages_language_overlay') + ->where( + $queryBuilder->expr()->eq( + 'pid', + $queryBuilder->createNamedParameter((int)$this->id, \PDO::PARAM_INT) + ), + $queryBuilder->expr()->eq( + 'sys_language_uid', + $queryBuilder->createNamedParameter( + $this->tt_contentConfig['sys_language_uid'], + \PDO::PARAM_INT + ) + ) + ) + ->setMaxResults(1); + + $languageOverlayId = (int)$queryBuilder->execute()->fetchColumn(0); + $pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/PageActions', 'function(PageActions) { PageActions.setPageId(' . (int)$this->id . '); PageActions.setLanguageOverlayId(' . $languageOverlayId . '); @@ -853,7 +857,29 @@ class PageLayoutView extends \TYPO3\CMS\Recordlist\RecordList\AbstractDatabaseRe } // Language overlay page header: if ($lP) { - list($lpRecord) = BackendUtility::getRecordsByField('pages_language_overlay', 'pid', $id, 'AND sys_language_uid=' . $lP); + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable('pages_language_overlay'); + $queryBuilder->getRestrictions() + ->removeAll() + ->add(GeneralUtility::makeInstance(DeletedRestriction::class)) + ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class)); + + $lpRecord = $queryBuilder->select('*') + ->from('pages_language_overlay') + ->where( + $queryBuilder->expr()->eq( + 'pid', + $queryBuilder->createNamedParameter($id, \PDO::PARAM_INT) + ), + $queryBuilder->expr()->eq( + 'sys_language_uid', + $queryBuilder->createNamedParameter($lP, \PDO::PARAM_INT) + ) + ) + ->setMaxResults(1) + ->execute() + ->fetch(); + BackendUtility::workspaceOL('pages_language_overlay', $lpRecord); $recordIcon = BackendUtility::wrapClickMenuOnIcon( $this->iconFactory->getIconForRecord('pages_language_overlay', $lpRecord, Icon::SIZE_SMALL)->render(), diff --git a/typo3/sysext/backend/Tests/Functional/Utility/BackendUtilityTest.php b/typo3/sysext/backend/Tests/Functional/Utility/BackendUtilityTest.php new file mode 100644 index 000000000000..6d0b903dc1cf --- /dev/null +++ b/typo3/sysext/backend/Tests/Functional/Utility/BackendUtilityTest.php @@ -0,0 +1,48 @@ +<?php +namespace TYPO3\CMS\Backend\Tests\Functional\Controller\Page; + +/* + * 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! + */ + +use TYPO3\CMS\Backend\Utility\BackendUtility; +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Core\Utility\RootlineUtility; + +/** + * Test case for TYPO3\CMS\Backend\Controller\Page\LocalizationController + */ +class BackendUtilityTest extends \TYPO3\TestingFramework\Core\Functional\FunctionalTestCase +{ + /** + * Sets up this test case. + * + * @return void + */ + protected function setUp() + { + parent::setUp(); + + $this->importDataSet(ORIGINAL_ROOT . 'components/testing_framework/Resources/Core/Functional/Fixtures/pages.xml'); + $this->importDataSet(ORIGINAL_ROOT . 'typo3/sysext/backend/Tests/Functional/Utility/Fixtures/sys_domain.xml'); + } + + /** + * @test + */ + public function determineFirstDomainRecord() + { + $rootLineUtility = GeneralUtility::makeInstance(RootlineUtility::class, 4); + $rootLine = $rootLineUtility->get(); + $this->assertEquals('example.com', BackendUtility::firstDomainRecord($rootLine)); + } +} diff --git a/typo3/sysext/backend/Tests/Functional/Utility/Fixtures/sys_domain.xml b/typo3/sysext/backend/Tests/Functional/Utility/Fixtures/sys_domain.xml new file mode 100644 index 000000000000..ba49fcfd0ab2 --- /dev/null +++ b/typo3/sysext/backend/Tests/Functional/Utility/Fixtures/sys_domain.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<dataset> + <sys_domain> + <uid>1</uid> + <pid>1</pid> + <tstamp>1487563944</tstamp> + <hidden>0</hidden> + <domainName>example.com</domainName> + <forced>1</forced> + </sys_domain> + <sys_domain> + <uid>2</uid> + <pid>7</pid> + <tstamp>1487563945</tstamp> + <hidden>0</hidden> + <domainName>www.example.net</domainName> + <redirectTo>http://example.com/</redirectTo> + <redirectHttpStatusCode>301</redirectHttpStatusCode> + <forced>0</forced> + </sys_domain> +</dataset> diff --git a/typo3/sysext/core/Classes/Authentication/BackendUserAuthentication.php b/typo3/sysext/core/Classes/Authentication/BackendUserAuthentication.php index b7c888764b59..b977620ae09a 100644 --- a/typo3/sysext/core/Classes/Authentication/BackendUserAuthentication.php +++ b/typo3/sysext/core/Classes/Authentication/BackendUserAuthentication.php @@ -19,6 +19,7 @@ use TYPO3\CMS\Core\Database\Connection; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder; use TYPO3\CMS\Core\Database\Query\QueryHelper; +use TYPO3\CMS\Core\Database\Query\Restriction\BackendWorkspaceRestriction; use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction; use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction; use TYPO3\CMS\Core\Database\Query\Restriction\RootLevelRestriction; @@ -701,15 +702,30 @@ class BackendUserAuthentication extends \TYPO3\CMS\Core\Authentication\AbstractU $pointerField = $GLOBALS['TCA'][$l10nTable]['ctrl']['transOrigPointerField']; $pointerValue = $record[$pointerField] > 0 ? $record[$pointerField] : $record['uid']; } - $recordLocalizations = BackendUtility::getRecordsByField($l10nTable, $pointerField, $pointerValue, '', '', '', '1'); - if (is_array($recordLocalizations)) { - foreach ($recordLocalizations as $localization) { - $recordLocalizationAccess = $recordLocalizationAccess - && $this->checkLanguageAccess($localization[$GLOBALS['TCA'][$l10nTable]['ctrl']['languageField']]); - if (!$recordLocalizationAccess) { - break; - } - } + + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($l10nTable); + $queryBuilder->getRestrictions() + ->removeAll() + ->add(GeneralUtility::makeInstance(DeletedRestriction::class)) + ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class)); + + $recordLocalization = $queryBuilder->select('*') + ->from($l10nTable) + ->where( + $queryBuilder->expr()->eq( + $pointerField, + $queryBuilder->createNamedParameter($pointerValue, \PDO::PARAM_INT) + ) + ) + ->setMaxResults(1) + ->execute() + ->fetch(); + + if (is_array($recordLocalization)) { + $languageAccess = $this->checkLanguageAccess( + $recordLocalization[$GLOBALS['TCA'][$l10nTable]['ctrl']['languageField']] + ); + $recordLocalizationAccess = $recordLocalizationAccess && $languageAccess; } } return $recordLocalizationAccess; diff --git a/typo3/sysext/core/Classes/DataHandling/DataHandler.php b/typo3/sysext/core/Classes/DataHandling/DataHandler.php index 7e2f10283c52..3ae0c18b0788 100644 --- a/typo3/sysext/core/Classes/DataHandling/DataHandler.php +++ b/typo3/sysext/core/Classes/DataHandling/DataHandler.php @@ -27,6 +27,7 @@ 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\BackendWorkspaceRestriction; use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction; use TYPO3\CMS\Core\Database\Query\Restriction\QueryRestrictionContainerInterface; use TYPO3\CMS\Core\Database\ReferenceIndex; @@ -2630,12 +2631,44 @@ class DataHandler public function getRecordsWithSameValue($tableName, $uid, $fieldName, $value, $pageId = 0) { $result = []; - if (!empty($GLOBALS['TCA'][$tableName]['columns'][$fieldName])) { - $uid = (int)$uid; - $pageId = (int)$pageId; - $whereStatement = ' AND uid <> ' . $uid . ' AND ' . ($pageId ? 'pid = ' . $pageId : 'pid >= 0'); - $result = BackendUtility::getRecordsByField($tableName, $fieldName, $value, $whereStatement); + if (empty($GLOBALS['TCA'][$tableName]['columns'][$fieldName])) { + return $result; } + + $uid = (int)$uid; + $pageId = (int)$pageId; + + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($tableName); + $queryBuilder->getRestrictions() + ->removeAll() + ->add(GeneralUtility::makeInstance(DeletedRestriction::class)) + ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class)); + + $queryBuilder->select('*') + ->from($tableName) + ->where( + $queryBuilder->expr()->eq( + $fieldName, + $queryBuilder->createNamedParameter($value, \PDO::PARAM_STR) + ), + $queryBuilder->expr()->neq( + 'uid', + $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT) + ) + ); + + if ($pageId) { + $queryBuilder->andWhere( + $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($pageId, \PDO::PARAM_INT)) + ); + } else { + $queryBuilder->andWhere( + $queryBuilder->expr()->gte('pid', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)) + ); + } + + $result = $queryBuilder->execute()->fetchAll(); + return $result; } @@ -4112,20 +4145,38 @@ class DataHandler if (!BackendUtility::isTableLocalizable($table) || $table === 'pages' || $table === 'pages_language_overlay') { return; } - $where = ''; + + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table); + $queryBuilder->getRestrictions() + ->removeAll() + ->add(GeneralUtility::makeInstance(DeletedRestriction::class)) + ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class)); + + $queryBuilder->select('*') + ->from($table) + ->where( + $queryBuilder->expr()->eq( + $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'], + $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT, ':pointer') + ) + ); + if (isset($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) && $GLOBALS['TCA'][$table]['ctrl']['versioningWS']) { - $where = ' AND t3ver_oid=0'; + $queryBuilder->andWhere( + $queryBuilder->expr()->eq('t3ver_oid', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)) + ); } // If $destPid is < 0, get the pid of the record with uid equal to abs($destPid) $tscPID = BackendUtility::getTSconfig_pidValue($table, $uid, $destPid); // Get the localized records to be copied - $l10nRecords = BackendUtility::getRecordsByField($table, $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'], $uid, $where); + $l10nRecords = $queryBuilder->execute()->fetchAll(); if (is_array($l10nRecords)) { $localizedDestPids = []; // If $destPid < 0, then it is the uid of the original language record we are inserting after if ($destPid < 0) { // Get the localized records of the record we are inserting after - $destL10nRecords = BackendUtility::getRecordsByField($table, $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'], abs($destPid), $where); + $queryBuilder->setParameter('pointer', abs($destPid), \PDO::PARAM_INT); + $destL10nRecords = $queryBuilder->execute()->fetchAll(); // Index the localized record uids by language if (is_array($destL10nRecords)) { foreach ($destL10nRecords as $record) { @@ -4512,17 +4563,36 @@ class DataHandler if (!BackendUtility::isTableLocalizable($table) || $table === 'pages' || $table === 'pages_language_overlay') { return; } - $where = ''; + + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table); + $queryBuilder->getRestrictions() + ->removeAll() + ->add(GeneralUtility::makeInstance(DeletedRestriction::class)) + ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class)); + + $queryBuilder->select('*') + ->from($table) + ->where( + $queryBuilder->expr()->eq( + $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'], + $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT, ':pointer') + ) + ); + if (isset($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) && $GLOBALS['TCA'][$table]['ctrl']['versioningWS']) { - $where = ' AND t3ver_oid=0'; + $queryBuilder->andWhere( + $queryBuilder->expr()->eq('t3ver_oid', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)) + ); } - $l10nRecords = BackendUtility::getRecordsByField($table, $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'], $uid, $where); + + $l10nRecords = $queryBuilder->execute()->fetchAll(); if (is_array($l10nRecords)) { $localizedDestPids = []; // If $$originalRecordDestinationPid < 0, then it is the uid of the original language record we are inserting after if ($originalRecordDestinationPid < 0) { // Get the localized records of the record we are inserting after - $destL10nRecords = BackendUtility::getRecordsByField($table, $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'], abs($originalRecordDestinationPid), $where); + $queryBuilder->setParameter('pointer', abs($originalRecordDestinationPid), \PDO::PARAM_INT); + $destL10nRecords = $queryBuilder->execute()->fetchAll(); // Index the localized record uids by language if (is_array($destL10nRecords)) { foreach ($destL10nRecords as $record) { @@ -4620,10 +4690,32 @@ class DataHandler } if ($table === 'pages') { - $pass = !BackendUtility::getRecordsByField('pages_language_overlay', 'pid', $uid, (' AND ' . $GLOBALS['TCA']['pages_language_overlay']['ctrl']['languageField'] . '=' . (int)$langRec['uid'])); + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable('pages_language_overlay'); + $queryBuilder->getRestrictions() + ->removeAll() + ->add(GeneralUtility::makeInstance(DeletedRestriction::class)) + ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class)); + + $recordCount = $queryBuilder->count('*') + ->from('pages_language_overlay') + ->where( + $queryBuilder->expr()->eq( + 'pid', + $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT) + ), + $queryBuilder->expr()->eq( + $GLOBALS['TCA']['pages_language_overlay']['ctrl']['languageField'], + $queryBuilder->createNamedParameter((int)$langRec['uid'], \PDO::PARAM_INT) + ) + ) + ->execute() + ->fetchColumn(0); + + $pass = !$recordCount; $Ttable = 'pages_language_overlay'; } else { - $pass = !BackendUtility::getRecordLocalization($table, $uid, $langRec['uid'], ('AND pid=' . (int)$row['pid'])); + $pass = !BackendUtility::getRecordLocalization($table, $uid, $langRec['uid'], 'AND pid=' . (int)$row['pid']); $Ttable = $table; } @@ -5472,23 +5564,39 @@ class DataHandler if (!BackendUtility::isTableLocalizable($table) || $table === 'pages' || $table === 'pages_language_overlay') { return; } - $where = ''; + + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table); + $queryBuilder->getRestrictions() + ->removeAll() + ->add(GeneralUtility::makeInstance(DeletedRestriction::class)) + ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class)); + + $queryBuilder->select('*') + ->from($table) + ->where( + $queryBuilder->expr()->eq( + $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'], + $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT) + ) + ); + if (isset($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) && $GLOBALS['TCA'][$table]['ctrl']['versioningWS']) { - $where = ' AND t3ver_oid=0'; + $queryBuilder->andWhere( + $queryBuilder->expr()->eq('t3ver_oid', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)) + ); } - $l10nRecords = BackendUtility::getRecordsByField($table, $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'], $uid, $where); - if (is_array($l10nRecords)) { - foreach ($l10nRecords as $record) { - // Ignore workspace delete placeholders. Those records have been marked for - // deletion before - deleting them again in a workspace would revert that state. - if ($this->BE_USER->workspace > 0 && BackendUtility::isTableWorkspaceEnabled($table)) { - BackendUtility::workspaceOL($table, $record); - if (VersionState::cast($record['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)) { - continue; - } + + $result = $queryBuilder->execute(); + while ($record = $result->fetch()) { + // Ignore workspace delete placeholders. Those records have been marked for + // deletion before - deleting them again in a workspace would revert that state. + if ($this->BE_USER->workspace > 0 && BackendUtility::isTableWorkspaceEnabled($table)) { + BackendUtility::workspaceOL($table, $record); + if (VersionState::cast($record['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)) { + continue; } - $this->deleteAction($table, (int)$record['t3ver_oid'] > 0 ? (int)$record['t3ver_oid'] : (int)$record['uid']); } + $this->deleteAction($table, (int)$record['t3ver_oid'] > 0 ? (int)$record['t3ver_oid'] : (int)$record['uid']); } } diff --git a/typo3/sysext/core/Classes/Database/Query/QueryBuilder.php b/typo3/sysext/core/Classes/Database/Query/QueryBuilder.php index 5720b42a2ee1..4b2ab3571da0 100644 --- a/typo3/sysext/core/Classes/Database/Query/QueryBuilder.php +++ b/typo3/sysext/core/Classes/Database/Query/QueryBuilder.php @@ -209,11 +209,11 @@ class QueryBuilder * * @param string|int $key The parameter position or name. * @param mixed $value The parameter value. - * @param string|null $type One of the Connection::PARAM_* constants. + * @param int|null $type One of the Connection::PARAM_* constants. * * @return QueryBuilder This QueryBuilder instance. */ - public function setParameter($key, $value, string $type = null): QueryBuilder + public function setParameter($key, $value, int $type = null): QueryBuilder { $this->concreteQueryBuilder->setParameter($key, $value, $type); diff --git a/typo3/sysext/core/Classes/Database/SoftReferenceIndex.php b/typo3/sysext/core/Classes/Database/SoftReferenceIndex.php index b2891282e0c4..98beccd4a498 100644 --- a/typo3/sysext/core/Classes/Database/SoftReferenceIndex.php +++ b/typo3/sysext/core/Classes/Database/SoftReferenceIndex.php @@ -14,6 +14,8 @@ namespace TYPO3\CMS\Core\Database; * The TYPO3 project - inspiring people to share! */ +use TYPO3\CMS\Core\Database\Query\Restriction\BackendWorkspaceRestriction; +use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction; use TYPO3\CMS\Core\LinkHandling\LinkService; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Frontend\Service\TypoLinkCodecService; @@ -616,13 +618,27 @@ class SoftReferenceIndex /** * Look up and return page uid for alias * - * @param int $link_param Page alias string value + * @param string $link_param Page alias string value * @return int Page uid corresponding to alias value. */ public function getPageIdFromAlias($link_param) { - $pRec = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecordsByField('pages', 'alias', $link_param); - return $pRec[0]['uid']; + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages'); + $queryBuilder->getRestrictions() + ->removeAll() + ->add(GeneralUtility::makeInstance(DeletedRestriction::class)) + ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class)); + + $pageUid = $queryBuilder->select('uid') + ->from('pages') + ->where( + $queryBuilder->expr()->eq('alias', $queryBuilder->createNamedParameter($link_param, \PDO::PARAM_STR)) + ) + ->setMaxResults(1) + ->execute() + ->fetchColumn(0); + + return (int)$pageUid; } /** diff --git a/typo3/sysext/core/Documentation/Changelog/master/Deprecation-79122-DeprecateBackendUtilitygetRecordsByField.rst b/typo3/sysext/core/Documentation/Changelog/master/Deprecation-79122-DeprecateBackendUtilitygetRecordsByField.rst new file mode 100644 index 000000000000..07098d0392c9 --- /dev/null +++ b/typo3/sysext/core/Documentation/Changelog/master/Deprecation-79122-DeprecateBackendUtilitygetRecordsByField.rst @@ -0,0 +1,32 @@ +.. include:: ../../Includes.txt + +======================================================== +Deprecation: #79122 - Deprecate method getRecordsByField +======================================================== + +See :issue:`79122` + +Description +=========== + +The method :php:`TYPO3\CMS\Backend\Utility\BackendUtility::getRecordsByField()` has been deprecated and should not be used any longer. + + +Impact +====== + +Calling the deprecated :php:`getRecordsByField()` method will trigger a deprecation log entry. + + +Affected Installations +====================== + +Any installation using the mentioned method :php:`getRecordsByField()`. + + +Migration +========= + +Use the `ConnectionPool` and the `QueryBuilder` classes directly to query the database from your code. + +.. index:: Backend diff --git a/typo3/sysext/recordlist/Classes/LinkHandler/PageLinkHandler.php b/typo3/sysext/recordlist/Classes/LinkHandler/PageLinkHandler.php index 48f2179ae7f3..7f9231cdad0a 100644 --- a/typo3/sysext/recordlist/Classes/LinkHandler/PageLinkHandler.php +++ b/typo3/sysext/recordlist/Classes/LinkHandler/PageLinkHandler.php @@ -62,11 +62,29 @@ class PageLinkHandler extends AbstractLinkHandler implements LinkHandlerInterfac $data = $linkParts['url']; // Checking if the id-parameter is an alias. if (isset($data['pagealias'])) { - $records = BackendUtility::getRecordsByField('pages', 'alias', $data['pagealias']); - if (empty($records)) { + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable('pages'); + $queryBuilder->getRestrictions() + ->removeAll() + ->add(GeneralUtility::makeInstance(DeletedRestriction::class)) + ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class)); + + $pageUid = $queryBuilder->select('uid') + ->from('pages') + ->where( + $queryBuilder->expr()->eq( + 'alias', + $queryBuilder->createNamedParameter($data['pagealias'], \PDO::PARAM_STR) + ) + ) + ->setMaxResults(1) + ->execute() + ->fetchColumn(0); + + if ($pageUid === false) { return false; } - $data['pageuid'] = (int)$records[0]['uid']; + $data['pageuid'] = (int)$pageUid; } // Check if the page still exists if ((int)$data['pageuid'] > 0) { diff --git a/typo3/sysext/recycler/Classes/Domain/Model/DeletedRecords.php b/typo3/sysext/recycler/Classes/Domain/Model/DeletedRecords.php index 7ad57088a5ad..ed1c04795b27 100644 --- a/typo3/sysext/recycler/Classes/Domain/Model/DeletedRecords.php +++ b/typo3/sysext/recycler/Classes/Domain/Model/DeletedRecords.php @@ -16,6 +16,8 @@ namespace TYPO3\CMS\Recycler\Domain\Model; use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Core\Database\ConnectionPool; +use TYPO3\CMS\Core\Database\Query\QueryBuilder; +use TYPO3\CMS\Core\Database\Query\Restriction\BackendWorkspaceRestriction; use TYPO3\CMS\Core\DataHandling\DataHandler; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\MathUtility; @@ -89,22 +91,22 @@ class DeletedRecords // set the limit $this->limit = trim($limit); if ($table) { - if (in_array($table, RecyclerUtility::getModifyableTables())) { + if (in_array($table, RecyclerUtility::getModifyableTables(), true)) { $this->table[] = $table; - $this->setData($id, $table, $depth, $GLOBALS['TCA'][$table]['ctrl'], $filter); + $this->setData($id, $table, $depth, $filter); } } else { - foreach ($GLOBALS['TCA'] as $tableKey => $tableValue) { + foreach (array_keys($GLOBALS['TCA']) as $tableKey) { // only go into this table if the limit allows it if ($this->limit !== '') { - $parts = GeneralUtility::trimExplode(',', $this->limit); + $parts = GeneralUtility::intExplode(',', $this->limit, true); // abort loop if LIMIT 0,0 - if ((int)$parts[0] === 0 && (int)$parts[1] === 0) { + if ($parts[0] === 0 && $parts[1] === 0) { break; } } $this->table[] = $tableKey; - $this->setData($id, $tableKey, $depth, $tableValue['ctrl'], $filter); + $this->setData($id, $tableKey, $depth, $filter); } } return $this; @@ -135,67 +137,41 @@ class DeletedRecords * @param int $id UID from record * @param string $table Tablename from record * @param int $depth How many levels recursive - * @param array $tcaCtrl TCA CTRL array * @param string $filter Filter text * @return void */ - protected function setData($id, $table, $depth, $tcaCtrl, $filter) + protected function setData($id, $table, $depth, $filter) { - $id = (int)$id; - if (!array_key_exists('delete', $tcaCtrl)) { + if (!array_key_exists('delete', $GLOBALS['TCA'][$table]['ctrl'])) { return; } - $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table); - $queryBuilder->getRestrictions()->removeAll(); - // find the 'deleted' field for this table + $id = (int)$id; + $tcaCtrl = $GLOBALS['TCA'][$table]['ctrl']; $deletedField = RecyclerUtility::getDeletedField($table); - - // create the filter WHERE-clause - $filterConstraint = null; - if (trim($filter) !== '') { - $labelConstraint = $queryBuilder->expr()->like( - $tcaCtrl['label'], - $queryBuilder->createNamedParameter( - $queryBuilder->quote('%' . $queryBuilder->escapeLikeWildcards($filter) . '%'), - \PDO::PARAM_STR - ) - ); - if (MathUtility::canBeInterpretedAsInteger($filter)) { - $filterConstraint = $queryBuilder->expr()->orX( - $queryBuilder->expr()->eq( - 'uid', - $queryBuilder->createNamedParameter($filter, \PDO::PARAM_INT) - ), - $queryBuilder->expr()->eq( - 'pid', - $queryBuilder->createNamedParameter($filter, \PDO::PARAM_INT) - ), - $labelConstraint - ); - } else { - $filterConstraint = $labelConstraint; - } - } + $firstResult = 0; + $maxResults = 0; // get the limit if (!empty($this->limit)) { // count the number of deleted records for this pid - $deletedCount = $queryBuilder + $queryBuilder = $this->getFilteredQueryBuilder($table, $id, $filter); + $queryBuilder->getRestrictions()->removeAll(); + + $deletedCount = (int)$queryBuilder ->count('*') ->from($table) - ->where( - $queryBuilder->expr()->neq($deletedField, $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)), - $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($id, \PDO::PARAM_INT)), - $filterConstraint + ->andWhere( + $queryBuilder->expr()->neq( + $deletedField, + $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT) + ) ) ->execute() - ->fetchColumn(); + ->fetchColumn(0); // split the limit - $parts = GeneralUtility::trimExplode(',', $this->limit); - $offset = $parts[0]; - $rowCount = $parts[1]; + list($offset, $rowCount) = GeneralUtility::intExplode(',', $this->limit, true); // subtract the number of deleted records from the limit's offset $result = $offset - $deletedCount; // if the result is >= 0 @@ -206,14 +182,12 @@ class DeletedRecords // do NOT query this depth; limit also does not need to be set, we set it anyways $allowQuery = false; $allowDepth = true; - $limit = ''; } else { // the offset for the temporary limit has to remain like the original offset // in case the original offset was just crossed by the amount of deleted records + $tempOffset = 0; if ($offset !== 0) { $tempOffset = $offset; - } else { - $tempOffset = 0; } // set the offset in the limit to 0 $newOffset = 0; @@ -222,7 +196,8 @@ class DeletedRecords // if the result now is > limit's row count if ($absResult > $rowCount) { // use the limit's row count as the temporary limit - $limit = implode(',', [$tempOffset, $rowCount]); + $firstResult = $tempOffset; + $maxResults = $rowCount; // set the limit's row count to 0 $this->limit = implode(',', [$newOffset, 0]); // do not go into new depth @@ -230,7 +205,8 @@ class DeletedRecords } else { // if the result now is <= limit's row count // use the result as the temporary limit - $limit = implode(',', [$tempOffset, $absResult]); + $firstResult = $tempOffset; + $maxResults = $absResult; // subtract the result from the row count $newCount = $rowCount - $absResult; // store the new result in the limit's row count @@ -249,31 +225,35 @@ class DeletedRecords $allowQuery = true; } } else { - $limit = ''; $allowDepth = true; $allowQuery = true; } // query for actual deleted records if ($allowQuery) { - $where = $queryBuilder->expr()->andX( - $queryBuilder->expr()->eq( - 'pid', - $queryBuilder->createNamedParameter($id, \PDO::PARAM_INT) - ), - $filterConstraint - ); - $recordsToCheck = BackendUtility::getRecordsByField( - $table, - $deletedField, - '1', - ' AND ' . $where, - '', - 'uid', - $limit, - false, - $queryBuilder - ); - if ($recordsToCheck) { + $queryBuilder = $this->getFilteredQueryBuilder($table, $id, $filter); + if ($firstResult) { + $queryBuilder->setFirstResult($firstResult); + } + if ($maxResults) { + $queryBuilder->setMaxResults($maxResults); + } + $recordsToCheck = $queryBuilder->select('*') + ->from($table) + ->andWhere( + $queryBuilder->expr()->eq( + 'pid', + $queryBuilder->createNamedParameter($id, \PDO::PARAM_INT) + ), + $queryBuilder->expr()->eq( + $deletedField, + $queryBuilder->createNamedParameter(1, \PDO::PARAM_INT) + ) + ) + ->orderBy('uid') + ->execute() + ->fetchAll(); + + if ($recordsToCheck !== false) { $this->checkRecordAccess($table, $recordsToCheck); } } @@ -290,12 +270,12 @@ class DeletedRecords ->execute(); while ($row = $resPages->fetch()) { - $this->setData($row['uid'], $table, $depth - 1, $tcaCtrl, $filter); + $this->setData($row['uid'], $table, $depth - 1, $filter); // some records might have been added, check if we still have the limit for further queries if (!empty($this->limit)) { - $parts = GeneralUtility::trimExplode(',', $this->limit); + $parts = GeneralUtility::intExplode(',', $this->limit, true); // abort loop if LIMIT 0,0 - if ((int)$parts[0] === 0 && (int)$parts[1] === 0) { + if ($parts[0] === 0 && $parts[1] === 0) { $resPages->closeCursor(); break; } @@ -306,6 +286,55 @@ class DeletedRecords $this->title[$table] = $tcaCtrl['title']; } + /** + * Helper method for setData() to create a QueryBuilder that filters the records by default. + * + * @param string $table + * @param int $pid + * @param string $filter + * @return \TYPO3\CMS\Core\Database\Query\QueryBuilder + * @throws \InvalidArgumentException + */ + protected function getFilteredQueryBuilder(string $table, int $pid, string $filter): QueryBuilder + { + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table); + $queryBuilder->getRestrictions() + ->removeAll() + ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class)); + + // create the filter WHERE-clause + $filterConstraint = null; + if (trim($filter) !== '') { + $filterConstraint = $queryBuilder->expr()->like( + $GLOBALS['TCA'][$table]['ctrl']['label'], + $queryBuilder->createNamedParameter( + $queryBuilder->quote('%' . $queryBuilder->escapeLikeWildcards($filter) . '%'), + \PDO::PARAM_STR + ) + ); + if (MathUtility::canBeInterpretedAsInteger($filter)) { + $filterConstraint = $queryBuilder->expr()->orX( + $queryBuilder->expr()->eq( + 'uid', + $queryBuilder->createNamedParameter($filter, \PDO::PARAM_INT) + ), + $queryBuilder->expr()->eq( + 'pid', + $queryBuilder->createNamedParameter($filter, \PDO::PARAM_INT) + ), + $filterConstraint + ); + } + } + + $queryBuilder->where( + $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($pid, \PDO::PARAM_INT)), + $filterConstraint + ); + + return $queryBuilder; + } + /** * Checks whether the current backend user has access to the given records. * diff --git a/typo3/sysext/workspaces/Classes/Service/WorkspaceService.php b/typo3/sysext/workspaces/Classes/Service/WorkspaceService.php index b1b7fe03bab4..4f8cbf6eccc2 100644 --- a/typo3/sysext/workspaces/Classes/Service/WorkspaceService.php +++ b/typo3/sysext/workspaces/Classes/Service/WorkspaceService.php @@ -14,11 +14,13 @@ namespace TYPO3\CMS\Workspaces\Service; * The TYPO3 project - inspiring people to share! */ +use TYPO3\CMS\Backend\Configuration\TranslationConfigurationProvider; use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Core\Database\Connection; use TYPO3\CMS\Core\Database\ConnectionPool; -use TYPO3\CMS\Core\Database\Query\Restriction; +use TYPO3\CMS\Core\Database\Query\Restriction\BackendWorkspaceRestriction; use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction; +use TYPO3\CMS\Core\Database\Query\Restriction\RootLevelRestriction; use TYPO3\CMS\Core\SingletonInterface; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\MathUtility; @@ -64,7 +66,7 @@ class WorkspaceService implements SingletonInterface $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_workspace'); $queryBuilder->getRestrictions() - ->add(GeneralUtility::makeInstance(Restriction\RootLevelRestriction::class)); + ->add(GeneralUtility::makeInstance(RootLevelRestriction::class)); $result = $queryBuilder ->select('uid', 'title', 'adminusers', 'members') @@ -1102,24 +1104,36 @@ class WorkspaceService implements SingletonInterface { $languageOptions = []; /** @var \TYPO3\CMS\Backend\Configuration\TranslationConfigurationProvider $translationConfigurationProvider */ - $translationConfigurationProvider = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Configuration\TranslationConfigurationProvider::class); + $translationConfigurationProvider = GeneralUtility::makeInstance(TranslationConfigurationProvider::class); $systemLanguages = $translationConfigurationProvider->getSystemLanguages($pageId); if ($GLOBALS['BE_USER']->checkLanguageAccess(0)) { // Use configured label for default language $languageOptions[0] = $systemLanguages[0]['title']; } - $pages = BackendUtility::getRecordsByField('pages_language_overlay', 'pid', $pageId); - if (!is_array($pages)) { - return $languageOptions; - } + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable('pages_language_overlay'); + $queryBuilder->getRestrictions() + ->removeAll() + ->add(GeneralUtility::makeInstance(DeletedRestriction::class)) + ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class)); + + $result = $queryBuilder->select('sys_language_uid') + ->from('pages_language_overlay') + ->where( + $queryBuilder->expr()->eq( + 'pid', + $queryBuilder->createNamedParameter($pageId, \PDO::PARAM_INT) + ) + ) + ->execute(); - foreach ($pages as $page) { - $languageId = (int)$page['sys_language_uid']; + while ($row = $result->fetch()) { + $languageId = (int)$row['sys_language_uid']; // Only add links to active languages the user has access to if (isset($systemLanguages[$languageId]) && $GLOBALS['BE_USER']->checkLanguageAccess($languageId)) { - $languageOptions[$page['sys_language_uid']] = $systemLanguages[$languageId]['title']; + $languageOptions[$languageId] = $systemLanguages[$languageId]['title']; } } -- GitLab