From 27738cdc41e79db5cdeea35df696e7ad88c11353 Mon Sep 17 00:00:00 2001
From: Morton Jonuschat <m.jonuschat@mojocode.de>
Date: Thu, 4 Aug 2016 09:57:36 +0200
Subject: [PATCH] [TASK] Doctrine: Migrate
 \TYPO3\CMS\Frontend\Page\PageRepository

Resolves: #76543
Releases: master
Change-Id: I7993a04b44838f7f425a09bac812b02e1fc19cbe
Reviewed-on: https://review.typo3.org/48555
Tested-by: Bamboo TYPO3com <info@typo3.com>
Reviewed-by: Morton Jonuschat <m.jonuschat@mojocode.de>
Tested-by: Morton Jonuschat <m.jonuschat@mojocode.de>
Reviewed-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
---
 .../Storage/Typo3DbQueryParserTest.php        |  10 +-
 .../TypoScriptFrontendController.php          |   9 +-
 .../frontend/Classes/Page/PageRepository.php  | 540 +++++++++++++-----
 .../ContentObjectRendererTest.php             | 519 +++++++++++++++++
 .../Tests/Functional/Fixtures/pages.xml       |  24 +
 .../Functional/Page/PageRepositoryTest.php    | 255 +++++++++
 .../ContentObjectRendererTest.php             | 416 --------------
 .../Tests/Unit/Page/PageRepositoryTest.php    | 273 ---------
 8 files changed, 1201 insertions(+), 845 deletions(-)
 create mode 100644 typo3/sysext/frontend/Tests/Functional/ContentObject/ContentObjectRendererTest.php

diff --git a/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Storage/Typo3DbQueryParserTest.php b/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Storage/Typo3DbQueryParserTest.php
index bd34d205d8a5..f27f730ffa49 100644
--- a/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Storage/Typo3DbQueryParserTest.php
+++ b/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Storage/Typo3DbQueryParserTest.php
@@ -289,8 +289,8 @@ class Typo3DbQueryParserTest extends \TYPO3\CMS\Core\Tests\UnitTestCase
             'in be: respect enable fields and do not include deleted' => array('BE', false, array(), false, 'tx_foo_table.disabled_column=0 AND (tx_foo_table.starttime_column<=123456789) AND tx_foo_table.deleted_column = 0'),
             'in fe: include all' => array('FE', true, array(), true, ''),
             'in fe: ignore enable fields but do not include deleted' => array('FE', true, array(), false, 'tx_foo_table.deleted_column=0'),
-            'in fe: ignore only starttime and do not include deleted' => array('FE', true, array('starttime'), false, 'tx_foo_table.deleted_column=0 AND tx_foo_table.disabled_column=0'),
-            'in fe: respect enable fields and do not include deleted' => array('FE', false, array(), false, 'tx_foo_table.deleted_column=0 AND tx_foo_table.disabled_column=0 AND tx_foo_table.starttime_column<=123456789')
+            'in fe: ignore only starttime and do not include deleted' => array('FE', true, array('starttime'), false, '(tx_foo_table.deleted_column = 0) AND (tx_foo_table.disabled_column = 0)'),
+            'in fe: respect enable fields and do not include deleted' => array('FE', false, array(), false, '(tx_foo_table.deleted_column = 0) AND (tx_foo_table.disabled_column = 0) AND (tx_foo_table.starttime_column <= 123456789)')
         );
     }
 
@@ -321,7 +321,7 @@ class Typo3DbQueryParserTest extends \TYPO3\CMS\Core\Tests\UnitTestCase
         );
 
         $connectionPoolProphet = $this->prophesize(ConnectionPool::class);
-        $connectionPoolProphet->getQueryBuilderForTable($tableName)->willReturn($queryBuilderProphet->reveal());
+        $connectionPoolProphet->getQueryBuilderForTable(Argument::any($tableName, 'pages'))->willReturn($queryBuilderProphet->reveal());
         GeneralUtility::addInstance(ConnectionPool::class, $connectionPoolProphet->reveal());
 
         $mockQuerySettings = $this->getMockBuilder(\TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings::class)
@@ -351,7 +351,7 @@ class Typo3DbQueryParserTest extends \TYPO3\CMS\Core\Tests\UnitTestCase
             'in be: respectEnableFields=false' => array('BE', false, ''),
             'in be: respectEnableFields=true' => array('BE', true, 'tx_foo_table.disabled_column=0 AND (tx_foo_table.starttime_column<=123456789) AND tx_foo_table.deleted_column = 0'),
             'in FE: respectEnableFields=false' => array('FE', false, ''),
-            'in FE: respectEnableFields=true' => array('FE', true, 'tx_foo_table.deleted_column=0 AND tx_foo_table.disabled_column=0 AND tx_foo_table.starttime_column<=123456789')
+            'in FE: respectEnableFields=true' => array('FE', true, '(tx_foo_table.deleted_column = 0) AND (tx_foo_table.disabled_column = 0) AND (tx_foo_table.starttime_column <= 123456789)')
         );
     }
 
@@ -382,7 +382,7 @@ class Typo3DbQueryParserTest extends \TYPO3\CMS\Core\Tests\UnitTestCase
         );
 
         $connectionPoolProphet = $this->prophesize(ConnectionPool::class);
-        $connectionPoolProphet->getQueryBuilderForTable($tableName)->willReturn($queryBuilderProphet->reveal());
+        $connectionPoolProphet->getQueryBuilderForTable(Argument::any($tableName, 'pages'))->willReturn($queryBuilderProphet->reveal());
         GeneralUtility::addInstance(ConnectionPool::class, $connectionPoolProphet->reveal());
 
         /** @var \TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings $mockQuerySettings */
diff --git a/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php b/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php
index 21b400b2aace..307de2806612 100644
--- a/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php
+++ b/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php
@@ -21,6 +21,7 @@ use TYPO3\CMS\Core\Cache\CacheManager;
 use TYPO3\CMS\Core\Charset\CharsetConverter;
 use TYPO3\CMS\Core\Controller\ErrorPageController;
 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\FrontendRestrictionContainer;
 use TYPO3\CMS\Core\Error\Http\PageNotFoundException;
@@ -1906,7 +1907,13 @@ class TypoScriptFrontendController
      */
     public function setSysPageWhereClause()
     {
-        $this->sys_page->where_hid_del .= ' AND pages.doktype<200';
+        $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+            ->getConnectionForTable('pages')
+            ->getExpressionBuilder();
+        $this->sys_page->where_hid_del = ' AND ' . (string)$expressionBuilder->andX(
+            QueryHelper::stripLogicalOperatorPrefix($this->sys_page->where_hid_del),
+            $expressionBuilder->lt('pages.doktype', 200)
+        );
         $this->sys_page->where_groupAccess = $this->sys_page->getMultipleGroupsWhereClause('pages.fe_group', 'pages');
     }
 
diff --git a/typo3/sysext/frontend/Classes/Page/PageRepository.php b/typo3/sysext/frontend/Classes/Page/PageRepository.php
index ec02d41fbf42..aa9963f8f373 100644
--- a/typo3/sysext/frontend/Classes/Page/PageRepository.php
+++ b/typo3/sysext/frontend/Classes/Page/PageRepository.php
@@ -15,6 +15,11 @@ namespace TYPO3\CMS\Frontend\Page;
  */
 
 use TYPO3\CMS\Core\Cache\CacheManager;
+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\FrontendRestrictionContainer;
+use TYPO3\CMS\Core\Database\Query\Restriction\FrontendWorkspaceRestriction;
 use TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException;
 use TYPO3\CMS\Core\Resource\FileRepository;
 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
@@ -186,7 +191,16 @@ class PageRepository
             // de-selecting hidden pages - we need versionOL() to unset them only
             // if the overlay record instructs us to.
             // Clear where_hid_del and restrict to live and current workspaces
-            $this->where_hid_del = ' AND pages.deleted=0 AND (pages.t3ver_wsid=0 OR pages.t3ver_wsid=' . (int)$this->versioningWorkspaceId . ')';
+            $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+                ->getQueryBuilderForTable('pages')
+                ->expr();
+            $this->where_hid_del = ' AND ' . $expressionBuilder->andX(
+                $expressionBuilder->eq('pages.deleted', 0),
+                $expressionBuilder->orX(
+                    $expressionBuilder->eq('pages.t3ver_wsid', 0),
+                    $expressionBuilder->eq('pages.t3ver_wsid', (int)$this->versioningWorkspaceId)
+                )
+            );
         } else {
             // add starttime / endtime, and check for hidden/deleted
             // Filter out new/deleted place-holder pages in case we are NOT in a
@@ -233,13 +247,34 @@ class PageRepository
                 $hookObject->getPage_preProcess($uid, $disableGroupAccessCheck, $this);
             }
         }
-        $accessCheck = $disableGroupAccessCheck ? '' : $this->where_groupAccess;
-        $cacheKey = md5($accessCheck . '-' . $this->where_hid_del . '-' . $this->sys_language_uid);
+        $cacheKey = md5(
+            implode(
+                '-',
+                [
+                    ($disableGroupAccessCheck ? '' : $this->where_groupAccess),
+                    $this->where_hid_del,
+                    $this->sys_language_uid
+                ]
+            )
+        );
         if (is_array($this->cache_getPage[$uid][$cacheKey])) {
             return $this->cache_getPage[$uid][$cacheKey];
         }
         $result = array();
