From d31e9a6964ce5610e9ba18ed6f7eca5c8831ce4b Mon Sep 17 00:00:00 2001 From: Helmut Hummel <info@helhum.io> Date: Fri, 3 Jun 2016 07:46:45 +0200 Subject: [PATCH] [!!!][TASK] Doctrine: Simplify restriction handling To make the set of restrictions that are in effect when creating queries with the QueryBuilder becomes more understandable, we need to remove the QueryContext and the magic applied to that and replace it with the following behavior: delete, hidden, starttime, endtime restrictions are always enabled and need to be removed if needed. Other restrictions need to be provided manually depending on the desired result. Resolves: #76167 Resolves: #76264 Releases: master Change-Id: Iaf0cb08475ed44966838c3fbdd5756d3ba6ebcc1 Reviewed-on: https://review.typo3.org/48049 Tested-by: Helmut Hummel <helmut.hummel@typo3.org> Reviewed-by: Morton Jonuschat <m.jonuschat@mojocode.de> Tested-by: Morton Jonuschat <m.jonuschat@mojocode.de> Reviewed-by: Jan Helke <typo3@helke.de> Tested-by: Jan Helke <typo3@helke.de> Reviewed-by: Anja Leichsenring <aleichsenring@ab-softlab.de> Tested-by: Anja Leichsenring <aleichsenring@ab-softlab.de> Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch> Tested-by: Christian Kuhn <lolli@schwarzbu.ch> Reviewed-by: Nicole Cordes <typo3@cordes.co> Tested-by: Nicole Cordes <typo3@cordes.co> Reviewed-by: Helmut Hummel <helmut.hummel@typo3.org> --- .../Backend/Avatar/DefaultAvatarProvider.php | 13 +- .../backend/Classes/Clipboard/Clipboard.php | 26 +- .../TranslationConfigurationProvider.php | 20 +- .../backend/Classes/History/RecordHistory.php | 49 +- .../Classes/Tree/View/AbstractTreeView.php | 22 +- .../Classes/Tree/View/PagePositionMap.php | 22 +- .../SystemInformationController.php | 8 +- .../BackendUserSessionRepository.php | 8 +- .../AbstractUserAuthentication.php | 102 +- .../BackendUserAuthentication.php | 54 +- .../core/Classes/Database/Connection.php | 6 +- .../Query/Expression/CompositeExpression.php | 23 +- .../Classes/Database/Query/QueryBuilder.php | 99 +- .../Classes/Database/Query/QueryContext.php | 596 ----------- .../Database/Query/QueryContextType.php | 46 - .../Query/QueryRestrictionBuilder.php | 383 ------- .../AbstractRestrictionContainer.php | 95 ++ .../BackendWorkspaceRestriction.php | 85 ++ .../DefaultRestrictionContainer.php | 44 + .../Query/Restriction/DeletedRestriction.php | 50 + .../Query/Restriction/EndTimeRestriction.php | 69 ++ .../Restriction/FrontendGroupRestriction.php | 70 ++ .../FrontendRestrictionContainer.php | 78 ++ .../FrontendWorkspaceRestriction.php | 91 ++ .../Query/Restriction/HiddenRestriction.php | 49 + .../QueryRestrictionContainerInterface.php | 46 + .../Restriction/QueryRestrictionInterface.php | 34 + .../Restriction/RootLevelRestriction.php | 60 ++ .../Restriction/StartTimeRestriction.php | 69 ++ ...454-DoctrineDBALForDatabaseConnections.rst | 6 +- .../AbstractUserAuthenticationTest.php | 5 +- .../Unit/Database/Query/QueryBuilderTest.php | 20 +- .../Unit/Database/Query/QueryContextTest.php | 283 ------ .../Query/QueryRestrictionBuilderTest.php | 942 ------------------ .../AbstractRestrictionTestCase.php | 49 + .../BackendWorkspaceRestrictionTest.php | 81 ++ .../DefaultRestrictionContainerTest.php | 51 + .../Restriction/DeletedRestrictionTest.php | 42 + .../Restriction/EndTimeRestrictionTest.php | 64 ++ .../FrontendGroupRestrictionTest.php | 59 ++ .../FrontendRestrictionContainerTest.php | 185 ++++ .../FrontendWorkspaceRestrictionTest.php | 95 ++ .../Restriction/HiddenRestrictionTest.php | 44 + .../Restriction/RootLevelRestrictionTest.php | 71 ++ .../Restriction/StartTimeRestrictionTest.php | 64 ++ .../Controller/FrontendLoginController.php | 72 +- .../FrontendLoginControllerTest.php | 7 +- typo3/sysext/filelist/Classes/FileFacade.php | 11 +- typo3/sysext/filelist/Classes/FileList.php | 18 +- .../Classes/MissingRelationsCommand.php | 6 +- .../Classes/Domain/Model/DeletedRecords.php | 17 +- .../recycler/Classes/Domain/Model/Tables.php | 3 +- .../recycler/Classes/Task/CleanerTask.php | 18 +- .../Classes/Utility/RecyclerUtility.php | 7 +- .../Tests/Unit/Task/CleanerTaskTest.php | 10 +- .../Controller/SetupModuleController.php | 31 +- .../sys_note/Classes/Core/Bootstrap.php | 9 +- 57 files changed, 2010 insertions(+), 2577 deletions(-) delete mode 100644 typo3/sysext/core/Classes/Database/Query/QueryContext.php delete mode 100644 typo3/sysext/core/Classes/Database/Query/QueryContextType.php delete mode 100644 typo3/sysext/core/Classes/Database/Query/QueryRestrictionBuilder.php create mode 100644 typo3/sysext/core/Classes/Database/Query/Restriction/AbstractRestrictionContainer.php create mode 100644 typo3/sysext/core/Classes/Database/Query/Restriction/BackendWorkspaceRestriction.php create mode 100644 typo3/sysext/core/Classes/Database/Query/Restriction/DefaultRestrictionContainer.php create mode 100644 typo3/sysext/core/Classes/Database/Query/Restriction/DeletedRestriction.php create mode 100644 typo3/sysext/core/Classes/Database/Query/Restriction/EndTimeRestriction.php create mode 100644 typo3/sysext/core/Classes/Database/Query/Restriction/FrontendGroupRestriction.php create mode 100644 typo3/sysext/core/Classes/Database/Query/Restriction/FrontendRestrictionContainer.php create mode 100644 typo3/sysext/core/Classes/Database/Query/Restriction/FrontendWorkspaceRestriction.php create mode 100644 typo3/sysext/core/Classes/Database/Query/Restriction/HiddenRestriction.php create mode 100644 typo3/sysext/core/Classes/Database/Query/Restriction/QueryRestrictionContainerInterface.php create mode 100644 typo3/sysext/core/Classes/Database/Query/Restriction/QueryRestrictionInterface.php create mode 100644 typo3/sysext/core/Classes/Database/Query/Restriction/RootLevelRestriction.php create mode 100644 typo3/sysext/core/Classes/Database/Query/Restriction/StartTimeRestriction.php delete mode 100644 typo3/sysext/core/Tests/Unit/Database/Query/QueryContextTest.php delete mode 100644 typo3/sysext/core/Tests/Unit/Database/Query/QueryRestrictionBuilderTest.php create mode 100644 typo3/sysext/core/Tests/Unit/Database/Query/Restriction/AbstractRestrictionTestCase.php create mode 100644 typo3/sysext/core/Tests/Unit/Database/Query/Restriction/BackendWorkspaceRestrictionTest.php create mode 100644 typo3/sysext/core/Tests/Unit/Database/Query/Restriction/DefaultRestrictionContainerTest.php create mode 100644 typo3/sysext/core/Tests/Unit/Database/Query/Restriction/DeletedRestrictionTest.php create mode 100644 typo3/sysext/core/Tests/Unit/Database/Query/Restriction/EndTimeRestrictionTest.php create mode 100644 typo3/sysext/core/Tests/Unit/Database/Query/Restriction/FrontendGroupRestrictionTest.php create mode 100644 typo3/sysext/core/Tests/Unit/Database/Query/Restriction/FrontendRestrictionContainerTest.php create mode 100644 typo3/sysext/core/Tests/Unit/Database/Query/Restriction/FrontendWorkspaceRestrictionTest.php create mode 100644 typo3/sysext/core/Tests/Unit/Database/Query/Restriction/HiddenRestrictionTest.php create mode 100644 typo3/sysext/core/Tests/Unit/Database/Query/Restriction/RootLevelRestrictionTest.php create mode 100644 typo3/sysext/core/Tests/Unit/Database/Query/Restriction/StartTimeRestrictionTest.php diff --git a/typo3/sysext/backend/Classes/Backend/Avatar/DefaultAvatarProvider.php b/typo3/sysext/backend/Classes/Backend/Avatar/DefaultAvatarProvider.php index a7551b7e6bb5..b5ff3d145519 100644 --- a/typo3/sysext/backend/Classes/Backend/Avatar/DefaultAvatarProvider.php +++ b/typo3/sysext/backend/Classes/Backend/Avatar/DefaultAvatarProvider.php @@ -13,9 +13,7 @@ namespace TYPO3\CMS\Backend\Backend\Avatar; * * The TYPO3 project - inspiring people to share! */ -use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Core\Database\ConnectionPool; -use TYPO3\CMS\Core\Database\Query\QueryBuilder; use TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException; use TYPO3\CMS\Core\Resource\ProcessedFile; use TYPO3\CMS\Core\Resource\ResourceFactory; @@ -67,15 +65,16 @@ class DefaultAvatarProvider implements AvatarProviderInterface */ protected function getAvatarFileUid($beUserId) { - /** @var QueryBuilder $queryBuilder */ $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_file_reference'); $file = $queryBuilder ->select('uid_local') ->from('sys_file_reference') - ->where($queryBuilder->expr()->eq('tablenames', $queryBuilder->createNamedParameter('be_users'))) - ->andWhere($queryBuilder->expr()->eq('fieldname', $queryBuilder->createNamedParameter('avatar'))) - ->andWhere($queryBuilder->expr()->eq('table_local', $queryBuilder->createNamedParameter('sys_file'))) - ->andWhere($queryBuilder->expr()->eq('uid_foreign', (int)$beUserId)) + ->where( + $queryBuilder->expr()->eq('tablenames', $queryBuilder->createNamedParameter('be_users')), + $queryBuilder->expr()->eq('fieldname', $queryBuilder->createNamedParameter('avatar')), + $queryBuilder->expr()->eq('table_local', $queryBuilder->createNamedParameter('sys_file')), + $queryBuilder->expr()->eq('uid_foreign', (int)$beUserId) + ) ->execute() ->fetchColumn(); diff --git a/typo3/sysext/backend/Classes/Clipboard/Clipboard.php b/typo3/sysext/backend/Classes/Clipboard/Clipboard.php index 30848c57597a..8a907c046e78 100644 --- a/typo3/sysext/backend/Classes/Clipboard/Clipboard.php +++ b/typo3/sysext/backend/Classes/Clipboard/Clipboard.php @@ -16,7 +16,7 @@ namespace TYPO3\CMS\Backend\Clipboard; use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Core\Database\ConnectionPool; -use TYPO3\CMS\Core\Database\Query\QueryContextType; +use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction; use TYPO3\CMS\Core\Imaging\Icon; use TYPO3\CMS\Core\Imaging\IconFactory; use TYPO3\CMS\Core\Imaging\IconProvider\SvgIconProvider; @@ -496,23 +496,24 @@ class Clipboard { $lines = array(); $tcaCtrl = $GLOBALS['TCA'][$table]['ctrl']; - if ($table != 'pages' && BackendUtility::isTableLocalizable($table) && !$tcaCtrl['transOrigPointerTable']) { + if ($table !== 'pages' && BackendUtility::isTableLocalizable($table) && !$tcaCtrl['transOrigPointerTable']) { $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table); - $queryBuilder->getQueryContext()->setContext(QueryContextType::UNRESTRICTED); + $queryBuilder->getRestrictions() + ->removeAll() + ->add(GeneralUtility::makeInstance(DeletedRestriction::class)); + $queryBuilder ->select('*') ->from($table) - ->where($queryBuilder->expr()->eq($tcaCtrl['transOrigPointerField'], (int)$parentRec['uid'])) - ->andWhere($queryBuilder->expr()->neq($tcaCtrl['languageField'], 0)); - if (isset($tcaCtrl['delete']) && $tcaCtrl['delete']) { - $queryBuilder->andWhere($queryBuilder->expr()->eq($tcaCtrl['delete'], 0)); - } + ->where( + $queryBuilder->expr()->eq($tcaCtrl['transOrigPointerField'], (int)$parentRec['uid']), + $queryBuilder->expr()->neq($tcaCtrl['languageField'], 0) + ); + if (isset($tcaCtrl['versioningWS']) && $tcaCtrl['versioningWS']) { - $queryBuilder->andWhere($queryBuilder->expr()->eq('t3ver_wsid', $parentRec['t3ver_wsid'])); + $queryBuilder->andWhere($queryBuilder->expr()->eq('t3ver_wsid', (int)$parentRec['t3ver_wsid'])); } - $rows = $queryBuilder - ->execute() - ->fetchAll(); + $rows = $queryBuilder->execute()->fetchAll(); if (is_array($rows)) { $modeData = ''; if ($pad == 'normal') { @@ -1120,5 +1121,4 @@ class Clipboard { return $GLOBALS['BE_USER']; } - } diff --git a/typo3/sysext/backend/Classes/Configuration/TranslationConfigurationProvider.php b/typo3/sysext/backend/Classes/Configuration/TranslationConfigurationProvider.php index ece68f594cc9..c621fe68ecdf 100644 --- a/typo3/sysext/backend/Classes/Configuration/TranslationConfigurationProvider.php +++ b/typo3/sysext/backend/Classes/Configuration/TranslationConfigurationProvider.php @@ -16,7 +16,8 @@ namespace TYPO3\CMS\Backend\Configuration; use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Core\Database\ConnectionPool; -use TYPO3\CMS\Core\Database\Query\QueryContextType; +use TYPO3\CMS\Core\Database\Query\Restriction\BackendWorkspaceRestriction; +use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction; use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Lang\LanguageService; @@ -123,18 +124,21 @@ class TranslationConfigurationProvider $selFieldList = 'uid,' . $GLOBALS['TCA'][$translationTable]['ctrl']['languageField']; } $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($translationTable); - $queryBuilder->getQueryContext()->setContext(QueryContextType::BACKEND_NO_VERSIONING_PLACEHOLDERS); + $queryBuilder->getRestrictions() + ->removeAll() + ->add(GeneralUtility::makeInstance(DeletedRestriction::class)) + ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class)); $queryBuilder ->select(...GeneralUtility::trimExplode(',', $selFieldList)) ->from($translationTable) - ->where($queryBuilder->expr()->eq($GLOBALS['TCA'][$translationTable]['ctrl']['transOrigPointerField'], (int)$uid)) - ->andWhere($queryBuilder->expr()->eq('pid', (int)($table === 'pages' ? $row['uid'] : $row['pid']))); + ->where( + $queryBuilder->expr()->eq($GLOBALS['TCA'][$translationTable]['ctrl']['transOrigPointerField'], (int)$uid), + $queryBuilder->expr()->eq('pid', (int)($table === 'pages' ? $row['uid'] : $row['pid'])) + ); if (!$languageUid) { - $queryBuilder - ->andWhere($queryBuilder->expr()->gt($GLOBALS['TCA'][$translationTable]['ctrl']['languageField'], 0)); + $queryBuilder->andWhere($queryBuilder->expr()->gt($GLOBALS['TCA'][$translationTable]['ctrl']['languageField'], 0)); } else { - $queryBuilder - ->andWhere($queryBuilder->expr()->eq($GLOBALS['TCA'][$translationTable]['ctrl']['languageField'], (int)$languageUid)); + $queryBuilder->andWhere($queryBuilder->expr()->eq($GLOBALS['TCA'][$translationTable]['ctrl']['languageField'], (int)$languageUid)); } $translationRecords = $queryBuilder ->execute() diff --git a/typo3/sysext/backend/Classes/History/RecordHistory.php b/typo3/sysext/backend/Classes/History/RecordHistory.php index f54c76386aa6..28ced0ac67bf 100644 --- a/typo3/sysext/backend/Classes/History/RecordHistory.php +++ b/typo3/sysext/backend/Classes/History/RecordHistory.php @@ -16,8 +16,6 @@ namespace TYPO3\CMS\Backend\History; 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\QueryContextType; use TYPO3\CMS\Core\DataHandling\DataHandler; use TYPO3\CMS\Core\Imaging\Icon; use TYPO3\CMS\Core\Imaging\IconFactory; @@ -171,12 +169,11 @@ class RecordHistory public function toggleHighlight($uid) { $uid = (int)$uid; - /** @var QueryBuilder $queryBuilder */ $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_history'); $row = $queryBuilder ->select('snapshot') ->from('sys_history') - ->where($queryBuilder->expr()->eq('uid', $uid)) + ->where($queryBuilder->expr()->eq('uid', (int)$uid)) ->execute() ->fetch(); @@ -185,7 +182,7 @@ class RecordHistory $queryBuilder ->update('sys_history') ->set('snapshot', (int)!$row['snapshot']) - ->where($queryBuilder->expr()->eq('uid', $uid)) + ->where($queryBuilder->expr()->eq('uid', (int)$uid)) ->execute(); } } @@ -661,17 +658,15 @@ class RecordHistory if ($elParts[0] == 'pages' && $this->showSubElements && $this->hasPageAccess('pages', $elParts[1])) { foreach ($GLOBALS['TCA'] as $tablename => $value) { // check if there are records on the page - /** @var QueryBuilder $queryBuilder */ $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($tablename); - $queryBuilder->getQueryContext()->setContext(QueryContextType::UNRESTRICTED); + $queryBuilder->getRestrictions()->removeAll(); $rows = $queryBuilder ->select('uid') ->from($tablename) ->where($queryBuilder->expr()->eq('pid', (int)$elParts[1])) - ->execute() - ->fetchAll(); - if (empty($rows)) { + ->execute(); + if ($rows->rowCount() === 0) { continue; } foreach ($rows as $row) { @@ -709,7 +704,6 @@ class RecordHistory // If table is found in $GLOBALS['TCA']: $uid = $this->resolveElement($table, $uid); // Selecting the $this->maxSteps most recent states: - /** @var QueryBuilder $queryBuilder */ $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_history'); $rows = $queryBuilder ->select('sys_history.*', 'sys_log.userid', 'sys_log.log_data') @@ -719,10 +713,10 @@ class RecordHistory $queryBuilder->expr()->eq( 'sys_history.sys_log_uid', $queryBuilder->quoteIdentifier('sys_log.uid') - ) + ), + $queryBuilder->expr()->eq('sys_history.tablename', $queryBuilder->createNamedParameter($table)), + $queryBuilder->expr()->eq('sys_history.recuid', (int)$uid) ) - ->andWhere($queryBuilder->expr()->eq('sys_history.tablename', $queryBuilder->createNamedParameter($table))) - ->andWhere($queryBuilder->expr()->eq('sys_history.recuid', (int)$uid)) ->orderBy('sys_log.uid', 'DESC') ->setMaxResults((int)$this->maxSteps) ->execute() @@ -759,28 +753,28 @@ class RecordHistory // SELECT INSERTS/DELETES if ($this->showInsertDelete) { // Select most recent inserts and deletes // WITHOUT snapshots - /** @var QueryBuilder $queryBuilder */ $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_log'); - $rows = $queryBuilder + $result = $queryBuilder ->select('uid', 'userid', 'action', 'tstamp', 'log_data') ->from('sys_log') - ->where($queryBuilder->expr()->eq('type', 1)) - ->andWhere($queryBuilder->expr()->orX( - $queryBuilder->expr()->eq('action', 1), - $queryBuilder->expr()->eq('action', 3) - )) - ->andWhere($queryBuilder->expr()->eq('tablename', $queryBuilder->createNamedParameter($table))) - ->andWhere($queryBuilder->expr()->eq('recuid', (int)$uid)) + ->where( + $queryBuilder->expr()->eq('type', 1), + $queryBuilder->expr()->orX( + $queryBuilder->expr()->eq('action', 1), + $queryBuilder->expr()->eq('action', 3) + ), + $queryBuilder->expr()->eq('tablename', $queryBuilder->createNamedParameter($table)), + $queryBuilder->expr()->eq('recuid', (int)$uid) + ) ->orderBy('uid', 'DESC') ->setMaxResults((int)$this->maxSteps) - ->execute() - ->fetchAll(); + ->execute(); // If none are found, nothing more to do - if (empty($rows)) { + if ($result->rowCount() === 0) { return $changeLog; } - foreach ($rows as $row) { + foreach ($result as $row) { if ($this->lastSyslogId && $row['uid'] < $this->lastSyslogId) { continue; } @@ -914,7 +908,6 @@ class RecordHistory if (empty($shUid)) { return; } - /** @var QueryBuilder $queryBuilder */ $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_history'); $record = $queryBuilder ->select('*') diff --git a/typo3/sysext/backend/Classes/Tree/View/AbstractTreeView.php b/typo3/sysext/backend/Classes/Tree/View/AbstractTreeView.php index 200e640d6f5f..9aae14354107 100644 --- a/typo3/sysext/backend/Classes/Tree/View/AbstractTreeView.php +++ b/typo3/sysext/backend/Classes/Tree/View/AbstractTreeView.php @@ -20,6 +20,8 @@ use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; use TYPO3\CMS\Core\Database\ConnectionPool; 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\Imaging\Icon; use TYPO3\CMS\Core\Imaging\IconFactory; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -834,11 +836,17 @@ abstract class AbstractTreeView return $this->getDataCount($res); } else { $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->table); + $queryBuilder->getRestrictions() + ->removeAll() + ->add(GeneralUtility::makeInstance(DeletedRestriction::class)) + ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class)); $count = $queryBuilder ->count('uid') ->from($this->table) - ->where($queryBuilder->expr()->eq($this->parentField, $queryBuilder->createNamedParameter($uid))) - ->andWhere(QueryHelper::stripLogicalOperatorPrefix($this->clause)) + ->where( + $queryBuilder->expr()->eq($this->parentField, (int)$uid), + QueryHelper::stripLogicalOperatorPrefix($this->clause) + ) ->execute() ->fetchColumn(); @@ -894,11 +902,17 @@ abstract class AbstractTreeView return $parentId; } else { $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->table); + $queryBuilder->getRestrictions() + ->removeAll() + ->add(GeneralUtility::makeInstance(DeletedRestriction::class)) + ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class)); $queryBuilder ->select(...$this->fieldArray) ->from($this->table) - ->where($queryBuilder->expr()->eq($this->parentField, $queryBuilder->createNamedParameter($parentId))) - ->andWhere(QueryHelper::stripLogicalOperatorPrefix($this->clause)); + ->where( + $queryBuilder->expr()->eq($this->parentField, (int)$parentId), + QueryHelper::stripLogicalOperatorPrefix($this->clause) + ); foreach (QueryHelper::parseOrderBy($this->orderByFields) as $orderPair) { list($fieldName, $order) = $orderPair; diff --git a/typo3/sysext/backend/Classes/Tree/View/PagePositionMap.php b/typo3/sysext/backend/Classes/Tree/View/PagePositionMap.php index 1d2b28c0747b..758fded4616d 100644 --- a/typo3/sysext/backend/Classes/Tree/View/PagePositionMap.php +++ b/typo3/sysext/backend/Classes/Tree/View/PagePositionMap.php @@ -17,7 +17,10 @@ namespace TYPO3\CMS\Backend\Tree\View; use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; use TYPO3\CMS\Core\Database\ConnectionPool; -use TYPO3\CMS\Core\Database\Query\QueryContextType; +use TYPO3\CMS\Core\Database\Query\Restriction\BackendWorkspaceRestriction; +use TYPO3\CMS\Core\Database\Query\Restriction\EndTimeRestriction; +use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction; +use TYPO3\CMS\Core\Database\Query\Restriction\StartTimeRestriction; use TYPO3\CMS\Core\Imaging\Icon; use TYPO3\CMS\Core\Imaging\IconFactory; use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; @@ -341,15 +344,20 @@ class PagePositionMap $lines = array(); foreach ($colPosArray as $kk => $vv) { $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tt_content'); - $queryBuilder->getQueryContext() - ->setContext(QueryContextType::BACKEND_NO_VERSIONING_PLACEHOLDERS) - ->setIgnoreEnableFields($showHidden); - + $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class)); + if ($showHidden) { + $queryBuilder->getRestrictions() + ->removeByType(HiddenRestriction::class) + ->removeByType(StartTimeRestriction::class) + ->removeByType(EndTimeRestriction::class); + } $queryBuilder ->select('*') ->from('tt_content') - ->where($queryBuilder->expr()->eq('pid', (int)$pid)) - ->andWhere($queryBuilder->expr()->eq('colPos', (int)$vv)) + ->where( + $queryBuilder->expr()->eq('pid', (int)$pid), + $queryBuilder->expr()->eq('colPos', (int)$vv) + ) ->orderBy('sorting'); if ((string)$this->cur_sys_language !== '') { diff --git a/typo3/sysext/belog/Classes/Controller/SystemInformationController.php b/typo3/sysext/belog/Classes/Controller/SystemInformationController.php index c018a24254b6..6c9b498796a4 100644 --- a/typo3/sysext/belog/Classes/Controller/SystemInformationController.php +++ b/typo3/sysext/belog/Classes/Controller/SystemInformationController.php @@ -19,7 +19,6 @@ use TYPO3\CMS\Backend\Toolbar\Enumeration\InformationStatus; use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Belog\Domain\Model\Constraint; use TYPO3\CMS\Core\Database\ConnectionPool; -use TYPO3\CMS\Core\Database\Query\QueryBuilder; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Utility\LocalizationUtility; @@ -51,12 +50,13 @@ class SystemInformationController extends AbstractController $this->setStartAndEndTimeFromTimeSelector($constraint); // we can't use the extbase repository here as the required TypoScript may not be parsed yet - /** @var QueryBuilder $queryBuilder */ $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_log'); $count = $queryBuilder->count('error') ->from('sys_log') - ->where($queryBuilder->expr()->gte('tstamp', $timestamp)) - ->andWhere($queryBuilder->expr()->in('error', [-1, 1, 2])) + ->where( + $queryBuilder->expr()->gte('tstamp', $timestamp), + $queryBuilder->expr()->in('error', [-1, 1, 2]) + ) ->execute() ->fetchColumn(0); diff --git a/typo3/sysext/beuser/Classes/Domain/Repository/BackendUserSessionRepository.php b/typo3/sysext/beuser/Classes/Domain/Repository/BackendUserSessionRepository.php index f8a694f89d81..f676c60111cb 100644 --- a/typo3/sysext/beuser/Classes/Domain/Repository/BackendUserSessionRepository.php +++ b/typo3/sysext/beuser/Classes/Domain/Repository/BackendUserSessionRepository.php @@ -72,9 +72,11 @@ class BackendUserSessionRepository extends Repository ->update('be_sessions') ->set('ses_userid', $authentication->user['ses_backuserid']) ->set('ses_backuserid', 0) - ->where($queryBuilder->expr()->eq('ses_id', $queryBuilder->createNamedParameter($GLOBALS['BE_USER']->id))) - ->andWhere($queryBuilder->expr()->eq('ses_name', $queryBuilder->createNamedParameter(BackendUserAuthentication::getCookieName()))) - ->andWhere($queryBuilder->expr()->eq('ses_userid', (int)$GLOBALS['BE_USER']->user['uid'])) + ->where( + $queryBuilder->expr()->eq('ses_id', $queryBuilder->createNamedParameter($GLOBALS['BE_USER']->id)), + $queryBuilder->expr()->eq('ses_name', $queryBuilder->createNamedParameter(BackendUserAuthentication::getCookieName())), + $queryBuilder->expr()->eq('ses_userid', (int)$GLOBALS['BE_USER']->user['uid']) + ) ->execute(); } } diff --git a/typo3/sysext/core/Classes/Authentication/AbstractUserAuthentication.php b/typo3/sysext/core/Classes/Authentication/AbstractUserAuthentication.php index dab73e90ecd5..a2fe2b12fae1 100644 --- a/typo3/sysext/core/Classes/Authentication/AbstractUserAuthentication.php +++ b/typo3/sysext/core/Classes/Authentication/AbstractUserAuthentication.php @@ -19,8 +19,14 @@ use TYPO3\CMS\Core\Crypto\Random; use TYPO3\CMS\Core\Database\Connection; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Database\DatabaseConnection; -use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder; use TYPO3\CMS\Core\Database\Query\QueryHelper; +use TYPO3\CMS\Core\Database\Query\Restriction\DefaultRestrictionContainer; +use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction; +use TYPO3\CMS\Core\Database\Query\Restriction\EndTimeRestriction; +use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction; +use TYPO3\CMS\Core\Database\Query\Restriction\QueryRestrictionContainerInterface; +use TYPO3\CMS\Core\Database\Query\Restriction\RootLevelRestriction; +use TYPO3\CMS\Core\Database\Query\Restriction\StartTimeRestriction; use TYPO3\CMS\Core\Exception; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\MathUtility; @@ -1050,6 +1056,7 @@ abstract class AbstractUserAuthentication { $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) ->getQueryBuilderForTable($this->session_table); + $queryBuilder->setRestrictions($this->userConstraints()); $queryBuilder->select('*') ->from($this->session_table) ->from($this->user_table) @@ -1057,28 +1064,21 @@ abstract class AbstractUserAuthentication $queryBuilder->expr()->eq( $this->session_table . '.ses_id', $queryBuilder->createNamedParameter($this->id) - ) - ) - ->andWhere( + ), $queryBuilder->expr()->eq( $this->session_table . '.ses_name', $queryBuilder->createNamedParameter($this->name) - ) - ) - // Condition on which to join the session and user table - ->andWhere( + ), + // Condition on which to join the session and user table $queryBuilder->expr()->eq( $this->session_table . '.ses_userid', $queryBuilder->quoteIdentifier($this->user_table . '.' . $this->userid_column) - ) - ) - ->andWhere( + ), $queryBuilder->expr()->eq( $this->session_table . '.ses_hashlock', $queryBuilder->createNamedParameter($this->hashLockClause_getHashInt()) ) - ) - ->andWhere($this->userConstraints($queryBuilder->expr())); + ); if ($this->lockIP) { $queryBuilder->andWhere( @@ -1097,50 +1097,38 @@ abstract class AbstractUserAuthentication } /** - * @param ExpressionBuilder $expressionBuilder - * @param string $tableAlias - * @return \Doctrine\DBAL\Query\Expression\CompositeExpression + * This returns the restrictions needed to select the user respecting + * enable columns and flags like deleted, hidden, starttime, endtime + * and rootLevel + * + * @return \TYPO3\CMS\Core\Database\Query\Restriction\QueryRestrictionContainerInterface * @internal */ - protected function userConstraints( - ExpressionBuilder $expressionBuilder, - string $tableAlias = '' - ): \Doctrine\DBAL\Query\Expression\CompositeExpression { - if ($tableAlias === '') { - $tableAlias = $this->user_table; - } + protected function userConstraints(): QueryRestrictionContainerInterface + { + $restrictionContainer = GeneralUtility::makeInstance(DefaultRestrictionContainer::class); - $constraints = $expressionBuilder->andX(); - if ($this->enablecolumns['rootLevel']) { - $constraints->add( - $expressionBuilder->eq($tableAlias . '.pid', 0) - ); + if (empty($this->enablecolumns['disabled'])) { + $restrictionContainer->removeByType(HiddenRestriction::class); } - if ($this->enablecolumns['disabled']) { - $constraints->add( - $expressionBuilder->eq($tableAlias . '.' . $this->enablecolumns['disabled'], 0) - ); + + if (empty($this->enablecolumns['deleted'])) { + $restrictionContainer->removeByType(DeletedRestriction::class); } - if ($this->enablecolumns['deleted']) { - $constraints->add( - $expressionBuilder->eq($tableAlias . '.' . $this->enablecolumns['deleted'], 0) - ); + + if (empty($this->enablecolumns['starttime'])) { + $restrictionContainer->removeByType(StartTimeRestriction::class); } - if ($this->enablecolumns['starttime']) { - $constraints->add( - $expressionBuilder->lte($tableAlias . '.' . $this->enablecolumns['starttime'], $GLOBALS['EXEC_TIME']) - ); + + if (empty($this->enablecolumns['endtime'])) { + $restrictionContainer->removeByType(EndTimeRestriction::class); } - if ($this->enablecolumns['endtime']) { - $constraints->add( - $expressionBuilder->orX( - $expressionBuilder->eq($tableAlias . '.' . $this->enablecolumns['endtime'], 0), - $expressionBuilder->gt($tableAlias . '.' . $this->enablecolumns['endtime'], $GLOBALS['EXEC_TIME']) - ) - ); + + if (!empty($this->enablecolumns['rootLevel'])) { + $restrictionContainer->add(GeneralUtility::makeInstance(RootLevelRestriction::class, [$this->user_table])); } - return $constraints; + return $restrictionContainer; } /** @@ -1479,7 +1467,10 @@ abstract class AbstractUserAuthentication $authInfo['db_user']['username_column'] = $this->username_column; $authInfo['db_user']['userident_column'] = $this->userident_column; $authInfo['db_user']['usergroup_column'] = $this->usergroup_column; - $authInfo['db_user']['enable_clause'] = $this->userConstraints($expressionBuilder); + $authInfo['db_user']['enable_clause'] = $this->userConstraints()->buildExpression( + [$this->user_table], + $expressionBuilder + ); if ($this->checkPid && $this->checkPid_value !== null) { $authInfo['db_user']['checkPidList'] = $this->checkPid_value; $authInfo['db_user']['check_pid_clause'] = $expressionBuilder->in( @@ -1521,9 +1512,7 @@ abstract class AbstractUserAuthentication $query->expr()->lt( 'ses_tstamp', $query->createNamedParameter((int)($GLOBALS['EXEC_TIME'] - $this->gc_time)) - ) - ) - ->andWhere( + ), $query->expr()->eq( 'ses_name', $query->createNamedParameter($this->name) @@ -1604,10 +1593,10 @@ abstract class AbstractUserAuthentication public function getRawUserByUid($uid) { $query = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->user_table); + $query->setRestrictions($this->userConstraints()); $query->select('*') ->from($this->user_table) - ->where($query->expr()->eq('uid', $query->createNamedParameter($uid))) - ->andWhere($this->userConstraints($query->expr())); + ->where($query->expr()->eq('uid', (int)$uid)); return $query->execute()->fetch(); } @@ -1623,10 +1612,10 @@ abstract class AbstractUserAuthentication public function getRawUserByName($name) { $query = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->user_table); + $query->setRestrictions($this->userConstraints()); $query->select('*') ->from($this->user_table) - ->where($query->expr()->eq('username', $query->createNamedParameter($name))) - ->andWhere($this->userConstraints($query->expr())); + ->where($query->expr()->eq('username', $query->createNamedParameter($name))); return $query->execute()->fetch(); } @@ -1650,6 +1639,7 @@ abstract class AbstractUserAuthentication $user = false; if ($username || $extraWhere) { $query = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($dbUser['table']); + $query->getRestrictions()->removeAll(); $constraints = array_filter([ QueryHelper::stripLogicalOperatorPrefix($dbUser['check_pid_clause']), diff --git a/typo3/sysext/core/Classes/Authentication/BackendUserAuthentication.php b/typo3/sysext/core/Classes/Authentication/BackendUserAuthentication.php index ca0ab847668a..909f293071c1 100644 --- a/typo3/sysext/core/Classes/Authentication/BackendUserAuthentication.php +++ b/typo3/sysext/core/Classes/Authentication/BackendUserAuthentication.php @@ -17,6 +17,9 @@ namespace TYPO3\CMS\Core\Authentication; use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Database\Query\QueryHelper; +use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction; +use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction; +use TYPO3\CMS\Core\Database\Query\Restriction\RootLevelRestriction; use TYPO3\CMS\Core\Resource\ResourceStorage; use TYPO3\CMS\Core\Type\Bitmask\JsConfirmation; use TYPO3\CMS\Core\Type\Bitmask\Permission; @@ -1339,17 +1342,20 @@ class BackendUserAuthentication extends \TYPO3\CMS\Core\Authentication\AbstractU // Explode mounts // Selecting all webmounts with permission clause for reading $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages'); + $queryBuilder->getRestrictions() + ->removeAll() + ->add(GeneralUtility::makeInstance(DeletedRestriction::class)); + $MProws = $queryBuilder->select('uid') ->from('pages') - ->where($queryBuilder->expr()->eq('deleted', 0)) - ->andWhere( + // @todo DOCTRINE: check how to make getPagePermsClause() portable + ->where( + $this->getPagePermsClause(1), $queryBuilder->expr()->in( 'uid', $queryBuilder->createNamedParameter($this->groupData['webmounts']) ) ) - // @todo DOCTRINE: check how to make getPagePermsClause() portable - ->andWhere($this->getPagePermsClause(1)) ->execute() ->fetchAll(); $MProws = array_column(($MProws ?: []), 'uid', 'uid'); @@ -1382,8 +1388,6 @@ class BackendUserAuthentication extends \TYPO3\CMS\Core\Authentication\AbstractU $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->usergroup_table); $expressionBuilder = $queryBuilder->expr(); $constraints = $expressionBuilder->andX( - $expressionBuilder->eq('deleted', 0), - $expressionBuilder->eq('hidden', 0), $expressionBuilder->eq('pid', 0), $expressionBuilder->in('uid', GeneralUtility::intExplode(',', $grList)), $expressionBuilder->orX( @@ -1582,11 +1586,15 @@ class BackendUserAuthentication extends \TYPO3\CMS\Core\Authentication\AbstractU $orderBy = $GLOBALS['TCA']['sys_filemounts']['ctrl']['default_sortby'] ?? 'sorting'; $queryBuilder = $connectionPool->getQueryBuilderForTable('sys_filemounts'); + $queryBuilder->getRestrictions() + ->removeAll() + ->add(GeneralUtility::makeInstance(DeletedRestriction::class)) + ->add(GeneralUtility::makeInstance(HiddenRestriction::class)) + ->add(GeneralUtility::makeInstance(RootLevelRestriction::class)); + $queryBuilder->select('*') ->from('sys_filemounts') - ->where($queryBuilder->expr()->eq('hidden', 0)) - ->andWhere($queryBuilder->expr()->eq('pid', 0)) - ->andWhere($queryBuilder->expr()->in('uid', $fileMounts)); + ->where($queryBuilder->expr()->in('uid', array_map('intval', $fileMounts))); foreach (QueryHelper::parseOrderBy($orderBy) as $fieldAndDirection) { $queryBuilder->addOrderBy(...$fieldAndDirection); @@ -2024,13 +2032,13 @@ class BackendUserAuthentication extends \TYPO3\CMS\Core\Authentication\AbstractU default: if (\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('workspaces')) { $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_workspace'); + $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(RootLevelRestriction::class)); $wsRec = $queryBuilder->select(...GeneralUtility::trimExplode(',', $fields)) ->from('sys_workspace') - ->where($queryBuilder->expr()->eq('pid', 0)) - ->andWhere( + ->where( $queryBuilder->expr()->eq( 'uid', - $queryBuilder->createNamedParameter((int)$wsRec) + (int)$wsRec ) ) ->orderBy('title') @@ -2195,9 +2203,9 @@ class BackendUserAuthentication extends \TYPO3\CMS\Core\Authentication\AbstractU } elseif (\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('workspaces')) { // Traverse custom workspaces: $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_workspace'); + $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(RootLevelRestriction::class)); $workspaces = $queryBuilder->select('uid', 'title', 'adminusers', 'reviewers') ->from('sys_workspace') - ->where($queryBuilder->expr()->eq('pid', 0)) ->orderBy('title') ->execute() ->fetchAll(\PDO::FETCH_ASSOC); @@ -2322,9 +2330,11 @@ class BackendUserAuthentication extends \TYPO3\CMS\Core\Authentication\AbstractU $queryBuilder = $connectionPool->getQueryBuilderForTable('sys_log'); $queryBuilder->select('tstamp') ->from('sys_log') - ->where($queryBuilder->expr()->eq('type', 255)) - ->andWhere($queryBuilder->expr()->eq('action', 4)) - ->andWhere($queryBuilder->expr()->gt('tstamp', $queryBuilder->createNamedParameter((int)$theTimeBack))) + ->where( + $queryBuilder->expr()->eq('type', 255), + $queryBuilder->expr()->eq('action', 4), + $queryBuilder->expr()->gt('tstamp', (int)$theTimeBack) + ) ->orderBy('tstamp', 'DESC') ->setMaxResults(1); if ($testRow = $queryBuilder->execute()->fetch(\PDO::FETCH_ASSOC)) { @@ -2334,10 +2344,12 @@ class BackendUserAuthentication extends \TYPO3\CMS\Core\Authentication\AbstractU $queryBuilder = $connectionPool->getQueryBuilderForTable('sys_log'); $result = $queryBuilder->select('*') ->from('sys_log') - ->where($queryBuilder->expr()->eq('type', 255)) - ->andWhere($queryBuilder->expr()->eq('action', 3)) - ->andWhere($queryBuilder->expr()->neq('error', 0)) - ->andWhere($queryBuilder->expr()->gt('tstamp', $queryBuilder->createNamedParameter((int)$theTimeBack))) + ->where( + $queryBuilder->expr()->eq('type', 255), + $queryBuilder->expr()->eq('action', 3), + $queryBuilder->expr()->neq('error', 0), + $queryBuilder->expr()->gt('tstamp', (int)$theTimeBack) + ) ->orderBy('tstamp') ->execute(); @@ -2600,7 +2612,7 @@ This is a dump of the failures: $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('be_users'); $isUserAllowedToLogin = (bool)$queryBuilder->count('uid') ->from('be_users') - ->where($queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($backendUserId))) + ->where($queryBuilder->expr()->eq('uid', (int)$backendUserId)) ->andWhere($queryBuilder->expr()->eq('admin', 1)) ->execute() ->fetchColumn(0); diff --git a/typo3/sysext/core/Classes/Database/Connection.php b/typo3/sysext/core/Classes/Database/Connection.php index c23f8867bceb..0f78d8073617 100644 --- a/typo3/sysext/core/Classes/Database/Connection.php +++ b/typo3/sysext/core/Classes/Database/Connection.php @@ -21,7 +21,6 @@ use Doctrine\DBAL\Driver; use Doctrine\DBAL\Driver\Statement; use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder; use TYPO3\CMS\Core\Database\Query\QueryBuilder; -use TYPO3\CMS\Core\Database\Query\QueryContext; use TYPO3\CMS\Core\Utility\GeneralUtility; class Connection extends \Doctrine\DBAL\Connection @@ -75,12 +74,11 @@ class Connection extends \Doctrine\DBAL\Connection /** * Creates a new instance of a SQL query builder. * - * @param \TYPO3\CMS\Core\Database\Query\QueryContext $queryContext * @return \TYPO3\CMS\Core\Database\Query\QueryBuilder */ - public function createQueryBuilder(QueryContext $queryContext = null): QueryBuilder + public function createQueryBuilder(): QueryBuilder { - return GeneralUtility::makeInstance(QueryBuilder::class, $this, $queryContext); + return GeneralUtility::makeInstance(QueryBuilder::class, $this); } /** diff --git a/typo3/sysext/core/Classes/Database/Query/Expression/CompositeExpression.php b/typo3/sysext/core/Classes/Database/Query/Expression/CompositeExpression.php index 14affb6d74f8..ea87c2f3ccfb 100644 --- a/typo3/sysext/core/Classes/Database/Query/Expression/CompositeExpression.php +++ b/typo3/sysext/core/Classes/Database/Query/Expression/CompositeExpression.php @@ -23,6 +23,8 @@ class CompositeExpression extends \Doctrine\DBAL\Query\Expression\CompositeExpre { /** * Retrieves the string representation of this composite expression. + * If expression is empty, just return an empty string. + * Native Doctrine expression would return () instead. * * @return string */ @@ -31,7 +33,26 @@ class CompositeExpression extends \Doctrine\DBAL\Query\Expression\CompositeExpre if ($this->count() === 0) { return ''; } - return parent::__toString(); } + + /** + * Adds an expression to composite expression. + * + * @param mixed $part + * + * @return \Doctrine\DBAL\Query\Expression\CompositeExpression + */ + public function add($part) + { + // Due to a bug in Doctrine DBAL, we must add our own check here, + // which we luckily can, as we use a subclass anyway. + // @see https://github.com/doctrine/dbal/issues/2388 + $isEmpty = $part instanceof self ? $part->count() === 0 : empty($part); + if (!$isEmpty) { + parent::add($part); + } + + return $this; + } } diff --git a/typo3/sysext/core/Classes/Database/Query/QueryBuilder.php b/typo3/sysext/core/Classes/Database/Query/QueryBuilder.php index 1530b212e6b3..7cdf3ed280db 100644 --- a/typo3/sysext/core/Classes/Database/Query/QueryBuilder.php +++ b/typo3/sysext/core/Classes/Database/Query/QueryBuilder.php @@ -18,6 +18,8 @@ namespace TYPO3\CMS\Core\Database\Query; use Doctrine\DBAL\Query\Expression\CompositeExpression; use TYPO3\CMS\Core\Database\Connection; use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder; +use TYPO3\CMS\Core\Database\Query\Restriction\DefaultRestrictionContainer; +use TYPO3\CMS\Core\Database\Query\Restriction\QueryRestrictionContainerInterface; use TYPO3\CMS\Core\Utility\GeneralUtility; /** @@ -51,52 +53,49 @@ class QueryBuilder protected $concreteQueryBuilder; /** - * @var QueryContext + * @var QueryRestrictionContainerInterface */ - protected $queryContext; + protected $restrictionContainer; /** * Initializes a new QueryBuilder. * * @param Connection $connection The DBAL Connection. - * @param QueryContext $queryContext + * @param QueryRestrictionContainerInterface $restrictionContainer * @param \Doctrine\DBAL\Query\QueryBuilder $concreteQueryBuilder */ public function __construct( Connection $connection, - QueryContext $queryContext = null, + QueryRestrictionContainerInterface $restrictionContainer = null, \Doctrine\DBAL\Query\QueryBuilder $concreteQueryBuilder = null ) { $this->connection = $connection; + $this->restrictionContainer = $restrictionContainer ?: GeneralUtility::makeInstance(DefaultRestrictionContainer::class); + $this->concreteQueryBuilder = $concreteQueryBuilder ?: GeneralUtility::makeInstance(\Doctrine\DBAL\Query\QueryBuilder::class, $connection); + } - if ($queryContext === null) { - $queryContext = GeneralUtility::makeInstance(QueryContext::class); - } - $this->queryContext = $queryContext; - - if ($concreteQueryBuilder === null) { - $concreteQueryBuilder = GeneralUtility::makeInstance( - \Doctrine\DBAL\Query\QueryBuilder::class, - $connection - ); - } - $this->concreteQueryBuilder = $concreteQueryBuilder; + /** + * @return QueryRestrictionContainerInterface + */ + public function getRestrictions() + { + return $this->restrictionContainer; } /** - * @return QueryContext + * @param QueryRestrictionContainerInterface $restrictionContainer */ - public function getQueryContext(): QueryContext + public function setRestrictions(QueryRestrictionContainerInterface $restrictionContainer) { - return $this->queryContext; + $this->restrictionContainer = $restrictionContainer; } /** - * @param QueryContext $queryContext + * Re-apply default restrictions */ - public function setQueryContext(QueryContext $queryContext) + public function resetRestrictions() { - $this->queryContext = $queryContext; + $this->restrictionContainer = GeneralUtility::makeInstance(DefaultRestrictionContainer::class); } /** @@ -167,12 +166,12 @@ class QueryBuilder return $this->concreteQueryBuilder->execute(); } - // set additional query restrictions based on context & TCA config - $originalWhereConditions = $this->addAdditonalWhereConditions(); + // Set additional query restrictions + $originalWhereConditions = $this->addAdditionalWhereConditions(); $result = $this->concreteQueryBuilder->execute(); - // restore the original query conditions in case the user keeps + // Restore the original query conditions in case the user keeps // on modifying the state. $this->concreteQueryBuilder->add('where', $originalWhereConditions, false); @@ -193,12 +192,12 @@ class QueryBuilder return $this->concreteQueryBuilder->getSQL(); } - // set additional query restrictions based on context & TCA config - $originalWhereConditions = $this->addAdditonalWhereConditions(); + // Set additional query restrictions + $originalWhereConditions = $this->addAdditionalWhereConditions(); $sql = $this->concreteQueryBuilder->getSQL(); - // restore the original query conditions in case the user keeps + // Restore the original query conditions in case the user keeps // on modifying the state. $this->concreteQueryBuilder->add('where', $originalWhereConditions, false); @@ -900,6 +899,18 @@ class QueryBuilder return $this->concreteQueryBuilder->createPositionalParameter($value, $type); } + /** + * Quotes like wildcards for given string value. + * + * @param string $value The value to be quoted. + * + * @return string The quoted value. + */ + public function escapeLikeWildcards(string $value): string + { + return addcslashes($value, '_%'); + } + /** * Quotes a given input parameter. * @@ -1054,28 +1065,24 @@ class QueryBuilder * * @return \Doctrine\DBAL\Query\Expression\CompositeExpression|mixed */ - protected function addAdditonalWhereConditions() + protected function addAdditionalWhereConditions() { - $queryRestrictionBuilder = GeneralUtility::makeInstance( - QueryRestrictionBuilder::class, - $this->getQueriedTables(), - $this->expr(), - $this->getQueryContext() - ); - $originalWhereConditions = $this->concreteQueryBuilder->getQueryPart('where'); - if ($originalWhereConditions instanceof CompositeExpression) { - $originalWhereConditions = clone($originalWhereConditions); + $expression = $this->restrictionContainer->buildExpression($this->getQueriedTables(), $this->expr()); + // This check would be obsolete, as the composite expression would not add empty expressions anyway + // But we keep it here to only clone the previous state, in case we really will change it. + // Once we remove this state preserving functionality, we can remove the count check here + // and just add the expression to the query builder. + if ($expression->count() > 0) { + if ($originalWhereConditions instanceof CompositeExpression) { + // Save the original query conditions so we can restore + // them after the query has been built. + $originalWhereConditions = clone($originalWhereConditions); + } + $this->concreteQueryBuilder->andWhere($expression); } - $additionalQueryRestrictions = $queryRestrictionBuilder->getVisibilityConstraints(); - - if ($additionalQueryRestrictions->count() !== 0) { - // save the original query conditions so we can restore - // them after the query has been built. - - $this->concreteQueryBuilder->andWhere($additionalQueryRestrictions); - } + // @todo add hook to be able to add additional restrictions return $originalWhereConditions; } diff --git a/typo3/sysext/core/Classes/Database/Query/QueryContext.php b/typo3/sysext/core/Classes/Database/Query/QueryContext.php deleted file mode 100644 index caf0e4d8ff06..000000000000 --- a/typo3/sysext/core/Classes/Database/Query/QueryContext.php +++ /dev/null @@ -1,596 +0,0 @@ -<?php -declare (strict_types = 1); -namespace TYPO3\CMS\Core\Database\Query; - -/* - * 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\Core\Utility\GeneralUtility; -use TYPO3\CMS\Frontend\Page\PageRepository; - -/** - * TYPO3 / TCA specific query settings that deal with enable/hidden fields, - * frontend groups and start-/endtimes. - */ -class QueryContext -{ - /** - * The context for which the restraints are to be built. - * - * @var QueryContextType - */ - protected $context; - - /** - * @var int[] - */ - protected $memberGroups = null; - - /** - * @var int - */ - protected $currentWorkspace = null; - - /** - * @var int - */ - protected $accessTime = null; - - /** - * Global flag if hidden records are to be included in the query result. - * - * In PageRepository::enableFields() this is called showHidden - * - * @var bool - */ - protected $includeHidden = null; - - /** - * Global flag if deleted records are to be included in the query result. - * - * @var bool - */ - protected $includeDeleted = false; - - /** - * Per table flag if deleted records are to be included in the query result. - * - * @var array - */ - protected $includeDeletedForTable = []; - - /** - * Global flag if records in a non-default versioned state should be - * included in the query results. - * - * In PageRepository the flag is called versioningPreview - * - * @var bool - */ - protected $includePlaceholders = null; - - /** - * Global flag if versioned records are to be included in the query result. - * Also influences if enable fields are respected for the query. - * - * In PageRepository the flag is called noVersionPreview - * - * @var bool - */ - protected $includeVersionedRecords = false; - - /** - * Global flag if enable fields are going to be checked for the query. - * - * @var bool - */ - protected $ignoreEnableFields = false; - - /** - * Global list of enable columns that are not checked for the query. - * This list is only checked if $ignoreEnableFields is enabled. - * - * @var string[] - */ - protected $ignoredEnableFields = []; - - /** - * Per table list of enable columns that are not checked for the query. - * This list is only checked if $ignoreEnableFields is enabled. - * - * @var string[] - */ - protected $ignoredEnableFieldsForTable = []; // Per Table list of ignored columns - - /** - * Associative array of table configs to override the TCA definition. If a table - * is not configured here the setup information from the TCA will be used. - * - * The array key is the table name, the value is in the format - * [ - * 'deleted' => 'fieldName', - * 'versioningWS' => true, - * 'enablecolumns' => [ 'disabled' => hidden, ... ] - * ] - * - * @var array - */ - protected $tableConfigs = []; - - /** - * QueryContext constructor. - * - * @param string $context A valid QueryContextType - */ - public function __construct(string $context = QueryContextType::AUTO) - { - $this->context = GeneralUtility::makeInstance(QueryContextType::class, $context); - } - - /** - * @return string - */ - public function getContext(): string - { - if ($this->context->equals(QueryContextType::AUTO)) { - if (TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_FE) { - return QueryContextType::FRONTEND; - } elseif (TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_BE) { - return QueryContextType::BACKEND; - } else { - return QueryContextType::UNRESTRICTED; - } - } - - return (string)$this->context; - } - - /** - * Set the context in which the query is going to be run. - * Used by the QueryRestrictionBuilder to determine the restrictions to be placed. - * - * @param string $context - * @return \TYPO3\CMS\Core\Database\Query\QueryContext - */ - public function setContext(string $context): QueryContext - { - $this->context = GeneralUtility::makeInstance(QueryContextType::class, $context); - - return $this; - } - - /** - * Get a list of member groups (fe_groups) that will be used in when building - * query restrictions in FE context. - * - * @return int[] - */ - public function getMemberGroups(): array - { - // If the member groups have not been explicitly set - // the group list from the frontend controller context - // will be inherited - if ($this->memberGroups === null) { - $this->memberGroups = GeneralUtility::intExplode( - ',', - $this->getTypoScriptFrontendController()->gr_list, - true - ); - } - - return (array)$this->memberGroups; - } - - /** - * Set the member groups that will be checked in frontend context. - * - * @param int[] $memberGroups - * @return \TYPO3\CMS\Core\Database\Query\QueryContext - */ - public function setMemberGroups(array $memberGroups): QueryContext - { - $this->memberGroups = $memberGroups; - - return $this; - } - - /** - * Get the current workspace. If not actively defined it will fall back - * to the current workspace set in the PageRepository. - * - * @return int - */ - public function getCurrentWorkspace(): int - { - return $this->currentWorkspace ?? (int)$this->getPageRepository()->versioningWorkspaceId; - } - - /** - * Set the current workspace id. - * - * @param int $currentWorkspace - * @return \TYPO3\CMS\Core\Database\Query\QueryContext - */ - public function setCurrentWorkspace(int $currentWorkspace): QueryContext - { - $this->currentWorkspace = $currentWorkspace; - - return $this; - } - - /** - * Return the current accesstime. If not explictly set fall back to the - * value of $GLOBALS['SIM_ACCESS_TIME'] - * - * @return int - */ - public function getAccessTime(): int - { - if ($this->accessTime === null) { - return empty($GLOBALS['SIM_ACCESS_TIME']) ? 0 : (int)$GLOBALS['SIM_ACCESS_TIME']; - } - - return $this->accessTime; - } - - /** - * Set the current access time. - * - * @param int $accessTime - * @return \TYPO3\CMS\Core\Database\Query\QueryContext - */ - public function setAccessTime(int $accessTime): QueryContext - { - $this->accessTime = $accessTime; - - return $this; - } - - /** - * Returns the global setting wether hidden records should be included - * in the query result. Preferrably getIncludeHiddenForTable() should - * be used as the proper information from TSFE can be inherited by - * using the table name information. - * - * Defaults to false in case the flag has not been explictly set. - * - * @return bool - * @internal - */ - public function getIncludeHidden(): bool - { - // Casting to bool to accomodate for the legacy fallback: - // When showHidden has not been explicitly set it's going to - // be determined by the settings in the TyposcriptFrontendController. - // As we don't now the table being queried here it's better to use - // getIncludeHiddenForTable() - return (bool)$this->includeHidden; - } - - /** - * Flag if hidden records for the given table should be included in the query result. - * If $includeHidden has not been explictly set the information from TSFE will be - * used to determine the setting. - * - * @param string $table - * @return bool - */ - public function getIncludeHiddenForTable(string $table): bool - { - if ($this->includeHidden === null && is_object($this->getTypoScriptFrontendController())) { - $showHidden = $table === 'pages' || $table === 'pages_language_overlay' - ? $this->getTypoScriptFrontendController()->showHiddenPage - : $this->getTypoScriptFrontendController()->showHiddenRecords; - - if ($showHidden === -1) { - $showHidden = false; - } - - $this->includeHidden = (bool)$showHidden; - } - - return (bool)$this->includeHidden; - } - - /** - * Set if hidden records should be part of the query result set. - * - * @param bool $includeHidden - * @return \TYPO3\CMS\Core\Database\Query\QueryContext - */ - public function setIncludeHidden(bool $includeHidden): QueryContext - { - $this->includeHidden = $includeHidden; - - return $this; - } - - /** - * Get if deleted records should be part of the query result set at all. - * - * @return bool - */ - public function getIncludeDeleted(): bool - { - return $this->includeDeleted; - } - - /** - * Set wether deleted records shoult be part of the query result. - * - * @param bool $includeDeleted - * @return \TYPO3\CMS\Core\Database\Query\QueryContext - */ - public function setIncludeDeleted(bool $includeDeleted): QueryContext - { - $this->includeDeleted = $includeDeleted; - - return $this; - } - - /** - * Get if records in a non-default versioning state should be part of the query result set. - * - * @return bool - */ - public function getIncludePlaceholders(): bool - { - if ($this->includePlaceholders === null) { - $this->includePlaceholders = $this->getPageRepository()->versioningPreview; - } - - return (bool)$this->includePlaceholders; - } - - /** - * Set if records in a non-default versioning state should be part of the query result set. - * - * @param bool $includePlaceholders - * @return \TYPO3\CMS\Core\Database\Query\QueryContext - */ - public function setIncludePlaceholders(bool $includePlaceholders): QueryContext - { - $this->includePlaceholders = $includePlaceholders; - - return $this; - } - - /** - * Get if versioned records shoult be part of the query result set. - * - * @return bool - */ - public function getIncludeVersionedRecords(): bool - { - return $this->includeVersionedRecords; - } - - /** - * Set if versioned records should be part of the query result set. - * - * @param bool $includeVersionedRecords - * @return \TYPO3\CMS\Core\Database\Query\QueryContext - */ - public function setIncludeVersionedRecords(bool $includeVersionedRecords): QueryContext - { - $this->includeVersionedRecords = $includeVersionedRecords; - - return $this; - } - - /** - * Get if enable fields should be ignored for this query. - * - * @return bool - */ - public function getIgnoreEnableFields(): bool - { - return $this->ignoreEnableFields; - } - - /** - * Set if enable fields should be ignored for this query. - * - * @param bool $ignoreEnableFields - * @return \TYPO3\CMS\Core\Database\Query\QueryContext - */ - public function setIgnoreEnableFields(bool $ignoreEnableFields): QueryContext - { - $this->ignoreEnableFields = $ignoreEnableFields; - - return $this; - } - - /** - * Return global list of ignored enable columns for the query. - * Can be overridden per table. Only checked if $ignoreEnableFields is enabled. - * - * @return string[] - */ - public function getIgnoredEnableFields(): array - { - return $this->ignoredEnableFields; - } - - /** - * Set the global list of ignored enable columns. - * - * @param string[] $ignoredEnableFields - * @return \TYPO3\CMS\Core\Database\Query\QueryContext - */ - public function setIgnoredEnableFields(array $ignoredEnableFields): QueryContext - { - $this->ignoredEnableFields = $ignoredEnableFields; - - return $this; - } - - /** - * Get the ignored enable columns for this table. - * If no specific list has been defined the global list will be returned. - * - * @param string $table - * @return string[] - */ - public function getIgnoredEnableFieldsForTable(string $table): array - { - if (isset($this->ignoredEnableFieldsForTable[$table])) { - return $this->ignoredEnableFieldsForTable[$table]; - } elseif (!empty($this->ignoredEnableFields)) { - return $this->ignoredEnableFields; - } - - return []; - } - - /** - * @param string $table - * @param string[] $ignoredEnableFieldsForTable - * @return \TYPO3\CMS\Core\Database\Query\QueryContext - */ - public function setIgnoredEnableFieldsForTable(string $table, array $ignoredEnableFieldsForTable): QueryContext - { - $this->ignoredEnableFieldsForTable[$table] = $ignoredEnableFieldsForTable; - - return $this; - } - - /** - * Get if deleted records for this table should be included in the query result set. - * - * @param string $table - * @return bool - */ - public function getIncludeDeletedForTable(string $table): bool - { - return $this->includeDeletedForTable[$table] ?? false; - } - - /** - * Set if deleted records for this table should be included in the query result. - * - * @param string $table - * @param bool $includeDeletedForTable - * @return \TYPO3\CMS\Core\Database\Query\QueryContext - */ - public function setIncludeDeletedForTable(string $table, bool $includeDeletedForTable): QueryContext - { - $this->includeDeletedForTable[$table] = $includeDeletedForTable; - - return $this; - } - - /** - * Get the table configuration information for all tables. - * - * @return array - */ - public function getTableConfigs(): array - { - return $this->tableConfigs; - } - - /** - * Set the table configuration for all tables. - * - * @param array $tableConfigs - * @return \TYPO3\CMS\Core\Database\Query\QueryContext - */ - public function setTableConfigs(array $tableConfigs): QueryContext - { - $this->tableConfigs = $tableConfigs; - - return $this; - } - - /** - * Get the table configuration for a single table. - * - * @param string $table - * @return array - */ - public function getTableConfig(string $table): array - { - return $this->tableConfigs[$table] ?? $this->getTcaDefiniton($table); - } - - /** - * Get the TCA definition for a tables and extract the relevant parts - * of the table configuration. - * - * @param string $table - * @return array - */ - protected function getTcaDefiniton(string $table): array - { - $ctrlDefiniton = $GLOBALS['TCA'][$table]['ctrl'] ?? []; - return array_intersect_key( - $ctrlDefiniton, - ['delete' => true, 'versioningWS' => true, 'enablecolumns' => true] - ); - } - - /** - * Add a table configuration entry to the table config array. - * - * @param string $table - * @param string $deletedField - * @param bool $versioningSupport - * @param array $enableColumns - * @return \TYPO3\CMS\Core\Database\Query\QueryContext - */ - public function addTableConfig( - string $table, - string $deletedField = null, - bool $versioningSupport = false, - array $enableColumns = [] - ): QueryContext { - $this->tableConfigs[$table] = [ - 'deleted' => $deletedField, - 'versioningWS' => $versioningSupport, - 'enablecolumns' => $enableColumns - ]; - } - - /** - * Remove a table override from the config array. - * - * @param string $table - * @return \TYPO3\CMS\Core\Database\Query\QueryContext - */ - public function removeTableConfig(string $table): QueryContext - { - unset($this->tableConfigs[$table]); - - return $this; - } - - /** - * @return \TYPO3\CMS\Frontend\Page\PageRepository - */ - protected function getPageRepository(): PageRepository - { - if ($this->getContext() === QueryContextType::FRONTEND && is_object($this->getTypoScriptFrontendController())) { - return $this->getTypoScriptFrontendController()->sys_page; - } else { - return GeneralUtility::makeInstance(PageRepository::class); - } - } - - /** - * @return \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController - */ - protected function getTypoScriptFrontendController() - { - return $GLOBALS['TSFE']; - } -} diff --git a/typo3/sysext/core/Classes/Database/Query/QueryContextType.php b/typo3/sysext/core/Classes/Database/Query/QueryContextType.php deleted file mode 100644 index 8b20d795470f..000000000000 --- a/typo3/sysext/core/Classes/Database/Query/QueryContextType.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php -declare (strict_types = 1); -namespace TYPO3\CMS\Core\Database\Query; - -/* - * 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! - */ - -/** - * Enumeration object for query context type - * - */ -class QueryContextType extends \TYPO3\CMS\Core\Type\Enumeration -{ - const __default = self::AUTO; - - /** - * Constants reflecting the query context type - */ - const AUTO = 'AUTO'; - const UNRESTRICTED = 'UNRESTRICTED'; - const FRONTEND = 'FRONTEND'; - const BACKEND = 'BACKEND'; - const BACKEND_NO_VERSIONING_PLACEHOLDERS = 'BACKEND_NO_VERSIONING_PLACEHOLDERS'; - - /** - * @param mixed $type - */ - public function __construct($type = null) - { - if ($type !== null) { - $type = strtoupper((string)$type); - } - - parent::__construct($type); - } -} diff --git a/typo3/sysext/core/Classes/Database/Query/QueryRestrictionBuilder.php b/typo3/sysext/core/Classes/Database/Query/QueryRestrictionBuilder.php deleted file mode 100644 index 66ba323c69a1..000000000000 --- a/typo3/sysext/core/Classes/Database/Query/QueryRestrictionBuilder.php +++ /dev/null @@ -1,383 +0,0 @@ -<?php -declare (strict_types = 1); -namespace TYPO3\CMS\Core\Database\Query; - -/* - * 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\Core\Database\Query\Expression\CompositeExpression; -use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder; -use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Core\Versioning\VersionState; - -/** - * Builder for SQL query constraints based on TCA settings. - * The resulting composite expressions can be added to a query - * being built using the QueryBuilder object. - * - * The restrictions being built by this class are to be used for all - * select queries done by the QueryBuilder to avoid returning data - * that should not be available to the caller based on the current - * TYPO3 context. - * - * Restrictions that will be created can be configured using the - * QuerySettings on the main QueryBuilder object. - * - * WARNING: This code has cross cutting concerns as it requires access - * to the TypoScriptFrontEndController and $GLOBALS['TCA'] to build the - * right queries. - */ -class QueryRestrictionBuilder -{ - /** - * @var \TYPO3\CMS\Frontend\Page\PageRepository - */ - protected $pageRepository; - - /** - * @var \TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder - */ - protected $expressionBuilder; - - /** - * @var \TYPO3\CMS\Core\Database\Query\QueryContext - */ - protected $queryContext; - - /** - * @var string[] - */ - protected $queriedTables = []; - - /** - * Initializes a new QueryBuilder. - * - * @param string[] $queriedTables - * @param ExpressionBuilder $expressionBuilder The ExpressionBuilder with which to create restrictions - * @param \TYPO3\CMS\Core\Database\Query\QueryContext $queryContext - */ - public function __construct( - array $queriedTables, - ExpressionBuilder $expressionBuilder, - QueryContext $queryContext = null - ) { - $this->queriedTables = $queriedTables; - $this->expressionBuilder = $expressionBuilder; - $this->queryContext = $queryContext ?? GeneralUtility::makeInstance(QueryContext::class); - } - - /** - * Returns a composite expression to add visibility restrictions for - * the selected tables based on the current context (FE/BE). - * - * You need to check if any conditions are added to the CompositeExpression - * before adding it to your query using `->count()`. - * - * @return CompositeExpression - */ - public function getVisibilityConstraints(): CompositeExpression - { - switch ($this->queryContext->getContext()) { - case QueryContextType::FRONTEND: - return $this->getFrontendVisibilityRestrictions(); - case QueryContextType::BACKEND: - case QueryContextType::BACKEND_NO_VERSIONING_PLACEHOLDERS: - return $this->getBackendVisibilityConstraints(); - case QueryContextType::UNRESTRICTED: - return $this->expressionBuilder->andX(); - default: - throw new \RuntimeException( - 'Unknown TYPO3 Context / Request type: "' . TYPO3_REQUESTTYPE . '".', - 1459708283 - ); - } - } - - /** - * Returns a composite expression takeing into account visibility restrictions - * imposed by enableFields, versioning/workspaces and deletion. - * - * @return \TYPO3\CMS\Core\Database\Query\Expression\CompositeExpression - * @throws \LogicException - */ - protected function getFrontendVisibilityRestrictions(): CompositeExpression - { - $queryContext = $this->queryContext; - $ignoreEnableFields = $queryContext->getIgnoreEnableFields(); - $includeDeleted = $queryContext->getIncludeDeleted(); - - if (!$ignoreEnableFields && $includeDeleted) { - throw new \LogicException( - 'The query settings "ignoreEnableFields=FALSE" and "includeDeleted=TRUE" can not be used together ' - . 'in frontend context.', - 1459690516 - ); - } - - $constraints = []; - foreach ($this->queriedTables as $tableName => $tableAlias) { - $tableConfig = $queryContext->getTableConfig($tableName); - if (!$ignoreEnableFields && !$includeDeleted) { - $constraint = $this->getEnableFieldConstraints( - $tableName, - $tableAlias, - $queryContext->getIncludeHiddenForTable($tableName), - [], - $queryContext->getIncludeVersionedRecords() - ); - if ($constraint->count() !== 0) { - $constraints[] = $constraint; - } - } elseif ($ignoreEnableFields && !$includeDeleted) { - if (!empty($queryContext->getIgnoredEnableFieldsForTable($tableName))) { - $constraint = $this->getEnableFieldConstraints( - $tableName, - $tableAlias, - $queryContext->getIncludeHiddenForTable($tableName), - $queryContext->getIgnoredEnableFieldsForTable($tableName), - $queryContext->getIncludeVersionedRecords() - ); - if ($constraint->count() !== 0) { - $constraints[] = $constraint; - } - } elseif (!empty($tableConfig['delete'])) { - $tablePrefix = empty($tableAlias) ? $tableName : $tableAlias; - $constraints[] = $this->expressionBuilder->eq( - $tablePrefix . '.' . $tableConfig['delete'], - 0 - ); - } - } - } - - return $this->expressionBuilder->andX(...$constraints); - } - - /** - * Returns a composite expression to restrict access to records for the backend context. - * - * @return CompositeExpression - * @todo: Lots of code duplication, check how/if this can be merged with the "getEnableFieldConstraints" - * @todo: after the test cases are done for backend and frontend. - */ - protected function getBackendVisibilityConstraints(): CompositeExpression - { - $queryContext = $this->queryContext; - $ignoreEnableFields = $queryContext->getIgnoreEnableFields(); - $includeDeleted = $queryContext->getIncludeDeleted(); - - $constraints = []; - $expressionBuilder = $this->expressionBuilder; - - foreach ($this->queriedTables as $tableName => $tableAlias) { - $tableConfig = $queryContext->getTableConfig($tableName); - $tablePrefix = empty($tableAlias) ? $tableName : $tableAlias; - - if (empty($tableConfig)) { - // No restrictions for this table, not configured by TCA - continue; - } - - if (!$ignoreEnableFields && is_array($tableConfig['enablecolumns'])) { - $enableColumns = $tableConfig['enablecolumns']; - - if (isset($enableColumns['disabled'])) { - $constraints[] = $expressionBuilder->eq( - $tablePrefix . '.' . $enableColumns['disabled'], - 0 - ); - } - if ($enableColumns['starttime']) { - $constraints[] = $expressionBuilder->lte( - $tablePrefix . '.' . $enableColumns['starttime'], - $queryContext->getAccessTime() - ); - } - if ($enableColumns['endtime']) { - $fieldName = $tablePrefix . '.' . $enableColumns['endtime']; - $constraints[] = $expressionBuilder->orX( - $expressionBuilder->eq($fieldName, 0), - $expressionBuilder->gt($fieldName, $queryContext->getAccessTime()) - ); - } - } - - if (!$includeDeleted && !empty($tableConfig['delete'])) { - $constraints[] = $this->expressionBuilder->eq( - $tablePrefix . '.' . $tableConfig['delete'], - 0 - ); - } - - if ($queryContext->getContext() === QueryContextType::BACKEND_NO_VERSIONING_PLACEHOLDERS - && !empty($tableConfig['versioningWS']) - ) { - $constraints[] = $this->expressionBuilder->orX( - $expressionBuilder->lte( - $tablePrefix . '.t3ver_state', - new VersionState(VersionState::DEFAULT_STATE) - ), - $expressionBuilder->eq($tablePrefix . '.t3ver_wsid', $queryContext->getCurrentWorkspace()) - ); - } - } - - return $expressionBuilder->andX(...$constraints); - } - - /** - * @param string $tableName The table name to query - * @param string|null $tableAlias The table alias to use for constraints. $tableName used when empty. - * @param bool $showHidden Select hidden records - * @param string[] $ignoreFields Names of enable columns to be ignored - * @param bool $noVersionPreview If set, enableFields will be applied regardless of any versioning preview - * settings which might otherwise disable enableFields - * @return \TYPO3\CMS\Core\Database\Query\Expression\CompositeExpression - */ - protected function getEnableFieldConstraints( - string $tableName, - string $tableAlias = null, - bool $showHidden = false, - array $ignoreFields = [], - bool $noVersionPreview = false - ): CompositeExpression { - $queryContext = $this->queryContext; - $tableConfig = $queryContext->getTableConfig($tableName); - - if (empty($tableConfig)) { - // No restrictions for this table, not configured by TCA - return $this->expressionBuilder->andX(); - } - - $tablePrefix = empty($tableAlias) ? $tableName : $tableAlias; - - $constraints = []; - $expressionBuilder = $this->expressionBuilder; - - // Restrict based on deleted flag of records - if (!empty($tableConfig['delete'])) { - $constraints[] = $expressionBuilder->eq($tablePrefix . '.deleted', 0); - } - - // Restrict based on Workspaces / Versioning - if (!empty($tableConfig['versioningWS'])) { - if (!$queryContext->getIncludePlaceholders()) { - // Filter out placeholder records (new/moved/deleted items) in case we are NOT in a versioning preview - // (This means that means we are online!) - $constraints[] = $expressionBuilder->lte( - $tablePrefix . '.t3ver_state', - new VersionState(VersionState::DEFAULT_STATE) - ); - } elseif ($tableName !== 'pages') { - // Show only records of the live and current workspace in case we are in a versioning preview - $constraints[] = $expressionBuilder->orX( - $expressionBuilder->eq($tablePrefix . '.t3ver_wsid', 0), - $expressionBuilder->eq($tablePrefix . '.t3ver_wsid', $queryContext->getCurrentWorkspace()) - ); - } - - // Filter out versioned records - if (!$noVersionPreview && !in_array('pid', $ignoreFields)) { - $constraints[] = $expressionBuilder->neq($tablePrefix . '.pid', -1); - } - } - - // Restrict based on enable fields. In case of versioning-preview, enableFields are ignored - // and later checked in versionOL(). - if (is_array($tableConfig['enablecolumns']) - && (!$queryContext->getIncludePlaceholders() || empty($tableConfig['versioningWS']) || $noVersionPreview) - ) { - $enableColumns = $tableConfig['enablecolumns']; - - // Filter out disabled records - if (isset($enableColumns['disabled']) && !$showHidden && !in_array('disabled', $ignoreFields)) { - $constraints[] = $expressionBuilder->eq( - $tablePrefix . '.' . $enableColumns['disabled'], - 0 - ); - } - - // Filter out records where the starttime has not yet been reached. - if (isset($enableColumns['starttime']) && !in_array('starttime', $ignoreFields)) { - $constraints[] = $expressionBuilder->lte( - $tablePrefix . '.' . $enableColumns['starttime'], - $queryContext->getAccessTime() - ); - } - - // Filter out records with a set endtime where the time is in the past. - if (isset($enableColumns['endtime']) && !in_array('endtime', $ignoreFields)) { - $constraints[] = $expressionBuilder->orX( - $expressionBuilder->eq($tablePrefix . '.' . $enableColumns['endtime'], 0), - $expressionBuilder->gt( - $tablePrefix . '.' . $enableColumns['endtime'], - $queryContext->getAccessTime() - ) - ); - } - - // Filter out records based on the frondend user groups - if ($enableColumns['fe_group'] && !in_array('fe_group', $ignoreFields)) { - $constraints[] = $this->getFrontendUserGroupConstraints( - $tablePrefix, - $enableColumns['fe_group'] - ); - } - - // Call hook functions for additional enableColumns - if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['addEnableColumns'])) { - $_params = [ - 'table' => $tableName, - 'tableAlias' => $tableAlias, - 'tablePrefix' => $tablePrefix, - 'show_hidden' => $showHidden, - 'ignore_array' => $ignoreFields, - 'ctrl' => $tableConfig - ]; - foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['addEnableColumns'] as $_funcRef) { - $constraint = GeneralUtility::callUserFunction($_funcRef, $_params, $this); - - $constraints[] = preg_replace('/^(?:AND[[:space:]]*)+/i', '', trim($constraint)); - } - } - } - - return $expressionBuilder->andX(...$constraints); - } - - /** - * @param string $tableName The table name to build constraints for - * @param string $fieldName The field name to build constraints for - * - * @return CompositeExpression - */ - protected function getFrontendUserGroupConstraints(string $tableName, string $fieldName): CompositeExpression - { - $expressionBuilder = $this->expressionBuilder; - // Allow records where no group access has been configured (field values NULL, 0 or empty string) - $constraints = [ - $expressionBuilder->isNull($tableName . '.' . $fieldName), - $expressionBuilder->eq($tableName . '.' . $fieldName, $expressionBuilder->literal('')), - $expressionBuilder->eq($tableName . '.' . $fieldName, $expressionBuilder->literal('0')), - ]; - - foreach ($this->queryContext->getMemberGroups() as $value) { - $constraints[] = $expressionBuilder->inSet( - $tableName . '.' . $fieldName, - $expressionBuilder->literal((string)$value) - ); - } - - return $expressionBuilder->orX(...$constraints); - } -} diff --git a/typo3/sysext/core/Classes/Database/Query/Restriction/AbstractRestrictionContainer.php b/typo3/sysext/core/Classes/Database/Query/Restriction/AbstractRestrictionContainer.php new file mode 100644 index 000000000000..5da68dcf58a4 --- /dev/null +++ b/typo3/sysext/core/Classes/Database/Query/Restriction/AbstractRestrictionContainer.php @@ -0,0 +1,95 @@ +<?php +declare (strict_types = 1); +namespace TYPO3\CMS\Core\Database\Query\Restriction; + +/* + * 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\Core\Database\Query\Expression\CompositeExpression; +use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder; +use TYPO3\CMS\Core\Utility\GeneralUtility; + +/** + * Base class for query restriction collections + */ +abstract class AbstractRestrictionContainer implements QueryRestrictionContainerInterface +{ + /** + * @var QueryRestrictionInterface[] + */ + protected $restrictions = []; + + /** + * Main method to build expressions for given tables. + * Iterating over all registered expressions and combine them with AND + * + * @param array $queriedTables Array of tables, where array key is table name and value potentially an alias + * @param ExpressionBuilder $expressionBuilder Expression builder instance to add restrictions with + * @return CompositeExpression The result of query builder expression(s) + */ + public function buildExpression(array $queriedTables, ExpressionBuilder $expressionBuilder): CompositeExpression + { + $constraints = []; + foreach ($this->restrictions as $restriction) { + $constraints[] = $restriction->buildExpression($queriedTables, $expressionBuilder); + } + return $expressionBuilder->andX(...$constraints); + } + + /** + * Removes all restrictions stored within this container + * + * @return QueryRestrictionContainerInterface + */ + public function removeAll() + { + $this->restrictions = []; + return $this; + } + + /** + * Remove restriction of a given type + * + * @param string $restrictionType Class name of the restriction to be removed + * @return QueryRestrictionContainerInterface + */ + public function removeByType(string $restrictionType) + { + unset($this->restrictions[$restrictionType]); + return $this; + } + + /** + * Add a new restriction instance to this collection + * + * @param QueryRestrictionInterface $restriction + * @return QueryRestrictionContainerInterface + */ + public function add(QueryRestrictionInterface $restriction) + { + $this->restrictions[get_class($restriction)] = $restriction; + return $this; + } + + /** + * Factory method for restrictions. + * Currently only instantiates the class. + * + * @param string $restrictionClass + * @return QueryRestrictionInterface + */ + protected function createRestriction($restrictionClass) + { + return GeneralUtility::makeInstance($restrictionClass); + } +} diff --git a/typo3/sysext/core/Classes/Database/Query/Restriction/BackendWorkspaceRestriction.php b/typo3/sysext/core/Classes/Database/Query/Restriction/BackendWorkspaceRestriction.php new file mode 100644 index 000000000000..0223e9a22916 --- /dev/null +++ b/typo3/sysext/core/Classes/Database/Query/Restriction/BackendWorkspaceRestriction.php @@ -0,0 +1,85 @@ +<?php +declare (strict_types = 1); +namespace TYPO3\CMS\Core\Database\Query\Restriction; + +/* + * 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\Core\Database\Query\Expression\CompositeExpression; +use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder; +use TYPO3\CMS\Core\Versioning\VersionState; + +/** + * Restriction to make queries in TYPO3 backend context versioning/ workspace aware + */ +class BackendWorkspaceRestriction implements QueryRestrictionInterface +{ + /** + * @var int + */ + protected $workspaceId; + + /** + * @var bool + */ + protected $includeRowsForWorkspaceOverlay; + + /** + * @param int $workspaceId + * @param bool $includeRowsForWorkspaceOverlay + */ + public function __construct(int $workspaceId = null, $includeRowsForWorkspaceOverlay = true) + { + $this->workspaceId = $workspaceId === null ? $GLOBALS['BE_USER']->workspace : $workspaceId; + $this->includeRowsForWorkspaceOverlay = $includeRowsForWorkspaceOverlay; + } + + /** + * Main method to build expressions for given tables + * + * @param array $queriedTables Array of tables, where array key is table name and value potentially an alias + * @param ExpressionBuilder $expressionBuilder Expression builder instance to add restrictions with + * @return CompositeExpression The result of query builder expression(s) + */ + public function buildExpression(array $queriedTables, ExpressionBuilder $expressionBuilder): CompositeExpression + { + $constraints = []; + foreach ($queriedTables as $tableName => $tableAlias) { + $workspaceEnabled = $GLOBALS['TCA'][$tableName]['ctrl']['versioningWS'] ?? null; + if (!empty($workspaceEnabled)) { + $tablePrefix = $tableAlias ?: $tableName; + $workspaceIdExpression = $expressionBuilder->eq( + $tablePrefix . '.t3ver_wsid', + (int)$this->workspaceId + ); + if ($this->includeRowsForWorkspaceOverlay) { + $constraints[] = $expressionBuilder->orX( + $workspaceIdExpression, + $expressionBuilder->lte( + $tablePrefix . '.t3ver_state', + new VersionState(VersionState::DEFAULT_STATE) + ) + ); + } else { + $comparisonExpression = $this->workspaceId === 0 ? 'neq' : 'eq'; + $constraints[] = $workspaceIdExpression; + $constraints[] = $expressionBuilder->{$comparisonExpression}( + $tablePrefix . '.pid', + -1 + ); + } + } + } + return $expressionBuilder->andX(...$constraints); + } +} diff --git a/typo3/sysext/core/Classes/Database/Query/Restriction/DefaultRestrictionContainer.php b/typo3/sysext/core/Classes/Database/Query/Restriction/DefaultRestrictionContainer.php new file mode 100644 index 000000000000..387cdc146078 --- /dev/null +++ b/typo3/sysext/core/Classes/Database/Query/Restriction/DefaultRestrictionContainer.php @@ -0,0 +1,44 @@ +<?php +declare (strict_types = 1); +namespace TYPO3\CMS\Core\Database\Query\Restriction; + +/* + * 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! + */ + +/** + * This is the container with restrictions, that are added to any doctrine query + */ +class DefaultRestrictionContainer extends AbstractRestrictionContainer +{ + /** + * Default restriction classes. + * + * @var QueryRestrictionInterface[] + */ + protected $defaultRestrictionTypes = [ + DeletedRestriction::class, + HiddenRestriction::class, + StartTimeRestriction::class, + EndTimeRestriction::class + ]; + + /** + * Creates instances of the registered default restriction classes + */ + public function __construct() + { + foreach ($this->defaultRestrictionTypes as $restrictionType) { + $this->add($this->createRestriction($restrictionType)); + } + } +} diff --git a/typo3/sysext/core/Classes/Database/Query/Restriction/DeletedRestriction.php b/typo3/sysext/core/Classes/Database/Query/Restriction/DeletedRestriction.php new file mode 100644 index 000000000000..9e4a192e0126 --- /dev/null +++ b/typo3/sysext/core/Classes/Database/Query/Restriction/DeletedRestriction.php @@ -0,0 +1,50 @@ +<?php +declare (strict_types = 1); +namespace TYPO3\CMS\Core\Database\Query\Restriction; + +/* + * 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\Core\Database\Query\Expression\CompositeExpression; +use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder; + +/** + * Restriction to respect the soft-delete functionality of TYPO3. + * Filters out records, that were marked as deleted. + */ +class DeletedRestriction implements QueryRestrictionInterface +{ + /** + * Main method to build expressions for given tables + * Evaluates the ctrl/delete flag of the table and adds the according restriction if set + * + * @param array $queriedTables Array of tables, where array key is table name and value potentially an alias + * @param ExpressionBuilder $expressionBuilder Expression builder instance to add restrictions with + * @return CompositeExpression The result of query builder expression(s) + */ + public function buildExpression(array $queriedTables, ExpressionBuilder $expressionBuilder): CompositeExpression + { + $constraints = []; + foreach ($queriedTables as $tableName => $tableAlias) { + $deletedFieldName = $GLOBALS['TCA'][$tableName]['ctrl']['delete'] ?? null; + if (!empty($deletedFieldName)) { + $tablePrefix = $tableAlias ?: $tableName; + $constraints[] = $expressionBuilder->eq( + $tablePrefix . '.' . $deletedFieldName, + 0 + ); + } + } + return $expressionBuilder->andX(...$constraints); + } +} diff --git a/typo3/sysext/core/Classes/Database/Query/Restriction/EndTimeRestriction.php b/typo3/sysext/core/Classes/Database/Query/Restriction/EndTimeRestriction.php new file mode 100644 index 000000000000..014cb4587d2c --- /dev/null +++ b/typo3/sysext/core/Classes/Database/Query/Restriction/EndTimeRestriction.php @@ -0,0 +1,69 @@ +<?php +declare (strict_types = 1); +namespace TYPO3\CMS\Core\Database\Query\Restriction; + +/* + * 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\Core\Database\Query\Expression\CompositeExpression; +use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder; + +/** + * Restriction to filter records with an end time set that has passed + */ +class EndTimeRestriction implements QueryRestrictionInterface +{ + /** + * @var int + */ + protected $accessTimeStamp; + + /** + * @param int $accessTimeStamp + */ + public function __construct(int $accessTimeStamp = null) + { + $this->accessTimeStamp = $accessTimeStamp ?: ($GLOBALS['SIM_ACCESS_TIME'] ?? null); + } + + /** + * Main method to build expressions for given tables + * Evaluates the ctrl/enablecolumns/endtime flag of the table and adds the according restriction if set + * + * @param array $queriedTables Array of tables, where array key is table name and value potentially an alias + * @param ExpressionBuilder $expressionBuilder Expression builder instance to add restrictions with + * @return CompositeExpression The result of query builder expression(s) + * @throws \RuntimeException + */ + public function buildExpression(array $queriedTables, ExpressionBuilder $expressionBuilder): CompositeExpression + { + $constraints = []; + foreach ($queriedTables as $tableName => $tableAlias) { + $endTimeFieldName = $GLOBALS['TCA'][$tableName]['ctrl']['enablecolumns']['endtime'] ?? null; + if (!empty($endTimeFieldName)) { + if (empty($this->accessTimeStamp)) { + throw new \RuntimeException( + 'accessTimeStamp needs to be set to an integer value, but is empty! Maybe $GLOBALS[\'SIM_ACCESS_TIME\'] has been overridden somewhere?', + 1462821084 + ); + } + $fieldName = ($tableAlias ?: $tableName) . '.' . $endTimeFieldName; + $constraints[] = $expressionBuilder->orX( + $expressionBuilder->eq($fieldName, 0), + $expressionBuilder->gt($fieldName, (int)$this->accessTimeStamp) + ); + } + } + return $expressionBuilder->andX(...$constraints); + } +} diff --git a/typo3/sysext/core/Classes/Database/Query/Restriction/FrontendGroupRestriction.php b/typo3/sysext/core/Classes/Database/Query/Restriction/FrontendGroupRestriction.php new file mode 100644 index 000000000000..efabc4e49b4b --- /dev/null +++ b/typo3/sysext/core/Classes/Database/Query/Restriction/FrontendGroupRestriction.php @@ -0,0 +1,70 @@ +<?php +declare (strict_types = 1); +namespace TYPO3\CMS\Core\Database\Query\Restriction; + +/* + * 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\Core\Database\Query\Expression\CompositeExpression; +use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder; + +/** + * Restriction to filter records, which are limited to the given user groups + */ +class FrontendGroupRestriction implements QueryRestrictionInterface +{ + /** + * @var array + */ + protected $frontendGroupIds; + + /** + * @param array $frontendGroupIds Normalized array with user groups of currently logged in user (typically expolded value of $GLOBALS['TSFE']->gr_list + */ + public function __construct(array $frontendGroupIds = null) + { + $this->frontendGroupIds = $frontendGroupIds === null ? explode(',', $GLOBALS['TSFE']->gr_list) : $frontendGroupIds; + } + + /** + * Main method to build expressions for given tables + * Evaluates the ctrl/enablecolumns/fe_group flag of the table and adds the according restriction if set + * + * @param array $queriedTables Array of tables, where array key is table name and value potentially an alias + * @param ExpressionBuilder $expressionBuilder Expression builder instance to add restrictions with + * @return CompositeExpression The result of query builder expression(s) + */ + public function buildExpression(array $queriedTables, ExpressionBuilder $expressionBuilder): CompositeExpression + { + $constraints = []; + foreach ($queriedTables as $tableName => $tableAlias) { + $groupFieldName = $GLOBALS['TCA'][$tableName]['ctrl']['enablecolumns']['fe_group'] ?? null; + if (!empty($groupFieldName)) { + $fieldName = ($tableAlias ?: $tableName) . '.' . $groupFieldName; + // Allow records where no group access has been configured (field values NULL, 0 or empty string) + $constraints = [ + $expressionBuilder->isNull($fieldName), + $expressionBuilder->eq($fieldName, $expressionBuilder->literal('')), + $expressionBuilder->eq($fieldName, $expressionBuilder->literal('0')), + ]; + foreach ($this->frontendGroupIds as $frontendGroupId) { + $constraints[] = $expressionBuilder->inSet( + $fieldName, + $expressionBuilder->literal((string)$frontendGroupId) + ); + } + } + } + return $expressionBuilder->orX(...$constraints); + } +} diff --git a/typo3/sysext/core/Classes/Database/Query/Restriction/FrontendRestrictionContainer.php b/typo3/sysext/core/Classes/Database/Query/Restriction/FrontendRestrictionContainer.php new file mode 100644 index 000000000000..5444fa0aa149 --- /dev/null +++ b/typo3/sysext/core/Classes/Database/Query/Restriction/FrontendRestrictionContainer.php @@ -0,0 +1,78 @@ +<?php +declare (strict_types = 1); +namespace TYPO3\CMS\Core\Database\Query\Restriction; + +/* + * 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\Core\Database\Query\Expression\CompositeExpression; +use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder; +use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController; + +/** + * A collection of restrictions to be used in frontend context. + * This is a replacement for PageRepository::enableFields() + */ +class FrontendRestrictionContainer extends AbstractRestrictionContainer +{ + /** + * @var QueryRestrictionInterface[] + */ + protected $defaultRestrictionTypes = [ + DeletedRestriction::class, + FrontendWorkspaceRestriction::class, + HiddenRestriction::class, + StartTimeRestriction::class, + EndTimeRestriction::class, + FrontendGroupRestriction::class, + ]; + + /** + * FrontendRestrictionContainer constructor. + * Initializes the default restrictions for frontend requests + */ + public function __construct() + { + foreach ($this->defaultRestrictionTypes as $restrictionType) { + $this->add($this->createRestriction($restrictionType)); + } + } + + /** + * Main method to build expressions for given tables + * Iterates over all registered restrictions and removes the hidden restriction if preview is requested + * + * @param array $queriedTables Array of tables, where array key is table name and value potentially an alias + * @param ExpressionBuilder $expressionBuilder Expression builder instance to add restrictions with + * @return CompositeExpression The result of query builder expression(s) + */ + public function buildExpression(array $queriedTables, ExpressionBuilder $expressionBuilder): CompositeExpression + { + $constraints = []; + /** @var TypoScriptFrontendController $typoScriptFrontendController */ + $typoScriptFrontendController = $GLOBALS['TSFE']; + foreach ($this->restrictions as $restriction) { + foreach ($queriedTables as $tableName => $tableAlias) { + $disableRestriction = false; + if ($restriction instanceof HiddenRestriction) { + // If display of hidden records is requested, we must disable the hidden restriction. + $disableRestriction = $tableName === 'pages' ? $typoScriptFrontendController->showHiddenPage : $typoScriptFrontendController->showHiddenRecords; + } + if (!$disableRestriction) { + $constraints[] = $restriction->buildExpression([$tableName => $tableAlias], $expressionBuilder); + } + } + } + return $expressionBuilder->andX(...$constraints); + } +} diff --git a/typo3/sysext/core/Classes/Database/Query/Restriction/FrontendWorkspaceRestriction.php b/typo3/sysext/core/Classes/Database/Query/Restriction/FrontendWorkspaceRestriction.php new file mode 100644 index 000000000000..744785dd90e8 --- /dev/null +++ b/typo3/sysext/core/Classes/Database/Query/Restriction/FrontendWorkspaceRestriction.php @@ -0,0 +1,91 @@ +<?php +declare (strict_types = 1); +namespace TYPO3\CMS\Core\Database\Query\Restriction; + +/* + * 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\Core\Database\Query\Expression\CompositeExpression; +use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder; +use TYPO3\CMS\Core\Versioning\VersionState; + +/** + * Restriction to filter records for fronted workspaces preview + */ +class FrontendWorkspaceRestriction implements QueryRestrictionInterface +{ + /** + * @var int + */ + protected $workspaceId; + + /** + * @var bool + */ + protected $includeRowsForWorkspacePreview; + + /** + * @var bool + */ + protected $enforceLiveRowsOnly; + + /** + * @param int $workspaceId (PageRepository::$versioningWorkspaceId property) + * @param bool $includeRowsForWorkspacePreview (PageRepository::$versioningPreview property) + * @param bool $enforceLiveRowsOnly (!$noVersionPreview argument from PageRepository::enableFields()) This is ONLY for use in PageRepository class and most likely will be removed + */ + public function __construct(int $workspaceId = null, bool $includeRowsForWorkspacePreview = null, bool $enforceLiveRowsOnly = true) + { + $this->workspaceId = $workspaceId === null ? $GLOBALS['TSFE']->sys_page->versioningWorkspaceId : $workspaceId; + $this->includeRowsForWorkspacePreview = $includeRowsForWorkspacePreview === null ? $GLOBALS['TSFE']->sys_page->versioningPreview : $includeRowsForWorkspacePreview; + $this->enforceLiveRowsOnly = $enforceLiveRowsOnly; + } + + /** + * Main method to build expressions for given tables + * Evaluates the ctrl/versioningWS flag of the table and adds various workspace related restrictions if set + * + * @param array $queriedTables Array of tables, where array key is table name and value potentially an alias + * @param ExpressionBuilder $expressionBuilder Expression builder instance to add restrictions with + * @return CompositeExpression The result of query builder expression(s) + */ + public function buildExpression(array $queriedTables, ExpressionBuilder $expressionBuilder): CompositeExpression + { + $constraints = []; + foreach ($queriedTables as $tableName => $tableAlias) { + $workspaceEnabled = $GLOBALS['TCA'][$tableName]['ctrl']['versioningWS'] ?? null; + if (!empty($workspaceEnabled)) { + $tablePrefix = $tableAlias ?: $tableName; + if (!$this->includeRowsForWorkspacePreview) { + // Filter out placeholder records (new/moved/deleted items) + // in case we are NOT in a versioning preview (That means we are online!) + $constraints[] = $expressionBuilder->lte( + $tablePrefix . '.t3ver_state', + new VersionState(VersionState::DEFAULT_STATE) + ); + } elseif ($tableName !== 'pages') { + // Show only records of the live and current workspace in case we are in a versioning preview + $constraints[] = $expressionBuilder->orX( + $expressionBuilder->eq($tablePrefix . '.t3ver_wsid', 0), + $expressionBuilder->eq($tablePrefix . '.t3ver_wsid', (int)$this->workspaceId) + ); + } + // Filter out versioned records + if ($this->enforceLiveRowsOnly) { + $constraints[] = $expressionBuilder->neq($tablePrefix . '.pid', -1); + } + } + } + return $expressionBuilder->andX(...$constraints); + } +} diff --git a/typo3/sysext/core/Classes/Database/Query/Restriction/HiddenRestriction.php b/typo3/sysext/core/Classes/Database/Query/Restriction/HiddenRestriction.php new file mode 100644 index 000000000000..227262aaff67 --- /dev/null +++ b/typo3/sysext/core/Classes/Database/Query/Restriction/HiddenRestriction.php @@ -0,0 +1,49 @@ +<?php +declare (strict_types = 1); +namespace TYPO3\CMS\Core\Database\Query\Restriction; + +/* + * 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\Core\Database\Query\Expression\CompositeExpression; +use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder; + +/** + * Restriction to filter records that have been marked as hidden + */ +class HiddenRestriction implements QueryRestrictionInterface +{ + /** + * Main method to build expressions for given tables + * Evaluates the ctrl/enablecolumns/disabled flag of the table and adds the according restriction if set + * + * @param array $queriedTables Array of tables, where array key is table name and value potentially an alias + * @param ExpressionBuilder $expressionBuilder Expression builder instance to add restrictions with + * @return CompositeExpression The result of query builder expression(s) + */ + public function buildExpression(array $queriedTables, ExpressionBuilder $expressionBuilder): CompositeExpression + { + $constraints = []; + foreach ($queriedTables as $tableName => $tableAlias) { + $hiddenFieldName = $GLOBALS['TCA'][$tableName]['ctrl']['enablecolumns']['disabled'] ?? null; + if (!empty($hiddenFieldName)) { + $tablePrefix = $tableAlias ?: $tableName; + $constraints[] = $expressionBuilder->eq( + $tablePrefix . '.' . $hiddenFieldName, + 0 + ); + } + } + return $expressionBuilder->andX(...$constraints); + } +} diff --git a/typo3/sysext/core/Classes/Database/Query/Restriction/QueryRestrictionContainerInterface.php b/typo3/sysext/core/Classes/Database/Query/Restriction/QueryRestrictionContainerInterface.php new file mode 100644 index 000000000000..6abc72d10c50 --- /dev/null +++ b/typo3/sysext/core/Classes/Database/Query/Restriction/QueryRestrictionContainerInterface.php @@ -0,0 +1,46 @@ +<?php +declare (strict_types = 1); +namespace TYPO3\CMS\Core\Database\Query\Restriction; + +/* + * 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! + */ + +/** + * Interface that all restriction collections must implement. + * It is an extension of the QueryRestrictionInterface, so collections can be treated as single restriction + */ +interface QueryRestrictionContainerInterface extends QueryRestrictionInterface +{ + /** + * Removes all restrictions stored within this container + * + * @return QueryRestrictionContainerInterface + */ + public function removeAll(); + + /** + * Remove restriction of a given type + * + * @param string $restrictionType Class name of the restriction to be removed + * @return QueryRestrictionContainerInterface + */ + public function removeByType(string $restrictionType); + + /** + * Add a new restriction instance to this collection + * + * @param QueryRestrictionInterface $restriction + * @return QueryRestrictionContainerInterface + */ + public function add(QueryRestrictionInterface $restriction); +} diff --git a/typo3/sysext/core/Classes/Database/Query/Restriction/QueryRestrictionInterface.php b/typo3/sysext/core/Classes/Database/Query/Restriction/QueryRestrictionInterface.php new file mode 100644 index 000000000000..914e8c081122 --- /dev/null +++ b/typo3/sysext/core/Classes/Database/Query/Restriction/QueryRestrictionInterface.php @@ -0,0 +1,34 @@ +<?php +declare (strict_types = 1); +namespace TYPO3\CMS\Core\Database\Query\Restriction; + +/* + * 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\Core\Database\Query\Expression\CompositeExpression; +use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder; + +/** + * The main restriction interface. All restrictions (including the collections) must implement this. + */ +interface QueryRestrictionInterface +{ + /** + * Main method to build expressions for given tables + * + * @param array $queriedTables Array of tables, where array key is table name and value potentially an alias + * @param ExpressionBuilder $expressionBuilder Expression builder instance to add restrictions with + * @return CompositeExpression The result of query builder expression(s) + */ + public function buildExpression(array $queriedTables, ExpressionBuilder $expressionBuilder): CompositeExpression; +} diff --git a/typo3/sysext/core/Classes/Database/Query/Restriction/RootLevelRestriction.php b/typo3/sysext/core/Classes/Database/Query/Restriction/RootLevelRestriction.php new file mode 100644 index 000000000000..ab56b85219f3 --- /dev/null +++ b/typo3/sysext/core/Classes/Database/Query/Restriction/RootLevelRestriction.php @@ -0,0 +1,60 @@ +<?php +declare (strict_types = 1); +namespace TYPO3\CMS\Core\Database\Query\Restriction; + +/* + * 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\Core\Database\Query\Expression\CompositeExpression; +use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder; + +/** + * Restriction to filter records which are not stored on the root page. + */ +class RootLevelRestriction implements QueryRestrictionInterface +{ + /** + * @var array + */ + protected $tableNames; + + /** + * @param array $tableNames + */ + public function __construct(array $tableNames = array()) + { + $this->tableNames = $tableNames; + } + + /** + * Main method to build expressions for given tables + * + * @param array $queriedTables Array of tables, where array key is table name and value potentially an alias + * @param ExpressionBuilder $expressionBuilder Expression builder instance to add restrictions with + * @return CompositeExpression The result of query builder expression(s) + */ + public function buildExpression(array $queriedTables, ExpressionBuilder $expressionBuilder): CompositeExpression + { + $constraints = []; + foreach ($queriedTables as $tableName => $tableAlias) { + $tablePrefix = $tableAlias ?: $tableName; + if (empty($this->tableNames) || in_array($tablePrefix, $this->tableNames, true)) { + $constraints[] = $expressionBuilder->eq( + $tablePrefix . '.pid', + 0 + ); + } + } + return $expressionBuilder->andX(...$constraints); + } +} diff --git a/typo3/sysext/core/Classes/Database/Query/Restriction/StartTimeRestriction.php b/typo3/sysext/core/Classes/Database/Query/Restriction/StartTimeRestriction.php new file mode 100644 index 000000000000..cbc960acbb55 --- /dev/null +++ b/typo3/sysext/core/Classes/Database/Query/Restriction/StartTimeRestriction.php @@ -0,0 +1,69 @@ +<?php +declare (strict_types = 1); +namespace TYPO3\CMS\Core\Database\Query\Restriction; + +/* + * 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\Core\Database\Query\Expression\CompositeExpression; +use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder; + +/** + * Restriction to filter records, that should not be shown until the start time has been reached + */ +class StartTimeRestriction implements QueryRestrictionInterface +{ + /** + * @param int $accessTimeStamp + */ + public function __construct(int $accessTimeStamp = null) + { + $this->accessTimeStamp = $accessTimeStamp ?: ($GLOBALS['SIM_ACCESS_TIME'] ?? null); + } + + /** + * @var int + */ + protected $accessTimeStamp; + + /** + * Main method to build expressions for given tables + * Evaluates the ctrl/enablecolumns/starttime flag of the table and adds the according restriction if set + * + * @param array $queriedTables Array of tables, where array key is table name and value potentially an alias + * @param ExpressionBuilder $expressionBuilder Expression builder instance to add restrictions with + * @return CompositeExpression The result of query builder expression(s) + * @throws \RuntimeException + */ + public function buildExpression(array $queriedTables, ExpressionBuilder $expressionBuilder): CompositeExpression + { + $constraints = []; + foreach ($queriedTables as $tableName => $tableAlias) { + $startTimeFieldName = $GLOBALS['TCA'][$tableName]['ctrl']['enablecolumns']['starttime'] ?? null; + if (!empty($startTimeFieldName)) { + if (empty($this->accessTimeStamp)) { + throw new \RuntimeException( + 'accessTimeStamp needs to be set to an integer value, but is empty! Maybe $GLOBALS[\'SIM_ACCESS_TIME\'] has been overridden somewhere?', + 1462820645 + ); + } + $tablePrefix = $tableAlias ?: $tableName; + $constraints[] = $expressionBuilder->lte( + $tablePrefix . '.' . $startTimeFieldName, + (int)$this->accessTimeStamp + ); + } + } + return $expressionBuilder->andX(...$constraints); + } +} diff --git a/typo3/sysext/core/Documentation/Changelog/8.1/Feature-75454-DoctrineDBALForDatabaseConnections.rst b/typo3/sysext/core/Documentation/Changelog/8.1/Feature-75454-DoctrineDBALForDatabaseConnections.rst index d2eab2675682..81831f33747d 100644 --- a/typo3/sysext/core/Documentation/Changelog/8.1/Feature-75454-DoctrineDBALForDatabaseConnections.rst +++ b/typo3/sysext/core/Documentation/Changelog/8.1/Feature-75454-DoctrineDBALForDatabaseConnections.rst @@ -57,13 +57,13 @@ The :php:``ConnectionPool`` class can be used like this: $query = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('aTable); $query->select('*') ->from('aTable) - ->where($query->expr()->eq('aField', $query->createNamedParameter($aValue))) - ->andWhere( + ->where( + $query->expr()->eq('aField', $query->createNamedParameter($aValue)), $query->expr()->lte( 'anotherField', $query->createNamedParameter($anotherValue) ) - ) + ); $rows = $query->execute()->fetchAll(); Extension authors are advised to use the ``ConnectionPool`` and ``Connection`` classes instead of using diff --git a/typo3/sysext/core/Tests/Unit/Authentication/AbstractUserAuthenticationTest.php b/typo3/sysext/core/Tests/Unit/Authentication/AbstractUserAuthenticationTest.php index 045a9064db90..b49bbc1865d8 100644 --- a/typo3/sysext/core/Tests/Unit/Authentication/AbstractUserAuthenticationTest.php +++ b/typo3/sysext/core/Tests/Unit/Authentication/AbstractUserAuthenticationTest.php @@ -39,8 +39,9 @@ class AbstractUserAuthenticationTest extends \TYPO3\CMS\Core\Tests\UnitTestCase $connection->getDatabasePlatform()->willReturn(new MockPlatform()); $connection->getExpressionBuilder()->willReturn(new ExpressionBuilder($connection->reveal())); - $queryBuilder = GeneralUtility::makeInstance( - QueryBuilder::class, + // TODO: This should rather be a functional test if we need a query builder + // or we should clean up the code itself to not need to mock internal behavior here + $queryBuilder = new QueryBuilder( $connection->reveal(), null, $this->prophesize(\Doctrine\DBAL\Query\QueryBuilder::class)->reveal() diff --git a/typo3/sysext/core/Tests/Unit/Database/Query/QueryBuilderTest.php b/typo3/sysext/core/Tests/Unit/Database/Query/QueryBuilderTest.php index ec91ddb69c25..e94e0df2bd83 100644 --- a/typo3/sysext/core/Tests/Unit/Database/Query/QueryBuilderTest.php +++ b/typo3/sysext/core/Tests/Unit/Database/Query/QueryBuilderTest.php @@ -19,6 +19,7 @@ use Prophecy\Argument; use TYPO3\CMS\Core\Database\Connection; use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder; use TYPO3\CMS\Core\Database\Query\QueryBuilder; +use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction; use TYPO3\CMS\Core\Tests\Unit\Database\Mocks\MockPlatform; use TYPO3\CMS\Core\Tests\UnitTestCase; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -922,7 +923,7 @@ class QueryBuilderTest extends UnitTestCase ->from('pages') ->where('uid=1'); - $expectedSQL = 'SELECT * FROM pages WHERE (uid=1) AND ((pages.hidden = 0) AND (pages.deleted = 0))'; + $expectedSQL = 'SELECT * FROM pages WHERE (uid=1) AND ((pages.deleted = 0) AND (pages.hidden = 0))'; $this->connection->executeQuery($expectedSQL, Argument::cetera()) ->shouldBeCalled(); @@ -969,7 +970,7 @@ class QueryBuilderTest extends UnitTestCase ->from('pages') ->where('uid=1'); - $expectedSQL = 'SELECT COUNT(uid) FROM pages WHERE (uid=1) AND ((pages.hidden = 0) AND (pages.deleted = 0))'; + $expectedSQL = 'SELECT COUNT(uid) FROM pages WHERE (uid=1) AND ((pages.deleted = 0) AND (pages.hidden = 0))'; $this->connection->executeQuery($expectedSQL, Argument::cetera()) ->shouldBeCalled(); @@ -1014,12 +1015,10 @@ class QueryBuilderTest extends UnitTestCase ->from('pages') ->where('uid=1'); - $expectedSQL = 'SELECT * FROM pages WHERE (uid=1) AND ((pages.hidden = 0) AND (pages.deleted = 0))'; + $expectedSQL = 'SELECT * FROM pages WHERE (uid=1) AND ((pages.deleted = 0) AND (pages.hidden = 0))'; $this->assertSame($expectedSQL, $subject->getSQL()); - $subject->getQueryContext() - ->setIgnoreEnableFields(true) - ->setIgnoredEnableFields(['disabled']); + $subject->getRestrictions()->removeAll()->add(new DeletedRestriction()); $expectedSQL = 'SELECT * FROM pages WHERE (uid=1) AND (pages.deleted = 0)'; $this->assertSame($expectedSQL, $subject->getSQL()); @@ -1063,9 +1062,7 @@ class QueryBuilderTest extends UnitTestCase ->from('pages') ->where('uid=1'); - $subject->getQueryContext() - ->setIgnoreEnableFields(true) - ->setIgnoredEnableFields(['disabled']); + $subject->getRestrictions()->removeAll()->add(new DeletedRestriction()); $expectedSQL = 'SELECT * FROM pages WHERE (uid=1) AND (pages.deleted = 0)'; $this->connection->executeQuery($expectedSQL, Argument::cetera()) @@ -1073,10 +1070,9 @@ class QueryBuilderTest extends UnitTestCase $subject->execute(); - $subject->getQueryContext() - ->setIgnoreEnableFields(false); + $subject->resetRestrictions(); - $expectedSQL = 'SELECT * FROM pages WHERE (uid=1) AND ((pages.hidden = 0) AND (pages.deleted = 0))'; + $expectedSQL = 'SELECT * FROM pages WHERE (uid=1) AND ((pages.deleted = 0) AND (pages.hidden = 0))'; $this->connection->executeQuery($expectedSQL, Argument::cetera()) ->shouldBeCalled(); diff --git a/typo3/sysext/core/Tests/Unit/Database/Query/QueryContextTest.php b/typo3/sysext/core/Tests/Unit/Database/Query/QueryContextTest.php deleted file mode 100644 index a3d286250200..000000000000 --- a/typo3/sysext/core/Tests/Unit/Database/Query/QueryContextTest.php +++ /dev/null @@ -1,283 +0,0 @@ -<?php -declare (strict_types = 1); -namespace TYPO3\CMS\Core\Tests\Unit\Database\Query; - -/* - * 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 Prophecy\Prophecy\ObjectProphecy; -use TYPO3\CMS\Core\Database\Query\QueryContext; -use TYPO3\CMS\Core\Tests\UnitTestCase; -use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController; -use TYPO3\CMS\Frontend\Page\PageRepository; - -class QueryContextTest extends UnitTestCase -{ - /** - * @var QueryContext - */ - protected $subject; - - /** - * @var TypoScriptFrontendController|ObjectProphecy - */ - protected $typoScriptFrontendController; - - /** - * Create a new database connection mock object for every test. - * - * @return void - */ - protected function setUp() - { - parent::setUp(); - - $this->typoScriptFrontendController = $this->prophesize(TypoScriptFrontendController::class); - $GLOBALS['TSFE'] = $this->typoScriptFrontendController->reveal(); - - $this->subject = GeneralUtility::makeInstance(QueryContext::class); - } - - /** - * @test - */ - public function contextCanBeSetByConstructiorArgument() - { - $subject = GeneralUtility::makeInstance(QueryContext::class, 'FRONTEND'); - - $this->assertSame('FRONTEND', $subject->getContext()); - } - - /** - * @test - * @expectedException \TYPO3\CMS\Core\Type\Exception\InvalidEnumerationValueException - * @expectedExceptionMessage Invalid value DUMMY for TYPO3\CMS\Core\Database\Query\QueryContextType - */ - public function unknownContextThrowExceptionInConstructor() - { - GeneralUtility::makeInstance(QueryContext::class, 'DUMMY'); - } - - /** - * @test - * @expectedException \TYPO3\CMS\Core\Type\Exception\InvalidEnumerationValueException - * @expectedExceptionMessage Invalid value DUMMY for TYPO3\CMS\Core\Database\Query\QueryContextType - */ - public function unknownContextThrowExceptionWhenSet() - { - $this->subject->setContext('DUMMY'); - } - - /** - * @test - */ - public function getMemberGroupsPrefersExplicitlySetInformation() - { - $GLOBALS['TSFE']->gr_list = '3,5'; - $this->subject->setMemberGroups([1, 2]); - - $this->assertSame([1, 2], $this->subject->getMemberGroups()); - } - - /** - * @test - */ - public function getMemberGroupsFallsBackToTSFE() - { - $GLOBALS['TSFE']->gr_list = '3,5'; - - $this->assertSame([3, 5], $this->subject->getMemberGroups()); - } - - /** - * @test - */ - public function getCurrentWorkspacePrefersExplicitlySetInformation() - { - /** @var PageRepository|ObjectProphecy $pageRepository */ - $pageRepository = $this->prophesize(PageRepository::class); - $pageRepository->versioningWorkspaceId = 3; - - $GLOBALS['TSFE']->sys_page = $pageRepository->reveal(); - - $this->subject->setCurrentWorkspace(1); - $this->subject->setContext('FRONTEND'); - - $this->assertSame(1, $this->subject->getCurrentWorkspace()); - } - - /** - * @test - */ - public function getCurrentWorkspaceFallsBackToTSFE() - { - /** @var PageRepository|ObjectProphecy $pageRepository */ - $pageRepository = $this->prophesize(PageRepository::class); - $pageRepository->versioningWorkspaceId = 3; - - $GLOBALS['TSFE']->sys_page = $pageRepository->reveal(); - - $this->subject->setContext('FRONTEND'); - - $this->assertSame(3, $this->subject->getCurrentWorkspace()); - } - - /** - * @test - */ - public function getAccessTimePrefersExplicitlySetInformation() - { - $GLOBALS['SIM_ACCESS_TIME'] = 100; - $this->subject->setAccessTime(200); - - $this->assertSame(200, $this->subject->getAccessTime()); - } - - /** - * @test - */ - public function getAccessTimeFallsBackToTSFE() - { - $GLOBALS['SIM_ACCESS_TIME'] = 100; - - $this->assertSame(100, $this->subject->getAccessTime()); - } - - /** - * @test - */ - public function getIncludeHiddenForTablePrefersExplicitlySetInformation() - { - $GLOBALS['TSFE']->showHiddenPage = false; - $GLOBALS['TSFE']->showHiddenRecords = false; - $this->subject->setIncludeHidden(true); - - $this->assertSame(true, $this->subject->getIncludeHiddenForTable('pages')); - } - - /** - * @test - */ - public function getIncludeHiddenForTablePagesFallsBackToTSFE() - { - $GLOBALS['TSFE']->showHiddenPage = true; - - $this->assertSame(true, $this->subject->getIncludeHiddenForTable('pages')); - } - - /** - * @test - */ - public function getIncludeHiddenForTablePagesLanguageOverlayFallsBackToTSFE() - { - $GLOBALS['TSFE']->showHiddenPage = true; - - $this->assertSame(true, $this->subject->getIncludeHiddenForTable('pages')); - } - - /** - * @test - */ - public function getIncludeHiddenForRecordsFallsBackToTSFE() - { - $GLOBALS['TSFE']->showHiddenRecords = true; - - $this->assertSame(true, $this->subject->getIncludeHiddenForTable('tt_content')); - } - - /** - * @test - */ - public function getIncludePlaceholdersPrefersExplicitlySetInformation() - { - $this->subject->setIncludePlaceholders(true); - - $this->assertSame(true, $this->subject->getIncludePlaceholders()); - } - - /** - * @test - */ - public function getIncludePlaceholdersFallsBackToTSFE() - { - /** @var PageRepository|ObjectProphecy $pageRepository */ - $pageRepository = $this->prophesize(PageRepository::class); - $pageRepository->versioningPreview = true; - - $GLOBALS['TSFE']->sys_page = $pageRepository->reveal(); - - $this->subject->setContext('FRONTEND'); - $this->assertSame(true, $this->subject->getIncludePlaceholders()); - } - - /** - * @test - */ - public function getIgnoredEnableFieldsForTableFallsBackToGlobalList() - { - $this->subject->setIgnoredEnableFields(['disabled']); - - $this->assertSame(['disabled'], $this->subject->getIgnoredEnableFieldsForTable('pages')); - } - - /** - * @test - */ - public function getIgnoredEnableFieldsForTablePrefersExplictlySetInformation() - { - $this->subject->setIgnoredEnableFields(['disabled']); - $this->subject->setIgnoredEnableFieldsForTable('pages', ['starttime', 'endtime']); - - $this->assertSame(['starttime', 'endtime'], $this->subject->getIgnoredEnableFieldsForTable('pages')); - } - - /** - * @test - */ - public function getIgnoredEnableFieldsForTableReturnsEmptyArrayWithoutInformation() - { - $this->assertSame([], $this->subject->getIgnoredEnableFieldsForTable('pages')); - } - - /** - * @test - */ - public function getTableConfigPrefersExplicitlySetInformation() - { - $this->subject->setTableConfigs(['pages' => ['delete' => 'deleted']]); - $GLOBALS['TCA']['pages']['ctrl'] = ['delete' => 'deleted']; - - $this->assertSame(['delete' => 'deleted'], $this->subject->getTableConfig('pages')); - } - - /** - * @test - */ - public function getTableConfigFallsBackToTCA() - { - $GLOBALS['TCA']['pages']['ctrl'] = [ - 'label' => 'title', - 'tstamp' => 'tstamp', - 'delete' => 'deleted', - 'enablecolumns' => [ - 'disabled' => 'hidden', - ], - ]; - - $this->assertSame( - ['delete' => 'deleted', 'enablecolumns' => ['disabled' => 'hidden']], - $this->subject->getTableConfig('pages') - ); - } -} diff --git a/typo3/sysext/core/Tests/Unit/Database/Query/QueryRestrictionBuilderTest.php b/typo3/sysext/core/Tests/Unit/Database/Query/QueryRestrictionBuilderTest.php deleted file mode 100644 index bf0ce978859c..000000000000 --- a/typo3/sysext/core/Tests/Unit/Database/Query/QueryRestrictionBuilderTest.php +++ /dev/null @@ -1,942 +0,0 @@ -<?php -declare (strict_types = 1); -namespace TYPO3\CMS\Core\Tests\Unit\Database\Query; - -/* - * 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 Prophecy\Argument; -use TYPO3\CMS\Core\Database\Connection; -use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder; -use TYPO3\CMS\Core\Database\Query\QueryContext; -use TYPO3\CMS\Core\Database\Query\QueryContextType; -use TYPO3\CMS\Core\Database\Query\QueryRestrictionBuilder; -use TYPO3\CMS\Core\Tests\Unit\Database\Mocks\MockPlatform; -use TYPO3\CMS\Core\Tests\UnitTestCase; -use TYPO3\CMS\Core\Utility\GeneralUtility; - -class QueryRestrictionBuilderTest extends UnitTestCase -{ - /** - * @var array - */ - protected $defaultTableConfig = [ - 'versioningWS' => true, - 'delete' => 'deleted', - 'enablecolumns' => [ - 'disabled' => 'hidden', - 'starttime' => 'starttime', - 'endtime' => 'endtime', - 'fe_group' => 'fe_group', - ], - ]; - - /** - * @var \TYPO3\CMS\Frontend\Page\PageRepository - */ - protected $pageRepository; - - /** - * @var \TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder; - */ - protected $expressionBuilder; - - /** - * @var Connection|\Prophecy\Prophecy\ObjectProphecy - */ - protected $connection; - - /** - * @var QueryContext - */ - protected $queryContext; - - /** - * Create a new database connection mock object for every test. - * - * @return void - */ - protected function setUp() - { - parent::setUp(); - - $this->connection = $this->prophesize(Connection::class); - $this->connection->quoteIdentifier(Argument::cetera())->will(function ($args) { - return '"' . join('"."', explode('.', $args[0])) . '"'; - }); - $this->connection->quote(Argument::cetera())->will(function ($args) { - return "'" . $args[0] . "'"; - }); - $this->connection->getDatabasePlatform()->willReturn(new MockPlatform()); - - $this->queryContext = GeneralUtility::makeInstance(QueryContext::class); - $this->expressionBuilder = GeneralUtility::makeInstance(ExpressionBuilder::class, $this->connection->reveal()); - } - - /** - * @test - */ - public function getVisibilityConstraintsReturnsEmptyConstraintForUnrestrictedContext() - { - $this->queryContext->setContext(QueryContextType::UNRESTRICTED); - - $subject = GeneralUtility::makeInstance( - QueryRestrictionBuilder::class, - [], - $this->expressionBuilder, - $this->queryContext - ); - - $this->assertEmpty((string)$subject->getVisibilityConstraints()); - } - - /** - * @test - */ - public function getFrontendVisibilityRestrictionsSkipsUnconfiguredTables() - { - $this->queryContext->setContext(QueryContextType::FRONTEND); - - $subject = GeneralUtility::makeInstance( - QueryRestrictionBuilder::class, - ['pages' => ''], - $this->expressionBuilder, - $this->queryContext - ); - - $this->assertEmpty((string)$subject->getVisibilityConstraints()); - } - - /** - * @test - */ - public function getFrontendVisibilityRestrictionsWithDefaultSettings() - { - $this->queryContext->setContext(QueryContextType::FRONTEND) - ->setAccessTime(1459706700) - ->setTableConfigs(['pages' => $this->defaultTableConfig]); - - $subject = GeneralUtility::makeInstance( - QueryRestrictionBuilder::class, - ['pages' => ''], - $this->expressionBuilder, - $this->queryContext - ); - - $expectedSql = join(' AND ', [ - '("pages"."deleted" = 0)', - '("pages"."t3ver_state" <= 0)', - '("pages"."pid" <> -1)', - '("pages"."hidden" = 0)', - '("pages"."starttime" <= 1459706700)', - '(("pages"."endtime" = 0) OR ("pages"."endtime" > 1459706700))', - '(("pages"."fe_group" IS NULL) OR ("pages"."fe_group" = \'\') OR ("pages"."fe_group" = \'0\'))' - ]); - - $this->assertSame($expectedSql, (string)$subject->getVisibilityConstraints()); - } - - /** - * @test - */ - public function getFrontendVisibilityRestrictionsWithUserGroups() - { - $this->queryContext->setContext(QueryContextType::FRONTEND) - ->setAccessTime(1459706700) - ->setMemberGroups([1, 2]) - ->setTableConfigs(['pages' => $this->defaultTableConfig]); - - $subject = GeneralUtility::makeInstance( - QueryRestrictionBuilder::class, - ['pages' => ''], - $this->expressionBuilder, - $this->queryContext - ); - - $expectedSql = join(' AND ', [ - '("pages"."deleted" = 0)', - '("pages"."t3ver_state" <= 0)', - '("pages"."pid" <> -1)', - '("pages"."hidden" = 0)', - '("pages"."starttime" <= 1459706700)', - '(("pages"."endtime" = 0) OR ("pages"."endtime" > 1459706700))', - '(("pages"."fe_group" IS NULL) OR ("pages"."fe_group" = \'\') OR ("pages"."fe_group" = \'0\') OR (FIND_IN_SET(\'1\', "pages"."fe_group")) OR (FIND_IN_SET(\'2\', "pages"."fe_group")))' - ]); - - $this->assertSame($expectedSql, (string)$subject->getVisibilityConstraints()); - } - - /** - * @test - */ - public function getFrontendVisibilityRestrictionsWithVersioningPreview() - { - $this->queryContext->setContext(QueryContextType::FRONTEND) - ->setAccessTime(1459706700) - ->setIncludePlaceholders(true) - ->setTableConfigs(['pages' => $this->defaultTableConfig]); - - $subject = GeneralUtility::makeInstance( - QueryRestrictionBuilder::class, - ['pages' => ''], - $this->expressionBuilder, - $this->queryContext - ); - - $expectedSql = '("pages"."deleted" = 0) AND ("pages"."pid" <> -1)'; - - $this->assertSame($expectedSql, (string)$subject->getVisibilityConstraints()); - } - - /** - * @test - */ - public function getFrontendVisibilityRestrictionsWithVersioningPreviewAndNoPreviewSet() - { - $this->queryContext->setContext(QueryContextType::FRONTEND) - ->setAccessTime(1459706700) - ->setIncludePlaceholders(true) - ->setIncludeVersionedRecords(true) - ->setTableConfigs(['pages' => $this->defaultTableConfig]); - - $subject = GeneralUtility::makeInstance( - QueryRestrictionBuilder::class, - ['pages' => ''], - $this->expressionBuilder, - $this->queryContext - ); - - $expectedSql = join(' AND ', [ - '("pages"."deleted" = 0)', - '("pages"."hidden" = 0)', - '("pages"."starttime" <= 1459706700)', - '(("pages"."endtime" = 0) OR ("pages"."endtime" > 1459706700))', - '(("pages"."fe_group" IS NULL) OR ("pages"."fe_group" = \'\') OR ("pages"."fe_group" = \'0\'))' - ]); - - $this->assertSame($expectedSql, (string)$subject->getVisibilityConstraints()); - } - - /** - * @test - */ - public function getFrontendVisibilityRestrictionsWithoutDisabledColumn() - { - $this->queryContext->setContext(QueryContextType::FRONTEND) - ->setAccessTime(1459706700) - ->setTableConfigs(['pages' => [ - 'versioningWS' => true, - 'delete' => 'deleted', - 'enablecolumns' => [ - 'starttime' => 'starttime', - 'endtime' => 'endtime', - 'fe_group' => 'fe_group', - ] - ]]); - - $subject = GeneralUtility::makeInstance( - QueryRestrictionBuilder::class, - ['pages' => ''], - $this->expressionBuilder, - $this->queryContext - ); - - $expectedSql = join(' AND ', [ - '("pages"."deleted" = 0)', - '("pages"."t3ver_state" <= 0)', - '("pages"."pid" <> -1)', - '("pages"."starttime" <= 1459706700)', - '(("pages"."endtime" = 0) OR ("pages"."endtime" > 1459706700))', - '(("pages"."fe_group" IS NULL) OR ("pages"."fe_group" = \'\') OR ("pages"."fe_group" = \'0\'))' - ]); - - $this->assertSame($expectedSql, (string)$subject->getVisibilityConstraints()); - } - - /** - * @test - */ - public function getFrontendVisibilityRestrictionsWithoutStarttimeColumn() - { - $this->queryContext->setContext(QueryContextType::FRONTEND) - ->setAccessTime(1459706700) - ->setTableConfigs([ - 'pages' => [ - 'versioningWS' => true, - 'delete' => 'deleted', - 'enablecolumns' => [ - 'disabled' => 'hidden', - 'endtime' => 'endtime', - 'fe_group' => 'fe_group', - ] - ] - ]); - - $subject = GeneralUtility::makeInstance( - QueryRestrictionBuilder::class, - ['pages' => ''], - $this->expressionBuilder, - $this->queryContext - ); - - $expectedSql = join(' AND ', [ - '("pages"."deleted" = 0)', - '("pages"."t3ver_state" <= 0)', - '("pages"."pid" <> -1)', - '("pages"."hidden" = 0)', - '(("pages"."endtime" = 0) OR ("pages"."endtime" > 1459706700))', - '(("pages"."fe_group" IS NULL) OR ("pages"."fe_group" = \'\') OR ("pages"."fe_group" = \'0\'))' - ]); - - $this->assertSame($expectedSql, (string)$subject->getVisibilityConstraints()); - } - - /** - * @test - */ - public function getFrontendVisibilityRestrictionsWithoutEndtimeColumn() - { - $this->queryContext->setContext(QueryContextType::FRONTEND) - ->setAccessTime(1459706700) - ->setTableConfigs([ - 'pages' => [ - 'versioningWS' => true, - 'delete' => 'deleted', - 'enablecolumns' => [ - 'disabled' => 'hidden', - 'starttime' => 'starttime', - 'fe_group' => 'fe_group', - ] - ] - ]); - - $subject = GeneralUtility::makeInstance( - QueryRestrictionBuilder::class, - ['pages' => ''], - $this->expressionBuilder, - $this->queryContext - ); - - $expectedSql = join(' AND ', [ - '("pages"."deleted" = 0)', - '("pages"."t3ver_state" <= 0)', - '("pages"."pid" <> -1)', - '("pages"."hidden" = 0)', - '("pages"."starttime" <= 1459706700)', - '(("pages"."fe_group" IS NULL) OR ("pages"."fe_group" = \'\') OR ("pages"."fe_group" = \'0\'))' - ]); - - $this->assertSame($expectedSql, (string)$subject->getVisibilityConstraints()); - } - - /** - * @test - */ - public function getFrontendVisibilityRestrictionsWithoutUsergroupsColumn() - { - $this->queryContext->setContext(QueryContextType::FRONTEND) - ->setAccessTime(1459706700) - ->setTableConfigs([ - 'pages' => [ - 'versioningWS' => true, - 'delete' => 'deleted', - 'enablecolumns' => [ - 'disabled' => 'hidden', - 'starttime' => 'starttime', - 'endtime' => 'endtime', - ] - ] - ]); - - $subject = GeneralUtility::makeInstance( - QueryRestrictionBuilder::class, - ['pages' => ''], - $this->expressionBuilder, - $this->queryContext - ); - - $expectedSql = join(' AND ', [ - '("pages"."deleted" = 0)', - '("pages"."t3ver_state" <= 0)', - '("pages"."pid" <> -1)', - '("pages"."hidden" = 0)', - '("pages"."starttime" <= 1459706700)', - '(("pages"."endtime" = 0) OR ("pages"."endtime" > 1459706700))', - ]); - - $this->assertSame($expectedSql, (string)$subject->getVisibilityConstraints()); - } - - /** - * @test - */ - public function getFrontendVisibilityRestrictionsWithIgnoreEnableFieldsSet() - { - $this->queryContext->setContext(QueryContextType::FRONTEND) - ->setAccessTime(1459706700) - ->setTableConfigs(['pages' => $this->defaultTableConfig]) - ->setIgnoreEnableFields(true); - - $subject = GeneralUtility::makeInstance( - QueryRestrictionBuilder::class, - ['pages' => ''], - $this->expressionBuilder, - $this->queryContext - ); - - $expectedSql = '"pages"."deleted" = 0'; - - $this->assertSame($expectedSql, (string)$subject->getVisibilityConstraints()); - } - - /** - * Data provider for getFrontendVisibilityRestrictionsWithSelectiveIgnoreEnableFieldsSet - * - * @return array - */ - public function getFrontendVisibilityRestrictionsIgnoreEnableFieldsDataProvider() - { - return [ - 'disabled' => [ - ['disabled'], - ], - 'starttime' => [ - ['starttime'], - ], - 'endtime' => [ - ['endtime'], - ], - 'starttime, endtime' => [ - ['starttime', 'endtime'], - ], - 'fe_group' => [ - ['fe_group'], - ], - 'disabled, starttime, endtime' => [ - ['disabled', 'starttime', 'endtime'], - ], - 'disabled, starttime, endtime, fe_group' => [ - ['disabled', 'starttime', 'endtime', 'fe_group'], - ], - ]; - } - - /** - * @test - * @dataProvider getFrontendVisibilityRestrictionsIgnoreEnableFieldsDataProvider - * @param string[] $ignoreFields - */ - public function getFrontendVisibilityRestrictionsWithSelectiveIgnoreEnableFieldsSet(array $ignoreFields) - { - $this->queryContext->setContext(QueryContextType::FRONTEND) - ->setAccessTime(1459706700) - ->setTableConfigs(['pages' => $this->defaultTableConfig]) - ->setIgnoreEnableFields(true) - ->setIgnoredEnableFields($ignoreFields); - - $subject = GeneralUtility::makeInstance( - QueryRestrictionBuilder::class, - ['pages' => ''], - $this->expressionBuilder, - $this->queryContext - ); - - $expectedSqlFragments = [ - 'deleted' => '("pages"."deleted" = 0)', - 'versioningWS' => '("pages"."t3ver_state" <= 0) AND ("pages"."pid" <> -1)', - 'disabled' => '("pages"."hidden" = 0)', - 'starttime' => '("pages"."starttime" <= 1459706700)', - 'endtime' => '(("pages"."endtime" = 0) OR ("pages"."endtime" > 1459706700))', - 'fe_group' => '(("pages"."fe_group" IS NULL) OR ("pages"."fe_group" = \'\') OR ("pages"."fe_group" = \'0\'))' - ]; - - foreach ($ignoreFields as $fragmentName) { - unset($expectedSqlFragments[$fragmentName]); - } - - $this->assertSame(join(' AND ', $expectedSqlFragments), (string)$subject->getVisibilityConstraints()); - } - - /** - * @test - */ - public function getFrontendVisibilityRestrictionsForMultipleTablesWithDefaultSettings() - { - $this->queryContext->setContext(QueryContextType::FRONTEND) - ->setAccessTime(1459706700) - ->setTableConfigs([ - 'pages' => $this->defaultTableConfig, - 'tt_content' => $this->defaultTableConfig, - ]); - - $subject = GeneralUtility::makeInstance( - QueryRestrictionBuilder::class, - ['pages' => '', 'tt_content' => ''], - $this->expressionBuilder, - $this->queryContext - ); - - $expectedSqlPages = join(' AND ', [ - '("pages"."deleted" = 0)', - '("pages"."t3ver_state" <= 0)', - '("pages"."pid" <> -1)', - '("pages"."hidden" = 0)', - '("pages"."starttime" <= 1459706700)', - '(("pages"."endtime" = 0) OR ("pages"."endtime" > 1459706700))', - '(("pages"."fe_group" IS NULL) OR ("pages"."fe_group" = \'\') OR ("pages"."fe_group" = \'0\'))' - ]); - - $expectedSqlTtContent = join(' AND ', [ - '("tt_content"."deleted" = 0)', - '("tt_content"."t3ver_state" <= 0)', - '("tt_content"."pid" <> -1)', - '("tt_content"."hidden" = 0)', - '("tt_content"."starttime" <= 1459706700)', - '(("tt_content"."endtime" = 0) OR ("tt_content"."endtime" > 1459706700))', - '(("tt_content"."fe_group" IS NULL) OR ("tt_content"."fe_group" = \'\') OR ("tt_content"."fe_group" = \'0\'))' - ]); - - $this->assertSame( - '(' . $expectedSqlPages . ') AND (' . $expectedSqlTtContent . ')', - (string)$subject->getVisibilityConstraints() - ); - } - - /** - * @test - */ - public function getFrontendVisibilityRestrictionsForMultipleTablesWithIgnoreEnableFields() - { - $this->queryContext->setContext(QueryContextType::FRONTEND) - ->setAccessTime(1459706700) - ->setTableConfigs([ - 'pages' => $this->defaultTableConfig, - 'tt_content' => $this->defaultTableConfig, - ]) - ->setIgnoreEnableFields(true); - - $subject = GeneralUtility::makeInstance( - QueryRestrictionBuilder::class, - ['pages' => '', 'tt_content' => ''], - $this->expressionBuilder, - $this->queryContext - ); - - $expectedSql = join(' AND ', [ - '("pages"."deleted" = 0)', - '("tt_content"."deleted" = 0)', - ]); - - $this->assertSame($expectedSql, (string)$subject->getVisibilityConstraints()); - } - - /** - * @test - */ - public function getFrontendVisibilityRestrictionsForMultipleTablesWithDifferentEnableColumns() - { - $this->queryContext->setContext(QueryContextType::FRONTEND) - ->setAccessTime(1459706700) - ->setTableConfigs([ - 'pages' => $this->defaultTableConfig, - 'tt_content' => [ - 'versioningWS' => false, - 'delete' => 'deleted', - 'enablecolumns' => [ - 'disabled' => 'hidden', - ], - ], - ]); - - $subject = GeneralUtility::makeInstance( - QueryRestrictionBuilder::class, - ['pages' => '', 'tt_content' => ''], - $this->expressionBuilder, - $this->queryContext - ); - - $expectedSql = join(' AND ', [ - '("pages"."deleted" = 0)', - '("pages"."t3ver_state" <= 0)', - '("pages"."pid" <> -1)', - '("pages"."hidden" = 0)', - '("pages"."starttime" <= 1459706700)', - '(("pages"."endtime" = 0) OR ("pages"."endtime" > 1459706700))', - '(("pages"."fe_group" IS NULL) OR ("pages"."fe_group" = \'\') OR ("pages"."fe_group" = \'0\'))' - ]); - - $this->assertSame( - '(' . $expectedSql . ') AND (("tt_content"."deleted" = 0) AND ("tt_content"."hidden" = 0))', - (string)$subject->getVisibilityConstraints() - ); - } - - /** - * @test - */ - public function getFrontendVisibilityRestrictionsForJoinedTablesWithDefaultSettings() - { - $this->queryContext->setContext(QueryContextType::FRONTEND) - ->setAccessTime(1459706700) - ->setTableConfigs([ - 'pages' => $this->defaultTableConfig, - 'tt_content' => $this->defaultTableConfig, - ]); - - $subject = GeneralUtility::makeInstance( - QueryRestrictionBuilder::class, - ['pages' => '', 'tt_content' => 't'], - $this->expressionBuilder, - $this->queryContext - ); - - $expectedSqlPages = join(' AND ', [ - '("pages"."deleted" = 0)', - '("pages"."t3ver_state" <= 0)', - '("pages"."pid" <> -1)', - '("pages"."hidden" = 0)', - '("pages"."starttime" <= 1459706700)', - '(("pages"."endtime" = 0) OR ("pages"."endtime" > 1459706700))', - '(("pages"."fe_group" IS NULL) OR ("pages"."fe_group" = \'\') OR ("pages"."fe_group" = \'0\'))' - ]); - - $expectedSqlTtContent = join(' AND ', [ - '("t"."deleted" = 0)', - '("t"."t3ver_state" <= 0)', - '("t"."pid" <> -1)', - '("t"."hidden" = 0)', - '("t"."starttime" <= 1459706700)', - '(("t"."endtime" = 0) OR ("t"."endtime" > 1459706700))', - '(("t"."fe_group" IS NULL) OR ("t"."fe_group" = \'\') OR ("t"."fe_group" = \'0\'))' - ]); - - $this->assertSame( - '(' . $expectedSqlPages . ') AND (' . $expectedSqlTtContent . ')', - (string)$subject->getVisibilityConstraints() - ); - } - - /** - * @test - */ - public function getFrontendVisibilityRestrictionsForJoinedTablesWithIgnoreEnableFields() - { - $this->queryContext->setContext(QueryContextType::FRONTEND) - ->setAccessTime(1459706700) - ->setTableConfigs([ - 'pages' => $this->defaultTableConfig, - 'tt_content' => $this->defaultTableConfig, - ]) - ->setIgnoreEnableFields(true); - - $subject = GeneralUtility::makeInstance( - QueryRestrictionBuilder::class, - ['pages' => '', 'tt_content' => 't'], - $this->expressionBuilder, - $this->queryContext - ); - - $expectedSql = join(' AND ', [ - '("pages"."deleted" = 0)', - '("t"."deleted" = 0)', - ]); - - $this->assertSame($expectedSql, (string)$subject->getVisibilityConstraints()); - } - - /** - * @test - */ - public function getFrontendVisibilityRestrictionsForJoinedTablesWithDifferentEnableColumns() - { - $this->queryContext->setContext(QueryContextType::FRONTEND) - ->setAccessTime(1459706700) - ->setTableConfigs([ - 'pages' => $this->defaultTableConfig, - 'tt_content' => [ - 'versioningWS' => false, - 'delete' => 'deleted', - 'enablecolumns' => [ - 'disabled' => 'hidden', - ], - ], - ]); - - $subject = GeneralUtility::makeInstance( - QueryRestrictionBuilder::class, - ['pages' => '', 'tt_content' => 't'], - $this->expressionBuilder, - $this->queryContext - ); - - $expectedSql = join(' AND ', [ - '("pages"."deleted" = 0)', - '("pages"."t3ver_state" <= 0)', - '("pages"."pid" <> -1)', - '("pages"."hidden" = 0)', - '("pages"."starttime" <= 1459706700)', - '(("pages"."endtime" = 0) OR ("pages"."endtime" > 1459706700))', - '(("pages"."fe_group" IS NULL) OR ("pages"."fe_group" = \'\') OR ("pages"."fe_group" = \'0\'))' - ]); - - $this->assertSame( - '(' . $expectedSql . ') AND (("t"."deleted" = 0) AND ("t"."hidden" = 0))', - (string)$subject->getVisibilityConstraints() - ); - } - - /** - * @test - */ - public function getBackendVisibilityRestrictionsSkipsUnconfiguredTables() - { - $this->queryContext->setContext(QueryContextType::BACKEND); - - $subject = GeneralUtility::makeInstance( - QueryRestrictionBuilder::class, - ['pages' => ''], - $this->expressionBuilder, - $this->queryContext - ); - - $this->assertEmpty((string)$subject->getVisibilityConstraints()); - } - - /** - * @test - */ - public function getBackendVisibilityRestrictionsWithDefaultSettings() - { - $this->queryContext->setContext(QueryContextType::BACKEND) - ->setAccessTime(1459706700) - ->setTableConfigs(['pages' => $this->defaultTableConfig]); - - $subject = GeneralUtility::makeInstance( - QueryRestrictionBuilder::class, - ['pages' => ''], - $this->expressionBuilder, - $this->queryContext - ); - - $expectedSql = join(' AND ', [ - '("pages"."hidden" = 0)', - '("pages"."starttime" <= 1459706700)', - '(("pages"."endtime" = 0) OR ("pages"."endtime" > 1459706700))', - '("pages"."deleted" = 0)', - ]); - - $this->assertSame($expectedSql, (string)$subject->getVisibilityConstraints()); - } - - /** - * @test - */ - public function getBackendVisibilityRestrictionsWithoutDisabledColumn() - { - $this->queryContext->setContext(QueryContextType::BACKEND) - ->setAccessTime(1459706700) - ->setTableConfigs(['pages' => [ - 'versioningWS' => true, - 'delete' => 'deleted', - 'enablecolumns' => [ - 'starttime' => 'starttime', - 'endtime' => 'endtime', - 'fe_group' => 'fe_group', - ], - ]]); - - $subject = GeneralUtility::makeInstance( - QueryRestrictionBuilder::class, - ['pages' => ''], - $this->expressionBuilder, - $this->queryContext - ); - - $expectedSql = join(' AND ', [ - '("pages"."starttime" <= 1459706700)', - '(("pages"."endtime" = 0) OR ("pages"."endtime" > 1459706700))', - '("pages"."deleted" = 0)', - ]); - - $this->assertSame($expectedSql, (string)$subject->getVisibilityConstraints()); - } - - /** - * @test - */ - public function getBackendVisibilityRestrictionsWithoutStarttimeColumn() - { - $this->queryContext->setContext(QueryContextType::BACKEND) - ->setAccessTime(1459706700) - ->setTableConfigs([ - 'pages' => [ - 'versioningWS' => true, - 'delete' => 'deleted', - 'enablecolumns' => [ - 'disabled' => 'hidden', - 'endtime' => 'endtime', - 'fe_group' => 'fe_group', - ], - ] - ]); - - $subject = GeneralUtility::makeInstance( - QueryRestrictionBuilder::class, - ['pages' => ''], - $this->expressionBuilder, - $this->queryContext - ); - - $expectedSql = join(' AND ', [ - '("pages"."hidden" = 0)', - '(("pages"."endtime" = 0) OR ("pages"."endtime" > 1459706700))', - '("pages"."deleted" = 0)', - ]); - - $this->assertSame($expectedSql, (string)$subject->getVisibilityConstraints()); - } - - /** - * @test - */ - public function getBackendVisibilityRestrictionsWithoutEndtimeColumn() - { - $this->queryContext->setContext(QueryContextType::BACKEND) - ->setAccessTime(1459706700) - ->setTableConfigs([ - 'pages' => [ - 'versioningWS' => true, - 'delete' => 'deleted', - 'enablecolumns' => [ - 'disabled' => 'hidden', - 'starttime' => 'starttime', - 'fe_group' => 'fe_group', - ], - ] - ]); - - $subject = GeneralUtility::makeInstance( - QueryRestrictionBuilder::class, - ['pages' => ''], - $this->expressionBuilder, - $this->queryContext - ); - - $expectedSql = join(' AND ', [ - '("pages"."hidden" = 0)', - '("pages"."starttime" <= 1459706700)', - '("pages"."deleted" = 0)', - ]); - - $this->assertSame($expectedSql, (string)$subject->getVisibilityConstraints()); - } - - /** - * @test - */ - public function getBackendVisibilityRestrictionsWithIgnoreEnableFieldsSet() - { - $this->queryContext->setContext(QueryContextType::BACKEND) - ->setAccessTime(1459706700) - ->setTableConfigs(['pages' => $this->defaultTableConfig]) - ->setIgnoreEnableFields(true); - - $subject = GeneralUtility::makeInstance( - QueryRestrictionBuilder::class, - ['pages' => ''], - $this->expressionBuilder, - $this->queryContext - ); - - $expectedSql = '"pages"."deleted" = 0'; - - $this->assertSame($expectedSql, (string)$subject->getVisibilityConstraints()); - } - - /** - * @test - */ - public function getBackendVisibilityRestrictionsWithIncludeDeletedSet() - { - $this->queryContext->setContext(QueryContextType::BACKEND) - ->setAccessTime(1459706700) - ->setTableConfigs(['pages' => $this->defaultTableConfig]) - ->setIncludeDeleted(true); - - $subject = GeneralUtility::makeInstance( - QueryRestrictionBuilder::class, - ['pages' => ''], - $this->expressionBuilder, - $this->queryContext - ); - - $expectedSql = join(' AND ', [ - 'disabled' => '("pages"."hidden" = 0)', - 'starttime' => '("pages"."starttime" <= 1459706700)', - 'endtime' => '(("pages"."endtime" = 0) OR ("pages"."endtime" > 1459706700))', - ]); - - $this->assertSame($expectedSql, (string)$subject->getVisibilityConstraints()); - } - - /** - * @test - */ - public function getBackendVisibilityRestrictionsWithNoVersionPlaceholdersContext() - { - $this->queryContext->setContext(QueryContextType::BACKEND_NO_VERSIONING_PLACEHOLDERS) - ->setCurrentWorkspace(4) - ->setAccessTime(1459706700) - ->setTableConfigs(['pages' => $this->defaultTableConfig]); - - $subject = GeneralUtility::makeInstance( - QueryRestrictionBuilder::class, - ['pages' => ''], - $this->expressionBuilder, - $this->queryContext - ); - - $expectedSql = join(' AND ', [ - 'disabled' => '("pages"."hidden" = 0)', - 'starttime' => '("pages"."starttime" <= 1459706700)', - 'endtime' => '(("pages"."endtime" = 0) OR ("pages"."endtime" > 1459706700))', - 'deleted' => '("pages"."deleted" = 0)', - 'placeholders' => '(("pages"."t3ver_state" <= 0) OR ("pages"."t3ver_wsid" = 4))', - ]); - - $this->assertSame($expectedSql, (string)$subject->getVisibilityConstraints()); - } - - /** - * @test - */ - public function getBackendVisibilityRestrictionsWithoutRestrictions() - { - $this->queryContext->setContext(QueryContextType::BACKEND) - ->setAccessTime(1459706700) - ->setTableConfigs(['pages' => $this->defaultTableConfig]) - ->setIncludeDeleted(true) - ->setIgnoreEnableFields(true); - - $subject = GeneralUtility::makeInstance( - QueryRestrictionBuilder::class, - ['pages' => ''], - $this->expressionBuilder, - $this->queryContext - ); - - $this->assertSame('', (string)$subject->getVisibilityConstraints()); - } - - // @todo: Test for per table overrides -} diff --git a/typo3/sysext/core/Tests/Unit/Database/Query/Restriction/AbstractRestrictionTestCase.php b/typo3/sysext/core/Tests/Unit/Database/Query/Restriction/AbstractRestrictionTestCase.php new file mode 100644 index 000000000000..6ee152c70885 --- /dev/null +++ b/typo3/sysext/core/Tests/Unit/Database/Query/Restriction/AbstractRestrictionTestCase.php @@ -0,0 +1,49 @@ +<?php +declare (strict_types = 1); +namespace TYPO3\CMS\Core\Tests\Unit\Database\Query\Restriction; + +/* + * 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 Prophecy\Argument; +use TYPO3\CMS\Core\Database\Connection; +use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder; +use TYPO3\CMS\Core\Tests\Unit\Database\Mocks\MockPlatform; +use TYPO3\CMS\Core\Tests\UnitTestCase; +use TYPO3\CMS\Core\Utility\GeneralUtility; + +class AbstractRestrictionTestCase extends UnitTestCase +{ + /** + * @var \TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder; + */ + protected $expressionBuilder; + + /** + * Create a new database connection mock object for every test. + */ + protected function setUp() + { + /** @var Connection|\Prophecy\Prophecy\ObjectProphecy $connection */ + $connection = $this->prophesize(Connection::class); + $connection->quoteIdentifier(Argument::cetera())->will(function ($args) { + return '"' . implode('"."', explode('.', $args[0])) . '"'; + }); + $connection->quote(Argument::cetera())->will(function ($args) { + return '\'' . $args[0] . '\''; + }); + $connection->getDatabasePlatform()->willReturn(new MockPlatform()); + + $this->expressionBuilder = GeneralUtility::makeInstance(ExpressionBuilder::class, $connection->reveal()); + } +} diff --git a/typo3/sysext/core/Tests/Unit/Database/Query/Restriction/BackendWorkspaceRestrictionTest.php b/typo3/sysext/core/Tests/Unit/Database/Query/Restriction/BackendWorkspaceRestrictionTest.php new file mode 100644 index 000000000000..d0863dfa372a --- /dev/null +++ b/typo3/sysext/core/Tests/Unit/Database/Query/Restriction/BackendWorkspaceRestrictionTest.php @@ -0,0 +1,81 @@ +<?php +declare (strict_types = 1); +namespace TYPO3\CMS\Core\Tests\Unit\Database\Query\Restriction; + +/* + * 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\Core\Database\Query\Restriction\BackendWorkspaceRestriction; + +class BackendWorkspaceRestrictionTest extends AbstractRestrictionTestCase +{ + /** + * @return void + */ + protected function setUp() + { + parent::setUp(); + } + + /** + * @test + */ + public function buildExpressionAddsLiveWorkspaceWhereClause() + { + $GLOBALS['TCA']['aTable']['ctrl'] = [ + 'versioningWS' => 2, + ]; + $subject = new BackendWorkspaceRestriction(0); + $expression = $subject->buildExpression(['aTable' => ''], $this->expressionBuilder); + $this->assertSame('("aTable"."t3ver_wsid" = 0) OR ("aTable"."t3ver_state" <= 0)', (string)$expression); + } + + /** + * @test + */ + public function buildExpressionAddsNonLiveWorkspaceWhereClause() + { + $GLOBALS['TCA']['aTable']['ctrl'] = [ + 'versioningWS' => 2, + ]; + $subject = new BackendWorkspaceRestriction(42); + $expression = $subject->buildExpression(['aTable' => ''], $this->expressionBuilder); + $this->assertSame('("aTable"."t3ver_wsid" = 42) OR ("aTable"."t3ver_state" <= 0)', (string)$expression); + } + + /** + * @test + */ + public function buildExpressionAddsLiveWorkspaceLimitedWhereClause() + { + $GLOBALS['TCA']['aTable']['ctrl'] = [ + 'versioningWS' => 2, + ]; + $subject = new BackendWorkspaceRestriction(0, false); + $expression = $subject->buildExpression(['aTable' => ''], $this->expressionBuilder); + $this->assertSame('("aTable"."t3ver_wsid" = 0) AND ("aTable"."pid" <> -1)', (string)$expression); + } + + /** + * @test + */ + public function buildExpressionAddsNonLiveWorkspaceLimitedWhereClause() + { + $GLOBALS['TCA']['aTable']['ctrl'] = [ + 'versioningWS' => 2, + ]; + $subject = new BackendWorkspaceRestriction(42, false); + $expression = $subject->buildExpression(['aTable' => ''], $this->expressionBuilder); + $this->assertSame('("aTable"."t3ver_wsid" = 42) AND ("aTable"."pid" = -1)', (string)$expression); + } +} diff --git a/typo3/sysext/core/Tests/Unit/Database/Query/Restriction/DefaultRestrictionContainerTest.php b/typo3/sysext/core/Tests/Unit/Database/Query/Restriction/DefaultRestrictionContainerTest.php new file mode 100644 index 000000000000..fa48e0468027 --- /dev/null +++ b/typo3/sysext/core/Tests/Unit/Database/Query/Restriction/DefaultRestrictionContainerTest.php @@ -0,0 +1,51 @@ +<?php +declare (strict_types = 1); +namespace TYPO3\CMS\Core\Tests\Unit\Database\Query\Restriction; + +/* + * 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\Core\Database\Query\Restriction\DefaultRestrictionContainer; + +class DefaultRestrictionContainerTest extends AbstractRestrictionTestCase +{ + /** + * @return void + */ + protected function setUp() + { + parent::setUp(); + } + + /** + * @test + */ + public function buildRestrictionsAddsAllDefaultRestrictions() + { + $GLOBALS['TCA']['aTable']['ctrl'] = [ + 'delete' => 'deleted', + 'enablecolumns' => [ + 'disabled' => 'myHiddenField', + 'starttime' => 'myStartTimeField', + 'endtime' => 'myEndTimeField', + ], + ]; + $GLOBALS['SIM_ACCESS_TIME'] = 123; + $subject = new DefaultRestrictionContainer(); + $expression = $subject->buildExpression(['aTable' => ''], $this->expressionBuilder); + $more[] = $expression; + $expression = $this->expressionBuilder->andX($expression); + + $this->assertSame('("aTable"."deleted" = 0) AND ("aTable"."myHiddenField" = 0) AND ("aTable"."myStartTimeField" <= 123) AND (("aTable"."myEndTimeField" = 0) OR ("aTable"."myEndTimeField" > 123))', (string)$expression); + } +} diff --git a/typo3/sysext/core/Tests/Unit/Database/Query/Restriction/DeletedRestrictionTest.php b/typo3/sysext/core/Tests/Unit/Database/Query/Restriction/DeletedRestrictionTest.php new file mode 100644 index 000000000000..686019050c6e --- /dev/null +++ b/typo3/sysext/core/Tests/Unit/Database/Query/Restriction/DeletedRestrictionTest.php @@ -0,0 +1,42 @@ +<?php +declare (strict_types = 1); +namespace TYPO3\CMS\Core\Tests\Unit\Database\Query\Restriction; + +/* + * 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\Core\Database\Query\Restriction\DeletedRestriction; + +class DeletedRestrictionTest extends AbstractRestrictionTestCase +{ + /** + * @return void + */ + protected function setUp() + { + parent::setUp(); + } + + /** + * @test + */ + public function buildRestrictionsAddsDeletedWhereClause() + { + $GLOBALS['TCA']['aTable']['ctrl'] = [ + 'delete' => 'deleted', + ]; + $subject = new DeletedRestriction(); + $expression = $subject->buildExpression(['aTable' => ''], $this->expressionBuilder); + $this->assertSame('"aTable"."deleted" = 0', (string)$expression); + } +} diff --git a/typo3/sysext/core/Tests/Unit/Database/Query/Restriction/EndTimeRestrictionTest.php b/typo3/sysext/core/Tests/Unit/Database/Query/Restriction/EndTimeRestrictionTest.php new file mode 100644 index 000000000000..2e9c74207c8d --- /dev/null +++ b/typo3/sysext/core/Tests/Unit/Database/Query/Restriction/EndTimeRestrictionTest.php @@ -0,0 +1,64 @@ +<?php +declare (strict_types = 1); +namespace TYPO3\CMS\Core\Tests\Unit\Database\Query\Restriction; + +/* + * 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\Core\Database\Query\Restriction\EndTimeRestriction; + +class EndTimeRestrictionTest extends AbstractRestrictionTestCase +{ + /** + * @return void + */ + protected function setUp() + { + parent::setUp(); + } + + /** + * @test + */ + public function buildRestrictionsThrowsExceptionInStartTimeIfGlobalsAccessTimeIsMissing() + { + $GLOBALS['TCA']['aTable']['ctrl'] = [ + 'enablecolumns' => [ + 'endtime' => 'myEndTimeField', + ], + ]; + unset($GLOBALS['SIM_ACCESS_TIME']); + + $this->expectException(\RuntimeException::class); + $this->expectExceptionCode(1462821084); + + $subject = new EndTimeRestriction(); + $subject->buildExpression(['aTable' => ''], $this->expressionBuilder); + } + + /** + * @test + */ + public function buildRestrictionsAddsStartTimeWhereClause() + { + $GLOBALS['TCA']['aTable']['ctrl'] = [ + 'enablecolumns' => [ + 'endtime' => 'myEndTimeField', + ], + ]; + + $subject = new EndTimeRestriction(42); + $expression = $subject->buildExpression(['aTable' => ''], $this->expressionBuilder); + $this->assertSame('("aTable"."myEndTimeField" = 0) OR ("aTable"."myEndTimeField" > 42)', (string)$expression); + } +} diff --git a/typo3/sysext/core/Tests/Unit/Database/Query/Restriction/FrontendGroupRestrictionTest.php b/typo3/sysext/core/Tests/Unit/Database/Query/Restriction/FrontendGroupRestrictionTest.php new file mode 100644 index 000000000000..38a4b8fdb3c5 --- /dev/null +++ b/typo3/sysext/core/Tests/Unit/Database/Query/Restriction/FrontendGroupRestrictionTest.php @@ -0,0 +1,59 @@ +<?php +declare (strict_types = 1); +namespace TYPO3\CMS\Core\Tests\Unit\Database\Query\Restriction; + +/* + * 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\Core\Database\Query\Restriction\FrontendGroupRestriction; + +class FrontendGroupRestrictionTest extends AbstractRestrictionTestCase +{ + /** + * @return void + */ + protected function setUp() + { + parent::setUp(); + } + + /** + * @test + */ + public function buildExpressionAddsNoAccessGroupWhereClause() + { + $GLOBALS['TCA']['aTable']['ctrl'] = [ + 'enablecolumns' => [ + 'fe_group' => 'myGroupField', + ], + ]; + $subject = new FrontendGroupRestriction([]); + $expression = $subject->buildExpression(['aTable' => ''], $this->expressionBuilder); + $this->assertSame('("aTable"."myGroupField" IS NULL) OR ("aTable"."myGroupField" = \'\') OR ("aTable"."myGroupField" = \'0\')', (string)$expression); + } + + /** + * @test + */ + public function buildExpressionAddsGroupWhereClause() + { + $GLOBALS['TCA']['aTable']['ctrl'] = [ + 'enablecolumns' => [ + 'fe_group' => 'myGroupField', + ], + ]; + $subject = new FrontendGroupRestriction([2, 3]); + $expression = $subject->buildExpression(['aTable' => ''], $this->expressionBuilder); + $this->assertSame('("aTable"."myGroupField" IS NULL) OR ("aTable"."myGroupField" = \'\') OR ("aTable"."myGroupField" = \'0\') OR (FIND_IN_SET(\'2\', "aTable"."myGroupField")) OR (FIND_IN_SET(\'3\', "aTable"."myGroupField"))', (string)$expression); + } +} diff --git a/typo3/sysext/core/Tests/Unit/Database/Query/Restriction/FrontendRestrictionContainerTest.php b/typo3/sysext/core/Tests/Unit/Database/Query/Restriction/FrontendRestrictionContainerTest.php new file mode 100644 index 000000000000..0927168e5b39 --- /dev/null +++ b/typo3/sysext/core/Tests/Unit/Database/Query/Restriction/FrontendRestrictionContainerTest.php @@ -0,0 +1,185 @@ +<?php +declare (strict_types = 1); +namespace TYPO3\CMS\Core\Tests\Unit\Database\Query\Restriction; + +/* + * 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\Core\Database\DatabaseConnection; +use TYPO3\CMS\Core\Database\Query\Restriction\FrontendRestrictionContainer; +use TYPO3\CMS\Frontend\Page\PageRepository; + +class FrontendRestrictionContainerTest extends AbstractRestrictionTestCase +{ + /** + * @return void + */ + protected function setUp() + { + parent::setUp(); + } + + public function frontendStatesDataProvider() + { + return [ + 'Live, no preview' => [ + 'tableName' => 'aTable', + 'workspaceId' => 0, + 'workspacePreview' => false, + 'hiddenPagePreview' => false, + 'hiddenRecordPreview' => false, + 'feGroupList' => '0,-1', + 'expectedSQL' => '("aTable"."deleted" = 0) AND (("aTable"."t3ver_state" <= 0) AND ("aTable"."pid" <> -1)) AND ("aTable"."myHiddenField" = 0) AND ("aTable"."myStartTimeField" <= 42) AND (("aTable"."myEndTimeField" = 0) OR ("aTable"."myEndTimeField" > 42)) AND (("aTable"."myGroupField" IS NULL) OR ("aTable"."myGroupField" = \'\') OR ("aTable"."myGroupField" = \'0\') OR (FIND_IN_SET(\'0\', "aTable"."myGroupField")) OR (FIND_IN_SET(\'-1\', "aTable"."myGroupField")))' + ], + 'Live, with hidden record preview' => [ + 'tableName' => 'aTable', + 'workspaceId' => 0, + 'workspacePreview' => false, + 'hiddenPagePreview' => true, + 'hiddenRecordPreview' => true, + 'feGroupList' => '0,-1', + 'expectedSQL' => '("aTable"."deleted" = 0) AND (("aTable"."t3ver_state" <= 0) AND ("aTable"."pid" <> -1)) AND ("aTable"."myStartTimeField" <= 42) AND (("aTable"."myEndTimeField" = 0) OR ("aTable"."myEndTimeField" > 42)) AND (("aTable"."myGroupField" IS NULL) OR ("aTable"."myGroupField" = \'\') OR ("aTable"."myGroupField" = \'0\') OR (FIND_IN_SET(\'0\', "aTable"."myGroupField")) OR (FIND_IN_SET(\'-1\', "aTable"."myGroupField")))' + ], + 'Workspace, with WS preview' => [ + 'tableName' => 'aTable', + 'workspaceId' => 1, + 'workspacePreview' => true, + 'hiddenPagePreview' => false, + 'hiddenRecordPreview' => false, + 'feGroupList' => '0,-1', + 'expectedSQL' => '("aTable"."deleted" = 0) AND ((("aTable"."t3ver_wsid" = 0) OR ("aTable"."t3ver_wsid" = 1)) AND ("aTable"."pid" <> -1)) AND ("aTable"."myHiddenField" = 0) AND ("aTable"."myStartTimeField" <= 42) AND (("aTable"."myEndTimeField" = 0) OR ("aTable"."myEndTimeField" > 42)) AND (("aTable"."myGroupField" IS NULL) OR ("aTable"."myGroupField" = \'\') OR ("aTable"."myGroupField" = \'0\') OR (FIND_IN_SET(\'0\', "aTable"."myGroupField")) OR (FIND_IN_SET(\'-1\', "aTable"."myGroupField")))' + ], + 'Workspace, with WS preview and hidden record preview' => [ + 'tableName' => 'aTable', + 'workspaceId' => 1, + 'workspacePreview' => true, + 'hiddenPagePreview' => true, + 'hiddenRecordPreview' => true, + 'feGroupList' => '0,-1', + 'expectedSQL' => '("aTable"."deleted" = 0) AND ((("aTable"."t3ver_wsid" = 0) OR ("aTable"."t3ver_wsid" = 1)) AND ("aTable"."pid" <> -1)) AND ("aTable"."myStartTimeField" <= 42) AND (("aTable"."myEndTimeField" = 0) OR ("aTable"."myEndTimeField" > 42)) AND (("aTable"."myGroupField" IS NULL) OR ("aTable"."myGroupField" = \'\') OR ("aTable"."myGroupField" = \'0\') OR (FIND_IN_SET(\'0\', "aTable"."myGroupField")) OR (FIND_IN_SET(\'-1\', "aTable"."myGroupField")))' + ], + 'Live page, no preview' => [ + 'tableName' => 'pages', + 'workspaceId' => 0, + 'workspacePreview' => false, + 'hiddenPagePreview' => false, + 'hiddenRecordPreview' => false, + 'feGroupList' => '0,-1', + 'expectedSQL' => '("pages"."deleted" = 0) AND (("pages"."t3ver_state" <= 0) AND ("pages"."pid" <> -1)) AND ("pages"."hidden" = 0) AND ("pages"."starttime" <= 42) AND (("pages"."endtime" = 0) OR ("pages"."endtime" > 42)) AND (("pages"."fe_group" IS NULL) OR ("pages"."fe_group" = \'\') OR ("pages"."fe_group" = \'0\') OR (FIND_IN_SET(\'0\', "pages"."fe_group")) OR (FIND_IN_SET(\'-1\', "pages"."fe_group")))' + ], + 'Live page, with hidden page preview' => [ + 'tableName' => 'pages', + 'workspaceId' => 0, + 'workspacePreview' => false, + 'hiddenPagePreview' => true, + 'hiddenRecordPreview' => true, + 'feGroupList' => '0,-1', + 'expectedSQL' => '("pages"."deleted" = 0) AND (("pages"."t3ver_state" <= 0) AND ("pages"."pid" <> -1)) AND ("pages"."starttime" <= 42) AND (("pages"."endtime" = 0) OR ("pages"."endtime" > 42)) AND (("pages"."fe_group" IS NULL) OR ("pages"."fe_group" = \'\') OR ("pages"."fe_group" = \'0\') OR (FIND_IN_SET(\'0\', "pages"."fe_group")) OR (FIND_IN_SET(\'-1\', "pages"."fe_group")))' + ], + 'Workspace page, with WS preview' => [ + 'tableName' => 'pages', + 'workspaceId' => 1, + 'workspacePreview' => true, + 'hiddenPagePreview' => false, + 'hiddenRecordPreview' => false, + 'feGroupList' => '0,-1', + 'expectedSQL' => '("pages"."deleted" = 0) AND ("pages"."pid" <> -1) AND ("pages"."hidden" = 0) AND ("pages"."starttime" <= 42) AND (("pages"."endtime" = 0) OR ("pages"."endtime" > 42)) AND (("pages"."fe_group" IS NULL) OR ("pages"."fe_group" = \'\') OR ("pages"."fe_group" = \'0\') OR (FIND_IN_SET(\'0\', "pages"."fe_group")) OR (FIND_IN_SET(\'-1\', "pages"."fe_group")))' + ], + 'Workspace page, with WS preview and hidden pages preview' => [ + 'tableName' => 'pages', + 'workspaceId' => 1, + 'workspacePreview' => true, + 'hiddenPagePreview' => true, + 'hiddenRecordPreview' => true, + 'feGroupList' => '0,-1', + 'expectedSQL' => '("pages"."deleted" = 0) AND ("pages"."pid" <> -1) AND ("pages"."starttime" <= 42) AND (("pages"."endtime" = 0) OR ("pages"."endtime" > 42)) AND (("pages"."fe_group" IS NULL) OR ("pages"."fe_group" = \'\') OR ("pages"."fe_group" = \'0\') OR (FIND_IN_SET(\'0\', "pages"."fe_group")) OR (FIND_IN_SET(\'-1\', "pages"."fe_group")))' + ], + ]; + } + + /** + * @param string $tableName + * @param int $workspaceId + * @param bool $workspacePreview + * @param bool $hiddenPagePreview + * @param bool $hiddenRecordPreview + * @param string $feGroupList + * @param string $expectedSQL + * + * @test + * @dataProvider frontendStatesDataProvider + */ + public function buildExpressionAddsCorrectClause( + string $tableName, + int $workspaceId, + bool $workspacePreview, + bool $hiddenPagePreview, + bool $hiddenRecordPreview, + string $feGroupList, + string $expectedSQL + ) { + $GLOBALS['TCA'] = [ + 'aTable' => [ + 'ctrl' => [ + 'versioningWS' => 2, + 'delete' => 'deleted', + 'enablecolumns' => [ + 'disabled' => 'myHiddenField', + 'starttime' => 'myStartTimeField', + 'endtime' => 'myEndTimeField', + 'fe_group' => 'myGroupField', + ], + ], + ], + 'pages' => array( + 'ctrl' => array( + 'label' => 'title', + 'tstamp' => 'tstamp', + 'sortby' => 'sorting', + 'type' => 'doktype', + 'versioningWS' => true, + 'origUid' => 't3_origuid', + 'delete' => 'deleted', + 'enablecolumns' => array( + 'disabled' => 'hidden', + 'starttime' => 'starttime', + 'endtime' => 'endtime', + 'fe_group' => 'fe_group' + ), + ), + 'columns' => array() + ) + ]; + + $pageRepository = $this->getMock(PageRepository::class); + $pageRepository->versioningWorkspaceId = $workspaceId; + $pageRepository->versioningPreview = $workspacePreview; + + $typoScriptFrontendController = new \stdClass(); + $typoScriptFrontendController->showHiddenPage = $hiddenPagePreview; + $typoScriptFrontendController->showHiddenRecords = $hiddenRecordPreview; + $typoScriptFrontendController->gr_list = $feGroupList; + $typoScriptFrontendController->sys_page = $pageRepository; + + $dbMock = $this->getMock(DatabaseConnection::class, ['quoteStr']); + $dbMock->expects($this->any())->method('quoteStr')->willReturnArgument(0); + + $GLOBALS['TSFE'] = $typoScriptFrontendController; + $GLOBALS['SIM_ACCESS_TIME'] = 42; + $GLOBALS['TYPO3_DB'] = $dbMock; + + $subject = new FrontendRestrictionContainer(); + $expression = $subject->buildExpression([$tableName => ''], $this->expressionBuilder); + $this->assertSame($expectedSQL, (string)$expression); + } +} diff --git a/typo3/sysext/core/Tests/Unit/Database/Query/Restriction/FrontendWorkspaceRestrictionTest.php b/typo3/sysext/core/Tests/Unit/Database/Query/Restriction/FrontendWorkspaceRestrictionTest.php new file mode 100644 index 000000000000..e7689f7e0c9a --- /dev/null +++ b/typo3/sysext/core/Tests/Unit/Database/Query/Restriction/FrontendWorkspaceRestrictionTest.php @@ -0,0 +1,95 @@ +<?php +declare (strict_types = 1); +namespace TYPO3\CMS\Core\Tests\Unit\Database\Query\Restriction; + +/* + * 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\Core\Database\Query\Restriction\FrontendWorkspaceRestriction; +use TYPO3\CMS\Frontend\Page\PageRepository; + +class FrontendWorkspaceRestrictionTest extends AbstractRestrictionTestCase +{ + /** + * @return void + */ + protected function setUp() + { + parent::setUp(); + } + + /** + * @test + */ + public function buildExpressionAddsLiveWorkspaceWhereClause() + { + $GLOBALS['TCA'] = [ + 'aTable' => [ + 'ctrl' => [ + 'versioningWS' => 2, + ], + ] + ]; + + $pageRepository = $this->getMock(PageRepository::class); + $pageRepository->versioningPreview = false; + + $subject = new FrontendWorkspaceRestriction(0); + $expression = $subject->buildExpression(['aTable' => ''], $this->expressionBuilder); + $this->assertSame('("aTable"."t3ver_state" <= 0) AND ("aTable"."pid" <> -1)', (string)$expression); + } + + /** + * @test + */ + public function buildExpressionAddsNonLiveWorkspaceWhereClause() + { + $GLOBALS['TCA'] = [ + 'aTable' => [ + 'ctrl' => [ + 'versioningWS' => 2, + ], + ] + ]; + + $pageRepository = $this->getMock(PageRepository::class); + $pageRepository->versioningPreview = true; + $pageRepository->versioningWorkspaceId = 42; + + $subject = new FrontendWorkspaceRestriction(42, true); + $expression = $subject->buildExpression(['aTable' => ''], $this->expressionBuilder); + $this->assertSame('(("aTable"."t3ver_wsid" = 0) OR ("aTable"."t3ver_wsid" = 42)) AND ("aTable"."pid" <> -1)', (string)$expression); + } + + /** + * @test + */ + public function buildExpressionAddsNonLiveWorkspaceExclusiveWhereClause() + { + $GLOBALS['TCA'] = [ + 'aTable' => [ + 'ctrl' => [ + 'versioningWS' => 2, + ], + ] + ]; + + $pageRepository = $this->getMock(PageRepository::class); + $pageRepository->versioningPreview = true; + $pageRepository->versioningWorkspaceId = 42; + + $subject = new FrontendWorkspaceRestriction(42, true, false); + $expression = $subject->buildExpression(['aTable' => ''], $this->expressionBuilder); + $this->assertSame('("aTable"."t3ver_wsid" = 0) OR ("aTable"."t3ver_wsid" = 42)', (string)$expression); + } +} diff --git a/typo3/sysext/core/Tests/Unit/Database/Query/Restriction/HiddenRestrictionTest.php b/typo3/sysext/core/Tests/Unit/Database/Query/Restriction/HiddenRestrictionTest.php new file mode 100644 index 000000000000..41c4f2e8cf96 --- /dev/null +++ b/typo3/sysext/core/Tests/Unit/Database/Query/Restriction/HiddenRestrictionTest.php @@ -0,0 +1,44 @@ +<?php +declare (strict_types = 1); +namespace TYPO3\CMS\Core\Tests\Unit\Database\Query\Restriction; + +/* + * 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\Core\Database\Query\Restriction\HiddenRestriction; + +class HiddenRestrictionTest extends AbstractRestrictionTestCase +{ + /** + * @return void + */ + protected function setUp() + { + parent::setUp(); + } + + /** + * @test + */ + public function buildRestrictionsAddsHiddenWhereClause() + { + $GLOBALS['TCA']['aTable']['ctrl'] = [ + 'enablecolumns' => [ + 'disabled' => 'myHiddenField', + ], + ]; + $subject = new HiddenRestriction(); + $expression = $subject->buildExpression(['aTable' => ''], $this->expressionBuilder); + $this->assertSame('"aTable"."myHiddenField" = 0', (string)$expression); + } +} diff --git a/typo3/sysext/core/Tests/Unit/Database/Query/Restriction/RootLevelRestrictionTest.php b/typo3/sysext/core/Tests/Unit/Database/Query/Restriction/RootLevelRestrictionTest.php new file mode 100644 index 000000000000..c345e2c0ea99 --- /dev/null +++ b/typo3/sysext/core/Tests/Unit/Database/Query/Restriction/RootLevelRestrictionTest.php @@ -0,0 +1,71 @@ +<?php +declare (strict_types = 1); +namespace TYPO3\CMS\Core\Tests\Unit\Database\Query\Restriction; + +/* + * 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\Core\Database\Query\Restriction\RootLevelRestriction; + +class RootLevelRestrictionTest extends AbstractRestrictionTestCase +{ + /** + * @test + */ + public function buildRestrictionsAddsPidWhereClause() + { + $subject = new RootLevelRestriction(); + $expression = $subject->buildExpression(['aTable' => ''], $this->expressionBuilder); + $this->assertSame('"aTable"."pid" = 0', (string)$expression); + } + + /** + * @test + */ + public function buildRestrictionsAddsAliasedPidWhereClause() + { + $subject = new RootLevelRestriction(); + $expression = $subject->buildExpression(['aTable' => 'aTableAlias'], $this->expressionBuilder); + $this->assertSame('"aTableAlias"."pid" = 0', (string)$expression); + } + + /** + * @test + */ + public function buildRestrictionsAddsPidWhereClauseIfTableIsSpecified() + { + $subject = new RootLevelRestriction(['aTable']); + $expression = $subject->buildExpression(['aTable' => ''], $this->expressionBuilder); + $this->assertSame('"aTable"."pid" = 0', (string)$expression); + } + + /** + * @test + */ + public function buildRestrictionsAddsAliasedPidWhereClauseIfAliasIsSpecified() + { + $subject = new RootLevelRestriction(['aTableAlias']); + $expression = $subject->buildExpression(['aTable' => 'aTableAlias'], $this->expressionBuilder); + $this->assertSame('"aTableAlias"."pid" = 0', (string)$expression); + } + + /** + * @test + */ + public function buildRestrictionsSkipsUnrestrictedTablesIfOtherTableIsSpecifiedThanUsedInTheQuery() + { + $subject = new RootLevelRestriction(['aTable']); + $expression = $subject->buildExpression(['anotherTable' => ''], $this->expressionBuilder); + $this->assertSame('', (string)$expression); + } +} diff --git a/typo3/sysext/core/Tests/Unit/Database/Query/Restriction/StartTimeRestrictionTest.php b/typo3/sysext/core/Tests/Unit/Database/Query/Restriction/StartTimeRestrictionTest.php new file mode 100644 index 000000000000..1dcd324ad878 --- /dev/null +++ b/typo3/sysext/core/Tests/Unit/Database/Query/Restriction/StartTimeRestrictionTest.php @@ -0,0 +1,64 @@ +<?php +declare (strict_types = 1); +namespace TYPO3\CMS\Core\Tests\Unit\Database\Query\Restriction; + +/* + * 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\Core\Database\Query\Restriction\StartTimeRestriction; + +class StartTimeRestrictionTest extends AbstractRestrictionTestCase +{ + /** + * @return void + */ + protected function setUp() + { + parent::setUp(); + } + + /** + * @test + */ + public function buildRestrictionsThrowsExceptionInStartTimeIfGlobalsAccessTimeIsMissing() + { + $GLOBALS['TCA']['aTable']['ctrl'] = [ + 'enablecolumns' => [ + 'starttime' => 'myStartTimeField', + ], + ]; + unset($GLOBALS['SIM_ACCESS_TIME']); + + $this->expectException(\RuntimeException::class); + $this->expectExceptionCode(1462820645); + + $subject = new StartTimeRestriction(); + $subject->buildExpression(['aTable' => ''], $this->expressionBuilder); + } + + /** + * @test + */ + public function buildRestrictionsAddsStartTimeWhereClause() + { + $GLOBALS['TCA']['aTable']['ctrl'] = [ + 'enablecolumns' => [ + 'starttime' => 'myStartTimeField', + ], + ]; + + $subject = new StartTimeRestriction(42); + $expression = $subject->buildExpression(['aTable' => ''], $this->expressionBuilder); + $this->assertSame('"aTable"."myStartTimeField" <= 42', (string)$expression); + } +} diff --git a/typo3/sysext/felogin/Classes/Controller/FrontendLoginController.php b/typo3/sysext/felogin/Classes/Controller/FrontendLoginController.php index d4fe68f4adc3..5b95ca29b40e 100644 --- a/typo3/sysext/felogin/Classes/Controller/FrontendLoginController.php +++ b/typo3/sysext/felogin/Classes/Controller/FrontendLoginController.php @@ -16,7 +16,7 @@ namespace TYPO3\CMS\Felogin\Controller; use TYPO3\CMS\Core\Crypto\Random; use TYPO3\CMS\Core\Database\ConnectionPool; -use TYPO3\CMS\Core\Database\Query\QueryBuilder; +use TYPO3\CMS\Core\Database\Query\Restriction\FrontendRestrictionContainer; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\StringUtility; @@ -227,26 +227,24 @@ class FrontendLoginController extends \TYPO3\CMS\Frontend\Plugin\AbstractPlugin $postedHash = $postData['forgot_hash']; $hashData = $this->frontendController->fe_user->getKey('ses', 'forgot_hash'); if ($postedHash === $hashData['forgot_hash']) { - - /** @var QueryBuilder $queryBuilder */ - $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('fe_users'); + $userTable = $this->frontendController->fe_user->user_table; + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($userTable); + $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class)); $row = $queryBuilder ->select('uid', 'username', 'password', 'email') - ->from('fe_users') + ->from($userTable) ->where( - $queryBuilder->expr()->andX( - $queryBuilder->expr()->orX( - $queryBuilder->expr()->eq( - 'email', - $queryBuilder->createNamedParameter($this->piVars['forgot_email']) - ), - $queryBuilder->expr()->eq( - 'username', - $queryBuilder->createNamedParameter($this->piVars['forgot_email']) - ) + $queryBuilder->expr()->orX( + $queryBuilder->expr()->eq( + 'email', + $queryBuilder->createNamedParameter($this->piVars['forgot_email']) ), - $queryBuilder->expr()->in('pid', GeneralUtility::intExplode(',', $this->spid)) - ) + $queryBuilder->expr()->eq( + 'username', + $queryBuilder->createNamedParameter($this->piVars['forgot_email']) + ) + ), + $queryBuilder->expr()->in('pid', GeneralUtility::intExplode(',', $this->spid)) ) ->execute() ->fetch(); @@ -366,12 +364,13 @@ class FrontendLoginController extends \TYPO3\CMS\Frontend\Plugin\AbstractPlugin } // Save new password and clear DB-hash - /** @var QueryBuilder $queryBuilder */ - $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('fe_users'); - $queryBuilder->update('fe_users') + $userTable = $this->frontendController->fe_user->user_table; + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($userTable); + $queryBuilder->getRestrictions()->removeAll(); + $queryBuilder->update($userTable) ->set('password', $newPass) ->set('felogin_forgotHash', '') - ->set('tstamp', $GLOBALS['EXEC_TIME']) + ->set('tstamp', (int)$GLOBALS['EXEC_TIME']) ->where($queryBuilder->expr()->eq('uid', (int)$user['uid'])) ->execute(); @@ -423,10 +422,11 @@ class FrontendLoginController extends \TYPO3\CMS\Frontend\Plugin\AbstractPlugin $randHashDB = $validEnd . '|' . md5($hash); // Write hash to DB - /** @var QueryBuilder $queryBuilder */ - $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('fe_users'); - $queryBuilder->update('fe_users') - ->set('felogin_forgotHash', (string)$randHashDB) + $userTable = $this->frontendController->fe_user->user_table; + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($userTable); + $queryBuilder->getRestrictions()->removeAll(); + $queryBuilder->update($userTable) + ->set('felogin_forgotHash', $randHashDB) ->where($queryBuilder->expr()->eq('uid', (int)$user['uid'])) ->execute(); @@ -671,16 +671,14 @@ class FrontendLoginController extends \TYPO3\CMS\Frontend\Plugin\AbstractPlugin // take the first group with a redirect page $userGroupTable = $this->frontendController->fe_user->usergroup_table; - /** @var QueryBuilder $queryBuilder */ $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($userGroupTable); + $queryBuilder->getRestrictions()->removeAll(); $row = $queryBuilder ->select('felogin_redirectPid') ->from($userGroupTable) ->where( - $queryBuilder->expr()->andX( - $queryBuilder->expr()->neq('felogin_redirectPid', $queryBuilder->quote('')), - $queryBuilder->expr()->in('uid', implode(',', $groupData['uid'])) - ) + $queryBuilder->expr()->neq('felogin_redirectPid', $queryBuilder->quote('')), + $queryBuilder->expr()->in('uid', array_map('intval', $groupData['uid'])) ) ->execute() ->fetch(); @@ -693,18 +691,16 @@ class FrontendLoginController extends \TYPO3\CMS\Frontend\Plugin\AbstractPlugin case 'userLogin': $userTable = $this->frontendController->fe_user->user_table; - /** @var QueryBuilder $queryBuilder */ $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($userTable); + $queryBuilder->getRestrictions()->removeAll(); $row = $queryBuilder ->select('felogin_redirectPid') ->from($userTable) ->where( - $queryBuilder->expr()->andX( - $queryBuilder->expr()->neq('felogin_redirectPid', $queryBuilder->quote('')), - $queryBuilder->expr()->eq( - $this->frontendController->fe_user->userid_column, - (int)$this->frontendController->fe_user->user['uid'] - ) + $queryBuilder->expr()->neq('felogin_redirectPid', $queryBuilder->quote('')), + $queryBuilder->expr()->eq( + $this->frontendController->fe_user->userid_column, + (int)$this->frontendController->fe_user->user['uid'] ) ) ->execute() @@ -1046,8 +1042,8 @@ class FrontendLoginController extends \TYPO3\CMS\Frontend\Plugin\AbstractPlugin // Removes the last path segment and slash sequences like /// (if given): $path = preg_replace('#/+[^/]*$#', '', $parsedUrl['path']); - /** @var QueryBuilder $queryBuilder */ $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_domain'); + $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class)); $localDomains = $queryBuilder->select('domainName') ->from('sys_domain') ->execute() diff --git a/typo3/sysext/felogin/Tests/Unit/Controller/FrontendLoginControllerTest.php b/typo3/sysext/felogin/Tests/Unit/Controller/FrontendLoginControllerTest.php index 0757f8bdf93a..3edb460dfb96 100644 --- a/typo3/sysext/felogin/Tests/Unit/Controller/FrontendLoginControllerTest.php +++ b/typo3/sysext/felogin/Tests/Unit/Controller/FrontendLoginControllerTest.php @@ -54,6 +54,8 @@ class FrontendLoginControllerTest extends \TYPO3\CMS\Core\Tests\UnitTestCase */ protected function setUp() { + $GLOBALS['TSFE'] = new \stdClass(); + $GLOBALS['TSFE']->gr_list = '0,-1'; $this->testTableName = 'sys_domain'; $this->testHostName = 'hostname.tld'; $this->testSitePath = '/'; @@ -84,8 +86,9 @@ class FrontendLoginControllerTest extends \TYPO3\CMS\Core\Tests\UnitTestCase $connection->getExpressionBuilder()->willReturn(new ExpressionBuilder($connection->reveal())); $connection->quoteIdentifier(Argument::cetera())->willReturnArgument(0); - $queryBuilder = GeneralUtility::makeInstance( - QueryBuilder::class, + // TODO: This should rather be a functional test if we need a query builder + // or we should clean up the code itself to not need to mock internal behavior here + $queryBuilder = new QueryBuilder( $connection->reveal(), null, new \Doctrine\DBAL\Query\QueryBuilder($connection->reveal()) diff --git a/typo3/sysext/filelist/Classes/FileFacade.php b/typo3/sysext/filelist/Classes/FileFacade.php index 131fb9c4f812..413f156049b5 100644 --- a/typo3/sysext/filelist/Classes/FileFacade.php +++ b/typo3/sysext/filelist/Classes/FileFacade.php @@ -18,8 +18,8 @@ use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Imaging\Icon; use TYPO3\CMS\Core\Imaging\IconFactory; -use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Resource\FileInterface; +use TYPO3\CMS\Core\Utility\GeneralUtility; /** * Class FileFacade @@ -256,9 +256,12 @@ class FileFacade $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_refindex'); $count = $queryBuilder->count('*') ->from('sys_refindex') - ->where($queryBuilder->expr()->eq('ref_table', $queryBuilder->quote('sys_file'))) - ->andWhere($queryBuilder->expr()->eq('ref_uid', (int)$this->resource->getProperty('uid'))) - ->andWhere($queryBuilder->expr()->neq('tablename', $queryBuilder->quote('sys_file_metadata'))) + ->where( + $queryBuilder->expr()->eq('deleted', 0), + $queryBuilder->expr()->eq('ref_table', $queryBuilder->createNamedParameter('sys_file')), + $queryBuilder->expr()->eq('ref_uid', (int)$this->resource->getProperty('uid')), + $queryBuilder->expr()->neq('tablename', $queryBuilder->createNamedParameter('sys_file_metadata')) + ) ->execute() ->fetchColumn(); diff --git a/typo3/sysext/filelist/Classes/FileList.php b/typo3/sysext/filelist/Classes/FileList.php index 7c40ee37853b..311fd25ddc4b 100644 --- a/typo3/sysext/filelist/Classes/FileList.php +++ b/typo3/sysext/filelist/Classes/FileList.php @@ -19,7 +19,6 @@ use TYPO3\CMS\Backend\Configuration\TranslationConfigurationProvider; use TYPO3\CMS\Backend\RecordList\AbstractRecordList; use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Core\Database\ConnectionPool; -use TYPO3\CMS\Core\Database\Query\QueryContextType; use TYPO3\CMS\Core\Imaging\Icon; use TYPO3\CMS\Core\Imaging\IconFactory; use TYPO3\CMS\Core\Messaging\FlashMessage; @@ -785,11 +784,13 @@ class FileList extends AbstractRecordList protected function getTranslationsForMetaData($metaDataRecord) { $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_file_metadata'); - $queryBuilder->getQueryContext()->setContext(QueryContextType::UNRESTRICTED); + $queryBuilder->getRestrictions()->removeAll(); $translationRecords = $queryBuilder->select('*') ->from('sys_file_metadata') - ->where($queryBuilder->expr()->eq($GLOBALS['TCA']['sys_file_metadata']['ctrl']['transOrigPointerField'], (int)$metaDataRecord['uid'])) - ->andWhere($queryBuilder->expr()->gt($GLOBALS['TCA']['sys_file_metadata']['ctrl']['languageField'], 0)) + ->where( + $queryBuilder->expr()->eq($GLOBALS['TCA']['sys_file_metadata']['ctrl']['transOrigPointerField'], (int)$metaDataRecord['uid']), + $queryBuilder->expr()->gt($GLOBALS['TCA']['sys_file_metadata']['ctrl']['languageField'], 0) + ) ->execute() ->fetchAll(); @@ -1045,9 +1046,12 @@ class FileList extends AbstractRecordList $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_refindex'); $referenceCount = $queryBuilder->count('*') ->from('sys_refindex') - ->where($queryBuilder->expr()->eq('ref_table', $queryBuilder->quote('sys_file'))) - ->andWhere($queryBuilder->expr()->eq('ref_uid', (int)$fileOrFolderObject->getUid())) - ->andWhere($queryBuilder->expr()->neq('tablename', $queryBuilder->quote('sys_file_metadata'))) + ->where( + $queryBuilder->expr()->eq('deleted', 0), + $queryBuilder->expr()->eq('ref_table', $queryBuilder->quote('sys_file')), + $queryBuilder->expr()->eq('ref_uid', (int)$fileOrFolderObject->getUid()), + $queryBuilder->expr()->neq('tablename', $queryBuilder->quote('sys_file_metadata')) + ) ->execute() ->fetchColumn(); diff --git a/typo3/sysext/lowlevel/Classes/MissingRelationsCommand.php b/typo3/sysext/lowlevel/Classes/MissingRelationsCommand.php index 3bb4d46d4f1c..c59cf1d26faf 100644 --- a/typo3/sysext/lowlevel/Classes/MissingRelationsCommand.php +++ b/typo3/sysext/lowlevel/Classes/MissingRelationsCommand.php @@ -16,7 +16,6 @@ namespace TYPO3\CMS\Lowlevel; use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Core\Database\ConnectionPool; -use TYPO3\CMS\Core\Database\Query\QueryBuilder; use TYPO3\CMS\Core\Database\ReferenceIndex; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -92,15 +91,12 @@ Reports missing relations'; ); // Select DB relations from reference table - /** @var QueryBuilder $queryBuilder */ $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_refindex'); $rowIterator = $queryBuilder ->select('ref_uid', 'ref_table', 'softref_key', 'hash', 'tablename', 'recuid', 'field', 'flexpointer', 'deleted') ->from('sys_refindex') ->where( - $queryBuilder->expr()->neq('ref_table', $queryBuilder->quote('_FILE')) - ) - ->andWhere( + $queryBuilder->expr()->neq('ref_table', $queryBuilder->createNamedParameter('_FILE')), $queryBuilder->expr()->gt('ref_uid', 0) ) ->orderBy('sorting', 'DESC') diff --git a/typo3/sysext/recycler/Classes/Domain/Model/DeletedRecords.php b/typo3/sysext/recycler/Classes/Domain/Model/DeletedRecords.php index b699a8adb764..55379925e59f 100644 --- a/typo3/sysext/recycler/Classes/Domain/Model/DeletedRecords.php +++ b/typo3/sysext/recycler/Classes/Domain/Model/DeletedRecords.php @@ -16,7 +16,6 @@ namespace TYPO3\CMS\Recycler\Domain\Model; use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Core\Database\ConnectionPool; -use TYPO3\CMS\Core\Database\Query\QueryContextType; use TYPO3\CMS\Core\DataHandling\DataHandler; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\MathUtility; @@ -147,17 +146,17 @@ class DeletedRecords return; } $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table); - $queryBuilder->getQueryContext()->setContext(QueryContextType::UNRESTRICTED); + $queryBuilder->getRestrictions()->removeAll(); // find the 'deleted' field for this table $deletedField = RecyclerUtility::getDeletedField($table); // create the filter WHERE-clause $filterConstraint = null; - if (trim($filter) != '') { + if (trim($filter) !== '') { $labelConstraint = $queryBuilder->expr()->like( $tcaCtrl['label'], - $queryBuilder->quote('%' . addcslashes($filter, '_%') . '%') + $queryBuilder->quote('%' . $queryBuilder->escapeLikeWildcards($filter) . '%') ); if (MathUtility::canBeInterpretedAsInteger($filter)) { $filterConstraint = $queryBuilder->expr()->orX( @@ -265,7 +264,7 @@ class DeletedRecords if ($allowDepth && $depth >= 1) { // check recursively for elements beneath this page $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages'); - $queryBuilder->getQueryContext()->setContext(QueryContextType::UNRESTRICTED); + $queryBuilder->getRestrictions()->removeAll(); $resPages = $queryBuilder ->select('uid') ->from('pages') @@ -415,12 +414,14 @@ class DeletedRecords protected function getDeletedParentPages($uid, &$pages = array()) { $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages'); - $queryBuilder->getQueryContext()->setContext(QueryContextType::UNRESTRICTED); + $queryBuilder->getRestrictions()->removeAll(); $record = $queryBuilder ->select('uid', 'pid') ->from('pages') - ->where($queryBuilder->expr()->eq('uid', (int)$uid)) - ->andWhere($queryBuilder->expr()->eq($GLOBALS['TCA']['pages']['ctrl']['delete'], 1)) + ->where( + $queryBuilder->expr()->eq('uid', (int)$uid), + $queryBuilder->expr()->eq($GLOBALS['TCA']['pages']['ctrl']['delete'], 1) + ) ->execute() ->fetch(); if ($record) { diff --git a/typo3/sysext/recycler/Classes/Domain/Model/Tables.php b/typo3/sysext/recycler/Classes/Domain/Model/Tables.php index a17270ec37f6..1d2cd0be8ef9 100644 --- a/typo3/sysext/recycler/Classes/Domain/Model/Tables.php +++ b/typo3/sysext/recycler/Classes/Domain/Model/Tables.php @@ -15,7 +15,6 @@ namespace TYPO3\CMS\Recycler\Domain\Model; */ use TYPO3\CMS\Core\Database\ConnectionPool; -use TYPO3\CMS\Core\Database\Query\QueryContextType; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Recycler\Utility\RecyclerUtility; @@ -42,7 +41,7 @@ class Tables if ($deletedField) { // Determine whether the table has deleted records: $queryBuilder = $connection->getQueryBuilderForTable($tableName); - $queryBuilder->getQueryContext()->setContext(QueryContextType::UNRESTRICTED); + $queryBuilder->getRestrictions()->removeAll(); $deletedCount = $queryBuilder->count('uid') ->from($tableName) diff --git a/typo3/sysext/recycler/Classes/Task/CleanerTask.php b/typo3/sysext/recycler/Classes/Task/CleanerTask.php index a16d9ee8f0d0..a503850f2d96 100644 --- a/typo3/sysext/recycler/Classes/Task/CleanerTask.php +++ b/typo3/sysext/recycler/Classes/Task/CleanerTask.php @@ -62,6 +62,7 @@ class CleanerTask extends AbstractTask { if (isset($GLOBALS['TCA'][$tableName]['ctrl']['delete'])) { $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($tableName); + $queryBuilder->getRestrictions()->removeAll(); $constraints = [ $queryBuilder->expr()->eq($GLOBALS['TCA'][$tableName]['ctrl']['delete'], 1), @@ -69,25 +70,17 @@ class CleanerTask extends AbstractTask if ($GLOBALS['TCA'][$tableName]['ctrl']['tstamp']) { $dateBefore = $this->getPeriodAsTimestamp(); - $constraints[] = $queryBuilder->expr()->lt($GLOBALS['TCA'][$tableName]['ctrl']['tstamp'], $dateBefore); + $constraints[] = $queryBuilder->expr()->lt($GLOBALS['TCA'][$tableName]['ctrl']['tstamp'], (int)$dateBefore); } - $this->checkFileResourceFieldsBeforeDeletion($tableName, $constraints); - - $queryBuilder - ->getQueryContext() - ->setIgnoreEnableFields(true) - ->setIncludeDeleted(true); - try { $queryBuilder->delete($tableName) - ->where($queryBuilder->expr()->andX(...$constraints)) + ->where(...$constraints) ->execute(); } catch (\Doctrine\DBAL\DBALException $e) { return false; } } - return true; } @@ -189,10 +182,7 @@ class CleanerTask extends AbstractTask protected function deleteFilesForTable($table, array $constraints, array $fieldList) { $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table); - $queryBuilder - ->getQueryContext() - ->setIgnoreEnableFields(true) - ->setIncludeDeleted(true); + $queryBuilder->getRestrictions()->removeAll(); $result = $queryBuilder ->select(...$fieldList) diff --git a/typo3/sysext/recycler/Classes/Utility/RecyclerUtility.php b/typo3/sysext/recycler/Classes/Utility/RecyclerUtility.php index f605b9ae12b6..72d1e8ee9c32 100644 --- a/typo3/sysext/recycler/Classes/Utility/RecyclerUtility.php +++ b/typo3/sysext/recycler/Classes/Utility/RecyclerUtility.php @@ -16,7 +16,6 @@ namespace TYPO3\CMS\Recycler\Utility; use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Core\Database\ConnectionPool; -use TYPO3\CMS\Core\Database\Query\QueryContextType; use TYPO3\CMS\Core\Type\Bitmask\Permission; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -86,7 +85,7 @@ class RecyclerUtility return $output; } $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages'); - $queryBuilder->getQueryContext()->setContext(QueryContextType::UNRESTRICTED); + $queryBuilder->getRestrictions()->removeAll(); $clause = trim($clause); $loopCheck = 100; @@ -154,7 +153,7 @@ class RecyclerUtility return false; } $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages'); - $queryBuilder->getQueryContext()->setContext(QueryContextType::UNRESTRICTED); + $queryBuilder->getRestrictions()->removeAll(); $deleted = $queryBuilder ->select('deleted') @@ -176,7 +175,7 @@ class RecyclerUtility public static function getPidOfUid($uid, $table) { $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table); - $queryBuilder->getQueryContext()->setContext(QueryContextType::UNRESTRICTED); + $queryBuilder->getRestrictions()->removeAll(); $pid = $queryBuilder ->select('pid') diff --git a/typo3/sysext/recycler/Tests/Unit/Task/CleanerTaskTest.php b/typo3/sysext/recycler/Tests/Unit/Task/CleanerTaskTest.php index 4cb11dcf022b..3dfb434c5647 100644 --- a/typo3/sysext/recycler/Tests/Unit/Task/CleanerTaskTest.php +++ b/typo3/sysext/recycler/Tests/Unit/Task/CleanerTaskTest.php @@ -83,8 +83,9 @@ class CleanerTaskTest extends \TYPO3\CMS\Core\Tests\UnitTestCase $connection->getExpressionBuilder()->willReturn(new ExpressionBuilder($connection->reveal())); $connection->quoteIdentifier(Argument::cetera())->willReturnArgument(0); - $queryBuilder = GeneralUtility::makeInstance( - QueryBuilder::class, + // TODO: This should rather be a functional test if we need a query builder + // or we should clean up the code itself to not need to mock internal behavior here + $queryBuilder = new QueryBuilder( $connection->reveal(), null, new \Doctrine\DBAL\Query\QueryBuilder($connection->reveal()) @@ -116,8 +117,9 @@ class CleanerTaskTest extends \TYPO3\CMS\Core\Tests\UnitTestCase $connection->getExpressionBuilder()->willReturn(new ExpressionBuilder($connection->reveal())); $connection->quoteIdentifier(Argument::cetera())->willReturnArgument(0); - $queryBuilder = GeneralUtility::makeInstance( - QueryBuilder::class, + // TODO: This should rather be a functional test if we need a query builder + // or we should clean up the code itself to not need to mock internal behavior here + $queryBuilder = new QueryBuilder( $connection->reveal(), null, new \Doctrine\DBAL\Query\QueryBuilder($connection->reveal()) diff --git a/typo3/sysext/setup/Classes/Controller/SetupModuleController.php b/typo3/sysext/setup/Classes/Controller/SetupModuleController.php index 26b0efe66489..0e13d88bd390 100644 --- a/typo3/sysext/setup/Classes/Controller/SetupModuleController.php +++ b/typo3/sysext/setup/Classes/Controller/SetupModuleController.php @@ -22,7 +22,6 @@ use TYPO3\CMS\Backend\Module\ModuleLoader; use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; use TYPO3\CMS\Core\Database\ConnectionPool; -use TYPO3\CMS\Core\Database\Query\QueryBuilder; use TYPO3\CMS\Core\DataHandling\DataHandler; use TYPO3\CMS\Core\FormProtection\FormProtectionFactory; use TYPO3\CMS\Core\Imaging\Icon; @@ -747,13 +746,14 @@ class SetupModuleController extends AbstractModule unset($this->OLD_BE_USER); if ($this->getBackendUser()->isAdmin()) { $this->simUser = (int)GeneralUtility::_GP('simUser'); - /** @var QueryBuilder $queryBuilder */ $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('be_users'); $users = $queryBuilder ->select('*') ->from('be_users') - ->where($queryBuilder->expr()->neq('uid', (int)$this->getBackendUser()->user['uid'])) - ->andWhere($queryBuilder->expr()->notLike('username', $queryBuilder->createNamedParameter('_cli_%'))) + ->where( + $queryBuilder->expr()->neq('uid', (int)$this->getBackendUser()->user['uid']), + $queryBuilder->expr()->notLike('username', $queryBuilder->createNamedParameter($queryBuilder->escapeLikeWildcards('_cli_') . '%')) + ) ->orderBy('username') ->execute() ->fetchAll(); @@ -891,15 +891,16 @@ class SetupModuleController extends AbstractModule */ protected function getAvatarFileUid($beUserId) { - /** @var QueryBuilder $queryBuilder */ $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_file_reference'); $file = $queryBuilder ->select('uid_local') ->from('sys_file_reference') - ->where($queryBuilder->expr()->eq('tablenames', $queryBuilder->createNamedParameter('be_users'))) - ->andWhere($queryBuilder->expr()->eq('fieldname', $queryBuilder->createNamedParameter('avatar'))) - ->andWhere($queryBuilder->expr()->eq('table_local', $queryBuilder->createNamedParameter('sys_file'))) - ->andWhere($queryBuilder->expr()->eq('uid_foreign', (int)$beUserId)) + ->where( + $queryBuilder->expr()->eq('tablenames', $queryBuilder->createNamedParameter('be_users')), + $queryBuilder->expr()->eq('fieldname', $queryBuilder->createNamedParameter('avatar')), + $queryBuilder->expr()->eq('table_local', $queryBuilder->createNamedParameter('sys_file')), + $queryBuilder->expr()->eq('uid_foreign', (int)$beUserId) + ) ->execute() ->fetchColumn(); return (int)$file; @@ -920,14 +921,16 @@ class SetupModuleController extends AbstractModule return; } - /** @var QueryBuilder $queryBuilder */ $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_file_reference'); + $queryBuilder->getRestrictions()->removeAll(); $queryBuilder ->delete('sys_file_reference') - ->where($queryBuilder->expr()->eq('tablenames', $queryBuilder->createNamedParameter('be_users'))) - ->andWhere($queryBuilder->expr()->eq('fieldname', $queryBuilder->createNamedParameter('avatar'))) - ->andWhere($queryBuilder->expr()->eq('table_local', $queryBuilder->createNamedParameter('sys_file'))) - ->andWhere($queryBuilder->expr()->eq('uid_foreign', (int)$beUserId)) + ->where( + $queryBuilder->expr()->eq('tablenames', $queryBuilder->createNamedParameter('be_users')), + $queryBuilder->expr()->eq('fieldname', $queryBuilder->createNamedParameter('avatar')), + $queryBuilder->expr()->eq('table_local', $queryBuilder->createNamedParameter('sys_file')), + $queryBuilder->expr()->eq('uid_foreign', (int)$beUserId) + ) ->execute(); // Create new reference diff --git a/typo3/sysext/sys_note/Classes/Core/Bootstrap.php b/typo3/sysext/sys_note/Classes/Core/Bootstrap.php index cb76d08992c7..80384abde494 100644 --- a/typo3/sysext/sys_note/Classes/Core/Bootstrap.php +++ b/typo3/sysext/sys_note/Classes/Core/Bootstrap.php @@ -14,9 +14,7 @@ namespace TYPO3\CMS\SysNote\Core; * The TYPO3 project - inspiring people to share! */ -use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Core\Database\ConnectionPool; -use TYPO3\CMS\Core\Database\Query\QueryBuilder; use TYPO3\CMS\Core\Utility\GeneralUtility; /** @@ -76,16 +74,15 @@ class Bootstrap if (!isset($arguments['pids']) || empty($arguments['pids']) || empty($GLOBALS['BE_USER']->user['uid'])) { return false; } - $pidList = GeneralUtility::intExplode(',', $arguments['pids'], true); - if (empty($pidList)) { + $cleanedPageIds = GeneralUtility::intExplode(',', $arguments['pids'], true); + if (empty($cleanedPageIds)) { return false; } - /** @var QueryBuilder $queryBuilder */ $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_note'); $count = $queryBuilder ->count('uid') ->from('sys_note') - ->where($queryBuilder->expr()->in('pid', $pidList)) + ->where($queryBuilder->expr()->in('pid', $cleanedPageIds)) ->execute() ->fetchColumn(); return (bool)$count; -- GitLab