[TASK] Doctrine: Migrate ext:workspace 00/48600/4
authorEugene Kenah Djomo <ekd@starfinanz.de>
Sat, 18 Jun 2016 13:20:21 +0000 (15:20 +0200)
committerJan Helke <typo3@helke.de>
Sun, 10 Jul 2016 09:49:03 +0000 (11:49 +0200)
Resolves: #76626
Releases: master
Change-Id: I2d5183bdf56411300eb5b68c842b8f043a51570c
Reviewed-on: https://review.typo3.org/48600
Reviewed-by: Morton Jonuschat <m.jonuschat@mojocode.de>
Tested-by: Morton Jonuschat <m.jonuschat@mojocode.de>
Tested-by: Bamboo TYPO3com <info@typo3.com>
Reviewed-by: Joerg Boesche <typo3@joergboesche.de>
Reviewed-by: Jan Helke <typo3@helke.de>
Tested-by: Jan Helke <typo3@helke.de>
typo3/sysext/workspaces/Classes/Domain/Record/WorkspaceRecord.php
typo3/sysext/workspaces/Classes/ExtDirect/ExtDirectServer.php
typo3/sysext/workspaces/Classes/Hook/DataHandlerHook.php
typo3/sysext/workspaces/Classes/Service/AutoPublishService.php
typo3/sysext/workspaces/Classes/Service/RecordService.php
typo3/sysext/workspaces/Classes/Service/StagesService.php
typo3/sysext/workspaces/Classes/Service/WorkspaceService.php
typo3/sysext/workspaces/Classes/Task/CleanupPreviewLinkTask.php

index aa66a9d..dd65964 100644 (file)
@@ -13,6 +13,8 @@ namespace TYPO3\CMS\Workspaces\Domain\Record;
  *
  * The TYPO3 project - inspiring people to share!
  */
+use TYPO3\CMS\Core\Database\ConnectionPool;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Workspaces\Service\StagesService;
 
 /**
@@ -108,16 +110,21 @@ class WorkspaceRecord extends AbstractRecord
             $this->stages = array();
             $this->addStage($this->createInternalStage(StagesService::STAGE_EDIT_ID));
 
-            $records = self::getDatabaseConnection()->exec_SELECTgetRows(
-                '*', 'sys_workspace_stage',
-                'deleted=0 AND parentid=' . $this->getUid() . ' AND parenttable='
-                    . self::getDatabaseConnection()->fullQuoteStr('sys_workspace', 'sys_workspace_stage'),
-                '', 'sorting'
-            );
-            if (!empty($records)) {
-                foreach ($records as $record) {
-                    $this->addStage(StageRecord::build($this, $record['uid'], $record));
-                }
+            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+                ->getQueryBuilderForTable('sys_workspace_stage');
+
+            $result = $queryBuilder
+                ->select('*')
+                ->from('sys_workspace_stage')
+                ->where(
+                    $queryBuilder->expr()->eq('parentid', $this->getUid()),
+                    $queryBuilder->expr()->eq('parenttable', $queryBuilder->quote('sys_workspace'))
+                )
+                ->orderBy('sorting')
+                ->execute();
+
+            while ($record = $result->fetch()) {
+                $this->addStage(StageRecord::build($this, $record['uid'], $record));
             }
 
             $this->addStage($this->createInternalStage(StagesService::STAGE_PUBLISH_ID));
index dde139e..dec2b2d 100644 (file)
@@ -17,6 +17,7 @@ namespace TYPO3\CMS\Workspaces\ExtDirect;
 use TYPO3\CMS\Backend\Backend\Avatar\Avatar;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
+use TYPO3\CMS\Core\Database\ConnectionPool;
 use TYPO3\CMS\Core\Database\DatabaseConnection;
 use TYPO3\CMS\Core\Html\RteHtmlParser;
 use TYPO3\CMS\Core\Imaging\Icon;
@@ -375,19 +376,24 @@ class ExtDirectServer extends AbstractHandler
     public function getCommentsForRecord($uid, $table)
     {
         $sysLogReturnArray = array();
-        $sysLogRows = $this->getDatabaseConnection()->exec_SELECTgetRows(
-            'log_data,tstamp,userid',
-            'sys_log',
-            'action=6 and details_nr=30 AND tablename=' . $this->getDatabaseConnection()->fullQuoteStr($table, 'sys_log')
-                . ' AND recuid=' . (int)$uid,
-            '',
-            'tstamp DESC'
-        );
+        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_log');
+
+        $result = $queryBuilder
+            ->select('log_data', 'tstamp,userid')
+            ->from('sys_log')
+            ->where(
+                $queryBuilder->expr()->eq('action', 6),
+                $queryBuilder->expr()->eq('details_nr', 30),
+                $queryBuilder->expr()->eq('tablename', $queryBuilder->createNamedParameter($table)),
+                $queryBuilder->expr()->eq('recuid', (int)$uid)
+            )
+            ->orderBy('tstamp', 'DESC')
+            ->execute();
 
         /** @var Avatar $avatar */
         $avatar = GeneralUtility::makeInstance(Avatar::class);
 
