‪TYPO3CMS  ‪main
PageTreeRepository.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use TYPO3\CMS\Backend\Utility\BackendUtility;
31 
41 {
47  protected readonly array ‪$fields;
48 
54  protected readonly array ‪$quotedFields;
55 
59  protected readonly int ‪$currentWorkspace;
60 
64  protected array ‪$fullPageTree = [];
65 
66  protected readonly array ‪$additionalQueryRestrictions;
67 
68  protected ?string ‪$additionalWhereClause = null;
69 
75  public function ‪__construct(int $workspaceId = 0, array $additionalFieldsToQuery = [], array ‪$additionalQueryRestrictions = [])
76  {
77  $this->currentWorkspace = $workspaceId;
78  $this->fields = array_merge([
79  'uid',
80  'pid',
81  'sorting',
82  'starttime',
83  'endtime',
84  'hidden',
85  'fe_group',
86  'title',
87  'nav_title',
88  'nav_hide',
89  'php_tree_stop',
90  'doktype',
91  'is_siteroot',
92  'module',
93  'extendToSubpages',
94  'content_from_pid',
95  't3ver_oid',
96  't3ver_wsid',
97  't3ver_state',
98  't3ver_stage',
99  'perms_userid',
100  'perms_user',
101  'perms_groupid',
102  'perms_group',
103  'perms_everybody',
104  'mount_pid',
105  'shortcut',
106  'shortcut_mode',
107  'mount_pid_ol',
108  'url',
109  'sys_language_uid',
110  'l10n_parent',
111  ], $additionalFieldsToQuery);
112  $this->additionalQueryRestrictions = ‪$additionalQueryRestrictions;
113 
114  $this->quotedFields = GeneralUtility::makeInstance(ConnectionPool::class)
115  ->getQueryBuilderForTable('pages')
116  ->quoteIdentifiersForSelect($this->fields);
117  }
118 
120  {
121  $this->additionalWhereClause = ‪$additionalWhereClause;
122  }
123 
131  public function ‪getTree(
132  int $entryPoint,
133  callable $callback = null,
134  array $dbMounts = []
135  ): array {
136  $this->‪fetchAllPages($dbMounts);
137  if ($entryPoint === 0) {
138  $tree = ‪$this->fullPageTree;
139  } else {
140  $tree = $this->‪findInPageTree($entryPoint, $this->fullPageTree);
141  }
142  if (!empty($tree) && $callback !== null) {
143  $this->‪applyCallbackToChildren($tree, $callback);
144  }
145  return $tree;
146  }
147 
151  protected function ‪applyCallbackToChildren(array &$tree, callable $callback): void
152  {
153  if (!isset($tree['_children'])) {
154  return;
155  }
156  foreach ($tree['_children'] as $k => &$childPage) {
157  if (!$callback($childPage)) {
158  unset($tree['_children'][$k]);
159  continue;
160  }
161  $this->‪applyCallbackToChildren($childPage, $callback);
162  }
163  }
164 
173  public function ‪getTreeLevels(array $pageTree, int $depth, array $entryPointIds = []): array
174  {
175  $groupedAndSortedPagesByPid = [];
176 
177  if (count($entryPointIds) > 0) {
178  $pageRecords = $this->‪getPageRecords($entryPointIds);
179  $groupedAndSortedPagesByPid[$pageTree['uid']] = $pageRecords;
180  $parentPageIds = $entryPointIds;
181  } else {
182  $parentPageIds = [$pageTree['uid']];
183  }
184 
185  for ($i = 0; $i < $depth; $i++) {
186  if (empty($parentPageIds)) {
187  break;
188  }
189  $pageRecords = $this->‪getChildPageRecords($parentPageIds);
190 
191  $groupedAndSortedPagesByPid = $this->‪groupAndSortPages($pageRecords, $groupedAndSortedPagesByPid);
192 
193  $parentPageIds = array_column($pageRecords, 'uid');
194  }
195  $this->‪addChildrenToPage($pageTree, $groupedAndSortedPagesByPid);
196  return $pageTree;
197  }
198 
205  public function ‪getFlattenedPages(array $entryPointIds, int $depth): array
206  {
207  $allPageRecords = $this->‪getPageRecords($entryPointIds);
208  $parentPageIds = $entryPointIds;
209  for ($i = 0; $i < $depth; $i++) {
210  if (empty($parentPageIds)) {
211  break;
212  }
213  $pageRecords = $this->‪getChildPageRecords($parentPageIds);
214  $parentPageIds = array_column($pageRecords, 'uid');
215  $allPageRecords = array_merge($allPageRecords, $pageRecords);
216  }
217  return $allPageRecords;
218  }
219 
220  protected function ‪getChildPageRecords(array $parentPageIds): array
221  {
222  return $this->‪getPageRecords([], $parentPageIds);
223  }
224 
228  protected function ‪getPageRecords(array $pageIds = [], array $parentPageIds = []): array
229  {
230  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
231  ->getQueryBuilderForTable('pages');
232  $queryBuilder->getRestrictions()
233  ->removeAll()
234  ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
235  ->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, $this->currentWorkspace));
236 
237  if (!empty($this->additionalQueryRestrictions)) {
238  foreach ($this->additionalQueryRestrictions as $additionalQueryRestriction) {
239  $queryBuilder->getRestrictions()->add($additionalQueryRestriction);
240  }
241  }
242 
243  $queryBuilder->getConcreteQueryBuilder()->select(...$this->quotedFields);
244  $queryBuilder
245  ->from('pages')
246  ->where(
247  $queryBuilder->expr()->eq('sys_language_uid', $queryBuilder->createNamedParameter(0, ‪Connection::PARAM_INT))
248  )
249  // ensure deterministic sorting
250  ->orderBy('sorting', 'ASC')
251  ->addOrderBy('uid', 'ASC');
252 
253  if (!empty($this->additionalWhereClause)) {
254  $queryBuilder->andWhere(
255  ‪QueryHelper::stripLogicalOperatorPrefix($this->additionalWhereClause)
256  );
257  }
258 
259  if (count($pageIds) > 0) {
260  $queryBuilder->andWhere(
261  $queryBuilder->expr()->in('uid', $queryBuilder->createNamedParameter($pageIds, ‪Connection::PARAM_INT_ARRAY))
262  );
263  }
264 
265  if (count($parentPageIds) > 0) {
266  $queryBuilder->andWhere(
267  $queryBuilder->expr()->in('pid', $queryBuilder->createNamedParameter($parentPageIds, ‪Connection::PARAM_INT_ARRAY))
268  );
269  }
270 
271  $pageRecords = $queryBuilder
272  ->executeQuery()
273  ->fetchAllAssociative();
274 
275  // This is necessary to resolve all IDs in a workspace
276  if ($this->currentWorkspace !== 0 && !empty($pageRecords)) {
277  $livePageIds = [];
278  $movedPages = [];
279  foreach ($pageRecords as $pageRecord) {
280  $livePageIds[] = (int)$pageRecord['uid'];
281  if (VersionState::tryFrom($pageRecord['t3ver_state'] ?? 0) === VersionState::MOVE_POINTER) {
282  $movedPages[$pageRecord['t3ver_oid']] = [
283  'pid' => (int)$pageRecord['pid'],
284  'sorting' => (int)$pageRecord['sorting'],
285  ];
286  }
287  }
288 
289  // Resolve placeholders of workspace versions
290  $resolver = GeneralUtility::makeInstance(
291  PlainDataResolver::class,
292  'pages',
293  $livePageIds
294  );
295  $resolver->setWorkspaceId($this->currentWorkspace);
296  $resolver->setKeepDeletePlaceholder(false);
297  $resolver->setKeepMovePlaceholder(false);
298  $resolver->setKeepLiveIds(false);
299  $recordIds = $resolver->get();
300 
301  if (!empty($recordIds)) {
302  $queryBuilder->getRestrictions()->removeAll();
303  $queryBuilder->getConcreteQueryBuilder()->select(...$this->quotedFields);
304  $pageRecords = $queryBuilder
305  ->from('pages')
306  ->where(
307  $queryBuilder->expr()->in('uid', $queryBuilder->createNamedParameter($recordIds, ‪Connection::PARAM_INT_ARRAY))
308  )
309  // ensure deterministic sorting
310  ->orderBy('sorting', 'ASC')
311  ->addOrderBy('uid', 'ASC')
312  ->executeQuery()
313  ->fetchAllAssociative();
314 
315  foreach ($pageRecords as &$pageRecord) {
316  if (VersionState::tryFrom($pageRecord['t3ver_state'] ?? 0) === VersionState::MOVE_POINTER && !empty($movedPages[$pageRecord['t3ver_oid']])) {
317  $pageRecord['uid'] = $pageRecord['t3ver_oid'];
318  $pageRecord['sorting'] = (int)$movedPages[$pageRecord['t3ver_oid']]['sorting'];
319  $pageRecord['pid'] = (int)$movedPages[$pageRecord['t3ver_oid']]['pid'];
320  } elseif ((int)$pageRecord['t3ver_oid'] > 0) {
321  $liveRecord = BackendUtility::getRecord('pages', $pageRecord['t3ver_oid']);
322  $pageRecord['sorting'] = (int)$liveRecord['sorting'];
323  $pageRecord['uid'] = (int)$liveRecord['uid'];
324  $pageRecord['pid'] = (int)$liveRecord['pid'];
325  }
326  }
327  unset($pageRecord);
328  } else {
329  $pageRecords = [];
330  }
331  }
332  foreach ($pageRecords as &$pageRecord) {
333  $pageRecord['uid'] = (int)$pageRecord['uid'];
334  }
335 
336  return $pageRecords;
337  }
338 
339  public function ‪hasChildren(int $pid): bool
340  {
341  $pageRecords = $this->‪getChildPageRecords([$pid]);
342  return !empty($pageRecords);
343  }
344 
351  protected function ‪fetchAllPages(array $dbMounts): array
352  {
353  if (!empty($this->fullPageTree)) {
354  return ‪$this->fullPageTree;
355  }
356  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
357  ->getQueryBuilderForTable('pages');
358  $queryBuilder->getRestrictions()
359  ->removeAll()
360  ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
361  ->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, $this->currentWorkspace));
362 
363  if (!empty($this->additionalQueryRestrictions)) {
364  foreach ($this->additionalQueryRestrictions as $additionalQueryRestriction) {
365  $queryBuilder->getRestrictions()->add($additionalQueryRestriction);
366  }
367  }
368 
369  $queryBuilder->getConcreteQueryBuilder()->select(...$this->quotedFields);
370  $query = $queryBuilder
371  ->from('pages')
372  ->where(
373  // Only show records in default language
374  $queryBuilder->expr()->eq('sys_language_uid', $queryBuilder->createNamedParameter(0, ‪Connection::PARAM_INT))
375  );
376 
377  if (!empty($this->additionalWhereClause)) {
378  $queryBuilder->andWhere(
379  ‪QueryHelper::stripLogicalOperatorPrefix($this->additionalWhereClause)
380  );
381  }
382 
383  $pageRecords = $query->executeQuery()->fetchAllAssociative();
384 
385  $ids = array_column($pageRecords, 'uid');
386  foreach ($dbMounts as $mount) {
387  $entryPointRootLine = BackendUtility::BEgetRootLine($mount, '', false, $this->fields);
388  foreach ($entryPointRootLine as $page) {
389  $pageId = (int)$page['uid'];
390  if (in_array($pageId, $ids) || $pageId === 0) {
391  continue;
392  }
393  $pageRecords[] = $page;
394  $ids[] = $pageId;
395  }
396  }
397 
398  $livePagePids = [];
399  $movedPages = [];
400  // This is necessary to resolve all IDs in a workspace
401  if ($this->currentWorkspace !== 0 && !empty($pageRecords)) {
402  $livePageIds = [];
403  foreach ($pageRecords as $pageRecord) {
404  $livePageIds[] = (int)$pageRecord['uid'];
405  $livePagePids[(int)$pageRecord['uid']] = (int)$pageRecord['pid'];
406  if (VersionState::tryFrom($pageRecord['t3ver_state'] ?? 0) === VersionState::MOVE_POINTER) {
407  $movedPages[$pageRecord['t3ver_oid']] = [
408  'pid' => (int)$pageRecord['pid'],
409  'sorting' => (int)$pageRecord['sorting'],
410  ];
411  }
412  }
413  // Resolve placeholders of workspace versions
414  $resolver = GeneralUtility::makeInstance(
415  PlainDataResolver::class,
416  'pages',
417  $livePageIds
418  );
419  $resolver->setWorkspaceId($this->currentWorkspace);
420  $resolver->setKeepDeletePlaceholder(false);
421  $resolver->setKeepMovePlaceholder(false);
422  $resolver->setKeepLiveIds(false);
423  $recordIds = $resolver->get();
424 
425  $queryBuilder->getRestrictions()->removeAll();
426  $queryBuilder->getConcreteQueryBuilder()->select(...$this->quotedFields);
427  $pageRecords = $queryBuilder
428  ->from('pages')
429  ->where(
430  $queryBuilder->expr()->in('uid', $recordIds)
431  )
432  ->executeQuery()
433  ->fetchAllAssociative();
434  }
435 
436  // Now set up sorting, nesting (tree-structure) for all pages based on pid+sorting fields
437  $groupedAndSortedPagesByPid = [];
438  foreach ($pageRecords as $pageRecord) {
439  $parentPageId = (int)$pageRecord['pid'];
440  // In case this is a record from a workspace
441  // The uid+pid of the live-version record is fetched
442  // This is done in order to avoid fetching records again (e.g. via BackendUtility::workspaceOL()
443  if ((int)$pageRecord['t3ver_oid'] > 0) {
444  // When a move pointer is found, the pid+sorting of the versioned record should be used
445  if (VersionState::tryFrom($pageRecord['t3ver_state'] ?? 0) === VersionState::MOVE_POINTER && !empty($movedPages[$pageRecord['t3ver_oid']])) {
446  $parentPageId = (int)$movedPages[$pageRecord['t3ver_oid']]['pid'];
447  $pageRecord['sorting'] = (int)$movedPages[$pageRecord['t3ver_oid']]['sorting'];
448  } else {
449  // Just a record in a workspace (not moved etc)
450  $parentPageId = (int)($livePagePids[$pageRecord['t3ver_oid']] ?? $pageRecord['pid']);
451  }
452  // this is necessary so the links to the modules are still pointing to the live IDs
453  $pageRecord['uid'] = (int)$pageRecord['t3ver_oid'];
454  $pageRecord['pid'] = $parentPageId;
455  }
456 
457  $sorting = (int)$pageRecord['sorting'];
458  while (isset($groupedAndSortedPagesByPid[$parentPageId][$sorting])) {
459  $sorting++;
460  }
461  $groupedAndSortedPagesByPid[$parentPageId][$sorting] = $pageRecord;
462  }
463 
464  $this->fullPageTree = [
465  'uid' => 0,
466  'title' => ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] ?: 'TYPO3',
467  ];
468  $this->‪addChildrenToPage($this->fullPageTree, $groupedAndSortedPagesByPid);
469  return ‪$this->fullPageTree;
470  }
471 
477  protected function ‪addChildrenToPage(array &$page, array &$groupedAndSortedPagesByPid): void
478  {
479  $page['_children'] = $groupedAndSortedPagesByPid[(int)$page['uid']] ?? [];
480  ksort($page['_children']);
481  foreach ($page['_children'] as &$child) {
482  $this->‪addChildrenToPage($child, $groupedAndSortedPagesByPid);
483  }
484  }
485 
493  protected function ‪findInPageTree(int $pageId, array $pages): array
494  {
495  foreach ($pages['_children'] as $childPage) {
496  if ((int)$childPage['uid'] === $pageId) {
497  return $childPage;
498  }
499  $result = $this->‪findInPageTree($pageId, $childPage);
500  if (!empty($result)) {
501  return $result;
502  }
503  }
504  return [];
505  }
506 
510  public function ‪fetchFilteredTree(string $searchFilter, array $allowedMountPointPageIds, string ‪$additionalWhereClause): array
511  {
512  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
513  ->getQueryBuilderForTable('pages');
514  $queryBuilder->getRestrictions()
515  ->removeAll()
516  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
517 
518  if (!empty($this->additionalQueryRestrictions)) {
519  foreach ($this->additionalQueryRestrictions as $additionalQueryRestriction) {
520  $queryBuilder->getRestrictions()->add($additionalQueryRestriction);
521  }
522  }
523 
524  $expressionBuilder = $queryBuilder->expr();
525 
526  if ($this->currentWorkspace === 0) {
527  // Only include records from live workspace
528  $workspaceIdExpression = $expressionBuilder->eq('t3ver_wsid', 0);
529  } else {
530  // Include live records PLUS records from the given workspace
531  $workspaceIdExpression = $expressionBuilder->in(
532  't3ver_wsid',
533  [0, $this->currentWorkspace]
534  );
535  }
536 
537  $queryBuilder->getConcreteQueryBuilder()->select(...$this->quotedFields);
538  $queryBuilder = $queryBuilder
539  ->from('pages')
540  ->where(
541  // Only show records in default language
542  $expressionBuilder->eq('sys_language_uid', $queryBuilder->createNamedParameter(0, ‪Connection::PARAM_INT)),
543  $workspaceIdExpression,
545  );
546 
547  $searchParts = $expressionBuilder->or();
548 
549  // Extract true integers from search string
550  $searchUids = [];
551  $searchPhrases = ‪GeneralUtility::trimExplode(',', $searchFilter, true);
552  foreach ($searchPhrases as $searchPhrase) {
553  if (‪MathUtility::canBeInterpretedAsInteger($searchPhrase) && $searchPhrase > 0) {
554  $searchUids[] = (int)$searchPhrase;
555  }
556  }
557  $searchUids = array_unique($searchUids);
558 
559  if (!empty($searchUids)) {
560  // Ensure that the LIVE id is also found
561  if ($this->currentWorkspace > 0) {
562  $uidFilter = $expressionBuilder->or(
563  $expressionBuilder->and(
564  $expressionBuilder->in('uid', $queryBuilder->createNamedParameter($searchUids, ‪Connection::PARAM_INT_ARRAY)),
565  $expressionBuilder->eq('t3ver_wsid', $queryBuilder->createNamedParameter(0, ‪Connection::PARAM_INT)),
566  ),
567  $expressionBuilder->and(
568  $expressionBuilder->in('t3ver_oid', $queryBuilder->createNamedParameter($searchUids, ‪Connection::PARAM_INT_ARRAY)),
569  $expressionBuilder->eq('t3ver_wsid', $queryBuilder->createNamedParameter($this->currentWorkspace, ‪Connection::PARAM_INT)),
570  )
571  );
572  } else {
573  $uidFilter = $expressionBuilder->in('uid', $queryBuilder->createNamedParameter($searchUids, ‪Connection::PARAM_INT_ARRAY));
574  }
575  $searchParts = $searchParts->with($uidFilter);
576  }
577  $searchFilter = '%' . $queryBuilder->escapeLikeWildcards($searchFilter) . '%';
578 
579  $searchWhereAlias = $expressionBuilder->or(
580  $expressionBuilder->like(
581  'nav_title',
582  $queryBuilder->createNamedParameter($searchFilter)
583  ),
584  $expressionBuilder->like(
585  'title',
586  $queryBuilder->createNamedParameter($searchFilter)
587  )
588  );
589  $searchParts = $searchParts->with($searchWhereAlias);
590 
591  $queryBuilder->andWhere($searchParts);
592  $pageRecords = $queryBuilder
593  ->executeQuery()
594  ->fetchAllAssociative();
595 
596  $livePagePids = [];
597  if ($this->currentWorkspace !== 0 && !empty($pageRecords)) {
598  $livePageIds = [];
599  foreach ($pageRecords as $pageRecord) {
600  $livePageIds[] = (int)$pageRecord['uid'];
601  $livePagePids[(int)$pageRecord['uid']] = (int)$pageRecord['pid'];
602  if ((int)$pageRecord['t3ver_oid'] > 0) {
603  $livePagePids[(int)$pageRecord['t3ver_oid']] = (int)$pageRecord['pid'];
604  }
605  if (VersionState::tryFrom($pageRecord['t3ver_state'] ?? 0) === VersionState::MOVE_POINTER) {
606  $movedPages[$pageRecord['t3ver_oid']] = [
607  'pid' => (int)$pageRecord['pid'],
608  'sorting' => (int)$pageRecord['sorting'],
609  ];
610  }
611  }
612  // Resolve placeholders of workspace versions
613  $resolver = GeneralUtility::makeInstance(
614  PlainDataResolver::class,
615  'pages',
616  $livePageIds
617  );
618  $resolver->setWorkspaceId($this->currentWorkspace);
619  $resolver->setKeepDeletePlaceholder(false);
620  $resolver->setKeepMovePlaceholder(false);
621  $resolver->setKeepLiveIds(false);
622  $recordIds = $resolver->get();
623 
624  $pageRecords = [];
625  if (!empty($recordIds)) {
626  $queryBuilder->getRestrictions()->removeAll();
627  $queryBuilder->getConcreteQueryBuilder()->select(...$this->quotedFields);
628  $queryBuilder
629  ->from('pages')
630  ->where(
631  $queryBuilder->expr()->in('uid', $queryBuilder->createNamedParameter($recordIds, ‪Connection::PARAM_INT_ARRAY))
632  );
633  $queryBuilder->andWhere($searchParts);
634  $pageRecords = $queryBuilder
635  ->executeQuery()
636  ->fetchAllAssociative();
637  }
638  }
639 
640  $pages = [];
641  foreach ($pageRecords as $pageRecord) {
642  // In case this is a record from a workspace
643  // The uid+pid of the live-version record is fetched
644  // This is done in order to avoid fetching records again (e.g. via BackendUtility::workspaceOL()
645  if ((int)$pageRecord['t3ver_oid'] > 0) {
646  // This probably should also remove the live version
647  $versionState = VersionState::tryFrom($pageRecord['t3ver_state'] ?? 0);
648  if ($versionState === VersionState::DELETE_PLACEHOLDER) {
649  continue;
650  }
651  // When a move pointer is found, the pid+sorting of the versioned record be used
652  if ($versionState === VersionState::MOVE_POINTER && !empty($movedPages[$pageRecord['t3ver_oid']])) {
653  $parentPageId = (int)$movedPages[$pageRecord['t3ver_oid']]['pid'];
654  $pageRecord['sorting'] = (int)$movedPages[$pageRecord['t3ver_oid']]['sorting'];
655  } else {
656  // Just a record in a workspace (not moved etc)
657  $parentPageId = (int)$livePagePids[$pageRecord['t3ver_oid']];
658  }
659  // this is necessary so the links to the modules are still pointing to the live IDs
660  $pageRecord['uid'] = (int)$pageRecord['t3ver_oid'];
661  $pageRecord['pid'] = $parentPageId;
662  }
663  $pages[(int)$pageRecord['uid']] = $pageRecord;
664  }
665  unset($pageRecords);
666 
667  $pages = $this->‪filterPagesOnMountPoints($pages, $allowedMountPointPageIds);
668 
669  $groupedAndSortedPagesByPid = $this->‪groupAndSortPages($pages);
670 
671  $this->fullPageTree = [
672  'uid' => 0,
673  'title' => ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] ?: 'TYPO3',
674  ];
675  $this->‪addChildrenToPage($this->fullPageTree, $groupedAndSortedPagesByPid);
676 
677  return ‪$this->fullPageTree;
678  }
679 
683  protected function ‪filterPagesOnMountPoints(array $pages, array $mountPoints): array
684  {
685  foreach ($pages as $key => $pageRecord) {
686  $rootline = BackendUtility::BEgetRootLine(
687  $pageRecord['uid'],
688  '',
689  $this->currentWorkspace !== 0,
690  $this->fields
691  );
692  $rootline = array_reverse($rootline);
693  if (!in_array(0, $mountPoints, true)) {
694  $isInsideMountPoints = false;
695  foreach ($rootline as $rootlineElement) {
696  if (in_array((int)$rootlineElement['uid'], $mountPoints, true)) {
697  $isInsideMountPoints = true;
698  break;
699  }
700  }
701  if (!$isInsideMountPoints) {
702  unset($pages[$key]);
703  //skip records outside of the allowed mount points
704  continue;
705  }
706  }
707 
708  $inFilteredRootline = false;
709  $amountOfRootlineElements = count($rootline);
710  for ($i = 0; $i < $amountOfRootlineElements; ++$i) {
711  $rootlineElement = $rootline[$i];
712  $rootlineElement['uid'] = (int)$rootlineElement['uid'];
713  $isInWebMount = false;
714  if ($rootlineElement['uid'] > 0) {
715  $isInWebMount = (int)$this->‪getBackendUser()->isInWebMount($rootlineElement);
716  }
717 
718  if (!$isInWebMount
719  || ($rootlineElement['uid'] === (int)$mountPoints[0]
720  && $rootlineElement['uid'] !== $isInWebMount)
721  ) {
722  continue;
723  }
724  if ($this->‪getBackendUser()->isAdmin() || ($rootlineElement['uid'] === $isInWebMount && in_array($rootlineElement['uid'], $mountPoints, true))) {
725  $inFilteredRootline = true;
726  }
727  if (!$inFilteredRootline) {
728  continue;
729  }
730 
731  if (!isset($pages[$rootlineElement['uid']])) {
732  $pages[$rootlineElement['uid']] = $rootlineElement;
733  }
734  }
735  }
736  // Make sure the mountpoints show up in page tree even when parent pages are not accessible pages
737  foreach ($mountPoints as $mountPoint) {
738  if ($mountPoint !== 0) {
739  if (!array_key_exists($mountPoint, $pages)) {
740  $pages[$mountPoint] = BackendUtility::getRecordWSOL('pages', $mountPoint);
741  $pages[$mountPoint]['uid'] = (int)$pages[$mountPoint]['uid'];
742  }
743  $pages[$mountPoint]['pid'] = 0;
744  }
745  }
746 
747  return $pages;
748  }
749 
755  protected function ‪groupAndSortPages(array $pages, array $groupedAndSortedPagesByPid = []): array
756  {
757  foreach ($pages as $key => $pageRecord) {
758  $parentPageId = (int)$pageRecord['pid'];
759  $sorting = (int)$pageRecord['sorting'];
760  // If the page record was already added in another depth level, don't add it another time.
761  // This may happen, if entry points are intersecting each other (Entry point B is inside entry point A).
762  if (($groupedAndSortedPagesByPid[$parentPageId][$sorting]['uid'] ?? 0) === $pageRecord['uid']) {
763  continue;
764  }
765  while (isset($groupedAndSortedPagesByPid[$parentPageId][$sorting])) {
766  $sorting++;
767  }
768  $groupedAndSortedPagesByPid[$parentPageId][$sorting] = $pageRecord;
769  }
770 
771  return $groupedAndSortedPagesByPid;
772  }
773 
775  {
776  return ‪$GLOBALS['BE_USER'];
777  }
778 }
‪TYPO3\CMS\Backend\Tree\Repository\PageTreeRepository\$fullPageTree
‪array $fullPageTree
Definition: PageTreeRepository.php:64
‪TYPO3\CMS\Core\DataHandling\PlainDataResolver
Definition: PlainDataResolver.php:34
‪TYPO3\CMS\Core\Database\Connection\PARAM_INT
‪const PARAM_INT
Definition: Connection.php:52
‪TYPO3\CMS\Backend\Tree\Repository\PageTreeRepository\groupAndSortPages
‪groupAndSortPages(array $pages, array $groupedAndSortedPagesByPid=[])
Definition: PageTreeRepository.php:755
‪TYPO3\CMS\Backend\Tree\Repository\PageTreeRepository\getPageRecords
‪getPageRecords(array $pageIds=[], array $parentPageIds=[])
Definition: PageTreeRepository.php:228
‪TYPO3\CMS\Backend\Tree\Repository\PageTreeRepository\hasChildren
‪hasChildren(int $pid)
Definition: PageTreeRepository.php:339
‪TYPO3\CMS\Backend\Tree\Repository\PageTreeRepository\getTreeLevels
‪array getTreeLevels(array $pageTree, int $depth, array $entryPointIds=[])
Definition: PageTreeRepository.php:173
‪TYPO3\CMS\Core\Versioning\VersionState
‪VersionState
Definition: VersionState.php:22
‪TYPO3\CMS\Backend\Tree\Repository\PageTreeRepository\$currentWorkspace
‪readonly int $currentWorkspace
Definition: PageTreeRepository.php:59
‪TYPO3\CMS\Backend\Tree\Repository\PageTreeRepository\getFlattenedPages
‪getFlattenedPages(array $entryPointIds, int $depth)
Definition: PageTreeRepository.php:205
‪TYPO3\CMS\Backend\Tree\Repository\PageTreeRepository\$additionalQueryRestrictions
‪readonly array $additionalQueryRestrictions
Definition: PageTreeRepository.php:66
‪TYPO3\CMS\Backend\Tree\Repository\PageTreeRepository\fetchFilteredTree
‪fetchFilteredTree(string $searchFilter, array $allowedMountPointPageIds, string $additionalWhereClause)
Definition: PageTreeRepository.php:510
‪TYPO3\CMS\Backend\Tree\Repository
Definition: PageTreeRepository.php:18
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger(mixed $var)
Definition: MathUtility.php:69
‪TYPO3\CMS\Backend\Tree\Repository\PageTreeRepository\findInPageTree
‪array findInPageTree(int $pageId, array $pages)
Definition: PageTreeRepository.php:493
‪TYPO3\CMS\Backend\Tree\Repository\PageTreeRepository\getBackendUser
‪getBackendUser()
Definition: PageTreeRepository.php:774
‪TYPO3\CMS\Backend\Tree\Repository\PageTreeRepository\$fields
‪readonly array $fields
Definition: PageTreeRepository.php:47
‪TYPO3\CMS\Core\Database\Query\QueryHelper
Definition: QueryHelper.php:32
‪TYPO3\CMS\Backend\Tree\Repository\PageTreeRepository\$quotedFields
‪readonly array $quotedFields
Definition: PageTreeRepository.php:54
‪TYPO3\CMS\Backend\Tree\Repository\PageTreeRepository\addChildrenToPage
‪addChildrenToPage(array &$page, array &$groupedAndSortedPagesByPid)
Definition: PageTreeRepository.php:477
‪TYPO3\CMS\Backend\Tree\Repository\PageTreeRepository\getTree
‪getTree(int $entryPoint, callable $callback=null, array $dbMounts=[])
Definition: PageTreeRepository.php:131
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication
Definition: BackendUserAuthentication.php:62
‪TYPO3\CMS\Backend\Tree\Repository\PageTreeRepository\filterPagesOnMountPoints
‪filterPagesOnMountPoints(array $pages, array $mountPoints)
Definition: PageTreeRepository.php:683
‪TYPO3\CMS\Backend\Tree\Repository\PageTreeRepository\fetchAllPages
‪array fetchAllPages(array $dbMounts)
Definition: PageTreeRepository.php:351
‪TYPO3\CMS\Core\Database\Connection
Definition: Connection.php:41
‪TYPO3\CMS\Core\Database\Query\QueryHelper\stripLogicalOperatorPrefix
‪static string stripLogicalOperatorPrefix(string $constraint)
Definition: QueryHelper.php:171
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction
Definition: DeletedRestriction.php:28
‪TYPO3\CMS\Backend\Tree\Repository\PageTreeRepository
Definition: PageTreeRepository.php:41
‪TYPO3\CMS\Backend\Tree\Repository\PageTreeRepository\getChildPageRecords
‪getChildPageRecords(array $parentPageIds)
Definition: PageTreeRepository.php:220
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:24
‪TYPO3\CMS\Backend\Tree\Repository\PageTreeRepository\$additionalWhereClause
‪string $additionalWhereClause
Definition: PageTreeRepository.php:68
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Backend\Tree\Repository\PageTreeRepository\__construct
‪__construct(int $workspaceId=0, array $additionalFieldsToQuery=[], array $additionalQueryRestrictions=[])
Definition: PageTreeRepository.php:75
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\Database\Connection\PARAM_INT_ARRAY
‪const PARAM_INT_ARRAY
Definition: Connection.php:72
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static list< string > trimExplode(string $delim, string $string, bool $removeEmptyValues=false, int $limit=0)
Definition: GeneralUtility.php:822
‪TYPO3\CMS\Backend\Tree\Repository\PageTreeRepository\applyCallbackToChildren
‪applyCallbackToChildren(array &$tree, callable $callback)
Definition: PageTreeRepository.php:151
‪TYPO3\CMS\Backend\Tree\Repository\PageTreeRepository\setAdditionalWhereClause
‪setAdditionalWhereClause(string $additionalWhereClause)
Definition: PageTreeRepository.php:119
‪TYPO3\CMS\Core\Database\Query\Restriction\WorkspaceRestriction
Definition: WorkspaceRestriction.php:39