diff --git a/typo3/sysext/backend/Classes/Backend/Avatar/DefaultAvatarProvider.php b/typo3/sysext/backend/Classes/Backend/Avatar/DefaultAvatarProvider.php index a7551b7e6bb5397ce9f1d204e9ed32be731a824b..b5ff3d145519de8f40c54af9fe0a80f9b4d39083 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 30848c57597a5cdfa4197e39ac7607e17868952f..8a907c046e780d2436e53fa81b5bb5ef2d47a4dd 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 ece68f594cc9b37614080bf038302f95e3d75974..c621fe68ecdf5cac2e8e47ac68eba4747b7df2c5 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 f54c76386aa6a932cf122aa1bc702886207d3e97..28ced0ac67bf46d12dd372f8022529725730cf33 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 200e640d6f5f3eaba92c4326f6f928a64af28e7a..9aae1435410739ae59b32a55d5cc746edd5162de 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 1d2b28c0747b39303e8e81d7f9f525218375205a..758fded4616dbdd4973dddc71ebd58edc31bad48 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 c018a24254b6286832b36a067cfbfe036b4fb389..6c9b498796a47f93a59252ad5b41381e32350988 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 f8a694f89d81e59b5536d8dd6618efbc8ec504ec..f676c60111cbfeaa89c08b79898253f752610b61 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 dab73e90ecd53fed5ab0d79876aeba06896a47a4..a2fe2b12fae11c6ed44ca2b45550ed56deee2503 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 ca0ab847668af15d880d3938d2503a36e39f875b..909f293071c16778c91eb5649a9a0c5ab7c3e954 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 c23f8867bcebc9fd6b2955123b2ab13e998a4487..0f78d8073617c4558eae946d3f748dadf559fc27 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 14affb6d74f8ac16de7ceaf922104e41491ecec0..ea87c2f3ccfb65773841bf5e70516046e978f052 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 1530b212e6b3b35b252b4935e6571d71d32294b7..7cdf3ed280db45f1d674563a587dea1dbad03d9a 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 caf0e4d8ff062a8155704281ba0ec9c3a6edfcbe..0000000000000000000000000000000000000000 --- 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 8b20d795470f8152d90766480be46170d3d64c46..0000000000000000000000000000000000000000 --- 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 66ba323c69a1f4e3b95d58f5b4f02dd179e542de..0000000000000000000000000000000000000000 --- 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 0000000000000000000000000000000000000000..5da68dcf58a462286019510ba3d42614d3a577f3 --- /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 0000000000000000000000000000000000000000..0223e9a2291618fe102e71f8fd8f4a4d2ff41c60 --- /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 0000000000000000000000000000000000000000..387cdc14607846fa76fa9dfb53e65c5eb5fd5135 --- /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 0000000000000000000000000000000000000000..9e4a192e01267600498655c89ff54f9a7acf9320 --- /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 0000000000000000000000000000000000000000..014cb4587d2c08fe3def07e33cfca8bc15095059 --- /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 0000000000000000000000000000000000000000..efabc4e49b4b93d28c4d794fcbf440af3d53c4d1 --- /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 0000000000000000000000000000000000000000..5444fa0aa1495338a46c40fd21ca15910388bc6d --- /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 0000000000000000000000000000000000000000..744785dd90e84ad143cb4dcfbeeadb5aa40912a0 --- /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 0000000000000000000000000000000000000000..227262aaff6768434a0e20aea63c280a266fe02e --- /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 0000000000000000000000000000000000000000..6abc72d10c50cd815c394656978a8f454b6facfa --- /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 0000000000000000000000000000000000000000..914e8c0811225fd8a61205e1189c83a95e883c95 --- /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 0000000000000000000000000000000000000000..ab56b85219f39ea61f000d0d84b433e906ee7655 --- /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 0000000000000000000000000000000000000000..cbc960acbb55790949d100770a64057ae5192ca5 --- /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 d2eab26756829991343e895d0fe97e5c62664976..81831f33747dd4bc8cd86b79f9ded418d30c18a3 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 045a9064db900c857618522ddc1596d8cf38aa8e..b49bbc1865d822c0d26c296bbdcb4d66619f6a49 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 ec91ddb69c25f4cbce4e9889f4be1b103a7cdd6e..e94e0df2bd83072a416e83e4b4a827744926c86b 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 a3d28625020027f32bbd6cbe4d9f08e21120812a..0000000000000000000000000000000000000000 --- 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 bf0ce978859c8849da95ffe39d253b0d49f87e49..0000000000000000000000000000000000000000 --- 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 0000000000000000000000000000000000000000..6ee152c7088546210cb46e342b8a83ff523a5b34 --- /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 0000000000000000000000000000000000000000..d0863dfa372a7a241895097f40339173e9e9bf1b --- /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 0000000000000000000000000000000000000000..fa48e0468027ec250136f2f51c9ed9b421267222 --- /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 0000000000000000000000000000000000000000..686019050c6e40c3f1c4a7652dd57f6b75613ed3 --- /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 0000000000000000000000000000000000000000..2e9c74207c8d01e3e5471085e331c6c78bda3b75 --- /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 0000000000000000000000000000000000000000..38a4b8fdb3c5e819f849f61523a868d7a3e0eac5 --- /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 0000000000000000000000000000000000000000..0927168e5b39db1f68dc8c3a98b72bff36aeaf1b --- /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 0000000000000000000000000000000000000000..e7689f7e0c9a47ace9d6e766adb9b8913eb86ae7 --- /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 0000000000000000000000000000000000000000..41c4f2e8cf962458bcd377b520df4a121972e18d --- /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 0000000000000000000000000000000000000000..c345e2c0ea99f8c8843f27339d9b515b00e52100 --- /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 0000000000000000000000000000000000000000..1dcd324ad8786b2c0cbf6240211f6babc55b95fb --- /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 d4fe68f4adc3ac6e1908b56d3012e990b5a16d2e..5b95ca29b40e67b90bd280b06e020396e40e657e 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 0757f8bdf93ac3aa001e43a23b93ef6be3533ee6..3edb460dfb96ad5c3fbeae8cc6b8596cc407ed3b 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 131fb9c4f812096e3451a2acca609ea744c37880..413f156049b55a4b094a060608ebdfe55fab7fa6 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 7c40ee37853b4534d3b2354dd1801faee10a95ed..311fd25ddc4b92ac8bc7542cf7317e6ab28ea77c 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 3bb4d46d4f1cab7e0cd20a1910200847020b50a3..c59cf1d26fafd07d85e6d8baa0a74d8f693f2395 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 b699a8adb7644141730cdd36e9c08a05a1e76714..55379925e59f3c3447655547573a21cb3d5bb02f 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 a17270ec37f67ecfbe82ec58207a08fb53ccb773..1d2cd0be8ef927b67d2a484b010c8072de247f4a 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 a16d9ee8f0d0612ba941082aa75dcb0560dc0acc..a503850f2d962becfd18daeeb6999fd96cce9dde 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 f605b9ae12b62d0669c26ece3ef104272568efc8..72d1e8ee9c32f40d13ecbdccedf73f8b66e2c869 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 4cb11dcf022b2871ced8a9d9b63844bd99c7d6b8..3dfb434c56479aee81851f0d2f43e8699cc2d8f7 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 26b0efe664895f2cf6e91aeab3ddcacd0e94ccd2..0e13d88bd390a633237b05edf699321d71d1f74d 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 cb76d08992c7b3f59d9f416a2dd04631bbdf939a..80384abde49465b75a87f9cde1110e68d38927fe 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;