-        foreach ($sysLogRows as $sysLogRow) {
+        while ($sysLogRow = $result->fetch()) {
             $sysLogEntry = array();
             $data = unserialize($sysLogRow['log_data']);
             $beUserRecord = BackendUtility::getRecord('be_users', $sysLogRow['userid']);
index 943e05d..6cfa1d0 100644 (file)
@@ -15,6 +15,11 @@ namespace TYPO3\CMS\Workspaces\Hook;
  */
 
 use TYPO3\CMS\Backend\Utility\BackendUtility;
+use TYPO3\CMS\Core\Database\ConnectionPool;
+use TYPO3\CMS\Core\Database\Query\Restriction\BackendWorkspaceRestriction;
+use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Workspaces\Service\StagesService;
 
 /**
  * Tcemain service
@@ -36,7 +41,7 @@ class DataHandlerHook
     public function processCmdmap_postProcess($command, $table, $id, $value, \TYPO3\CMS\Core\DataHandling\DataHandler $tcemain)
     {
         if ($command === 'delete') {
-            if ($table === \TYPO3\CMS\Workspaces\Service\StagesService::TABLE_STAGE) {
+            if ($table === StagesService::TABLE_STAGE) {
                 $this->resetStageOfElements($id);
             } elseif ($table === \TYPO3\CMS\Workspaces\Service\WorkspaceService::TABLE_WORKSPACE) {
                 $this->flushWorkspaceElements($id);
@@ -66,13 +71,20 @@ class DataHandlerHook
      */
     protected function resetStageOfElements($stageId)
     {
-        $fields = array('t3ver_stage' => \TYPO3\CMS\Workspaces\Service\StagesService::STAGE_EDIT_ID);
         foreach ($this->getTcaTables() as $tcaTable) {
             if (BackendUtility::isTableWorkspaceEnabled($tcaTable)) {
-                $where = 't3ver_stage = ' . (int)$stageId;
-                $where .= ' AND t3ver_wsid > 0 AND pid=-1';
-                $where .= BackendUtility::deleteClause($tcaTable);
-                $GLOBALS['TYPO3_DB']->exec_UPDATEquery($tcaTable, $where, $fields);
+                $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+                    ->getQueryBuilderForTable($tcaTable);
+
+                $queryBuilder
+                    ->update($tcaTable)
+                    ->set('t3ver_stage', StagesService::STAGE_EDIT_ID)
+                    ->where(
+                        $queryBuilder->expr()->eq('t3ver_stage', (int)$stageId),
+                        $queryBuilder->expr()->eq('pid', -1),
+                        $queryBuilder->expr()->gt('t3ver_wsid', 0)
+                    )
+                    ->execute();
             }
         }
     }
@@ -88,14 +100,21 @@ class DataHandlerHook
         $command = array();
         foreach ($this->getTcaTables() as $tcaTable) {
             if (BackendUtility::isTableWorkspaceEnabled($tcaTable)) {
-                $where = '1=1';
-                $where .= BackendUtility::getWorkspaceWhereClause($tcaTable, $workspaceId);
-                $where .= BackendUtility::deleteClause($tcaTable);
-                $records = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid', $tcaTable, $where, '', '', '', 'uid');
-                if (is_array($records)) {
-                    foreach ($records as $recordId => $_) {
-                        $command[$tcaTable][$recordId]['version']['action'] = 'flush';
-                    }
+                $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+                    ->getQueryBuilderForTable($tcaTable);
+                $queryBuilder->getRestrictions()
+                    ->removeAll()
+                    ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
+                    ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
+
+                $result = $queryBuilder
+                    ->select('uid')
+                    ->from($tcaTable)
+                    ->orderBy('uid')
+                    ->execute();
+
+                while (($recordId = $result->fetchColumn()) !== false) {
+                    $command[$tcaTable][$recordId]['version']['action'] = 'flush';
                 }
             }
         }
index 28e0124..b0e3f53 100644 (file)
@@ -14,6 +14,10 @@ namespace TYPO3\CMS\Workspaces\Service;
  * The TYPO3 project - inspiring people to share!
  */
 
+use TYPO3\CMS\Core\Database\ConnectionPool;
+use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+
 /**
  * Automatic publishing of workspaces.
  */
@@ -33,25 +37,52 @@ class AutoPublishService
         // @todo once workspaces are cleaned up a better solution should be implemented
         $currentAdminStatus = $GLOBALS['BE_USER']->user['admin'];
         $GLOBALS['BE_USER']->user['admin'] = 1;
+
         // Select all workspaces that needs to be published / unpublished:
-        $workspaces = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
-            'uid,swap_modes,publish_time,unpublish_time',
-            'sys_workspace',
-            'pid=0
-                               AND
-                               ((publish_time!=0 AND publish_time<=' . (int)$GLOBALS['EXEC_TIME'] . ')
-                               OR (publish_time=0 AND unpublish_time!=0 AND unpublish_time<=' . (int)$GLOBALS['EXEC_TIME'] . '))' . \TYPO3\CMS\Backend\Utility\BackendUtility::deleteClause('sys_workspace')
-        );
-        $workspaceService = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Workspaces\Service\WorkspaceService::class);
-        foreach ($workspaces as $rec) {
+        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_workspace');
+        $queryBuilder->getRestrictions()
+            ->removeAll()
+            ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
+
+        $result = $queryBuilder
+            ->select('uid', 'swap_modes', 'publish_time', 'unpublish_time')
+            ->from('sys_workspace')
+            ->where(
+                $queryBuilder->expr()->eq('pid', 0),
+                $queryBuilder->orWhere(
+                    $queryBuilder->andWhere(
+                        $queryBuilder->expr()->neq('publish_time', 0),
+                        $queryBuilder->expr()->lte('publish_time', (int)$GLOBALS['EXEC_TIME'])
+                    ),
+                    $queryBuilder->andWhere(
+                        $queryBuilder->expr()->eq('publish_time', 0),
+                        $queryBuilder->expr()->neq('unpublish_time', 0),
+                        $queryBuilder->expr()->lte('unpublish_time', (int)$GLOBALS['EXEC_TIME'])
+                    )
+                )
+            )
+            ->execute();
+
+        $workspaceService = GeneralUtility::makeInstance(\TYPO3\CMS\Workspaces\Service\WorkspaceService::class);
+        while ($rec = $result->fetch()) {
             // First, clear start/end time so it doesn't get select once again:
-            $fieldArray = $rec['publish_time'] != 0 ? array('publish_time' => 0) : array('unpublish_time' => 0);
-            $GLOBALS['TYPO3_DB']->exec_UPDATEquery('sys_workspace', 'uid=' . (int)$rec['uid'], $fieldArray);
+            $fieldArray = $rec['publish_time'] != 0
+                ? ['publish_time' => 0]
+                : ['unpublish_time' => 0];
+
+            GeneralUtility::makeInstance(ConnectionPool::class)
+                ->getConnectionForTable('sys_workspace')
+                ->update(
+                    'sys_workspace',
+                    $fieldArray,
+                    ['uid' => (int)$rec['uid']]
+                );
+
             // Get CMD array:
             $cmd = $workspaceService->getCmdArrayForPublishWS($rec['uid'], $rec['swap_modes'] == 1);
             // $rec['swap_modes']==1 means that auto-publishing will swap versions, not just publish and empty the workspace.
             // Execute CMD array:
-            $tce = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\DataHandling\DataHandler::class);
+            $tce = GeneralUtility::makeInstance(\TYPO3\CMS\Core\DataHandling\DataHandler::class);
             $tce->start(array(), $cmd);
             $tce->process_cmdmap();
         }
index 5bccecf..c3f820c 100644 (file)
@@ -13,6 +13,8 @@ namespace TYPO3\CMS\Workspaces\Service;
  *
  * The TYPO3 project - inspiring people to share!
  */
+use TYPO3\CMS\Core\Database\ConnectionPool;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Workspaces\Domain\Model\DatabaseRecord;
 
 /**
@@ -57,31 +59,30 @@ class RecordService implements \TYPO3\CMS\Core\SingletonInterface
      */
     public function getCreateUserIds()
     {
-        $createUserIds = array();
+        $createUserIds = [];
         foreach ($this->getIdsPerTable() as $tableName => $ids) {
             if (empty($GLOBALS['TCA'][$tableName]['ctrl']['cruser_id'])) {
                 continue;
             }
             $createUserIdFieldName = $GLOBALS['TCA'][$tableName]['ctrl']['cruser_id'];
-            $records = $this->getDatabaseConnection()->exec_SELECTgetRows(
-                $createUserIdFieldName, $tableName,
-                'uid IN (' . implode(',', $ids) . ')',
-                $createUserIdFieldName,
-                '', '',
-                $createUserIdFieldName
-            );
+
+            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($tableName);
+            $queryBuilder->getRestrictions()->removeAll();
+
+            $records = $queryBuilder
+                ->select($createUserIdFieldName)
+                ->from($tableName)
+                ->where($queryBuilder->expr()->in('uid', $ids))
+                ->groupBy($createUserIdFieldName)
+                ->execute()
+                ->fetchAll();
+
+            $records = array_column($records, $createUserIdFieldName);
+
             if (!empty($records)) {
-                $createUserIds = array_merge($createUserIds, array_keys($records));
+                $createUserIds = array_merge($createUserIds, $records);
             }
         }
         return array_unique($createUserIds);
     }
-
-    /**
-     * @return \TYPO3\CMS\Core\Database\DatabaseConnection
-     */
-    protected function getDatabaseConnection()
-    {
-        return $GLOBALS['TYPO3_DB'];
-    }
 }
