diff --git a/typo3/sysext/backend/Classes/Controller/BackendController.php b/typo3/sysext/backend/Classes/Controller/BackendController.php index 40d76e2350b9caf900037c1975e345ebe7d224ad..bff93714611c90fb1028e5f0be85b944398b1bbf 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 10b16e762db481ac990778076caf717d488c1521..dba66f8c464186224e890ebf1296a9c28ed01e41 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 3d30e6867e3f43c937df9f79dbb6364728799038..7308eda5aec6ce7e24e0277b2584eae792961b20 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 09951be563a17f2797eeee00922902d13a20d598..19fd03f16ce136e7a07cb7e4032e3b92e10705d1 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 0000000000000000000000000000000000000000..6d0b903dc1cf84ebbcb7d7b563247c58be3b371f --- /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 0000000000000000000000000000000000000000..ba49fcfd0ab283ddc184286be82ce3c4dff76908 --- /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 b7c888764b5962b2b90c2a02523a7d7d34039120..b977620ae09a2225a29964ed04b0025f218b2682 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 7e2f10283c52e635afd380974b162a70840c2cfb..3ae0c18b078896dd612ccac67d210ca89fbf0df8 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 5720b42a2ee1df8e70a5dbaafb8cab8251358762..4b2ab3571da0d4b05b80c54c73a35d4a18d362e6 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 b2891282e0c417ad4e6a54971601ab3db75d6d89..98beccd4a498da686e4beaaf49dabae6bbb050c5 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 0000000000000000000000000000000000000000..07098d0392c95ae178d70464b583a4010242bac6 --- /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 48f2179ae7f38c6a213600c5f5e5d299c5196c0f..7f9231cdad0a5d2fad4b6dfa75d30a220c463b48 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 7ad57088a5addf4457cb6a461551dba0bb683d78..ed1c04795b27fb1a8f3e4a8e0543a9379c4741a1 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 b1b7fe03bab4b232c5bb4128a57fc32247c79be2..4f8cbf6eccc209f8fae1d16e05072f5aa33e5d0f 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']; } }