-        $row = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('*', 'pages', 'uid=' . (int)$uid . $this->where_hid_del . $accessCheck);
+        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
+        $queryBuilder->getRestrictions()->removeAll();
+        $queryBuilder->select('*')
+            ->from('pages')
+            ->where(
+                $queryBuilder->expr()->eq('uid', (int)$uid),
+                QueryHelper::stripLogicalOperatorPrefix($this->where_hid_del)
+            );
+
+        if (!$disableGroupAccessCheck) {
+            $queryBuilder->andWhere(QueryHelper::stripLogicalOperatorPrefix($this->where_groupAccess));
+        }
+
+        $row = $queryBuilder->execute()->fetch();
         if ($row) {
             $this->versionOL('pages', $row);
             if (is_array($row)) {
@@ -263,9 +298,17 @@ class PageRepository
         if ($this->cache_getPage_noCheck[$uid]) {
             return $this->cache_getPage_noCheck[$uid];
         }
-        $res = $this->getDatabaseConnection()->exec_SELECTquery('*', 'pages', 'uid=' . (int)$uid . $this->deleteClause('pages'));
-        $row = $this->getDatabaseConnection()->sql_fetch_assoc($res);
-        $this->getDatabaseConnection()->sql_free_result($res);
+
+        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
+        $queryBuilder->getRestrictions()
+            ->removeAll()
+            ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
+        $row = $queryBuilder->select('*')
+            ->from('pages')
+            ->where($queryBuilder->expr()->eq('uid', (int)$uid))
+            ->execute()
+            ->fetch();
+
         $result = array();
         if ($row) {
             $this->versionOL('pages', $row);
@@ -287,9 +330,20 @@ class PageRepository
     public function getFirstWebPage($uid)
     {
         $output = '';
-        $res = $this->getDatabaseConnection()->exec_SELECTquery('*', 'pages', 'pid=' . (int)$uid . $this->where_hid_del . $this->where_groupAccess, '', 'sorting', '1');
-        $row = $this->getDatabaseConnection()->sql_fetch_assoc($res);
-        $this->getDatabaseConnection()->sql_free_result($res);
+        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
+        $queryBuilder->getRestrictions()->removeAll();
+        $row = $queryBuilder->select('*')
+            ->from('pages')
+            ->where(
+                $queryBuilder->expr()->eq('pid', (int)$uid),
+                QueryHelper::stripLogicalOperatorPrefix($this->where_hid_del),
+                QueryHelper::stripLogicalOperatorPrefix($this->where_groupAccess)
+            )
+            ->orderBy('sorting')
+            ->setMaxResults(1)
+            ->execute()
+            ->fetch();
+
         if ($row) {
             $this->versionOL('pages', $row);
             if (is_array($row)) {
@@ -312,9 +366,22 @@ class PageRepository
         if ($this->cache_getPageIdFromAlias[$alias]) {
             return $this->cache_getPageIdFromAlias[$alias];
         }
-        $db = $this->getDatabaseConnection();
-        $row = $db->exec_SELECTgetSingleRow('uid', 'pages', 'alias=' . $db->fullQuoteStr($alias, 'pages') . ' AND pid>=0 AND pages.deleted=0');
-        // "AND pid>=0" because of versioning (means that aliases sent MUST be online!)
+        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
+        $queryBuilder->getRestrictions()
+            ->removeAll()
+            ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
+
+        $row = $queryBuilder->select('uid')
+            ->from('pages')
+            ->where(
+                $queryBuilder->expr()->eq('alias', $queryBuilder->createNamedParameter($alias)),
+                // "AND pid>=0" because of versioning (means that aliases sent MUST be online!)
+                $queryBuilder->expr()->gte('pid', 0)
+            )
+            ->setMaxResults(1)
+            ->execute()
+            ->fetch();
+
         if ($row) {
             $this->cache_getPageIdFromAlias[$alias] = $row['uid'];
             return $row['uid'];
@@ -394,22 +461,26 @@ class PageRepository
                 if (!in_array('pid', $fieldArr, true)) {
                     $fieldArr[] = 'pid';
                 }
-                // NOTE to enabledFields('pages_language_overlay'):
+                // NOTE regarding the query restrictions
                 // Currently the showHiddenRecords of TSFE set will allow
                 // pages_language_overlay records to be selected as they are
                 // child-records of a page.
                 // However you may argue that the showHiddenField flag should
                 // determine this. But that's not how it's done right now.
                 // Selecting overlay record:
-                $db = $this->getDatabaseConnection();
-                $res = $db->exec_SELECTquery(
-                    implode(',', $fieldArr),
-                    'pages_language_overlay',
-                    'pid IN(' . implode(',', array_map('intval', $page_ids)) . ')'
-                    . ' AND sys_language_uid=' . (int)$lUid . $this->enableFields('pages_language_overlay')
-                );
+                $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+                    ->getQueryBuilderForTable('pages_language_overlay');
+                $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
+                $result = $queryBuilder->select(...$fieldArr)
+                    ->from('pages_language_overlay')
+                    ->where(
+                        $queryBuilder->expr()->in('pid', array_map('intval', $page_ids)),
+                        $queryBuilder->expr()->eq('sys_language_uid', (int)$lUid)
+                    )
+                    ->execute();
+
                 $overlays = array();
-                while ($row = $db->sql_fetch_assoc($res)) {
+                while ($row = $result->fetch()) {
                     $this->versionOL('pages_language_overlay', $row);
                     if (is_array($row)) {
                         $row['_PAGES_OVERLAY'] = true;
@@ -422,7 +493,6 @@ class PageRepository
                         $overlays[$origUid] = $row;
                     }
                 }
-                $db->sql_free_result($res);
             }
         }
         // Create output:
@@ -486,9 +556,28 @@ class PageRepository
                         // Must be default language, otherwise no overlaying
                         if ((int)$row[$GLOBALS['TCA'][$table]['ctrl']['languageField']] === 0) {
                             // Select overlay record:
-                            $res = $this->getDatabaseConnection()->exec_SELECTquery('*', $table, 'pid=' . (int)$row['pid'] . ' AND ' . $GLOBALS['TCA'][$table]['ctrl']['languageField'] . '=' . (int)$sys_language_content . ' AND ' . $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'] . '=' . (int)$row['uid'] . $this->enableFields($table), '', '', '1');
-                            $olrow = $this->getDatabaseConnection()->sql_fetch_assoc($res);
-                            $this->getDatabaseConnection()->sql_free_result($res);
+                            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+                                ->getQueryBuilderForTable($table);
+                            $queryBuilder->setRestrictions(
+                                GeneralUtility::makeInstance(FrontendRestrictionContainer::class)
+                            );
+                            $olrow = $queryBuilder->select('*')
+                                ->from($table)
+                                ->where(
+                                    $queryBuilder->expr()->eq('pid', (int)$row['pid']),
+                                    $queryBuilder->expr()->eq(
+                                        $GLOBALS['TCA'][$table]['ctrl']['languageField'],
+                                        (int)$sys_language_content
+                                    ),
+                                    $queryBuilder->expr()->eq(
+                                        $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'],
+                                        (int)$row['uid']
+                                    )
+                                )
+                                ->setMaxResults(1)
+                                ->execute()
+                                ->fetch();
+
                             $this->versionOL($table, $olrow);
                             // Merge record content by traversing all fields:
                             if (is_array($olrow)) {
@@ -605,24 +694,21 @@ class PageRepository
     {
         $pages = [];
         $relationField = $parentPages ? 'pid' : 'uid';
-        $db = $this->getDatabaseConnection();
-
-        $whereStatement = $relationField . ' IN ('
-            . implode(',', array_map('intval', $pageIds)) . ')'
-            . $this->where_hid_del
-            . $this->where_groupAccess
-            . ' '
-            . $additionalWhereClause;
-
-        $databaseResource = $db->exec_SELECTquery(
-            $fields,
-            'pages',
-            $whereStatement,
-            '',
-            $sortField
-        );
-
-        while (($page = $db->sql_fetch_assoc($databaseResource))) {
+        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
+        $queryBuilder->getRestrictions()->removeAll();
+
+        $result = $queryBuilder->select(...GeneralUtility::trimExplode(',', $fields, true))
+            ->from('pages')
+            ->where(
+                $queryBuilder->expr()->in($relationField, array_map('intval', $pageIds)),
+                QueryHelper::stripLogicalOperatorPrefix($this->where_hid_del),
+                QueryHelper::stripLogicalOperatorPrefix($this->where_groupAccess),
+                QueryHelper::stripLogicalOperatorPrefix($additionalWhereClause)
+            )
+            ->orderBy($sortField)
+            ->execute();
+
+        while ($page = $result->fetch()) {
             $originalUid = $page['uid'];
 
             // Versioning Preview Overlay
@@ -647,8 +733,6 @@ class PageRepository
             }
         }
 
-        $db->sql_free_result($databaseResource);
-
         // Finally load language overlays
         return $this->getPagesOverlay($pages);
     }
@@ -721,16 +805,18 @@ class PageRepository
                 $searchUid = 0;
             }
 
-            $whereStatement = $searchField . '=' . $searchUid
-                . $this->where_hid_del
-                . $this->where_groupAccess
-                . ' ' . $additionalWhereClause;
-
-            $count = $this->getDatabaseConnection()->exec_SELECTcountRows(
-                'uid',
-                'pages',
-                $whereStatement
-            );
+            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
+            $queryBuilder->getRestrictions()->removeAll();
+            $count = $queryBuilder->count('uid')
+                ->from('pages')
+                ->where(
+                    $queryBuilder->expr()->eq($searchField, (int)$searchUid),
+                    QueryHelper::stripLogicalOperatorPrefix($this->where_hid_del),
+                    QueryHelper::stripLogicalOperatorPrefix($this->where_groupAccess),
+                    QueryHelper::stripLogicalOperatorPrefix($additionalWhereClause)
+                )
+                ->execute()
+                ->fetchColumn();
 
             if (!$count) {
                 $page = [];
@@ -761,31 +847,58 @@ class PageRepository
         // Appending to domain string
         $domain .= $path;
         $domain = preg_replace('/\\/*$/', '', $domain);
-        $res = $this->getDatabaseConnection()->exec_SELECTquery('pages.uid,sys_domain.redirectTo,sys_domain.redirectHttpStatusCode,sys_domain.prepend_params', 'pages,sys_domain', 'pages.uid=sys_domain.pid
-						AND sys_domain.hidden=0
-						AND (sys_domain.domainName=' . $this->getDatabaseConnection()->fullQuoteStr($domain, 'sys_domain') . ' OR sys_domain.domainName=' . $this->getDatabaseConnection()->fullQuoteStr(($domain . '/'), 'sys_domain') . ') ' . $this->where_hid_del . $this->where_groupAccess, '', '', 1);
-        $row = $this->getDatabaseConnection()->sql_fetch_assoc($res);
-        $this->getDatabaseConnection()->sql_free_result($res);
-        if ($row) {
-            if ($row['redirectTo']) {
-                $redirectUrl = $row['redirectTo'];
-                if ($row['prepend_params']) {
-                    $redirectUrl = rtrim($redirectUrl, '/');
-                    $prependStr = ltrim(substr($request_uri, strlen($path)), '/');
-                    $redirectUrl .= '/' . $prependStr;
-                }
-                $statusCode = (int)$row['redirectHttpStatusCode'];
-                if ($statusCode && defined(HttpUtility::class . '::HTTP_STATUS_' . $statusCode)) {
-                    HttpUtility::redirect($redirectUrl, constant(HttpUtility::class . '::HTTP_STATUS_' . $statusCode));
-                } else {
-                    HttpUtility::redirect($redirectUrl, HttpUtility::HTTP_STATUS_301);
-                }
-                die;
+        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
+        $queryBuilder->getRestrictions()->removeAll();
+        $row = $queryBuilder
+            ->select(
+                'pages.uid',
+                'sys_domain.redirectTo',
+                'sys_domain.redirectHttpStatusCode',
+                'sys_domain.prepend_params'
+            )
+            ->from('pages')
+            ->from('sys_domain')
+            ->where(
+                $queryBuilder->expr()->eq('pages.uid', $queryBuilder->quoteIdentifier('sys_domain.pid')),
+                $queryBuilder->expr()->eq('sys_domain.hidden', 0),
+                $queryBuilder->expr()->orX(
+                    $queryBuilder->expr()->eq(
+                        'sys_domain.domainName',
+                        $queryBuilder->createNamedParameter($domain)
+                    ),
+                    $queryBuilder->expr()->eq(
+                        'sys_domain.domainName',
+                        $queryBuilder->createNamedParameter($domain . '/')
+                    )
+                ),
+                QueryHelper::stripLogicalOperatorPrefix($this->where_hid_del),
+                QueryHelper::stripLogicalOperatorPrefix($this->where_groupAccess)
+            )
+            ->setMaxResults(1)
+            ->execute()
+            ->fetch();
+
+        if (!$row) {
+            return '';
+        }
+
+        if ($row['redirectTo']) {
+            $redirectUrl = $row['redirectTo'];
+            if ($row['prepend_params']) {
+                $redirectUrl = rtrim($redirectUrl, '/');
+                $prependStr = ltrim(substr($request_uri, strlen($path)), '/');
+                $redirectUrl .= '/' . $prependStr;
+            }
+            $statusCode = (int)$row['redirectHttpStatusCode'];
+            if ($statusCode && defined(HttpUtility::class . '::HTTP_STATUS_' . $statusCode)) {
+                HttpUtility::redirect($redirectUrl, constant(HttpUtility::class . '::HTTP_STATUS_' . $statusCode));
             } else {
-                return $row['uid'];
+                HttpUtility::redirect($redirectUrl, HttpUtility::HTTP_STATUS_301);
             }
+            die;
+        } else {
+            return $row['uid'];
         }
-        return '';
     }
 
     /**
@@ -897,9 +1010,20 @@ class PageRepository
             }
             // Get pageRec if not supplied:
             if (!is_array($pageRec)) {
-                $res = $this->getDatabaseConnection()->exec_SELECTquery('uid,pid,doktype,mount_pid,mount_pid_ol,t3ver_state', 'pages', 'uid=' . (int)$pageId . ' AND pages.deleted=0 AND pages.doktype<>255');
-                $pageRec = $this->getDatabaseConnection()->sql_fetch_assoc($res);
-                $this->getDatabaseConnection()->sql_free_result($res);
+                $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
+                $queryBuilder->getRestrictions()
+                    ->removeAll()
+                    ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
+
+                $pageRec = $queryBuilder->select('uid', 'pid', 'doktype', 'mount_pid', 'mount_pid_ol', 't3ver_state')
+                    ->from('pages')
+                    ->where(
+                        $queryBuilder->expr()->eq('uid', (int)$pageId),
+                        $queryBuilder->expr()->neq('doktype', 255)
+                    )
+                    ->execute()
+                    ->fetch();
+
                 // Only look for version overlay if page record is not supplied; This assumes
                 // that the input record is overlaid with preview version, if any!
                 $this->versionOL('pages', $pageRec);
@@ -912,9 +1036,20 @@ class PageRepository
             $mount_pid = (int)$pageRec['mount_pid'];
             if (is_array($pageRec) && (int)$pageRec['doktype'] === self::DOKTYPE_MOUNTPOINT && $mount_pid > 0 && !in_array($mount_pid, $prevMountPids, true)) {
                 // Get the mount point record (to verify its general existence):
-                $res = $this->getDatabaseConnection()->exec_SELECTquery('uid,pid,doktype,mount_pid,mount_pid_ol,t3ver_state', 'pages', 'uid=' . $mount_pid . ' AND pages.deleted=0 AND pages.doktype<>255');
-                $mountRec = $this->getDatabaseConnection()->sql_fetch_assoc($res);
-                $this->getDatabaseConnection()->sql_free_result($res);
+                $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
+                $queryBuilder->getRestrictions()
+                    ->removeAll()
+                    ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
+
+                $mountRec = $queryBuilder->select('uid', 'pid', 'doktype', 'mount_pid', 'mount_pid_ol', 't3ver_state')
+                    ->from('pages')
+                    ->where(
+                        $queryBuilder->expr()->eq('uid', $mount_pid),
+                        $queryBuilder->expr()->neq('doktype', 255)
+                    )
+                    ->execute()
+                    ->fetch();
+
                 $this->versionOL('pages', $mountRec);
                 if (is_array($mountRec)) {
                     // Look for recursive mount point:
@@ -957,16 +1092,26 @@ class PageRepository
     {
         $uid = (int)$uid;
         if (is_array($GLOBALS['TCA'][$table]) && $uid > 0) {
-            $res = $this->getDatabaseConnection()->exec_SELECTquery('*', $table, 'uid = ' . $uid . $this->enableFields($table));
-            $row = $this->getDatabaseConnection()->sql_fetch_assoc($res);
-            $this->getDatabaseConnection()->sql_free_result($res);
+            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
+            $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
+            $row = $queryBuilder->select('*')
+                ->from($table)
+                ->where($queryBuilder->expr()->eq('uid', $uid))
+                ->execute()
+                ->fetch();
+
             if ($row) {
                 $this->versionOL($table, $row);
                 if (is_array($row)) {
                     if ($checkPage) {
-                        $res = $this->getDatabaseConnection()->exec_SELECTquery('uid', 'pages', 'uid=' . (int)$row['pid'] . $this->enableFields('pages'));
-                        $numRows = $this->getDatabaseConnection()->sql_num_rows($res);
-                        $this->getDatabaseConnection()->sql_free_result($res);
+                        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+                            ->getQueryBuilderForTable('pages');
+                        $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
+                        $numRows = (int)$queryBuilder->count('*')
+                            ->from('pages')
+                            ->where($queryBuilder->expr()->eq('uid', (int)$row['pid']))
+                            ->execute()
+                            ->fetchColumn();
                         if ($numRows > 0) {
                             return $row;
                         } else {
@@ -995,9 +1140,16 @@ class PageRepository
     {
         $uid = (int)$uid;
         if (isset($GLOBALS['TCA'][$table]) && is_array($GLOBALS['TCA'][$table]) && $uid > 0) {
-            $res = $this->getDatabaseConnection()->exec_SELECTquery($fields, $table, 'uid = ' . $uid . $this->deleteClause($table));
-            $row = $this->getDatabaseConnection()->sql_fetch_assoc($res);
-            $this->getDatabaseConnection()->sql_free_result($res);
+            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
+            $queryBuilder->getRestrictions()
+                ->removeAll()
+                ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
+            $row = $queryBuilder->select(...GeneralUtility::trimExplode(',', $fields, true))
+                ->from($table)
+                ->where($queryBuilder->expr()->eq('uid', $uid))
+                ->execute()
+                ->fetch();
+
             if ($row) {
                 if (!$noWSOL) {
                     $this->versionOL($table, $row);
@@ -1025,14 +1177,42 @@ class PageRepository
     public function getRecordsByField($theTable, $theField, $theValue, $whereClause = '', $groupBy = '', $orderBy = '', $limit = '')
     {
         if (is_array($GLOBALS['TCA'][$theTable])) {
-            $res = $this->getDatabaseConnection()->exec_SELECTquery('*', $theTable, $theField . '=' . $this->getDatabaseConnection()->fullQuoteStr($theValue, $theTable) . $this->deleteClause($theTable) . ' ' . $whereClause, $groupBy, $orderBy, $limit);
-            $rows = array();
-            while ($row = $this->getDatabaseConnection()->sql_fetch_assoc($res)) {
-                if (is_array($row)) {
-                    $rows[] = $row;
+            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($theTable);
+            $queryBuilder->getRestrictions()
+                ->removeAll()
+                ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
+
+            $queryBuilder->select('*')
+                ->from($theTable)
+                ->where($queryBuilder->expr()->eq($theField, $queryBuilder->createNamedParameter($theValue)));
+
+            if ($whereClause !== '') {
+                $queryBuilder->andWhere(QueryHelper::stripLogicalOperatorPrefix($whereClause));
+            }
+
+            if ($groupBy !== '') {
+                $queryBuilder->groupBy(QueryHelper::parseGroupBy($groupBy));
+            }
+
+            if ($orderBy !== '') {
+                foreach (QueryHelper::parseOrderBy($orderBy) as $orderPair) {
+                    list($fieldName, $order) = $orderPair;
+                    $queryBuilder->addOrderBy($fieldName, $order);
+                }
+            }
+
+            if ($limit !== '') {
+                if (strpos($limit, ',')) {
+                    $limitOffsetAndMax = GeneralUtility::intExplode(',', $limit);
+                    $queryBuilder->setFirstResult((int)$limitOffsetAndMax[0]);
+                    $queryBuilder->setMaxResults((int)$limitOffsetAndMax[1]);
+                } else {
+                    $queryBuilder->setMaxResults((int)$limit);
                 }
             }
-            $this->getDatabaseConnection()->sql_free_result($res);
+
+            $rows = $queryBuilder->execute()->fetchAll();
+
             if (!empty($rows)) {
                 return $rows;
             }
@@ -1118,7 +1298,7 @@ class PageRepository
      * @return string The clause starting like " AND ...=... AND ...=...
      * @see \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::enableFields(), deleteClause()
      */
-    public function enableFields($table, $show_hidden = -1, $ignore_array = array(), $noVersionPreview = false)
+    public function enableFields($table, $show_hidden = -1, $ignore_array = [], $noVersionPreview = false)
     {
         if ($show_hidden === -1 && is_object($this->getTypoScriptFrontendController())) {
             // If show_hidden was not set from outside and if TSFE is an object, set it
@@ -1132,29 +1312,35 @@ class PageRepository
         }
         // If show_hidden was not changed during the previous evaluation, do it here.
         $ctrl = $GLOBALS['TCA'][$table]['ctrl'];
-        $query = '';
+        $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+            ->getQueryBuilderForTable('pages')
+            ->expr();
+        $constraints = [];
         if (is_array($ctrl)) {
             // Delete field check:
             if ($ctrl['delete']) {
-                $query .= ' AND ' . $table . '.' . $ctrl['delete'] . '=0';
+                $constraints[] = $expressionBuilder->eq($table . '.' . $ctrl['delete'], 0);
             }
             if ($ctrl['versioningWS']) {
                 if (!$this->versioningPreview) {
                     // Filter out placeholder records (new/moved/deleted items)
                     // in case we are NOT in a versioning preview (that means we are online!)
-                    $query .= ' AND ' . $table . '.t3ver_state<=' . new VersionState(VersionState::DEFAULT_STATE);
+                    $constraints[] = $expressionBuilder->lte(
+                        $table . '.t3ver_state',
+                        new VersionState(VersionState::DEFAULT_STATE)
+                    );
                 } elseif ($table !== 'pages') {
                     // show only records of live and of the current workspace
                     // in case we are in a versioning preview
-                    $query .= ' AND (' .
-                                $table . '.t3ver_wsid=0 OR ' .
-                                $table . '.t3ver_wsid=' . (int)$this->versioningWorkspaceId .
-                                ')';
+                    $constraints[] = $expressionBuilder->orX(
+                        $expressionBuilder->eq($table . '.t3ver_wsid', 0),
+                        $expressionBuilder->eq($table . '.t3ver_wsid', (int)$this->versioningWorkspaceId)
+                    );
                 }
 
                 // Filter out versioned records
                 if (!$noVersionPreview && empty($ignore_array['pid'])) {
-                    $query .= ' AND ' . $table . '.pid<>-1';
+                    $constraints[] = $expressionBuilder->neq($table . '.pid', -1);
                 }
             }
 
@@ -1165,32 +1351,39 @@ class PageRepository
                 if (!$this->versioningPreview || !$ctrl['versioningWS'] || $noVersionPreview) {
                     if ($ctrl['enablecolumns']['disabled'] && !$show_hidden && !$ignore_array['disabled']) {
                         $field = $table . '.' . $ctrl['enablecolumns']['disabled'];
-                        $query .= ' AND ' . $field . '=0';
+                        $constraints[] = $expressionBuilder->eq($field, 0);
                     }
                     if ($ctrl['enablecolumns']['starttime'] && !$ignore_array['starttime']) {
                         $field = $table . '.' . $ctrl['enablecolumns']['starttime'];
-                        $query .= ' AND ' . $field . '<=' . $GLOBALS['SIM_ACCESS_TIME'];
+                        $constraints[] = $expressionBuilder->lte($field, (int)$GLOBALS['SIM_ACCESS_TIME']);
                     }
                     if ($ctrl['enablecolumns']['endtime'] && !$ignore_array['endtime']) {
                         $field = $table . '.' . $ctrl['enablecolumns']['endtime'];
-                        $query .= ' AND (' . $field . '=0 OR ' . $field . '>' . $GLOBALS['SIM_ACCESS_TIME'] . ')';
+                        $constraints[] = $expressionBuilder->orX(
+                            $expressionBuilder->eq($field, 0),
+                            $expressionBuilder->gt($field, (int)$GLOBALS['SIM_ACCESS_TIME'])
+                        );
                     }
                     if ($ctrl['enablecolumns']['fe_group'] && !$ignore_array['fe_group']) {
                         $field = $table . '.' . $ctrl['enablecolumns']['fe_group'];
-                        $query .= $this->getMultipleGroupsWhereClause($field, $table);
+                        $constraints[] = QueryHelper::stripLogicalOperatorPrefix(
+                            $this->getMultipleGroupsWhereClause($field, $table)
+                        );
                     }
                     // Call hook functions for additional enableColumns
                     // It is used by the extension ingmar_accessctrl which enables assigning more
                     // than one usergroup to content and page records
                     if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['addEnableColumns'])) {
-                        $_params = array(
+                        $_params = [
                             'table' => $table,
                             'show_hidden' => $show_hidden,
                             'ignore_array' => $ignore_array,
                             'ctrl' => $ctrl
-                        );
+                        ];
                         foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['addEnableColumns'] as $_funcRef) {
-                            $query .= GeneralUtility::callUserFunction($_funcRef, $_params, $this);
+                            $constraints[] = QueryHelper::stripLogicalOperatorPrefix(
+                                GeneralUtility::callUserFunction($_funcRef, $_params, $this)
+                            );
                         }
                     }
                 }
@@ -1198,7 +1391,8 @@ class PageRepository
         } else {
             throw new \InvalidArgumentException('There is no entry in the $TCA array for the table "' . $table . '". This means that the function enableFields() is ' . 'called with an invalid table name as argument.', 1283790586);
         }
-        return $query;
+
+        return empty($constraints) ? '' : ' AND ' . $expressionBuilder->andX(...$constraints);
     }
 
     /**
@@ -1212,18 +1406,22 @@ class PageRepository
      */
     public function getMultipleGroupsWhereClause($field, $table)
     {
+        $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+            ->getQueryBuilderForTable($table)
+            ->expr();
         $memberGroups = GeneralUtility::intExplode(',', $this->getTypoScriptFrontendController()->gr_list);
-        $orChecks = array();
+        $orChecks = [];
         // If the field is empty, then OK
-        $orChecks[] = $field . '=\'\'';
+        $orChecks[] = $expressionBuilder->eq($field, $expressionBuilder->literal(''));
         // If the field is NULL, then OK
-        $orChecks[] = $field . ' IS NULL';
-        // If the field contsains zero, then OK
-        $orChecks[] = $field . '=\'0\'';
+        $orChecks[] = $expressionBuilder->isNull($field);
+        // If the field contains zero, then OK
+        $orChecks[] = $expressionBuilder->eq($field, 0);
         foreach ($memberGroups as $value) {
-            $orChecks[] = $this->getDatabaseConnection()->listQuery($field, $value, $table);
+            $orChecks[] = $expressionBuilder->inSet($field, $expressionBuilder->literal($value));
         }
-        return ' AND (' . implode(' OR ', $orChecks) . ')';
+
+        return' AND (' . $expressionBuilder->orX(...$orChecks) . ')';
     }
 
     /**********************
@@ -1405,9 +1603,15 @@ class PageRepository
             }
             // Find pointed-to record.
             if ($moveID) {
-                $res = $this->getDatabaseConnection()->exec_SELECTquery(implode(',', array_keys($this->purgeComputedProperties($row))), $table, 'uid=' . (int)$moveID . $this->enableFields($table));
-                $origRow = $this->getDatabaseConnection()->sql_fetch_assoc($res);
-                $this->getDatabaseConnection()->sql_free_result($res);
+                $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
+                $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
+                $origRow = $queryBuilder->select(...array_keys($this->purgeComputedProperties($row)))
+                    ->from($table)
+                    ->where($queryBuilder->expr()->eq('uid', (int)$moveID))
+                    ->setMaxResults(1)
+                    ->execute()
+                    ->fetch();
+
                 if ($origRow) {
                     $row = $origRow;
                     return true;
@@ -1432,10 +1636,23 @@ class PageRepository
             $workspace = (int)$this->versioningWorkspaceId;
             if (!empty($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) && $workspace !== 0) {
                 // Select workspace version of record:
-                $row = $this->getDatabaseConnection()->exec_SELECTgetSingleRow($fields, $table, 'pid<>-1 AND
-						t3ver_state=' . new VersionState(VersionState::MOVE_PLACEHOLDER) . ' AND
-						t3ver_move_id=' . (int)$uid . ' AND
-						t3ver_wsid=' . (int)$workspace . $this->deleteClause($table));
+                $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
+                $queryBuilder->getRestrictions()
+                    ->removeAll()
+                    ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
+
+                $row = $queryBuilder->select(...GeneralUtility::trimExplode(',', $fields, true))
+                    ->from($table)
+                    ->where(
+                        $queryBuilder->expr()->neq('pid', -1),
+                        $queryBuilder->expr()->eq('t3ver_state', new VersionState(VersionState::MOVE_PLACEHOLDER)),
+                        $queryBuilder->expr()->eq('t3ver_move_id', (int)$uid),
+                        $queryBuilder->expr()->eq('t3ver_wsid', (int)$workspace)
+                    )
+                    ->setMaxResults(1)
+                    ->execute()
+                    ->fetch();
+
                 if (is_array($row)) {
                     return $row;
                 }
@@ -1460,18 +1677,40 @@ class PageRepository
         if ($workspace !== 0 && !empty($GLOBALS['TCA'][$table]['ctrl']['versioningWS'])) {
             $workspace = (int)$workspace;
             $uid = (int)$uid;
-            // Setting up enableFields for version record
-            $enFields = $this->enableFields($table, -1, array(), true);
             // Select workspace version of record, only testing for deleted.
-            $newrow = $this->getDatabaseConnection()->exec_SELECTgetSingleRow($fields, $table, 'pid=-1 AND
-					t3ver_oid=' . $uid . ' AND
-					t3ver_wsid=' . $workspace . $this->deleteClause($table));
+            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
+            $queryBuilder->getRestrictions()
+                ->removeAll()
+                ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
+
+            $newrow = $queryBuilder->select(...GeneralUtility::trimExplode(',', $fields, true))
+                ->from($table)
+                ->where(
+                    $queryBuilder->expr()->eq('pid', -1),
+                    $queryBuilder->expr()->eq('t3ver_oid', $uid),
+                    $queryBuilder->expr()->eq('t3ver_wsid', $workspace)
+                )
+                ->setMaxResults(1)
+                ->execute()
+                ->fetch();
+
             // If version found, check if it could have been selected with enableFields on
             // as well:
+            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
+            $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
+            // Remove the frontend workspace restriction because we are testing a version record
+            $queryBuilder->getRestrictions()->removeByType(FrontendWorkspaceRestriction::class);
+            $queryBuilder->select('uid')
+                ->from($table)
+                ->setMaxResults(1);
+
             if (is_array($newrow)) {
-                if ($bypassEnableFieldsCheck || $this->getDatabaseConnection()->exec_SELECTgetSingleRow('uid', $table, 'pid=-1 AND
-						t3ver_oid=' . $uid . ' AND
-						t3ver_wsid=' . $workspace . $enFields)) {
+                $queryBuilder->where(
+                    $queryBuilder->expr()->eq('pid', -1),
+                    $queryBuilder->expr()->eq('t3ver_oid', $uid),
+                    $queryBuilder->expr()->eq('t3ver_wsid', $workspace)
+                );
+                if ($bypassEnableFieldsCheck || $queryBuilder->execute()->fetchColumn()) {
                     // Return offline version, tested for its enableFields.
                     return $newrow;
                 } else {
@@ -1481,7 +1720,8 @@ class PageRepository
             } else {
                 // OK, so no workspace version was found. Then check if online version can be
                 // selected with full enable fields and if so, return 1:
-                if ($bypassEnableFieldsCheck || $this->getDatabaseConnection()->exec_SELECTgetSingleRow('uid', $table, 'uid=' . $uid . $enFields)) {
+                $queryBuilder->where($queryBuilder->expr()->eq('uid', $uid));
+                if ($bypassEnableFieldsCheck || $queryBuilder->execute()->fetchColumn()) {
                     // Means search was done, but no version found.
                     return 1;
                 } else {
@@ -1511,7 +1751,17 @@ class PageRepository
         } else {
             if ($wsid > 0) {
                 // No $GLOBALS['TCA'] yet!
-                $ws = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('*', 'sys_workspace', 'uid=' . (int)$wsid . ' AND deleted=0');
+                $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+                    ->getQueryBuilderForTable('sys_workspace');
+                $queryBuilder->getRestrictions()->removeAll();
+                $ws = $queryBuilder->select('*')
+                    ->from('sys_workspace')
+                    ->where(
+                        $queryBuilder->expr()->eq('uid', (int)$wsid),
+                        $queryBuilder->expr()->eq('deleted', 0)
+                    )
+                    ->execute()
+                    ->fetch();
                 if (!is_array($ws)) {
                     return false;
                 }
@@ -1634,16 +1884,6 @@ class PageRepository
         return $shouldFieldBeOverlaid;
     }
 
-    /**
-     * Returns the database connection
-     *
-     * @return \TYPO3\CMS\Core\Database\DatabaseConnection
-     */
-    protected function getDatabaseConnection()
-    {
-        return $GLOBALS['TYPO3_DB'];
-    }
-
     /**
      * @return \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController
      */
diff --git a/typo3/sysext/frontend/Tests/Functional/ContentObject/ContentObjectRendererTest.php b/typo3/sysext/frontend/Tests/Functional/ContentObject/ContentObjectRendererTest.php
new file mode 100644
index 000000000000..01f6c96fcd8d
--- /dev/null
+++ b/typo3/sysext/frontend/Tests/Functional/ContentObject/ContentObjectRendererTest.php
@@ -0,0 +1,519 @@
+<?php
+namespace TYPO3\CMS\Frontend\Tests\Functional\ContentObject;
+
+/*
+ * 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\TypoScript\TemplateService;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
+use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
+use TYPO3\CMS\Frontend\Page\PageRepository;
+
+/**
+ * Testcase for TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
+ */
+class ContentObjectRendererTest extends \TYPO3\CMS\Core\Tests\FunctionalTestCase
+{
+    /**
+     * @var ContentObjectRenderer
+     */
+    protected $subject;
+
+    protected function setUp()
+    {
+        parent::setUp();
+
+        $typoScriptFrontendController = GeneralUtility::makeInstance(
+            TypoScriptFrontendController::class,
+            null,
+            1,
+            0
+        );
+        $typoScriptFrontendController->sys_page = GeneralUtility::makeInstance(PageRepository::class);
+        $typoScriptFrontendController->tmpl = GeneralUtility::makeInstance(TemplateService::class);
+        $GLOBALS['TSFE'] = $typoScriptFrontendController;
+
+        $this->subject = GeneralUtility::makeInstance(ContentObjectRenderer::class);
+    }
+
+    /**
+     * Data provider for the getQuery test
+     *
+     * @return array multi-dimensional array with the second level like this:
+     * @see getQuery
+     */
+    public function getQueryDataProvider(): array
+    {
+        $data = [
+            'testing empty conf' => [
+                'tt_content',
+                [],
+                [
+                    'SELECT' => '*'
+                ]
+            ],
+            'testing #17284: adding uid/pid for workspaces' => [
+                'tt_content',
+                [
+                    'selectFields' => 'header,bodytext'
+                ],
+                [
+                    'SELECT' => 'header,bodytext, tt_content.uid as uid, tt_content.pid as pid, tt_content.t3ver_state as t3ver_state'
+                ]
+            ],
+            'testing #17284: no need to add' => [
+                'tt_content',
+                [
+                    'selectFields' => 'tt_content.*'
+                ],
+                [
+                    'SELECT' => 'tt_content.*'
+                ]
+            ],
+            'testing #17284: no need to add #2' => [
+                'tt_content',
+                [
+                    'selectFields' => '*'
+                ],
+                [
+                    'SELECT' => '*'
+                ]
+            ],
+            'testing #29783: joined tables, prefix tablename' => [
+                'tt_content',
+                [
+                    'selectFields' => 'tt_content.header,be_users.username',
+                    'join' => 'be_users ON tt_content.cruser_id = be_users.uid'
+                ],
+                [
+                    'SELECT' => 'tt_content.header,be_users.username, tt_content.uid as uid, tt_content.pid as pid, tt_content.t3ver_state as t3ver_state'
+                ]
+            ],
+            'testing #34152: single count(*), add nothing' => [
+                'tt_content',
+                [
+                    'selectFields' => 'count(*)'
+                ],
+                [
+                    'SELECT' => 'count(*)'
+                ]
+            ],
+            'testing #34152: single max(crdate), add nothing' => [
+                'tt_content',
+                [
+                    'selectFields' => 'max(crdate)'
+                ],
+                [
+                    'SELECT' => 'max(crdate)'
+                ]
+            ],
+            'testing #34152: single min(crdate), add nothing' => [
+                'tt_content',
+                [
+                    'selectFields' => 'min(crdate)'
+                ],
+                [
+                    'SELECT' => 'min(crdate)'
+                ]
+            ],
+            'testing #34152: single sum(is_siteroot), add nothing' => [
+                'tt_content',
+                [
+                    'selectFields' => 'sum(is_siteroot)'
+                ],
+                [
+                    'SELECT' => 'sum(is_siteroot)'
+                ]
+            ],
+            'testing #34152: single avg(crdate), add nothing' => [
+                'tt_content',
+                [
+                    'selectFields' => 'avg(crdate)'
+                ],
+                [
+                    'SELECT' => 'avg(crdate)'
+                ]
+            ]
+        ];
+
+        return $data;
+    }
+
+    /**
+     * Check if sanitizeSelectPart works as expected
+     *
+     * @dataProvider getQueryDataProvider
+     * @test
+     * @param string $table
+     * @param array $conf
+     * @param array $expected
+     */
+    public function getQuery(string $table, array $conf, array $expected)
+    {
+        $GLOBALS['TCA'] = [
+            'pages' => [
+                'ctrl' => [
+                    'enablecolumns' => [
+                        'disabled' => 'hidden'
+                    ]
+                ]
+            ],
+            'tt_content' => [
+                'ctrl' => [
+                    'enablecolumns' => [
+                        'disabled' => 'hidden'
+                    ],
+                    'versioningWS' => true
+                ]
+            ],
+        ];
+
+        $result = $this->subject->getQuery($table, $conf, true);
+        foreach ($expected as $field => $value) {
+            $this->assertEquals($value, $result[$field]);
+        }
+    }
+
+    /**
+     * @test
+     */
+    public function getQueryCallsGetTreeListWithNegativeValuesIfRecursiveIsSet()
+    {
+        $this->subject = $this->getAccessibleMock(ContentObjectRenderer::class, ['getTreeList']);
+        $this->subject->start([], 'tt_content');
+
+        $conf = [
+            'recursive' => '15',
+            'pidInList' => '16, -35'
+        ];
+
+        $this->subject->expects($this->at(0))
+            ->method('getTreeList')
+            ->with(-16, 15)
+            ->will($this->returnValue('15,16'));
+        $this->subject->expects($this->at(1))
+            ->method('getTreeList')
+            ->with(-35, 15)
+            ->will($this->returnValue('15,35'));
+
+        $this->subject->getQuery('tt_content', $conf, true);
+    }
+
+    /**
+     * @test
+     */
+    public function getQueryCallsGetTreeListWithCurrentPageIfThisIsSet()
+    {
+        $GLOBALS['TSFE']->id = 27;
+
+        $this->subject = $this->getAccessibleMock(ContentObjectRenderer::class, ['getTreeList']);
+        $this->subject->start([], 'tt_content');
+
+        $conf = [
+            'pidInList' => 'this',
+            'recursive' => '4'
+        ];
+
+        $this->subject->expects($this->once())
+            ->method('getTreeList')
+            ->with(-27)
+            ->will($this->returnValue('27'));
+
+        $this->subject->getQuery('tt_content', $conf, true);
+    }
+
+    /**
+     * @return array
+     */
+    public function getWhereReturnCorrectQueryDataProvider()
+    {
+        return [
+            [
+                [
+                    'tt_content' => [
+                        'ctrl' => [
+                        ],
+                        'columns' => [
+                        ]
+                    ],
+                ],
+                'tt_content',
+                [
+                    'uidInList' => '42',
+                    'pidInList' => 43,
+                    'where' => 'tt_content.cruser_id=5',
+                    'groupBy' => 'tt_content.title',
+                    'orderBy' => 'tt_content.sorting',
+                ],
+                'WHERE tt_content.uid=42 AND tt_content.pid IN (43) AND tt_content.cruser_id=5 GROUP BY tt_content.title ORDER BY tt_content.sorting',
+            ],
+            [
+                [
+                    'tt_content' => [
+                        'ctrl' => [
+                            'delete' => 'deleted',
+                            'enablecolumns' => [
+                                'disabled' => 'hidden',
+                                'starttime' => 'startdate',
+                                'endtime' => 'enddate',
+                            ],
+                            'languageField' => 'sys_language_uid',
+                            'transOrigPointerField' => 'l18n_parent',
+                        ],
+                        'columns' => [
+                        ]
+                    ],
+                ],
+                'tt_content',
+                [
+                    'uidInList' => 42,
+                    'pidInList' => 43,
+                    'where' => 'tt_content.cruser_id=5',
+                    'groupBy' => 'tt_content.title',
+                    'orderBy' => 'tt_content.sorting',
+                ],
+                'WHERE tt_content.uid=42 AND tt_content.pid IN (43) AND tt_content.cruser_id=5 AND (tt_content.sys_language_uid = 13)%s GROUP BY tt_content.title ORDER BY tt_content.sorting',
+            ],
+            [
+                [
+                    'tt_content' => [
+                        'ctrl' => [
+                            'languageField' => 'sys_language_uid',
+                            'transOrigPointerField' => 'l18n_parent',
+                        ],
+                        'columns' => [
+                        ]
+                    ],
+                ],
+                'tt_content',
+                [
+                    'uidInList' => 42,
+                    'pidInList' => 43,
+                    'where' => 'tt_content.cruser_id=5',
+                    'languageField' => 0,
+                ],
+                'WHERE tt_content.uid=42 AND tt_content.pid IN (43) AND tt_content.cruser_id=5',
+            ],
+        ];
+    }
+
+    /**
+     * @test
+     * @param array $tca
+     * @param string $table
+     * @param array $configuration
+     * @param string $expectedResult
+     * @dataProvider getWhereReturnCorrectQueryDataProvider
+     */
+    public function getWhereReturnCorrectQuery(array $tca, string $table, array $configuration, string $expectedResult)
+    {
+        $GLOBALS['TCA'] = $tca;
+        $GLOBALS['SIM_ACCESS_TIME'] = '4242';
+        $GLOBALS['TSFE']->sys_language_content = 13;
+        /** @var \PHPUnit_Framework_MockObject_MockObject|ContentObjectRenderer $contentObjectRenderer */
+        $contentObjectRenderer = $this->getMockBuilder(ContentObjectRenderer::class)
+            ->setMethods(['checkPidArray'])
+            ->getMock();
+        $contentObjectRenderer->expects($this->any())
+            ->method('checkPidArray')
+            ->willReturn(explode(',', $configuration['pidInList']));
+
+        // Embed the enable fields string into the expected result as the database
+        // connection is still unconfigured when the data provider is being run.
+        $expectedResult = sprintf($expectedResult, $GLOBALS['TSFE']->sys_page->enableFields($table));
+
+        $this->assertSame($expectedResult, $contentObjectRenderer->getWhere($table, $configuration));
+    }
+
+    /**
+     * @return array
+     */
+    public function typolinkReturnsCorrectLinksForPagesDataProvider()
+    {
+        return array(
+            'Link to page' => array(
+                'My page',
+                array(
+                    'parameter' => 42,
+                ),
+                array(
+                    'uid' => 42,
+                    'title' => 'Page title',
+                ),
+                '<a href="index.php?id=42">My page</a>',
+            ),
+            'Link to page without link text' => array(
+                '',
+                array(
+                    'parameter' => 42,
+                ),
+                array(
+                    'uid' => 42,
+                    'title' => 'Page title',
+                ),
+                '<a href="index.php?id=42">Page title</a>',
+            ),
+            'Link to page with attributes' => array(
+                'My page',
+                array(
+                    'parameter' => '42',
+                    'ATagParams' => 'class="page-class"',
+                    'target' => '_self',
+                    'title' => 'Link to internal page',
+                ),
+                array(
+                    'uid' => 42,
+                    'title' => 'Page title',
+                ),
+                '<a href="index.php?id=42" title="Link to internal page" target="_self" class="page-class">My page</a>',
+            ),
+            'Link to page with attributes in parameter' => array(
+                'My page',
+                array(
+                    'parameter' => '42 _self page-class "Link to internal page"',
+                ),
+                array(
+                    'uid' => 42,
+                    'title' => 'Page title',
+                ),
+                '<a href="index.php?id=42" title="Link to internal page" target="_self" class="page-class">My page</a>',
+            ),
+            'Link to page with bold tag in title' => array(
+                '',
+                array(
+                    'parameter' => 42,
+                ),
+                array(
+                    'uid' => 42,
+                    'title' => 'Page <b>title</b>',
+                ),
+                '<a href="index.php?id=42">Page <b>title</b></a>',
+            ),
+            'Link to page with script tag in title' => array(
+                '',
+                array(
+                    'parameter' => 42,
+                ),
+                array(
+                    'uid' => 42,
+                    'title' => '<script>alert(123)</script>Page title',
+                ),
+                '<a href="index.php?id=42">&lt;script&gt;alert(123)&lt;/script&gt;Page title</a>',
+            ),
+        );
+    }
+
+    /**
+     * @test
+     * @param string $linkText
+     * @param array $configuration
+     * @param array $pageArray
+     * @param string $expectedResult
+     * @dataProvider typolinkReturnsCorrectLinksForPagesDataProvider
+     */
+    public function typolinkReturnsCorrectLinksForPages($linkText, $configuration, $pageArray, $expectedResult)
+    {
+        $pageRepositoryMockObject = $this->getMockBuilder(PageRepository::class)
+            ->setMethods(['getPage'])
+            ->getMock();
+        $pageRepositoryMockObject->expects($this->any())->method('getPage')->willReturn($pageArray);
+
+        $typoScriptFrontendController = GeneralUtility::makeInstance(
+            TypoScriptFrontendController::class,
+            null,
+            1,
+            0
+        );
+        $typoScriptFrontendController->config = [
+            'config' => [],
+            'mainScript' => 'index.php',
+        ];
+        $typoScriptFrontendController->sys_page = $pageRepositoryMockObject;
+        $typoScriptFrontendController->tmpl = GeneralUtility::makeInstance(TemplateService::class);
+        $typoScriptFrontendController->tmpl->setup = [
+            'lib.' => [
+                'parseFunc.' => $this->getLibParseFunc(),
+            ],
+        ];
+        $GLOBALS['TSFE'] = $typoScriptFrontendController;
+
+        $subject = GeneralUtility::makeInstance(ContentObjectRenderer::class);
+        $this->assertEquals($expectedResult, $subject->typoLink($linkText, $configuration));
+    }
+
+    /**
+     * @return array
+     */
+    protected function getLibParseTarget()
+    {
+        return [
+            'override' => '',
+            'override.' => [
+                'if.' => [
+                    'isTrue.' => [
+                        'data' => 'TSFE:dtdAllowsFrames',
+                    ],
+                ],
+            ],
+        ];
+    }
+
+    /**
+     * @return array
+     */
+    protected function getLibParseFunc()
+    {
+        return [
+            'makelinks' => '1',
+            'makelinks.' => [
+                'http.' => [
+                    'keep' => '{$styles.content.links.keep}',
+                    'extTarget' => '',
+                    'extTarget.' => $this->getLibParseTarget(),
+                    'mailto.' => [
+                        'keep' => 'path',
+                    ],
+                ],
+            ],
+            'tags' => [
+                'link' => 'TEXT',
+                'link.' => [
+                    'current' => '1',
+                    'typolink.' => [
+                        'parameter.' => [
+                            'data' => 'parameters : allParams',
+                        ],
+                        'extTarget.' => $this->getLibParseTarget(),
+                        'target.' => $this->getLibParseTarget(),
+                    ],
+                    'parseFunc.' => [
+                        'constants' => '1',
+                    ],
+                ],
+            ],
+
+            'allowTags' => 'a, abbr, acronym, address, article, aside, b, bdo, big, blockquote, br, caption, center, cite, code, col, colgroup, dd, del, dfn, dl, div, dt, em, font, footer, header, h1, h2, h3, h4, h5, h6, hr, i, img, ins, kbd, label, li, link, meta, nav, ol, p, pre, q, samp, sdfield, section, small, span, strike, strong, style, sub, sup, table, thead, tbody, tfoot, td, th, tr, title, tt, u, ul, var',
+            'denyTags' => '*',
+            'sword' => '<span class="csc-sword">|</span>',
+            'constants' => '1',
+            'nonTypoTagStdWrap.' => [
+                'HTMLparser' => '1',
+                'HTMLparser.' => [
+                    'keepNonMatchedTags' => '1',
+                    'htmlSpecialChars' => '2',
+                ],
+            ],
+        ];
+    }
+}
diff --git a/typo3/sysext/frontend/Tests/Functional/Fixtures/pages.xml b/typo3/sysext/frontend/Tests/Functional/Fixtures/pages.xml
index b149d2bb2e0e..ff8cb0c32f37 100644
--- a/typo3/sysext/frontend/Tests/Functional/Fixtures/pages.xml
+++ b/typo3/sysext/frontend/Tests/Functional/Fixtures/pages.xml
@@ -70,6 +70,30 @@
         <deleted>0</deleted>
         <perms_everybody>15</perms_everybody>
     </pages>
+    <pages>
+        <uid>11</uid>
+        <pid>0</pid>
+        <title>Workspace Root</title>
+        <deleted>0</deleted>
+        <t3ver_oid>0</t3ver_oid>
+        <t3ver_id>0</t3ver_id>
+        <t3ver_wsid>987654321</t3ver_wsid>
+        <t3ver_label>INITIAL PLACEHOLDER</t3ver_label>
+        <t3ver_state>1</t3ver_state>
+        <perms_everybody>15</perms_everybody>
+    </pages>
+    <pages>
+        <uid>12</uid>
+        <pid>-1</pid>
+        <title>Workspace Root</title>
+        <deleted>0</deleted>
+        <t3ver_oid>11</t3ver_oid>
+        <t3ver_id>1</t3ver_id>
+        <t3ver_wsid>987654321</t3ver_wsid>
+        <t3ver_label>First draft version</t3ver_label>
+        <t3ver_state>-1</t3ver_state>
+        <perms_everybody>15</perms_everybody>
+    </pages>
 
     <pages_language_overlay>
         <uid>901</uid>
diff --git a/typo3/sysext/frontend/Tests/Functional/Page/PageRepositoryTest.php b/typo3/sysext/frontend/Tests/Functional/Page/PageRepositoryTest.php
index c9f181a26870..4e105dfa8a04 100644
--- a/typo3/sysext/frontend/Tests/Functional/Page/PageRepositoryTest.php
+++ b/typo3/sysext/frontend/Tests/Functional/Page/PageRepositoryTest.php
@@ -14,8 +14,12 @@ namespace TYPO3\CMS\Frontend\Tests\Functional\Page;
  * The TYPO3 project - inspiring people to share!
  */
 
+use Prophecy\Argument;
+use TYPO3\CMS\Core\Database\ConnectionPool;
 use TYPO3\CMS\Core\Tests\FunctionalTestCase;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Frontend\Page\PageRepository;
+use TYPO3\CMS\Frontend\Page\PageRepositoryGetPageHookInterface;
 
 /**
  * Test case
@@ -32,6 +36,7 @@ class PageRepositoryTest extends FunctionalTestCase
     protected function setUp()
     {
         parent::setUp();
+        $GLOBALS['TSFE']->gr_list = '';
         $this->importDataSet(__DIR__ . '/../Fixtures/pages.xml');
         $this->pageRepo = new PageRepository();
         $this->pageRepo->init(false);
@@ -281,6 +286,256 @@ class PageRepositoryTest extends FunctionalTestCase
         $this->assertEquals('Attrappe 1-3-9', $row['title']);
     }
 
+    /**
+     * Tests whether the getPage Hook is called correctly.
+     *
+     * @test
+     */
+    public function isGetPageHookCalled()
+    {
+        // Create a hook mock object
+        $getPageHookProphet = $this->prophesize(\stdClass::class);
+        $getPageHookProphet->willImplement(PageRepositoryGetPageHookInterface::class);
+        $getPageHookProphet->getPage_preProcess(42, false, Argument::type(PageRepository::class))->shouldBeCalled();
+        $getPageHookMock = $getPageHookProphet->reveal();
+        $className = get_class($getPageHookMock);
+
+        // Register hook mock object
+        GeneralUtility::addInstance($className, $getPageHookMock);
+        $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['getPage'][] = $className;
+        $this->pageRepo->getPage(42, false);
+    }
+
+    /**
+     * @test
+     */
+    public function initSetsPublicPropertyCorrectlyForWorkspacePreview()
+    {
+        $this->pageRepo->versioningPreview = true;
+        $this->pageRepo->versioningWorkspaceId = 2;
+        $this->pageRepo->init(false);
+
+        $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('pages');
+
+        $expectedSQL = sprintf(
+            ' AND (%s = 0) AND ((%s = 0) OR (%s = 2))',
+            $connection->quoteIdentifier('pages.deleted'),
+            $connection->quoteIdentifier('pages.t3ver_wsid'),
+            $connection->quoteIdentifier('pages.t3ver_wsid')
+        );
+
+        $this->assertSame($expectedSQL, $this->pageRepo->where_hid_del);
+    }
+
+    /**
+     * @test
+     */
+    public function initSetsPublicPropertyCorrectlyForLive()
+    {
+        $GLOBALS['SIM_ACCESS_TIME'] = 123;
+
+        $this->pageRepo->versioningPreview = false;
+        $this->pageRepo->versioningWorkspaceId = 0;
+        $this->pageRepo->init(false);
+
+        $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('pages');
+        $expectedSQL = sprintf(
+            ' AND (%s = 0) AND (%s <= 0) AND (%s = 0) AND (%s <= 123) AND ((%s = 0) OR (%s > 123))',
+            $connection->quoteIdentifier('pages.deleted'),
+            $connection->quoteIdentifier('pages.t3ver_state'),
+            $connection->quoteIdentifier('pages.hidden'),
+            $connection->quoteIdentifier('pages.starttime'),
+            $connection->quoteIdentifier('pages.endtime'),
+            $connection->quoteIdentifier('pages.endtime')
+        );
+
+        $this->assertSame($expectedSQL, $this->pageRepo->where_hid_del);
+    }
+
+    ////////////////////////////////
+    // Tests concerning workspaces
+    ////////////////////////////////
+
+    /**
+     * @test
+     */
+    public function noPagesFromWorkspaceAreShownLive()
+    {
+        // initialization
+        $wsid = 987654321;
+
+        // simulate calls from \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController->fetch_the_id()
+        $this->pageRepo->versioningPreview = false;
+        $this->pageRepo->versioningWorkspaceId = $wsid;
+        $this->pageRepo->init(false);
+
+        $this->assertSame([], $this->pageRepo->getPage(11));
+    }
+
+    /**
+     * @test
+     */
+    public function previewShowsPagesFromLiveAndCurrentWorkspace()
+    {
+        // initialization
+        $wsid = 987654321;
+
+        // simulate calls from \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController->fetch_the_id()
+        $this->pageRepo->versioningPreview = true;
+        $this->pageRepo->versioningWorkspaceId = $wsid;
+        $this->pageRepo->init(false);
+
+        $pageRec = $this->pageRepo->getPage(11);
+
+        $this->assertSame(11, $pageRec['uid']);
+        $this->assertSame(11, $pageRec['t3ver_oid']);
+        $this->assertSame(987654321, $pageRec['t3ver_wsid']);
+        $this->assertSame(-1, $pageRec['t3ver_state']);
+        $this->assertSame('First draft version', $pageRec['t3ver_label']);
+    }
+
+    /**
+     * @test
+     */
+    public function getWorkspaceVersionReturnsTheCorrectMethod()
+    {
+        // initialization
+        $wsid = 987654321;
+
+        // simulate calls from \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController->fetch_the_id()
+        $this->pageRepo->versioningPreview = true;
+        $this->pageRepo->versioningWorkspaceId = $wsid;
+        $this->pageRepo->init(false);
+
+        $pageRec = $this->pageRepo->getWorkspaceVersionOfRecord($wsid, 'pages', 11);
+
+        $this->assertSame(12, $pageRec['uid']);
+        $this->assertSame(11, $pageRec['t3ver_oid']);
+        $this->assertSame(987654321, $pageRec['t3ver_wsid']);
+        $this->assertSame(-1, $pageRec['t3ver_state']);
+        $this->assertSame('First draft version', $pageRec['t3ver_label']);
+    }
+
+    ////////////////////////////////
+    // Tests concerning versioning
+    ////////////////////////////////
+
+    /**
+     * @test
+     */
+    public function enableFieldsHidesVersionedRecordsAndPlaceholders()
+    {
+        $table = $this->getUniqueId('aTable');
+        $GLOBALS['TCA'][$table] = [
+            'ctrl' => [
+                'versioningWS' => true
+            ]
+        ];
+
+        $this->pageRepo->versioningPreview = false;
+        $this->pageRepo->init(false);
+
+        $conditions = $this->pageRepo->enableFields($table);
+        $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($table);
+
+        $this->assertThat(
+            $conditions,
+            $this->stringContains(' AND (' . $connection->quoteIdentifier($table . '.t3ver_state') . ' <= 0)'),
+            'Versioning placeholders'
+        );
+        $this->assertThat(
+            $conditions,
+            $this->stringContains(' AND (' . $connection->quoteIdentifier($table . '.pid') . ' <> -1)'),
+            'Records from page -1'
+        );
+    }
+
+    /**
+     * @test
+     */
+    public function enableFieldsDoesNotHidePlaceholdersInPreview()
+    {
+        $table = $this->getUniqueId('aTable');
+        $GLOBALS['TCA'][$table] = [
+            'ctrl' => [
+                'versioningWS' => true
+            ]
+        ];
+
+        $this->pageRepo->versioningPreview = true;
+        $this->pageRepo->init(false);
+
+        $conditions = $this->pageRepo->enableFields($table);
+        $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($table);
+
+        $this->assertThat(
+            $conditions,
+            $this->logicalNot($this->stringContains(' AND (' . $connection->quoteIdentifier($table . '.t3ver_state') . ' <= 0)')),
+            'No versioning placeholders'
+        );
+        $this->assertThat(
+            $conditions,
+            $this->stringContains(' AND (' . $connection->quoteIdentifier($table . '.pid') . ' <> -1)'),
+            'Records from page -1'
+        );
+    }
+
+    /**
+     * @test
+     */
+    public function enableFieldsDoesFilterToCurrentAndLiveWorkspaceForRecordsInPreview()
+    {
+        $table = $this->getUniqueId('aTable');
+        $GLOBALS['TCA'][$table] = [
+            'ctrl' => [
+                'versioningWS' => true
+            ]
+        ];
+
+        $this->pageRepo->versioningPreview = true;
+        $this->pageRepo->versioningWorkspaceId = 2;
+        $this->pageRepo->init(false);
+
+        $conditions = $this->pageRepo->enableFields($table);
+        $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($table);
+
+        $this->assertThat(
+            $conditions,
+            $this->stringContains(' AND ((' . $connection->quoteIdentifier($table . '.t3ver_wsid') . ' = 0) OR (' . $connection->quoteIdentifier($table . '.t3ver_wsid') . ' = 2))'),
+            'No versioning placeholders'
+        );
+    }
+
+    /**
+     * @test
+     */
+    public function enableFieldsDoesNotHideVersionedRecordsWhenCheckingVersionOverlays()
+    {
+        $table = $this->getUniqueId('aTable');
+        $GLOBALS['TCA'][$table] = [
+            'ctrl' => [
+                'versioningWS' => true
+            ]
+        ];
+
+        $this->pageRepo->versioningPreview = true;
+        $this->pageRepo->init(false);
+
+        $conditions = $this->pageRepo->enableFields($table, -1, [], true);
+        $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($table);
+
+        $this->assertThat(
+            $conditions,
+            $this->logicalNot($this->stringContains(' AND (' . $connection->quoteIdentifier($table . '.t3ver_state') . ' <= 0)')),
+            'No versioning placeholders'
+        );
+        $this->assertThat(
+            $conditions,
+            $this->logicalNot($this->stringContains(' AND (' . $connection->quoteIdentifier($table . '.pid') . ' <> -1)')),
+            'No necords from page -1'
+        );
+    }
+
     protected function assertOverlayRow($row)
     {
         $this->assertInternalType('array', $row);
diff --git a/typo3/sysext/frontend/Tests/Unit/ContentObject/ContentObjectRendererTest.php b/typo3/sysext/frontend/Tests/Unit/ContentObject/ContentObjectRendererTest.php
index 5b922725a4f2..8222836d3bed 100644
--- a/typo3/sysext/frontend/Tests/Unit/ContentObject/ContentObjectRendererTest.php
+++ b/typo3/sysext/frontend/Tests/Unit/ContentObject/ContentObjectRendererTest.php
@@ -54,7 +54,6 @@ use TYPO3\CMS\Frontend\ContentObject\TextContentObject;
 use TYPO3\CMS\Frontend\ContentObject\UserContentObject;
 use TYPO3\CMS\Frontend\ContentObject\UserInternalContentObject;
 use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
-use TYPO3\CMS\Frontend\Page\PageRepository;
 use TYPO3\CMS\Frontend\Tests\Unit\ContentObject\Fixtures\PageRepositoryFixture;
 
 /**
@@ -1013,212 +1012,6 @@ class ContentObjectRendererTest extends UnitTestCase
             $this->subject->_call('replacement', $content, $conf));
     }
 
-    /**
-     * Data provider for the getQuery test
-     *
-     * @return array multi-dimensional array with the second level like this:
-     * @see getQuery
-     */
-    public function getQueryDataProvider()
-    {
-        $data = array(
-            'testing empty conf' => array(
-                'tt_content',
-                array(),
-                array(
-                    'SELECT' => '*'
-                )
-            ),
-            'testing #17284: adding uid/pid for workspaces' => array(
-                'tt_content',
-                array(
-                    'selectFields' => 'header,bodytext'
-                ),
-                array(
-                    'SELECT' => 'header,bodytext, tt_content.uid as uid, tt_content.pid as pid, tt_content.t3ver_state as t3ver_state'
-                )
-            ),
-            'testing #17284: no need to add' => array(
-                'tt_content',
-                array(
-                    'selectFields' => 'tt_content.*'
-                ),
-                array(
-                    'SELECT' => 'tt_content.*'
-                )
-            ),
-            'testing #17284: no need to add #2' => array(
-                'tt_content',
-                array(
-                    'selectFields' => '*'
-                ),
-                array(
-                    'SELECT' => '*'
-                )
-            ),
-            'testing #29783: joined tables, prefix tablename' => array(
-                'tt_content',
-                array(
-                    'selectFields' => 'tt_content.header,be_users.username',
-                    'join' => 'be_users ON tt_content.cruser_id = be_users.uid'
-                ),
-                array(
-                    'SELECT' => 'tt_content.header,be_users.username, tt_content.uid as uid, tt_content.pid as pid, tt_content.t3ver_state as t3ver_state'
-                )
-            ),
-            'testing #34152: single count(*), add nothing' => array(
-                'tt_content',
-                array(
-                    'selectFields' => 'count(*)'
-                ),
-                array(
-                    'SELECT' => 'count(*)'
-                )
-            ),
-            'testing #34152: single max(crdate), add nothing' => array(
-                'tt_content',
-                array(
-                    'selectFields' => 'max(crdate)'
-                ),
-                array(
-                    'SELECT' => 'max(crdate)'
-                )
-            ),
-            'testing #34152: single min(crdate), add nothing' => array(
-                'tt_content',
-                array(
-                    'selectFields' => 'min(crdate)'
-                ),
-                array(
-                    'SELECT' => 'min(crdate)'
-                )
-            ),
-            'testing #34152: single sum(is_siteroot), add nothing' => array(
-                'tt_content',
-                array(
-                    'selectFields' => 'sum(is_siteroot)'
-                ),
-                array(
-                    'SELECT' => 'sum(is_siteroot)'
-                )
-            ),
-            'testing #34152: single avg(crdate), add nothing' => array(
-                'tt_content',
-                array(
-                    'selectFields' => 'avg(crdate)'
-                ),
-                array(
-                    'SELECT' => 'avg(crdate)'
-                )
-            )
-        );
-        return $data;
-    }
-
-    /**
-     * Check if sanitizeSelectPart works as expected
-     *
-     * @dataProvider getQueryDataProvider
-     * @test
-     */
-    public function getQuery($table, $conf, $expected)
-    {
-        $GLOBALS['TCA'] = array(
-            'pages' => array(
-                'ctrl' => array(
-                    'enablecolumns' => array(
-                        'disabled' => 'hidden'
-                    )
-                )
-            ),
-            'tt_content' => array(
-                'ctrl' => array(
-                    'enablecolumns' => array(
-                        'disabled' => 'hidden'
-                    ),
-                    'versioningWS' => true
-                )
-            ),
-        );
-        $result = $this->subject->getQuery($table, $conf, true);
-        foreach ($expected as $field => $value) {
-            $this->assertEquals($value, $result[$field]);
-        }
-    }
-
-    /**
-     * @test
-     */
-    public function getQueryCallsGetTreeListWithNegativeValuesIfRecursiveIsSet()
-    {
-        $GLOBALS['TCA'] = array(
-            'pages' => array(
-                'ctrl' => array(
-                    'enablecolumns' => array(
-                        'disabled' => 'hidden'
-                    )
-                )
-            ),
-            'tt_content' => array(
-                'ctrl' => array(
-                    'enablecolumns' => array(
-                        'disabled' => 'hidden'
-                    )
-                )
-            ),
-        );
-        $this->subject = $this->getAccessibleMock(ContentObjectRenderer::class, array('getTreeList'));
-        $this->subject->start(array(), 'tt_content');
-        $conf = array(
-            'recursive' => '15',
-            'pidInList' => '16, -35'
-        );
-        $this->subject->expects($this->at(0))
-            ->method('getTreeList')
-            ->with(-16, 15)
-            ->will($this->returnValue('15,16'));
-        $this->subject->expects($this->at(1))
-            ->method('getTreeList')
-            ->with(-35, 15)
-            ->will($this->returnValue('15,35'));
-        $this->subject->getQuery('tt_content', $conf, true);
-    }
-
-    /**
-     * @test
-     */
-    public function getQueryCallsGetTreeListWithCurrentPageIfThisIsSet()
-    {
-        $GLOBALS['TCA'] = array(
-            'pages' => array(
-                'ctrl' => array(
-                    'enablecolumns' => array(
-                        'disabled' => 'hidden'
-                    )
-                )
-            ),
-            'tt_content' => array(
-                'ctrl' => array(
-                    'enablecolumns' => array(
-                        'disabled' => 'hidden'
-                    )
-                )
-            ),
-        );
-        $this->subject = $this->getAccessibleMock(ContentObjectRenderer::class, array('getTreeList'));
-        $GLOBALS['TSFE']->id = 27;
-        $this->subject->start(array(), 'tt_content');
-        $conf = array(
-            'pidInList' => 'this',
-            'recursive' => '4'
-        );
-        $this->subject->expects($this->once())
-            ->method('getTreeList')
-            ->with(-27)
-            ->will($this->returnValue('27'));
-        $this->subject->getQuery('tt_content', $conf, true);
-    }
-
     /**
      * Data provider for calcAge.
      *
@@ -3084,84 +2877,6 @@ class ContentObjectRendererTest extends UnitTestCase
         $this->assertEquals($expectedResult, $this->subject->typoLink($linkText, $configuration));
     }
 
-    /**
-     * @return array
-     */
-    public function typolinkReturnsCorrectLinksForPagesDataProvider()
-    {
-        return array(
-            'Link to page' => array(
-                'My page',
-                array(
-                    'parameter' => 42,
-                ),
-                array(
-                    'uid' => 42,
-                    'title' => 'Page title',
-                ),
-                '<a href="index.php?id=42">My page</a>',
-            ),
-            'Link to page without link text' => array(
-                '',
-                array(
-                    'parameter' => 42,
-                ),
-                array(
-                    'uid' => 42,
-                    'title' => 'Page title',
-                ),
-                '<a href="index.php?id=42">Page title</a>',
-            ),
-            'Link to page with attributes' => array(
-                'My page',
-                array(
-                    'parameter' => '42',
-                    'ATagParams' => 'class="page-class"',
-                    'target' => '_self',
-                    'title' => 'Link to internal page',
-                ),
-                array(
-                    'uid' => 42,
-                    'title' => 'Page title',
-                ),
-                '<a href="index.php?id=42" title="Link to internal page" target="_self" class="page-class">My page</a>',
-            ),
-            'Link to page with attributes in parameter' => array(
-                'My page',
-                array(
-                    'parameter' => '42 _self page-class "Link to internal page"',
-                ),
-                array(
-                    'uid' => 42,
-                    'title' => 'Page title',
-                ),
-                '<a href="index.php?id=42" title="Link to internal page" target="_self" class="page-class">My page</a>',
-            ),
-            'Link to page with bold tag in title' => array(
-                '',
-                array(
-                    'parameter' => 42,
-                ),
-                array(
-                    'uid' => 42,
-                    'title' => 'Page <b>title</b>',
-                ),
-                '<a href="index.php?id=42">Page <b>title</b></a>',
-            ),
-            'Link to page with script tag in title' => array(
-                '',
-                array(
-                    'parameter' => 42,
-                ),
-                array(
-                    'uid' => 42,
-                    'title' => '<script>alert(123)</script>Page title',
-                ),
-                '<a href="index.php?id=42">&lt;script&gt;alert(123)&lt;/script&gt;Page title</a>',
-            ),
-        );
-    }
-
     /**
      * @param array $settings
      * @param string $linkText
@@ -3278,41 +2993,6 @@ class ContentObjectRendererTest extends UnitTestCase
         );
     }
 
-    /**
-     * @test
-     * @param string $linkText
-     * @param array $configuration
-     * @param array $pageArray
-     * @param string $expectedResult
-     * @dataProvider typolinkReturnsCorrectLinksForPagesDataProvider
-     */
-    public function typolinkReturnsCorrectLinksForPages($linkText, $configuration, $pageArray, $expectedResult)
-    {
-        $pageRepositoryMockObject = $this->getMockBuilder(PageRepository::class)
-            ->setMethods(array('getPage'))
-            ->getMock();
-        $pageRepositoryMockObject->expects($this->any())->method('getPage')->willReturn($pageArray);
-        $templateServiceObjectMock = $this->getMockBuilder(TemplateService::class)
-            ->setMethods(array('dummy'))
-            ->getMock();
-        $templateServiceObjectMock->setup = array(
-            'lib.' => array(
-                'parseFunc.' => $this->getLibParseFunc(),
-            ),
-        );
-        $typoScriptFrontendControllerMockObject = $this->createMock(TypoScriptFrontendController::class);
-        $typoScriptFrontendControllerMockObject->config = array(
-            'config' => array(),
-            'mainScript' => 'index.php',
-        );
-        $typoScriptFrontendControllerMockObject->sys_page = $pageRepositoryMockObject;
-        $typoScriptFrontendControllerMockObject->tmpl = $templateServiceObjectMock;
-        $GLOBALS['TSFE'] = $typoScriptFrontendControllerMockObject;
-        $this->subject->_set('typoScriptFrontendController', $typoScriptFrontendControllerMockObject);
-
-        $this->assertEquals($expectedResult, $this->subject->typoLink($linkText, $configuration));
-    }
-
     /**
      * @return array
      */
@@ -3557,102 +3237,6 @@ class ContentObjectRendererTest extends UnitTestCase
         );
     }
 
-    /**
-     * @return array
-     */
-    public function getWhereReturnCorrectQueryDataProvider()
-    {
-        return array(
-            array(
-                array(
-                    'tt_content' => array(
-                        'ctrl' => array(
-                        ),
-                        'columns' => array(
-                        )
-                    ),
-                ),
-                'tt_content',
-                array(
-                    'uidInList' => '42',
-                    'pidInList' => 43,
-                    'where' => 'tt_content.cruser_id=5',
-                    'groupBy' => 'tt_content.title',
-                    'orderBy' => 'tt_content.sorting',
-                ),
-                'WHERE tt_content.uid=42 AND tt_content.pid IN (43) AND tt_content.cruser_id=5 GROUP BY tt_content.title ORDER BY tt_content.sorting',
-            ),
-            array(
-                array(
-                    'tt_content' => array(
-                        'ctrl' => array(
-                            'delete' => 'deleted',
-                            'enablecolumns' => array(
-                                'disabled' => 'hidden',
-                                'starttime' => 'startdate',
-                                'endtime' => 'enddate',
-                            ),
-                            'languageField' => 'sys_language_uid',
-                            'transOrigPointerField' => 'l18n_parent',
-                        ),
-                        'columns' => array(
-                        )
-                    ),
-                ),
-                'tt_content',
-                array(
-                    'uidInList' => 42,
-                    'pidInList' => 43,
-                    'where' => 'tt_content.cruser_id=5',
-                    'groupBy' => 'tt_content.title',
-                    'orderBy' => 'tt_content.sorting',
-                ),
-                'WHERE tt_content.uid=42 AND tt_content.pid IN (43) AND tt_content.cruser_id=5 AND (tt_content.sys_language_uid = 13) AND tt_content.deleted=0 AND tt_content.hidden=0 AND tt_content.startdate<=4242 AND (tt_content.enddate=0 OR tt_content.enddate>4242) GROUP BY tt_content.title ORDER BY tt_content.sorting',
-            ),
-            array(
-                array(
-                    'tt_content' => array(
-                        'ctrl' => array(
-                            'languageField' => 'sys_language_uid',
-                            'transOrigPointerField' => 'l18n_parent',
-                        ),
-                        'columns' => array(
-                        )
-                    ),
-                ),
-                'tt_content',
-                array(
-                    'uidInList' => 42,
-                    'pidInList' => 43,
-                    'where' => 'tt_content.cruser_id=5',
-                    'languageField' => 0,
-                ),
-                'WHERE tt_content.uid=42 AND tt_content.pid IN (43) AND tt_content.cruser_id=5',
-            ),
-        );
-    }
-
-    /**
-     * @test
-     * @param array $tca
-     * @param string $table
-     * @param array $configuration
-     * @param string $expectedResult
-     * @dataProvider getWhereReturnCorrectQueryDataProvider
-     */
-    public function getWhereReturnCorrectQuery($tca, $table, $configuration, $expectedResult)
-    {
-        $GLOBALS['TCA'] = $tca;
-        $GLOBALS['SIM_ACCESS_TIME'] = '4242';
-        $GLOBALS['TSFE']->sys_language_content = 13;
-        /** @var \PHPUnit_Framework_MockObject_MockObject|ContentObjectRenderer $contentObjectRenderer */
-        $contentObjectRenderer = $this->getMockBuilder(ContentObjectRenderer::class)
-            ->setMethods(array('checkPidArray'))
-            ->getMock();
-        $contentObjectRenderer->expects($this->any())->method('checkPidArray')->willReturn(explode(',', $configuration['pidInList']));
-        $this->assertEquals($expectedResult, $contentObjectRenderer->getWhere($table, $configuration));
-    }
-
     ////////////////////////////////////
     // Test concerning link generation
     ////////////////////////////////////
diff --git a/typo3/sysext/frontend/Tests/Unit/Page/PageRepositoryTest.php b/typo3/sysext/frontend/Tests/Unit/Page/PageRepositoryTest.php
index dc90c59c1517..83e0ef00b7fd 100644
--- a/typo3/sysext/frontend/Tests/Unit/Page/PageRepositoryTest.php
+++ b/typo3/sysext/frontend/Tests/Unit/Page/PageRepositoryTest.php
@@ -13,7 +13,6 @@ namespace TYPO3\CMS\Frontend\Tests\Unit\Page;
  *
  * The TYPO3 project - inspiring people to share!
  */
-use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /**
  * Test case
@@ -56,41 +55,6 @@ class PageRepositoryTest extends \TYPO3\CMS\Core\Tests\UnitTestCase
         $this->pageSelectObject->expects($this->any())->method('getMultipleGroupsWhereClause')->will($this->returnValue(' AND 1=1'));
     }
 
-    /**
-     * Tests whether the getPage Hook is called correctly.
-     *
-     * @test
-     */
-    public function isGetPageHookCalled()
-    {
-        // Create a hook mock object
-        $className = $this->getUniqueId('tx_coretest');
-        $getPageHookMock = $this->getMockBuilder(\TYPO3\CMS\Frontend\Page\PageRepositoryGetPageHookInterface::class)
-            ->setMethods(array('getPage_preProcess'))
-            ->setMockClassName($className)
-            ->getMock();
-        // Register hook mock object
-        GeneralUtility::addInstance($className, $getPageHookMock);
-        $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['getPage'][] = $className;
-        // Test if hook is called and register a callback method to check given arguments
-        $getPageHookMock->expects($this->once())->method('getPage_preProcess')->will($this->returnCallback(array($this, 'isGetPagePreProcessCalledCallback')));
-        $this->pageSelectObject->getPage(42, false);
-    }
-
-    /**
-     * Handles the arguments that have been sent to the getPage_preProcess hook
-     *
-     * @param int $uid
-     * @param $disableGroupAccessCheck
-     * @param \TYPO3\CMS\Frontend\Page\PageRepository $parent
-     */
-    public function isGetPagePreProcessCalledCallback($uid, $disableGroupAccessCheck, $parent)
-    {
-        $this->assertEquals(42, $uid);
-        $this->assertFalse($disableGroupAccessCheck);
-        $this->assertTrue($parent instanceof \TYPO3\CMS\Frontend\Page\PageRepository);
-    }
-
     /////////////////////////////////////////
     // Tests concerning getPathFromRootline
     /////////////////////////////////////////
@@ -200,241 +164,4 @@ class PageRepositoryTest extends \TYPO3\CMS\Core\Tests\UnitTestCase
             array('prefixLangTitle',       'fake_table', '',       true,  'prefixLangTitle is merged with empty string'),
         );
     }
-
-    ////////////////////////////////
-    // Tests concerning workspaces
-    ////////////////////////////////
-
-    /**
-     * @test
-     */
-    public function noPagesFromWorkspaceAreShownLive()
-    {
-        // initialization
-        $wsid = 987654321;
-        $GLOBALS['TCA'] = array(
-            'pages' => $this->defaultTcaForPages
-        );
-
-        // simulate calls from \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController->fetch_the_id()
-        $this->pageSelectObject->versioningPreview = false;
-        $this->pageSelectObject->versioningWorkspaceId = $wsid;
-        $this->pageSelectObject->init(false);
-
-        // check SQL created by \TYPO3\CMS\Frontend\Page\PageRepository->getPage()
-        $GLOBALS['TYPO3_DB']->expects($this->once())
-            ->method('exec_SELECTgetSingleRow')
-            ->with(
-            '*',
-            'pages',
-            $this->logicalAnd(
-                $this->logicalNot(
-                    $this->stringContains('(pages.t3ver_wsid=0 or pages.t3ver_wsid=' . $wsid . ')')
-                ),
-                $this->stringContains('AND pages.t3ver_state<=0')
-            )
-        );
-
-        $this->pageSelectObject->getPage(1);
-    }
-
-    /**
-     * @test
-     */
-    public function previewShowsPagesFromLiveAndCurrentWorkspace()
-    {
-        // initialization
-        $wsid = 987654321;
-        $GLOBALS['TCA'] = array(
-            'pages' => $this->defaultTcaForPages
-        );
-
-        // simulate calls from \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController->fetch_the_id()
-        $this->pageSelectObject->versioningPreview = true;
-        $this->pageSelectObject->versioningWorkspaceId = $wsid;
-        $this->pageSelectObject->init(false);
-
-        // check SQL created by \TYPO3\CMS\Frontend\Page\PageRepository->getPage()
-        $GLOBALS['TYPO3_DB']->expects($this->once())
-            ->method('exec_SELECTgetSingleRow')
-            ->with(
-            '*',
-            'pages',
-            $this->stringContains('(pages.t3ver_wsid=0 or pages.t3ver_wsid=' . $wsid . ')')
-        );
-
-        $this->pageSelectObject->getPage(1);
-    }
-
-    /**
-     * @test
-     */
-    public function getWorkspaceVersionReturnsTheCorrectMethod()
-    {
-        // initialization
-        $wsid = 987654321;
-        $GLOBALS['TCA'] = array(
-            'pages' => $this->defaultTcaForPages
-        );
-        $GLOBALS['SIM_ACCESS_TIME'] = 123;
-
-        // simulate calls from \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController->fetch_the_id()
-        $this->pageSelectObject->versioningPreview = true;
-        $this->pageSelectObject->versioningWorkspaceId = $wsid;
-        $this->pageSelectObject->init(false);
-
-        $GLOBALS['TYPO3_DB']->expects($this->at(0))
-            ->method('exec_SELECTgetSingleRow')
-            ->with(
-            '*',
-            'pages',
-            $this->logicalAnd(
-                $this->stringContains('pid=-1 AND'),
-                $this->stringContains('t3ver_oid=1 AND'),
-                $this->stringContains('t3ver_wsid=' . $wsid . ' AND pages.deleted=0')
-            )
-        )->willReturn(array('uid' => 1));
-        $GLOBALS['TYPO3_DB']->expects($this->at(1))
-            ->method('exec_SELECTgetSingleRow')
-            ->with(
-            'uid',
-            'pages',
-            $this->logicalAnd(
-                $this->stringContains('t3ver_wsid=' . $wsid . ' AND pages.deleted=0 AND pages.hidden=0 AND pages.starttime<=123 AND (pages.endtime=0 OR pages.endtime>123) AND 1=1')
-            )
-        );
-        $this->pageSelectObject->getWorkspaceVersionOfRecord($wsid, 'pages', 1);
-    }
-
-    ////////////////////////////////
-    // Tests concerning versioning
-    ////////////////////////////////
-
-    /**
-     * @test
-     */
-    public function enableFieldsHidesVersionedRecordsAndPlaceholders()
-    {
-        $table = $this->getUniqueId('aTable');
-        $GLOBALS['TCA'] = array(
-            'pages' => $this->defaultTcaForPages,
-            $table => array(
-                'ctrl' => array(
-                    'versioningWS' => true
-                )
-            )
-        );
-
-        $this->pageSelectObject->versioningPreview = false;
-        $this->pageSelectObject->init(false);
-
-        $conditions = $this->pageSelectObject->enableFields($table);
-
-        $this->assertThat($conditions, $this->stringContains(' AND ' . $table . '.t3ver_state<=0'), 'Versioning placeholders');
-        $this->assertThat($conditions, $this->stringContains(' AND ' . $table . '.pid<>-1'), 'Records from page -1');
-    }
-
-    /**
-     * @test
-     */
-    public function enableFieldsDoesNotHidePlaceholdersInPreview()
-    {
-        $table = $this->getUniqueId('aTable');
-        $GLOBALS['TCA'] = array(
-            'pages' => $this->defaultTcaForPages,
-            $table => array(
-                'ctrl' => array(
-                    'versioningWS' => true
-                )
-            )
-        );
-
-        $this->pageSelectObject->versioningPreview = true;
-        $this->pageSelectObject->init(false);
-
-        $conditions = $this->pageSelectObject->enableFields($table);
-
-        $this->assertThat($conditions, $this->logicalNot($this->stringContains(' AND ' . $table . '.t3ver_state<=0')), 'No versioning placeholders');
-        $this->assertThat($conditions, $this->stringContains(' AND ' . $table . '.pid<>-1'), 'Records from page -1');
-    }
-
-    /**
-     * @test
-     */
-    public function enableFieldsDoesFilterToCurrentAndLiveWorkspaceForRecordsInPreview()
-    {
-        $table = $this->getUniqueId('aTable');
-        $GLOBALS['TCA'] = array(
-            'pages' => $this->defaultTcaForPages,
-            $table => array(
-                'ctrl' => array(
-                    'versioningWS' => true
-                )
-            )
-        );
-
-        $this->pageSelectObject->versioningPreview = true;
-        $this->pageSelectObject->versioningWorkspaceId = 2;
-        $this->pageSelectObject->init(false);
-
-        $conditions = $this->pageSelectObject->enableFields($table);
-
-        $this->assertThat($conditions, $this->stringContains(' AND (' . $table . '.t3ver_wsid=0 OR ' . $table . '.t3ver_wsid=2)'), 'No versioning placeholders');
-    }
-
-    /**
-     * @test
-     */
-    public function initSetsPublicPropertyCorrectlyForWorkspacePreview()
-    {
-        $GLOBALS['TCA'] = array(
-            'pages' => $this->defaultTcaForPages,
-        );
-
-        $this->pageSelectObject->versioningPreview = true;
-        $this->pageSelectObject->versioningWorkspaceId = 2;
-        $this->pageSelectObject->init(false);
-
-        $this->assertSame(' AND pages.deleted=0 AND (pages.t3ver_wsid=0 OR pages.t3ver_wsid=2)', $this->pageSelectObject->where_hid_del);
-    }
-
-    /**
-     * @test
-     */
-    public function initSetsPublicPropertyCorrectlyForLive()
-    {
-        $GLOBALS['TCA'] = array(
-            'pages' => $this->defaultTcaForPages,
-        );
-        $GLOBALS['SIM_ACCESS_TIME'] = 123;
-        $this->pageSelectObject->versioningPreview = false;
-        $this->pageSelectObject->versioningWorkspaceId = 0;
-        $this->pageSelectObject->init(false);
-
-        $this->assertSame(' AND pages.deleted=0 AND pages.t3ver_state<=0 AND pages.hidden=0 AND pages.starttime<=123 AND (pages.endtime=0 OR pages.endtime>123)', $this->pageSelectObject->where_hid_del);
-    }
-
-    /**
-     * @test
-     */
-    public function enableFieldsDoesNotHideVersionedRecordsWhenCheckingVersionOverlays()
-    {
-        $table = $this->getUniqueId('aTable');
-        $GLOBALS['TCA'] = array(
-            'pages' => $this->defaultTcaForPages,
-            $table => array(
-                'ctrl' => array(
-                    'versioningWS' => true
-                )
-            )
-        );
-
-        $this->pageSelectObject->versioningPreview = true;
-        $this->pageSelectObject->init(false);
-
-        $conditions = $this->pageSelectObject->enableFields($table, -1, array(), true);
-
-        $this->assertThat($conditions, $this->logicalNot($this->stringContains(' AND ' . $table . '.t3ver_state<=0')), 'No versioning placeholders');
-        $this->assertThat($conditions, $this->logicalNot($this->stringContains(' AND ' . $table . '.pid<>-1')), 'No ecords from page -1');
-    }
 }
-- 
GitLab