index 7c95c51..efdd164 100644 (file)
@@ -15,6 +15,7 @@ namespace TYPO3\CMS\Workspaces\Service;
  */
 
 use TYPO3\CMS\Backend\Utility\BackendUtility;
+use TYPO3\CMS\Core\Database\ConnectionPool;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Core\Utility\MathUtility;
 use TYPO3\CMS\Workspaces\Domain\Record\StageRecord;
@@ -575,13 +576,18 @@ class StagesService implements \TYPO3\CMS\Core\SingletonInterface
      */
     private function fetchGroupsFromDB(array $groups)
     {
-        $whereSQL = 'deleted=0 AND hidden=0 AND pid=0 AND uid IN (' . implode(',', $GLOBALS['TYPO3_DB']->cleanIntArray($groups)) . ') ';
-        $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', 'be_groups', $whereSQL);
         // The userGroups array is filled
-        while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
+        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('be_groups');
+
+        $result = $queryBuilder
+            ->select('*')
+            ->from('be_groups')
+            ->where($queryBuilder->expr()->in('uid', array_map('intval', $groups)))
+            ->execute();
+
+        while ($row = $result->fetch()) {
             $this->userGroups[$row['uid']] = $row;
         }
-        $GLOBALS['TYPO3_DB']->sql_free_result($res);
     }
 
     /**
index ff252eb..0c55db7 100644 (file)
@@ -15,9 +15,12 @@ namespace TYPO3\CMS\Workspaces\Service;
  */
 
 use TYPO3\CMS\Backend\Utility\BackendUtility;
