‪TYPO3CMS  9.5
WorkspaceService.php
Go to the documentation of this file.
1 <?php
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
29 
34 {
38  protected ‪$versionsOnPageCache = [];
39 
44 
45  const ‪TABLE_WORKSPACE = 'sys_workspace';
46  const ‪SELECT_ALL_WORKSPACES = -98;
47  const ‪LIVE_WORKSPACE_ID = 0;
48 
55  public function ‪getAvailableWorkspaces()
56  {
57  $availableWorkspaces = [];
58  // add default workspaces
59  if (‪$GLOBALS['BE_USER']->checkWorkspace(['uid' => (string)self::LIVE_WORKSPACE_ID])) {
60  $availableWorkspaces[‪self::LIVE_WORKSPACE_ID] = ‪self::getWorkspaceTitle(self::LIVE_WORKSPACE_ID);
61  }
62  // add custom workspaces (selecting all, filtering by BE_USER check):
63  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_workspace');
64  $queryBuilder->getRestrictions()
65  ->add(GeneralUtility::makeInstance(RootLevelRestriction::class));
66 
67  $result = $queryBuilder
68  ->select('uid', 'title', 'adminusers', 'members')
69  ->from('sys_workspace')
70  ->orderBy('title')
71  ->execute();
72 
73  while ($workspace = $result->fetch()) {
74  if (‪$GLOBALS['BE_USER']->checkWorkspace($workspace)) {
75  $availableWorkspaces[$workspace['uid']] = $workspace['title'];
76  }
77  }
78  return $availableWorkspaces;
79  }
80 
86  public function ‪getCurrentWorkspace()
87  {
88  $workspaceId = ‪$GLOBALS['BE_USER']->workspace;
89  $activeId = ‪$GLOBALS['BE_USER']->getSessionData('tx_workspace_activeWorkspace');
90 
91  // Avoid invalid workspace settings
92  if ($activeId !== null && $activeId !== self::SELECT_ALL_WORKSPACES) {
93  $availableWorkspaces = $this->‪getAvailableWorkspaces();
94  if (isset($availableWorkspaces[$activeId])) {
95  $workspaceId = $activeId;
96  }
97  }
98 
99  return $workspaceId;
100  }
101 
109  public static function ‪getWorkspaceTitle($wsId)
110  {
111  $title = false;
112  switch ($wsId) {
114  $title = ‪$GLOBALS['LANG']->sL('LLL:EXT:core/Resources/Private/Language/locallang_misc.xlf:shortcut_onlineWS');
115  break;
116  default:
117  $labelField = ‪$GLOBALS['TCA']['sys_workspace']['ctrl']['label'];
118  $wsRecord = ‪BackendUtility::getRecord('sys_workspace', $wsId, 'uid,' . $labelField);
119  if (is_array($wsRecord)) {
120  $title = $wsRecord[$labelField];
121  }
122  }
123  if ($title === false) {
124  throw new \InvalidArgumentException('No such workspace defined', 1476045469);
125  }
126  return $title;
127  }
128 
138  public function ‪getCmdArrayForPublishWS($wsid, $doSwap, $pageId = 0, $language = null)
139  {
140  $wsid = (int)$wsid;
141  $cmd = [];
142  if ($wsid > 0) {
143  // Define stage to select:
144  $stage = -99;
145  if ($wsid > 0) {
146  $workspaceRec = ‪BackendUtility::getRecord('sys_workspace', $wsid);
147  if ($workspaceRec['publish_access'] & 1) {
149  }
150  }
151  // Select all versions to swap:
152  $versions = $this->‪selectVersionsInWorkspace($wsid, 0, $stage, $pageId ?: -1, 999, 'tables_modify', $language);
153  // Traverse the selection to build CMD array:
154  foreach ($versions as $table => $records) {
155  foreach ($records as $rec) {
156  // Build the cmd Array:
157  $cmd[$table][$rec['t3ver_oid']]['version'] = ['action' => 'swap', 'swapWith' => $rec['uid'], 'swapIntoWS' => $doSwap ? 1 : 0];
158  }
159  }
160  }
161  return $cmd;
162  }
163 
173  public function ‪getCmdArrayForFlushWS($wsid, $flush = true, $pageId = 0, $language = null)
174  {
175  $wsid = (int)$wsid;
176  $cmd = [];
177  if ($wsid > 0) {
178  // Define stage to select:
179  $stage = -99;
180  // Select all versions to swap:
181  $versions = $this->‪selectVersionsInWorkspace($wsid, 0, $stage, $pageId ?: -1, 999, 'tables_modify', $language);
182  // Traverse the selection to build CMD array:
183  foreach ($versions as $table => $records) {
184  foreach ($records as $rec) {
185  // Build the cmd Array:
186  $cmd[$table][$rec['uid']]['version'] = ['action' => $flush ? 'flush' : 'clearWSID'];
187  }
188  }
189  }
190  return $cmd;
191  }
192 
207  public function ‪selectVersionsInWorkspace($wsid, $filter = 0, $stage = -99, $pageId = -1, $recursionLevel = 0, $selectionType = 'tables_select', $language = null)
208  {
209  $wsid = (int)$wsid;
210  $filter = (int)$filter;
211  ‪$output = [];
212  // Contains either nothing or a list with live-uids
213  if ($pageId != -1 && $recursionLevel > 0) {
214  $pageList = $this->‪getTreeUids($pageId, $wsid, $recursionLevel);
215  } elseif ($pageId != -1) {
216  $pageList = $pageId;
217  } else {
218  $pageList = '';
219  // check if person may only see a "virtual" page-root
220  $mountPoints = array_map('intval', ‪$GLOBALS['BE_USER']->returnWebmounts());
221  $mountPoints = array_unique($mountPoints);
222  if (!in_array(0, $mountPoints)) {
223  $tempPageIds = [];
224  foreach ($mountPoints as $mountPoint) {
225  $tempPageIds[] = $this->‪getTreeUids($mountPoint, $wsid, $recursionLevel);
226  }
227  $pageList = implode(',', $tempPageIds);
228  $pageList = implode(',', array_unique(explode(',', $pageList)));
229  }
230  }
231  // Traversing all tables supporting versioning:
232  foreach (‪$GLOBALS['TCA'] as $table => $cfg) {
233  // we do not collect records from tables without permissions on them.
234  if (!‪$GLOBALS['BE_USER']->check($selectionType, $table)) {
235  continue;
236  }
237  if (‪$GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
238  $recs = $this->‪selectAllVersionsFromPages($table, $pageList, $wsid, $filter, $stage, $language);
239  $moveRecs = $this->‪getMoveToPlaceHolderFromPages($table, $pageList, $wsid, $filter, $stage);
240  $recs = array_merge($recs, $moveRecs);
241  $recs = $this->‪filterPermittedElements($recs, $table);
242  if (!empty($recs)) {
243  ‪$output[$table] = $recs;
244  }
245  }
246  }
247  return ‪$output;
248  }
249 
261  protected function ‪selectAllVersionsFromPages($table, $pageList, $wsid, $filter, $stage, $language = null)
262  {
263  // Include root level page as there might be some records with where root level
264  // restriction is ignored (e.g. FAL records)
265  if ($pageList !== '' && ‪BackendUtility::isRootLevelRestrictionIgnored($table)) {
266  $pageList .= ',0';
267  }
268  $isTableLocalizable = ‪BackendUtility::isTableLocalizable($table);
269  $languageParentField = '';
270  // If table is not localizable, but localized reocrds shall
271  // be collected, an empty result array needs to be returned:
272  if ($isTableLocalizable === false && $language > 0) {
273  return [];
274  }
275  if ($isTableLocalizable) {
276  $languageParentField = 'A.' . ‪$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'];
277  }
278 
279  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
280  $queryBuilder->getRestrictions()->removeAll()
281  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
282 
283  ‪$fields = ['A.uid', 'A.pid', 'A.t3ver_oid', 'A.t3ver_stage', 'B.pid', 'B.pid AS wspid', 'B.pid AS livepid'];
284  if ($isTableLocalizable) {
285  ‪$fields[] = $languageParentField;
286  ‪$fields[] = 'A.' . ‪$GLOBALS['TCA'][$table]['ctrl']['languageField'];
287  }
288  // Table A is the offline version and pid=-1 defines offline
289  // Table B (online) must have PID >= 0 to signify being online.
290  $constraints = [
291  $queryBuilder->expr()->eq(
292  'A.pid',
293  $queryBuilder->createNamedParameter(-1, \PDO::PARAM_INT)
294  ),
295  $queryBuilder->expr()->gte(
296  'B.pid',
297  $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
298  ),
299  $queryBuilder->expr()->neq(
300  'A.t3ver_state',
301  $queryBuilder->createNamedParameter(
302  (string)new VersionState(‪VersionState::MOVE_POINTER),
303  \PDO::PARAM_INT
304  )
305  )
306  ];
307 
308  if ($pageList) {
309  $pageIdRestriction = GeneralUtility::intExplode(',', $pageList, true);
310  if ($table === 'pages') {
311  $constraints[] = $queryBuilder->expr()->orX(
312  $queryBuilder->expr()->in(
313  'B.uid',
314  $queryBuilder->createNamedParameter(
315  $pageIdRestriction,
316  Connection::PARAM_INT_ARRAY
317  )
318  ),
319  $queryBuilder->expr()->in(
320  'B.' . ‪$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'],
321  $queryBuilder->createNamedParameter(
322  $pageIdRestriction,
323  Connection::PARAM_INT_ARRAY
324  )
325  )
326  );
327  } else {
328  $constraints[] = $queryBuilder->expr()->in(
329  'B.pid',
330  $queryBuilder->createNamedParameter(
331  $pageIdRestriction,
332  Connection::PARAM_INT_ARRAY
333  )
334  );
335  }
336  }
337 
338  if ($isTableLocalizable && ‪MathUtility::canBeInterpretedAsInteger($language)) {
339  $constraints[] = $queryBuilder->expr()->eq(
340  'A.' . ‪$GLOBALS['TCA'][$table]['ctrl']['languageField'],
341  $queryBuilder->createNamedParameter($language, \PDO::PARAM_INT)
342  );
343  }
344 
345  // For "real" workspace numbers, select by that.
346  // If = -98, select all that are NOT online (zero).
347  // Anything else below -1 will not select on the wsid and therefore select all!
348  if ($wsid > self::SELECT_ALL_WORKSPACES) {
349  $constraints[] = $queryBuilder->expr()->eq(
350  'A.t3ver_wsid',
351  $queryBuilder->createNamedParameter($wsid, \PDO::PARAM_INT)
352  );
353  } elseif ($wsid === self::SELECT_ALL_WORKSPACES) {
354  $constraints[] = $queryBuilder->expr()->neq(
355  'A.t3ver_wsid',
356  $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
357  );
358  }
359 
360  // lifecycle filter:
361  // 1 = select all drafts (never-published),
362  // 2 = select all published one or more times (archive/multiple)
363  if ($filter === 1) {
364  $constraints[] = $queryBuilder->expr()->eq(
365  'A.t3ver_count',
366  $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
367  );
368  } elseif ($filter === 2) {
369  $constraints[] = $queryBuilder->expr()->gt(
370  'A.t3ver_count',
371  $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
372  );
373  }
374 
375  if ((int)$stage !== -99) {
376  $constraints[] = $queryBuilder->expr()->eq(
377  'A.t3ver_stage',
378  $queryBuilder->createNamedParameter($stage, \PDO::PARAM_INT)
379  );
380  }
381 
382  // ... and finally the join between the two tables.
383  $constraints[] = $queryBuilder->expr()->eq('A.t3ver_oid', $queryBuilder->quoteIdentifier('B.uid'));
384 
385  // Select all records from this table in the database from the workspace
386  // This joins the online version with the offline version as tables A and B
387  // Order by UID, mostly to have a sorting in the backend overview module which
388  // doesn't "jump around" when swapping.
389  $rows = $queryBuilder->select(...‪$fields)
390  ->from($table, 'A')
391  ->from($table, 'B')
392  ->where(...$constraints)
393  ->orderBy('B.uid')
394  ->execute()
395  ->fetchAll();
396 
397  return $rows;
398  }
399 
410  protected function ‪getMoveToPlaceHolderFromPages($table, $pageList, $wsid, $filter, $stage)
411  {
412  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
413  $queryBuilder->getRestrictions()->removeAll()
414  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
415 
416  // Aliases:
417  // A - moveTo placeholder
418  // B - online record
419  // C - moveFrom placeholder
420  $constraints = [
421  $queryBuilder->expr()->eq(
422  'A.t3ver_state',
423  $queryBuilder->createNamedParameter(
424  (string)new VersionState(‪VersionState::MOVE_PLACEHOLDER),
425  \PDO::PARAM_INT
426  )
427  ),
428  $queryBuilder->expr()->gt(
429  'B.pid',
430  $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
431  ),
432  $queryBuilder->expr()->eq(
433  'B.t3ver_state',
434  $queryBuilder->createNamedParameter(
435  (string)new VersionState(‪VersionState::DEFAULT_STATE),
436  \PDO::PARAM_INT
437  )
438  ),
439  $queryBuilder->expr()->eq(
440  'B.t3ver_wsid',
441  $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
442  ),
443  $queryBuilder->expr()->eq(
444  'C.pid',
445  $queryBuilder->createNamedParameter(-1, \PDO::PARAM_INT)
446  ),
447  $queryBuilder->expr()->eq(
448  'C.t3ver_state',
449  $queryBuilder->createNamedParameter(
450  (string)new VersionState(‪VersionState::MOVE_POINTER),
451  \PDO::PARAM_INT
452  )
453  ),
454  $queryBuilder->expr()->eq('A.t3ver_move_id', $queryBuilder->quoteIdentifier('B.uid')),
455  $queryBuilder->expr()->eq('B.uid', $queryBuilder->quoteIdentifier('C.t3ver_oid'))
456  ];
457 
458  if ($wsid > self::SELECT_ALL_WORKSPACES) {
459  $constraints[] = $queryBuilder->expr()->eq(
460  'A.t3ver_wsid',
461  $queryBuilder->createNamedParameter($wsid, \PDO::PARAM_INT)
462  );
463  $constraints[] = $queryBuilder->expr()->eq(
464  'C.t3ver_wsid',
465  $queryBuilder->createNamedParameter($wsid, \PDO::PARAM_INT)
466  );
467  } elseif ($wsid === self::SELECT_ALL_WORKSPACES) {
468  $constraints[] = $queryBuilder->expr()->neq(
469  'A.t3ver_wsid',
470  $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
471  );
472  $constraints[] = $queryBuilder->expr()->neq(
473  'C.t3ver_wsid',
474  $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
475  );
476  }
477 
478  // lifecycle filter:
479  // 1 = select all drafts (never-published),
480  // 2 = select all published one or more times (archive/multiple)
481  if ($filter === 1) {
482  $constraints[] = $queryBuilder->expr()->eq(
483  'C.t3ver_count',
484  $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
485  );
486  } elseif ($filter === 2) {
487  $constraints[] = $queryBuilder->expr()->gt(
488  'C.t3ver_count',
489  $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
490  );
491  }
492 
493  if ((int)$stage != -99) {
494  $constraints[] = $queryBuilder->expr()->eq(
495  'C.t3ver_stage',
496  $queryBuilder->createNamedParameter($stage, \PDO::PARAM_INT)
497  );
498  }
499 
500  if ($pageList) {
501  $pageIdRestriction = GeneralUtility::intExplode(',', $pageList, true);
502  if ($table === 'pages') {
503  $constraints[] = $queryBuilder->expr()->orX(
504  $queryBuilder->expr()->in(
505  'B.uid',
506  $queryBuilder->createNamedParameter(
507  $pageIdRestriction,
508  Connection::PARAM_INT_ARRAY
509  )
510  ),
511  $queryBuilder->expr()->in(
512  'B.' . ‪$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'],
513  $queryBuilder->createNamedParameter(
514  $pageIdRestriction,
515  Connection::PARAM_INT_ARRAY
516  )
517  )
518  );
519  } else {
520  $constraints[] = $queryBuilder->expr()->in(
521  'A.pid',
522  $queryBuilder->createNamedParameter(
523  $pageIdRestriction,
524  Connection::PARAM_INT_ARRAY
525  )
526  );
527  }
528  }
529 
530  $rows = $queryBuilder
531  ->select('A.pid AS wspid', 'B.uid AS t3ver_oid', 'C.uid AS uid', 'B.pid AS livepid')
532  ->from($table, 'A')
533  ->from($table, 'B')
534  ->from($table, 'C')
535  ->where(...$constraints)
536  ->orderBy('A.uid')
537  ->execute()
538  ->fetchAll();
539 
540  return $rows;
541  }
542 
551  protected function ‪getTreeUids($pageId, $wsid, $recursionLevel)
552  {
553  // Reusing existing functionality with the drawback that
554  // mount points are not covered yet
555  $perms_clause = ‪$GLOBALS['BE_USER']->getPagePermsClause(‪Permission::PAGE_SHOW);
556  $searchObj = GeneralUtility::makeInstance(QueryView::class);
557  if ($pageId > 0) {
558  $pageList = $searchObj->getTreeList($pageId, $recursionLevel, 0, $perms_clause);
559  } else {
560  $mountPoints = ‪$GLOBALS['BE_USER']->uc['pageTree_temporaryMountPoint'];
561  if (!is_array($mountPoints) || empty($mountPoints)) {
562  $mountPoints = array_map('intval', ‪$GLOBALS['BE_USER']->returnWebmounts());
563  $mountPoints = array_unique($mountPoints);
564  }
565  $newList = [];
566  foreach ($mountPoints as $mountPoint) {
567  $newList[] = $searchObj->getTreeList($mountPoint, $recursionLevel, 0, $perms_clause);
568  }
569  $pageList = implode(',', $newList);
570  }
571  unset($searchObj);
572 
573  if (‪BackendUtility::isTableWorkspaceEnabled('pages') && $pageList) {
574  // Remove the "subbranch" if a page was moved away
575  $pageIds = GeneralUtility::intExplode(',', $pageList, true);
576  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
577  $queryBuilder->getRestrictions()
578  ->removeAll()
579  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
580  $result = $queryBuilder
581  ->select('uid', 'pid', 't3ver_move_id')
582  ->from('pages')
583  ->where(
584  $queryBuilder->expr()->in(
585  't3ver_move_id',
586  $queryBuilder->createNamedParameter($pageIds, Connection::PARAM_INT_ARRAY)
587  ),
588  $queryBuilder->expr()->eq(
589  't3ver_wsid',
590  $queryBuilder->createNamedParameter($wsid, \PDO::PARAM_INT)
591  )
592  )
593  ->orderBy('uid')
594  ->execute();
595 
596  $movedAwayPages = [];
597  while ($row = $result->fetch()) {
598  $movedAwayPages[$row['t3ver_move_id']] = $row;
599  }
600 
601  // move all pages away
602  $newList = array_diff($pageIds, array_keys($movedAwayPages));
603  // keep current page in the list
604  $newList[] = $pageId;
605  // move back in if still connected to the "remaining" pages
606  do {
607  $changed = false;
608  foreach ($movedAwayPages as $uid => $rec) {
609  if (in_array($rec['pid'], $newList) && !in_array($uid, $newList)) {
610  $newList[] = $uid;
611  $changed = true;
612  }
613  }
614  } while ($changed);
615 
616  // In case moving pages is enabled we need to replace all move-to pointer with their origin
617  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
618  $queryBuilder->getRestrictions()
619  ->removeAll()
620  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
621  $result = $queryBuilder->select('uid', 't3ver_move_id')
622  ->from('pages')
623  ->where(
624  $queryBuilder->expr()->in(
625  'uid',
626  $queryBuilder->createNamedParameter($newList, Connection::PARAM_INT_ARRAY)
627  )
628  )
629  ->orderBy('uid')
630  ->execute();
631 
632  $pages = [];
633  while ($row = $result->fetch()) {
634  $pages[$row['uid']] = $row;
635  }
636 
637  $pageIds = $newList;
638  if (!in_array($pageId, $pageIds)) {
639  $pageIds[] = $pageId;
640  }
641 
642  $newList = [];
643  foreach ($pageIds as $pageId) {
644  if ((int)$pages[$pageId]['t3ver_move_id'] > 0) {
645  $newList[] = (int)$pages[$pageId]['t3ver_move_id'];
646  } else {
647  $newList[] = $pageId;
648  }
649  }
650  $pageList = implode(',', $newList);
651  }
652 
653  return $pageList;
654  }
655 
663  protected function ‪filterPermittedElements($recs, $table)
664  {
665  $permittedElements = [];
666  if (is_array($recs)) {
667  foreach ($recs as $rec) {
668  if ($this->‪isPageAccessibleForCurrentUser($table, $rec) && $this->‪isLanguageAccessibleForCurrentUser($table, $rec)) {
669  $permittedElements[] = $rec;
670  }
671  }
672  }
673  return $permittedElements;
674  }
675 
683  protected function ‪isPageAccessibleForCurrentUser($table, array $record)
684  {
685  $pageIdField = $table === 'pages' ? 'uid' : 'wspid';
686  $pageId = isset($record[$pageIdField]) ? (int)$record[$pageIdField] : null;
687  if ($pageId === null) {
688  return false;
689  }
690  if ($pageId === 0 && ‪BackendUtility::isRootLevelRestrictionIgnored($table)) {
691  return true;
692  }
693  $page = ‪BackendUtility::getRecord('pages', $pageId, 'uid,pid,perms_userid,perms_user,perms_groupid,perms_group,perms_everybody');
694 
695  return ‪$GLOBALS['BE_USER']->doesUserHaveAccess($page, 1);
696  }
697 
705  protected function ‪isLanguageAccessibleForCurrentUser($table, array $record)
706  {
708  $languageUid = $record[‪$GLOBALS['TCA'][$table]['ctrl']['languageField']];
709  } else {
710  return true;
711  }
712  return ‪$GLOBALS['BE_USER']->checkLanguageAccess($languageUid);
713  }
714 
722  public static function ‪isNewPage($id, $language = 0)
723  {
724  $isNewPage = false;
725  // If the language is not default, check state of overlay
726  if ($language > 0) {
727  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
728  ->getQueryBuilderForTable('pages');
729  $queryBuilder->getRestrictions()
730  ->removeAll()
731  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
732  $row = $queryBuilder->select('t3ver_state')
733  ->from('pages')
734  ->where(
735  $queryBuilder->expr()->eq(
736  ‪$GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField'],
737  $queryBuilder->createNamedParameter($id, \PDO::PARAM_INT)
738  ),
739  $queryBuilder->expr()->eq(
740  ‪$GLOBALS['TCA']['pages']['ctrl']['languageField'],
741  $queryBuilder->createNamedParameter($language, \PDO::PARAM_INT)
742  ),
743  $queryBuilder->expr()->eq(
744  't3ver_wsid',
745  $queryBuilder->createNamedParameter(‪$GLOBALS['BE_USER']->workspace, \PDO::PARAM_INT)
746  )
747  )
748  ->setMaxResults(1)
749  ->execute()
750  ->fetch();
751 
752  if ($row !== false) {
753  $isNewPage = ‪VersionState::cast($row['t3ver_state'])->equals(‪VersionState::NEW_PLACEHOLDER);
754  }
755  } else {
756  $rec = ‪BackendUtility::getRecord('pages', $id, 't3ver_state');
757  if (is_array($rec)) {
758  $isNewPage = ‪VersionState::cast($rec['t3ver_state'])->equals(‪VersionState::NEW_PLACEHOLDER);
759  }
760  }
761  return $isNewPage;
762  }
763 
771  public function ‪hasPageRecordVersions($workspaceId, $pageId)
772  {
773  if ((int)$workspaceId === 0 || (int)$pageId === 0) {
774  return false;
775  }
776 
777  if (isset($this->versionsOnPageCache[$workspaceId][$pageId])) {
778  return $this->versionsOnPageCache[$workspaceId][$pageId];
779  }
780 
781  $this->versionsOnPageCache[$workspaceId][$pageId] = false;
782 
783  foreach (‪$GLOBALS['TCA'] as $tableName => $tableConfiguration) {
784  if ($tableName === 'pages' || empty($tableConfiguration['ctrl']['versioningWS'])) {
785  continue;
786  }
787 
788  $pages = $this->‪fetchPagesWithVersionsInTable($workspaceId, $tableName);
789  // Early break on first match
790  if (!empty($pages[(string)$pageId])) {
791  $this->versionsOnPageCache[$workspaceId][$pageId] = true;
792  break;
793  }
794  }
795 
796  $parameters = [
797  'workspaceId' => $workspaceId,
798  'pageId' => $pageId,
799  'versionsOnPageCache' => &‪$this->versionsOnPageCache,
800  ];
801  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['TYPO3\\CMS\\Workspaces\\Service\\WorkspaceService']['hasPageRecordVersions'] ?? [] as $hookFunction) {
802  GeneralUtility::callUserFunction($hookFunction, $parameters, $this);
803  }
804 
805  return $this->versionsOnPageCache[$workspaceId][$pageId];
806  }
807 
831  public function ‪getPagesWithVersionsInTable($workspaceId)
832  {
833  foreach (‪$GLOBALS['TCA'] as $tableName => $tableConfiguration) {
834  if ($tableName === 'pages' || empty($tableConfiguration['ctrl']['versioningWS'])) {
835  continue;
836  }
837 
838  $this->‪fetchPagesWithVersionsInTable($workspaceId, $tableName);
839  }
840 
841  return $this->pagesWithVersionsInTable[$workspaceId];
842  }
843 
859  protected function ‪fetchPagesWithVersionsInTable($workspaceId, $tableName)
860  {
861  if ((int)$workspaceId === 0) {
862  return [];
863  }
864 
865  if (!isset($this->pagesWithVersionsInTable[$workspaceId])) {
866  $this->pagesWithVersionsInTable[$workspaceId] = [];
867  }
868 
869  if (!isset($this->pagesWithVersionsInTable[$workspaceId][$tableName])) {
870  $this->pagesWithVersionsInTable[$workspaceId][$tableName] = [];
871 
872  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($tableName);
873  $queryBuilder->getRestrictions()
874  ->removeAll()
875  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
876 
877  $movePointerParameter = $queryBuilder->createNamedParameter(
879  \PDO::PARAM_INT
880  );
881  $workspaceIdParameter = $queryBuilder->createNamedParameter(
882  $workspaceId,
883  \PDO::PARAM_INT
884  );
885  $pageIdParameter = $queryBuilder->createNamedParameter(
886  -1,
887  \PDO::PARAM_INT
888  );
889  // create sub-queries, parameters are available for main query
890  $versionQueryBuilder = $this->‪createQueryBuilderForTable($tableName)
891  ->‪select('A.t3ver_oid')
892  ->‪from($tableName, 'A')
893  ->‪where(
894  $queryBuilder->expr()->eq('A.pid', $pageIdParameter),
895  $queryBuilder->expr()->eq('A.t3ver_wsid', $workspaceIdParameter),
896  $queryBuilder->expr()->neq('A.t3ver_state', $movePointerParameter)
897  );
898  $movePointerQueryBuilder = $this->‪createQueryBuilderForTable($tableName)
899  ->‪select('A.t3ver_oid')
900  ->‪from($tableName, 'A')
901  ->‪where(
902  $queryBuilder->expr()->eq('A.pid', $pageIdParameter),
903  $queryBuilder->expr()->eq('A.t3ver_wsid', $workspaceIdParameter),
904  $queryBuilder->expr()->eq('A.t3ver_state', $movePointerParameter)
905  );
906  $subQuery = '%s IN (%s)';
907  // execute main query
908  $result = $queryBuilder
909  ->‪select('B.pid AS pageId')
910  ->‪from($tableName, 'B')
911  ->‪orWhere(
912  sprintf(
913  $subQuery,
914  $queryBuilder->quoteIdentifier('B.uid'),
915  $versionQueryBuilder->getSQL()
916  ),
917  sprintf(
918  $subQuery,
919  $queryBuilder->quoteIdentifier('B.t3ver_move_id'),
920  $movePointerQueryBuilder->getSQL()
921  )
922  )
923  ->‪groupBy('B.pid')
924  ->‪execute();
925 
926  $pageIds = [];
927  while ($row = $result->fetch()) {
928  $pageIds[$row['pageId']] = true;
929  }
930 
931  $this->pagesWithVersionsInTable[$workspaceId][$tableName] = $pageIds;
932 
933  $parameters = [
934  'workspaceId' => $workspaceId,
935  'tableName' => $tableName,
936  'pagesWithVersionsInTable' => &‪$this->pagesWithVersionsInTable,
937  ];
938  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['TYPO3\\CMS\\Workspaces\\Service\\WorkspaceService']['fetchPagesWithVersionsInTable'] ?? [] as $hookFunction) {
939  GeneralUtility::callUserFunction($hookFunction, $parameters, $this);
940  }
941  }
942 
943  return $this->pagesWithVersionsInTable[$workspaceId][$tableName];
944  }
945 
950  protected function ‪createQueryBuilderForTable(string $tableName)
951  {
952  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
953  ->getQueryBuilderForTable($tableName);
954  $queryBuilder->‪getRestrictions()
955  ->‪removeAll()
956  ->‪add(GeneralUtility::makeInstance(DeletedRestriction::class));
957  return $queryBuilder;
958  }
959 }
‪TYPO3\CMS\Workspaces\Service\WorkspaceService\selectVersionsInWorkspace
‪array selectVersionsInWorkspace($wsid, $filter=0, $stage=-99, $pageId=-1, $recursionLevel=0, $selectionType='tables_select', $language=null)
Definition: WorkspaceService.php:205
‪TYPO3\CMS\Workspaces\Service\WorkspaceService\hasPageRecordVersions
‪bool hasPageRecordVersions($workspaceId, $pageId)
Definition: WorkspaceService.php:769
‪TYPO3\CMS\Core\Database\Query\QueryBuilder\groupBy
‪QueryBuilder groupBy(... $groupBy)
Definition: QueryBuilder.php:679
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger($var)
Definition: MathUtility.php:73
‪TYPO3\CMS\Workspaces\Service\WorkspaceService\isLanguageAccessibleForCurrentUser
‪bool isLanguageAccessibleForCurrentUser($table, array $record)
Definition: WorkspaceService.php:703
‪TYPO3\CMS\Workspaces\Service\WorkspaceService\getTreeUids
‪string getTreeUids($pageId, $wsid, $recursionLevel)
Definition: WorkspaceService.php:549
‪TYPO3\CMS\Core\Database\Query\QueryBuilder\select
‪QueryBuilder select(string ... $selects)
Definition: QueryBuilder.php:390
‪TYPO3\CMS\Core\Versioning\VersionState\NEW_PLACEHOLDER
‪const NEW_PLACEHOLDER
Definition: VersionState.php:46
‪TYPO3\CMS\Workspaces\Service\WorkspaceService\TABLE_WORKSPACE
‪const TABLE_WORKSPACE
Definition: WorkspaceService.php:43
‪TYPO3\CMS\Workspaces\Service
Definition: AdditionalColumnService.php:2
‪TYPO3\CMS\Workspaces\Service\WorkspaceService\getAvailableWorkspaces
‪array getAvailableWorkspaces()
Definition: WorkspaceService.php:53
‪TYPO3\CMS\Workspaces\Service\WorkspaceService\getMoveToPlaceHolderFromPages
‪array getMoveToPlaceHolderFromPages($table, $pageList, $wsid, $filter, $stage)
Definition: WorkspaceService.php:408
‪TYPO3\CMS\Workspaces\Service\WorkspaceService\fetchPagesWithVersionsInTable
‪array fetchPagesWithVersionsInTable($workspaceId, $tableName)
Definition: WorkspaceService.php:857
‪TYPO3\CMS\Core\Database\Query\Restriction\QueryRestrictionContainerInterface\removeAll
‪QueryRestrictionContainerInterface removeAll()
‪TYPO3\CMS\Workspaces\Service\WorkspaceService\$versionsOnPageCache
‪array $versionsOnPageCache
Definition: WorkspaceService.php:37
‪TYPO3\CMS\Workspaces\Service\WorkspaceService\getCurrentWorkspace
‪int getCurrentWorkspace()
Definition: WorkspaceService.php:84
‪TYPO3\CMS\Workspaces\Service\WorkspaceService\getCmdArrayForFlushWS
‪array getCmdArrayForFlushWS($wsid, $flush=true, $pageId=0, $language=null)
Definition: WorkspaceService.php:171
‪TYPO3\CMS\Core\Database\Query\QueryBuilder\getRestrictions
‪QueryRestrictionContainerInterface getRestrictions()
Definition: QueryBuilder.php:89
‪TYPO3\CMS\Workspaces\Service\WorkspaceService\LIVE_WORKSPACE_ID
‪const LIVE_WORKSPACE_ID
Definition: WorkspaceService.php:45
‪TYPO3\CMS\Core\Database\QueryView
Definition: QueryView.php:41
‪TYPO3\CMS\Core\Versioning\VersionState\MOVE_POINTER
‪const MOVE_POINTER
Definition: VersionState.php:72
‪TYPO3\CMS\Backend\Utility\BackendUtility\isRootLevelRestrictionIgnored
‪static bool isRootLevelRestrictionIgnored($table)
Definition: BackendUtility.php:4535
‪$fields
‪$fields
Definition: pages.php:4
‪TYPO3\CMS\Workspaces\Service\WorkspaceService\isPageAccessibleForCurrentUser
‪bool isPageAccessibleForCurrentUser($table, array $record)
Definition: WorkspaceService.php:681
‪TYPO3\CMS\Core\Type\Bitmask\Permission
Definition: Permission.php:23
‪TYPO3\CMS\Workspaces\Service\WorkspaceService\$pagesWithVersionsInTable
‪array $pagesWithVersionsInTable
Definition: WorkspaceService.php:41
‪TYPO3\CMS\Workspaces\Service\StagesService\STAGE_PUBLISH_ID
‪const STAGE_PUBLISH_ID
Definition: StagesService.php:36
‪TYPO3\CMS\Core\Database\Query\QueryBuilder
Definition: QueryBuilder.php:47
‪TYPO3\CMS\Backend\Utility\BackendUtility\isTableWorkspaceEnabled
‪static bool isTableWorkspaceEnabled($table)
Definition: BackendUtility.php:4493
‪TYPO3\CMS\Core\Database\Query\QueryBuilder\from
‪QueryBuilder from(string $from, string $alias=null)
Definition: QueryBuilder.php:506
‪TYPO3\CMS\Core\Type\Enumeration\cast
‪static static cast($value)
Definition: Enumeration.php:182
‪TYPO3\CMS\Workspaces\Service\WorkspaceService\SELECT_ALL_WORKSPACES
‪const SELECT_ALL_WORKSPACES
Definition: WorkspaceService.php:44
‪TYPO3\CMS\Workspaces\Service\WorkspaceService\selectAllVersionsFromPages
‪array selectAllVersionsFromPages($table, $pageList, $wsid, $filter, $stage, $language=null)
Definition: WorkspaceService.php:259
‪TYPO3\CMS\Core\Database\Query\Restriction\RootLevelRestriction
Definition: RootLevelRestriction.php:25
‪TYPO3\CMS\Workspaces\Service\WorkspaceService\getPagesWithVersionsInTable
‪array getPagesWithVersionsInTable($workspaceId)
Definition: WorkspaceService.php:829
‪TYPO3\CMS\Workspaces\Service\WorkspaceService\filterPermittedElements
‪array filterPermittedElements($recs, $table)
Definition: WorkspaceService.php:661
‪TYPO3\CMS\Core\Type\Bitmask\Permission\PAGE_SHOW
‪const PAGE_SHOW
Definition: Permission.php:32
‪TYPO3\CMS\Backend\Utility\BackendUtility
Definition: BackendUtility.php:72
‪TYPO3\CMS\Core\Database\Query\QueryBuilder\execute
‪Doctrine DBAL Driver Statement int execute()
Definition: QueryBuilder.php:178
‪TYPO3\CMS\Workspaces\Service\WorkspaceService
Definition: WorkspaceService.php:34
‪TYPO3\CMS\Backend\Utility\BackendUtility\getRecord
‪static array null getRecord($table, $uid, $fields=' *', $where='', $useDeleteClause=true)
Definition: BackendUtility.php:130
‪TYPO3\CMS\Core\Versioning\VersionState
Definition: VersionState.php:23
‪$output
‪$output
Definition: annotationChecker.php:113
‪TYPO3\CMS\Core\Database\Connection
Definition: Connection.php:31
‪TYPO3\CMS\Workspaces\Service\WorkspaceService\getCmdArrayForPublishWS
‪array getCmdArrayForPublishWS($wsid, $doSwap, $pageId=0, $language=null)
Definition: WorkspaceService.php:136
‪TYPO3\CMS\Workspaces\Service\WorkspaceService\isNewPage
‪static bool isNewPage($id, $language=0)
Definition: WorkspaceService.php:720
‪TYPO3\CMS\Backend\Utility\BackendUtility\isTableLocalizable
‪static bool isTableLocalizable($table)
Definition: BackendUtility.php:616
‪TYPO3\CMS\Core\Versioning\VersionState\DEFAULT_STATE
‪const DEFAULT_STATE
Definition: VersionState.php:38
‪TYPO3\CMS\Core\SingletonInterface
Definition: SingletonInterface.php:22
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction
Definition: DeletedRestriction.php:26
‪TYPO3\CMS\Core\Database\Query\QueryBuilder\orWhere
‪QueryBuilder orWhere(... $where)
Definition: QueryBuilder.php:664
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:21
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:44
‪TYPO3\CMS\Core\Database\Query\Restriction\QueryRestrictionContainerInterface\add
‪QueryRestrictionContainerInterface add(QueryRestrictionInterface $restriction)
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:45
‪TYPO3\CMS\Core\Database\Query\QueryBuilder\where
‪QueryBuilder where(... $predicates)
Definition: QueryBuilder.php:630
‪TYPO3\CMS\Workspaces\Service\WorkspaceService\getWorkspaceTitle
‪static string getWorkspaceTitle($wsId)
Definition: WorkspaceService.php:107
‪TYPO3\CMS\Core\Versioning\VersionState\MOVE_PLACEHOLDER
‪const MOVE_PLACEHOLDER
Definition: VersionState.php:71
‪TYPO3\CMS\Workspaces\Service\WorkspaceService\createQueryBuilderForTable
‪QueryBuilder createQueryBuilderForTable(string $tableName)
Definition: WorkspaceService.php:948