-use TYPO3\CMS\Core\Database\DatabaseConnection;
+use TYPO3\CMS\Core\Database\ConnectionPool;
+use TYPO3\CMS\Core\Database\Query\Restriction;
+use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
 use TYPO3\CMS\Core\SingletonInterface;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Core\Utility\MathUtility;
 use TYPO3\CMS\Core\Versioning\VersionState;
 
 /**
@@ -47,7 +50,7 @@ class WorkspaceService implements SingletonInterface
      * retrieves the available workspaces from the database and checks whether
      * they're available to the current BE user
      *
-     * @return         array   array of worspaces available to the current user
+     * @return array array of worspaces available to the current user
      */
     public function getAvailableWorkspaces()
     {
@@ -57,12 +60,20 @@ class WorkspaceService implements SingletonInterface
             $availableWorkspaces[self::LIVE_WORKSPACE_ID] = self::getWorkspaceTitle(self::LIVE_WORKSPACE_ID);
         }
         // add custom workspaces (selecting all, filtering by BE_USER check):
-        $customWorkspaces = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid, title, adminusers, members', 'sys_workspace', 'pid = 0' . BackendUtility::deleteClause('sys_workspace'), '', 'title');
-        if (!empty($customWorkspaces)) {
-            foreach ($customWorkspaces as $workspace) {
-                if ($GLOBALS['BE_USER']->checkWorkspace($workspace)) {
-                    $availableWorkspaces[$workspace['uid']] = $workspace['title'];
-                }
+
+        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_workspace');
+        $queryBuilder->getRestrictions()
+            ->add(GeneralUtility::makeInstance(Restriction\RootLevelRestriction::class));
+
+        $result = $queryBuilder
+            ->select('uid', 'title', 'adminusers', 'members')
+            ->from('sys_workspace')
+            ->orderBy('title')
+            ->execute();
+
+        while ($workspace = $result->fetch()) {
+            if ($GLOBALS['BE_USER']->checkWorkspace($workspace)) {
+                $availableWorkspaces[$workspace['uid']] = $workspace['title'];
             }
         }
         return $availableWorkspaces;
@@ -254,7 +265,8 @@ class WorkspaceService implements SingletonInterface
      */
     protected function selectAllVersionsFromPages($table, $pageList, $wsid, $filter, $stage, $language = null)
     {
-        // Include root level page as there might be some records with where root level restriction is ignored (e.g. FAL records)
+        // Include root level page as there might be some records with where root level
+        // restriction is ignored (e.g. FAL records)
         if ($pageList !== '' && BackendUtility::isRootLevelRestrictionIgnored($table)) {
             $pageList .= ',0';
         }
@@ -265,51 +277,79 @@ class WorkspaceService implements SingletonInterface
         if ($isTableLocalizable === false && $language > 0) {
             return array();
         } elseif ($isTableLocalizable) {
-            $languageParentField = 'A.' . $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'] . ', ';
+            $languageParentField = 'A.' . $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'];
         }
-        $fields = 'A.uid, A.t3ver_oid, A.t3ver_stage, ' . $languageParentField . 'B.pid AS wspid, B.pid AS livepid';
+
+        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
+        $queryBuilder->getRestrictions()->removeAll()
+            ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
+
+        $fields = ['A.uid', 'A.t3ver_oid', 'A.t3ver_stage', 'B.pid AS wspid', 'B.pid AS livepid'];
         if ($isTableLocalizable) {
-            $fields .= ', A.' . $GLOBALS['TCA'][$table]['ctrl']['languageField'];
+            $fields[] = $languageParentField;
+            $fields[] = 'A.' . $GLOBALS['TCA'][$table]['ctrl']['languageField'];
         }
-        $from = $table . ' A,' . $table . ' B';
         // Table A is the offline version and pid=-1 defines offline
-        $where = 'A.pid=-1 AND A.t3ver_state!=' . new VersionState(VersionState::MOVE_POINTER);
+        // Table B (online) must have PID >= 0 to signify being online.
+        $constraints = [
+            $queryBuilder->expr()->eq('A.pid', -1),
+            $queryBuilder->expr()->gte('B.pid', 0),
+            $queryBuilder->expr()->neq('A.t3ver_state', new VersionState(VersionState::MOVE_POINTER))
+        ];
+
         if ($pageList) {
             $pidField = $table === 'pages' ? 'uid' : 'pid';
-            $pidConstraint = strstr($pageList, ',') ? ' IN (' . $pageList . ')' : '=' . $pageList;
-            $where .= ' AND B.' . $pidField . $pidConstraint;
+            $constraints[] = $queryBuilder->expr()->in(
+                'B.' . $pidField,
+                GeneralUtility::intExplode(',', $pageList, true)
+            );
         }
-        if ($isTableLocalizable && \TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($language)) {
-            $where .= ' AND A.' . $GLOBALS['TCA'][$table]['ctrl']['languageField'] . '=' . $language;
+
+        if ($isTableLocalizable && MathUtility::canBeInterpretedAsInteger($language)) {
+            $constraints[] = $queryBuilder->expr()->eq(
+                'A.' . $GLOBALS['TCA'][$table]['ctrl']['languageField'],
+                (int)$language
+            );
         }
+
         // For "real" workspace numbers, select by that.
         // If = -98, select all that are NOT online (zero).
         // Anything else below -1 will not select on the wsid and therefore select all!
         if ($wsid > self::SELECT_ALL_WORKSPACES) {
-            $where .= ' AND A.t3ver_wsid=' . $wsid;
+            $constraints[] = $queryBuilder->expr()->eq('A.t3ver_wsid', (int)$wsid);
         } elseif ($wsid === self::SELECT_ALL_WORKSPACES) {
-            $where .= ' AND A.t3ver_wsid!=0';
+            $constraints[] = $queryBuilder->expr()->neq('A.t3ver_wsid', 0);
         }
+
         // lifecycle filter:
         // 1 = select all drafts (never-published),
         // 2 = select all published one or more times (archive/multiple)
-        if ($filter === 1 || $filter === 2) {
-            $where .= ' AND A.t3ver_count ' . ($filter === 1 ? '= 0' : '> 0');
+        if ($filter === 1) {
+            $constraints[] = $queryBuilder->expr()->eq('A.t3ver_count', 0);
+        } elseif ($filter === 2) {
+            $constraints[] = $queryBuilder->expr()->gt('A.t3ver_count', 0);
         }
-        if ($stage != -99) {
-            $where .= ' AND A.t3ver_stage=' . (int)$stage;
+
+        if ((int)$stage !== -99) {
+            $constraints[] = $queryBuilder->expr()->eq('A.t3ver_stage', (int)$stage);
         }
-        // Table B (online) must have PID >= 0 to signify being online.
-        $where .= ' AND B.pid>=0';
+
         // ... and finally the join between the two tables.
-        $where .= ' AND A.t3ver_oid=B.uid';
-        $where .= BackendUtility::deleteClause($table, 'A');
-        $where .= BackendUtility::deleteClause($table, 'B');
+        $constraints[] = $queryBuilder->expr()->eq('A.t3ver_oid', $queryBuilder->quoteIdentifier('B.uid'));
+
         // Select all records from this table in the database from the workspace
         // This joins the online version with the offline version as tables A and B
-        // Order by UID, mostly to have a sorting in the backend overview module which doesn't "jump around" when swapping.
-        $res = $this->getDatabaseConnection()->exec_SELECTgetRows($fields, $from, $where, '', 'B.uid');
-        return is_array($res) ? $res : array();
+        // Order by UID, mostly to have a sorting in the backend overview module which
+        // doesn't "jump around" when swapping.
+        $rows = $queryBuilder->select(...$fields)
+            ->from($table, 'A')
+            ->from($table, 'B')
+            ->where(...$constraints)
+            ->orderBy('B.uid')
+            ->execute()
+            ->fetchAll();
+
+        return $rows;
     }
 
     /**
@@ -324,40 +364,62 @@ class WorkspaceService implements SingletonInterface
      */
     protected function getMoveToPlaceHolderFromPages($table, $pageList, $wsid, $filter, $stage)
     {
+        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
+        $queryBuilder->getRestrictions()->removeAll()
+            ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
+
         // Aliases:
         // A - moveTo placeholder
         // B - online record
         // C - moveFrom placeholder
-        $fields = 'A.pid AS wspid, B.uid AS t3ver_oid, C.uid AS uid, B.pid AS livepid';
-        $from = $table . ' A, ' . $table . ' B,' . $table . ' C';
-        $where = 'A.t3ver_state=' . new VersionState(VersionState::MOVE_PLACEHOLDER) . ' AND B.pid>0 AND B.t3ver_state='
-            . new VersionState(VersionState::DEFAULT_STATE) . ' AND B.t3ver_wsid=0 AND C.pid=-1 AND C.t3ver_state='
-            . new VersionState(VersionState::MOVE_POINTER);
+        $constraints = [
+            $queryBuilder->expr()->eq('A.t3ver_state', new VersionState(VersionState::MOVE_PLACEHOLDER)),
+            $queryBuilder->expr()->gt('B.pid', 0),
+            $queryBuilder->expr()->eq('B.t3ver_state', new VersionState(VersionState::DEFAULT_STATE)),
+            $queryBuilder->expr()->eq('B.t3ver_wsid', 0),
+            $queryBuilder->expr()->eq('C.pid', -1),
+            $queryBuilder->expr()->eq('C.t3ver_state', new VersionState(VersionState::MOVE_POINTER)),
+            $queryBuilder->expr()->eq('A.t3ver_move_id', $queryBuilder->quoteIdentifier('B.uid')),
+            $queryBuilder->expr()->eq('B.uid', $queryBuilder->quoteIdentifier('C.t3ver_oid'))
+        ];
+
         if ($wsid > self::SELECT_ALL_WORKSPACES) {
-            $where .= ' AND A.t3ver_wsid=' . $wsid . ' AND C.t3ver_wsid=' . $wsid;
+            $constraints[] = $queryBuilder->expr()->eq('A.t3ver_wsid', (int)$wsid);
+            $constraints[] = $queryBuilder->expr()->eq('C.t3ver_wsid', (int)$wsid);
         } elseif ($wsid === self::SELECT_ALL_WORKSPACES) {
-            $where .= ' AND A.t3ver_wsid!=0 AND C.t3ver_wsid!=0 ';
+            $constraints[] = $queryBuilder->expr()->neq('A.t3ver_wsid', 0);
+            $constraints[] = $queryBuilder->expr()->neq('C.t3ver_wsid', 0);
         }
+
         // lifecycle filter:
         // 1 = select all drafts (never-published),
         // 2 = select all published one or more times (archive/multiple)
-        if ($filter === 1 || $filter === 2) {
-            $where .= ' AND C.t3ver_count ' . ($filter === 1 ? '= 0' : '> 0');
+        if ($filter === 1) {
+            $constraints[] = $queryBuilder->expr()->eq('C.t3ver_count', 0);
+        } elseif ($filter === 2) {
+            $constraints[] = $queryBuilder->expr()->gt('C.t3ver_count', 0);
         }
-        if ($stage != -99) {
-            $where .= ' AND C.t3ver_stage=' . (int)$stage;
+
+        if ((int)$stage != -99) {
+            $constraints[] = $queryBuilder->expr()->eq('C.t3ver_stage', (int)$stage);
         }
+
         if ($pageList) {
             $pidField = $table === 'pages' ? 'B.uid' : 'A.pid';
-            $pidConstraint = strstr($pageList, ',') ? ' IN (' . $pageList . ')' : '=' . $pageList;
-            $where .= ' AND ' . $pidField . $pidConstraint;
-        }
-        $where .= ' AND A.t3ver_move_id = B.uid AND B.uid = C.t3ver_oid';
-        $where .= BackendUtility::deleteClause($table, 'A');
-        $where .= BackendUtility::deleteClause($table, 'B');
-        $where .= BackendUtility::deleteClause($table, 'C');
-        $res = $this->getDatabaseConnection()->exec_SELECTgetRows($fields, $from, $where, '', 'A.uid');
-        return is_array($res) ? $res : array();
+            $constraints[] =  $queryBuilder->expr()->in($pidField, GeneralUtility::intExplode(',', $pageList, true));
+        }
+
+        $rows = $queryBuilder
+            ->select('A.pid AS wspid', 'B.uid AS t3ver_oid', 'C.uid AS uid', 'B.pid AS livepid')
+            ->from($table, 'A')
+            ->from($table, 'B')
+            ->from($table, 'C')
+            ->where(...$constraints)
+            ->orderBy('A.uid')
+            ->execute()
+            ->fetchAll();
+
+        return $rows;
     }
 
     /**
@@ -390,10 +452,29 @@ class WorkspaceService implements SingletonInterface
             $pageList = implode(',', $newList);
         }
         unset($searchObj);
+
         if (BackendUtility::isTableWorkspaceEnabled('pages') && $pageList) {
             // Remove the "subbranch" if a page was moved away
-            $movedAwayPages = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid, pid, t3ver_move_id', 'pages', 't3ver_move_id IN (' . $pageList . ') AND t3ver_wsid=' . (int)$wsid . BackendUtility::deleteClause('pages'), '', 'uid', '', 't3ver_move_id');
             $pageIds = GeneralUtility::intExplode(',', $pageList, true);
+            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
+            $queryBuilder->getRestrictions()
+                ->removeAll()
+                ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
+            $result = $queryBuilder
+                ->select('uid', 'pid', 't3ver_move_id')
+                ->from('pages')
+                ->where(
+                    $queryBuilder->expr()->in('t3ver_move_id', $pageIds),
+                    $queryBuilder->expr()->eq('t3ver_wsid', (int)$wsid)
+                )
+                ->orderBy('uid')
+                ->execute();
+
+            $movedAwayPages = [];
+            while ($row = $result->fetch()) {
+                $movedAwayPages[$row['t3ver_move_id']] = $row;
+            }
+
             // move all pages away
             $newList = array_diff($pageIds, array_keys($movedAwayPages));
             // keep current page in the list
@@ -408,14 +489,29 @@ class WorkspaceService implements SingletonInterface
                     }
                 }
             } while ($changed);
-            $pageList = implode(',', $newList);
+
             // In case moving pages is enabled we need to replace all move-to pointer with their origin
-            $pages = $this->getDatabaseConnection()->exec_SELECTgetRows('uid, t3ver_move_id', 'pages', 'uid IN (' . $pageList . ')' . BackendUtility::deleteClause('pages'), '', 'uid', '', 'uid');
-            $newList = array();
-            $pageIds = GeneralUtility::intExplode(',', $pageList, true);
+            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
+            $queryBuilder->getRestrictions()
+                ->removeAll()
+                ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
+            $result = $queryBuilder->select('uid', 't3ver_move_id')
+                ->from('pages')
+                ->where($queryBuilder->expr()->in('uid', $newList))
+                ->orderBy('uid')
+                ->execute();
+
+            $pages = [];
+            while ($row = $result->fetch()) {
+                $pages[$row['uid']] = $row;
+            }
+
+            $pageIds = $newList;
             if (!in_array($pageId, $pageIds)) {
                 $pageIds[] = $pageId;
             }
+
+            $newList = [];
             foreach ($pageIds as $pageId) {
                 if ((int)$pages[$pageId]['t3ver_move_id'] > 0) {
                     $newList[] = (int)$pages[$pageId]['t3ver_move_id'];
@@ -425,6 +521,7 @@ class WorkspaceService implements SingletonInterface
             }
             $pageList = implode(',', $newList);
         }
+
         return $pageList;
     }
 
@@ -498,8 +595,26 @@ class WorkspaceService implements SingletonInterface
         $cacheKey = 'workspace-oldstyleworkspace-notused';
         $cacheResult = $GLOBALS['BE_USER']->getSessionData($cacheKey);
         if (!$cacheResult) {
-            $where = 'adminusers != \'\' AND adminusers NOT LIKE \'%be_users%\' AND adminusers NOT LIKE \'%be_groups%\' AND deleted=0';
-            $count = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows('uid', 'sys_workspace', $where);
+            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+                ->getQueryBuilderForTable('sys_workspace');
+            $queryBuilder->getRestrictions()
+                ->removeAll()
+                ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
+            $count = (int)$queryBuilder->count('uid')
+                ->from('sys_workspace')
+                ->where(
+                    $queryBuilder->expr()->neq('adminusers', $queryBuilder->quote('')),
+                    $queryBuilder->expr()->notLike(
+                        'adminusers',
+                        $queryBuilder->createNamedParameter('%' . $queryBuilder->escapeLikeWildcards('be_users') . '%')
+                    ),
+                    $queryBuilder->expr()->notLike(
+                        'adminusers',
+                        $queryBuilder->createNamedParameter('%' . $queryBuilder->escapeLikeWildcards('be_groups') . '%')
+                    )
+                )
+                ->execute()
+                ->fetchColumn(0);
             $oldStyleWorkspaceIsUsed = $count > 0;
             $GLOBALS['BE_USER']->setAndSaveSessionData($cacheKey, !$oldStyleWorkspaceIsUsed);
         } else {
@@ -520,12 +635,26 @@ class WorkspaceService implements SingletonInterface
         $isNewPage = false;
         // If the language is not default, check state of overlay
         if ($language > 0) {
-            $whereClause = 'pid = ' . (int)$id;
-            $whereClause .= ' AND ' . $GLOBALS['TCA']['pages_language_overlay']['ctrl']['languageField'] . ' = ' . (int)$language;
-            $whereClause .= ' AND t3ver_wsid = ' . (int)$GLOBALS['BE_USER']->workspace;
-            $whereClause .= BackendUtility::deleteClause('pages_language_overlay');
-            $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('t3ver_state', 'pages_language_overlay', $whereClause);
-            if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
+            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+                ->getQueryBuilderForTable('pages_language_overlay');
+            $queryBuilder->getRestrictions()
+                ->removeAll()
+                ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
+            $row = $queryBuilder->select('t3ver_state')
+                ->from('pages_language_overlay')
+                ->where(
+                    $queryBuilder->expr()->eq('pid', (int)$id),
+                    $queryBuilder->expr()->eq(
+                        $GLOBALS['TCA']['pages_language_overlay']['ctrl']['languageField'],
+                        (int)$language
+                    ),
+                    $queryBuilder->expr()->eq('t3ver_wsid', (int)$GLOBALS['BE_USER']->workspace)
+                )
+                ->setMaxResults(1)
+                ->execute()
+                ->fetch();
+
+            if ($row !== false) {
                 $isNewPage = VersionState::cast($row['t3ver_state'])->equals(VersionState::NEW_PLACEHOLDER);
             }
         } else {
@@ -834,17 +963,38 @@ class WorkspaceService implements SingletonInterface
 
             // Consider records that are moved to a different page
             $movePointer = new VersionState(VersionState::MOVE_POINTER);
-            $joinStatement = '(A.t3ver_oid=B.uid AND A.t3ver_state<>' . $movePointer
-                . ' OR A.t3ver_oid=B.t3ver_move_id AND A.t3ver_state=' . $movePointer . ')';
-
-            $pageIds = $this->getDatabaseConnection()->exec_SELECTgetRows(
-                'B.pid AS pageId',
-                $tableName . ' A,' . $tableName . ' B',
-                'A.pid=-1 AND A.t3ver_wsid=' . (int)$workspaceId . ' AND ' . $joinStatement
-                    . BackendUtility::deleteClause($tableName, 'A') . BackendUtility::deleteClause($tableName, 'B'),
-                'pageId', '', '',
-                'pageId'
-            );
+
+            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($tableName);
+            $queryBuilder->getRestrictions()
+                ->removeAll()
+                ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
+
+            $result = $queryBuilder
+                ->select('B.pid AS pageId')
+                ->from($tableName, 'A')
+                ->from($tableName, 'B')
+                ->where(
+                    $queryBuilder->expr()->eq('A.pid', -1),
+                    $queryBuilder->expr()->eq('A.t3ver_wsid', (int)$workspaceId),
+                    $queryBuilder->expr()->orX(
+                        $queryBuilder->expr()->andX(
+                            $queryBuilder->expr()->eq('A.t3ver_oid', $queryBuilder->quoteIdentifier('B.uid')),
+                            $queryBuilder->expr()->neq('A.t3ver_state', $movePointer)
+
+                        ),
+                        $queryBuilder->expr()->andX(
+                            $queryBuilder->expr()->eq('A.t3ver_oid', $queryBuilder->quoteIdentifier('B.t3ver_move_id')),
+                            $queryBuilder->expr()->eq('A.t3ver_state', $movePointer)
+                        )
+                    )
+                )
+                ->groupBy('pageId')
+                ->execute();
+
+            $pageIds = [];
+            while ($row = $result->fetch()) {
+                $pageIds[$row['uid']] = $row;
+            }
 
             $this->pagesWithVersionsInTable[$workspaceId][$tableName] = $pageIds;
 
@@ -865,14 +1015,6 @@ class WorkspaceService implements SingletonInterface
     }
 
     /**
-     * @return DatabaseConnection
-     */
-    protected function getDatabaseConnection()
-    {
-        return $GLOBALS['TYPO3_DB'];
-    }
-
-    /**
      * @return \TYPO3\CMS\Extbase\Object\ObjectManager
      */
     protected function getObjectManager()
index f676cec..4823154 100644 (file)
@@ -13,6 +13,8 @@ namespace TYPO3\CMS\Workspaces\Task;
  *
  * The TYPO3 project - inspiring people to share!
  */
+use TYPO3\CMS\Core\Database\ConnectionPool;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /**
  * This class provides a task to cleanup ol preview links.
@@ -27,7 +29,11 @@ class CleanupPreviewLinkTask extends \TYPO3\CMS\Scheduler\Task\AbstractTask
      */
     public function execute()
     {
-        $GLOBALS['TYPO3_DB']->exec_DELETEquery('sys_preview', 'endtime < ' . (int)$GLOBALS['EXEC_TIME']);
+        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_preview');
+        $queryBuilder
+            ->delete('sys_preview')
+            ->where($queryBuilder->expr()->lt('endtime', (int)$GLOBALS['EXEC_TIME']));
+
         return true;
     }
 }