Completed
Push — master ( 9dc190...fb61db )
by
unknown
16:04
created

PageTreeRepository::hasChildren()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 5
rs 10
c 0
b 0
f 0
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
18
namespace TYPO3\CMS\Backend\Tree\Repository;
19
20
use Doctrine\DBAL\Connection;
21
use TYPO3\CMS\Backend\Utility\BackendUtility;
22
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
23
use TYPO3\CMS\Core\Database\ConnectionPool;
24
use TYPO3\CMS\Core\Database\Query\QueryHelper;
25
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
26
use TYPO3\CMS\Core\Database\Query\Restriction\WorkspaceRestriction;
27
use TYPO3\CMS\Core\DataHandling\PlainDataResolver;
28
use TYPO3\CMS\Core\Type\Bitmask\Permission;
29
use TYPO3\CMS\Core\Utility\GeneralUtility;
30
use TYPO3\CMS\Core\Versioning\VersionState;
31
32
/**
33
 * Fetches ALL pages in the page tree, possibly overlaid with the workspace
34
 * in a sorted way.
35
 *
36
 * This works agnostic of the Backend User, allows to be used in FE as well in the future.
37
 *
38
 * @internal this class is not public API yet, as it needs to be proven stable enough first.
39
 */
40
class PageTreeRepository
41
{
42
    /**
43
     * Fields to be queried from the database
44
     *
45
     * @var string[]
46
     */
47
    protected $fields = [
48
        'uid',
49
        'pid',
50
        'sorting',
51
        'starttime',
52
        'endtime',
53
        'hidden',
54
        'fe_group',
55
        'title',
56
        'nav_title',
57
        'nav_hide',
58
        'php_tree_stop',
59
        'doktype',
60
        'is_siteroot',
61
        'module',
62
        'extendToSubpages',
63
        'content_from_pid',
64
        't3ver_oid',
65
        't3ver_wsid',
66
        't3ver_state',
67
        't3ver_stage',
68
        't3ver_tstamp',
69
        't3ver_move_id',
70
        'perms_userid',
71
        'perms_user',
72
        'perms_groupid',
73
        'perms_group',
74
        'perms_everybody',
75
        'mount_pid',
76
        'shortcut',
77
        'shortcut_mode',
78
        'mount_pid_ol',
79
        'url',
80
        'sys_language_uid',
81
        'l10n_parent',
82
    ];
83
84
    /**
85
     * The workspace ID to operate on
86
     *
87
     * @var int
88
     */
89
    protected $currentWorkspace = 0;
90
91
    /**
92
     * Full page tree when selected without permissions applied.
93
     *
94
     * @var array
95
     */
96
    protected $fullPageTree = [];
97
98
    /**
99
     * @var array
100
     */
101
    protected $additionalQueryRestrictions = [];
102
103
    /**
104
     * @param int $workspaceId the workspace ID to be checked for.
105
     * @param array $additionalFieldsToQuery an array with more fields that should be accessed.
106
     * @param array $additionalQueryRestrictions an array with more restrictions to add
107
     */
108
    public function __construct(int $workspaceId = 0, array $additionalFieldsToQuery = [], array $additionalQueryRestrictions = [])
109
    {
110
        $this->currentWorkspace = $workspaceId;
111
        if (!empty($additionalFieldsToQuery)) {
112
            $this->fields = array_merge($this->fields, $additionalFieldsToQuery);
113
        }
114
115
        if (!empty($additionalQueryRestrictions)) {
116
            $this->additionalQueryRestrictions = $additionalQueryRestrictions;
117
        }
118
    }
119
120
    /**
121
     * Main entry point for this repository, to fetch the tree data for a page.
122
     * Basically the page record, plus all child pages and their child pages recursively, stored within "_children" item.
123
     *
124
     * @param int $entryPoint the page ID to fetch the tree for
125
     * @param callable $callback a callback to be used to check for permissions and filter out pages not to be included.
126
     * @param array $dbMounts
127
     * @return array
128
     */
129
    public function getTree(int $entryPoint, callable $callback = null, array $dbMounts = []): array
130
    {
131
        $this->fetchAllPages($dbMounts);
132
        if ($entryPoint === 0) {
133
            $tree = $this->fullPageTree;
134
        } else {
135
            $tree = $this->findInPageTree($entryPoint, $this->fullPageTree);
136
        }
137
        if (!empty($tree) && $callback !== null) {
138
            $this->applyCallbackToChildren($tree, $callback);
139
        }
140
        return $tree;
141
    }
142
143
    /**
144
     * Removes items from a tree based on a callback, usually used for permission checks
145
     *
146
     * @param array $tree
147
     * @param callable $callback
148
     */
149
    protected function applyCallbackToChildren(array &$tree, callable $callback)
150
    {
151
        if (!isset($tree['_children'])) {
152
            return;
153
        }
154
        foreach ($tree['_children'] as $k => &$childPage) {
155
            if (!call_user_func_array($callback, [$childPage])) {
156
                unset($tree['_children'][$k]);
157
                continue;
158
            }
159
            $this->applyCallbackToChildren($childPage, $callback);
160
        }
161
    }
162
163
    /**
164
     * Get the page tree based on a given page record and a given depth
165
     *
166
     * @param array $pageTree The page record of the top level page you want to get the page tree of
167
     * @param int $depth Number of levels to fetch
168
     * @return array An array with page records and their children
169
     */
170
    public function getTreeLevels(array $pageTree, int $depth): array
171
    {
172
        $parentPageIds = [$pageTree['uid']];
173
        $groupedAndSortedPagesByPid = [];
174
        for ($i = 0; $i < $depth; $i++) {
175
            if (empty($parentPageIds)) {
176
                break;
177
            }
178
            $pageRecords = $this->getChildPages($parentPageIds);
179
180
            $groupedAndSortedPagesByPid = $this->groupAndSortPages($pageRecords, $groupedAndSortedPagesByPid);
181
182
            $parentPageIds = array_column($pageRecords, 'uid');
183
        }
184
        $this->addChildrenToPage($pageTree, $groupedAndSortedPagesByPid);
185
        return $pageTree;
186
    }
187
188
    /**
189
     * Get the child pages from the given parent pages
190
     *
191
     * @param array $parentPageIds
192
     * @return array
193
     */
194
    protected function getChildPages(array $parentPageIds): array
195
    {
196
        $pageRecords = $this->getChildPageRecords($parentPageIds);
197
198
        foreach ($pageRecords as &$pageRecord) {
199
            $pageRecord['uid'] = (int)$pageRecord['uid'];
200
201
            if ($this->currentWorkspace > 0) {
202
                if ((int)$pageRecord['t3ver_state'] === VersionState::MOVE_PLACEHOLDER) {
203
                    $liveRecord = BackendUtility::getRecord('pages', $pageRecord['t3ver_move_id']);
204
                    $pageRecord['uid'] = (int)$pageRecord['t3ver_move_id'];
205
                    $pageRecord['title'] = $liveRecord['title'];
206
                }
207
208
                if ((int)$pageRecord['t3ver_oid'] > 0) {
209
                    $liveRecord = BackendUtility::getRecord('pages', $pageRecord['t3ver_oid']);
210
211
                    $pageRecord['uid'] = (int)$pageRecord['t3ver_oid'];
212
                    $pageRecord['pid'] = (int)$liveRecord['pid'];
213
                }
214
            }
215
        }
216
        unset($pageRecord);
217
218
        return $pageRecords;
219
    }
220
221
    /**
222
     * Retrieve the page records based on the given parent page ids
223
     *
224
     * @param array $parentPageIds
225
     * @return array
226
     */
227
    protected function getChildPageRecords(array $parentPageIds): array
228
    {
229
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
230
            ->getQueryBuilderForTable('pages');
231
        $queryBuilder->getRestrictions()
232
            ->removeAll()
233
            ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
234
            ->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, $this->currentWorkspace));
235
236
        if (!empty($this->additionalQueryRestrictions)) {
237
            foreach ($this->additionalQueryRestrictions as $additionalQueryRestriction) {
238
                $queryBuilder->getRestrictions()->add($additionalQueryRestriction);
239
            }
240
        }
241
242
        $pageRecords = $queryBuilder
243
            ->select(...$this->fields)
244
            ->from('pages')
245
            ->where(
246
                $queryBuilder->expr()->eq('sys_language_uid', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)),
247
                $queryBuilder->expr()->in('pid', $queryBuilder->createNamedParameter($parentPageIds, Connection::PARAM_INT_ARRAY))
248
            )
249
            ->execute()
250
            ->fetchAll();
251
252
        // This is necessary to resolve all IDs in a workspace
253
        if ($this->currentWorkspace !== 0 && !empty($pageRecords)) {
254
            $livePageIds = array_column($pageRecords, 'uid');
255
            // Resolve placeholders of workspace versions
256
            $resolver = GeneralUtility::makeInstance(
257
                PlainDataResolver::class,
258
                'pages',
259
                $livePageIds
260
            );
261
            $resolver->setWorkspaceId($this->currentWorkspace);
262
            $resolver->setKeepDeletePlaceholder(false);
263
            $resolver->setKeepMovePlaceholder(false);
264
            $resolver->setKeepLiveIds(false);
265
            $recordIds = $resolver->get();
266
267
            if (!empty($recordIds)) {
268
                $queryBuilder->getRestrictions()->removeAll();
269
                $pageRecords = $queryBuilder
270
                    ->select(...$this->fields)
271
                    ->from('pages')
272
                    ->where(
273
                        $queryBuilder->expr()->in('uid', $queryBuilder->createNamedParameter($recordIds, Connection::PARAM_INT_ARRAY))
274
                    )
275
                    ->execute()
276
                    ->fetchAll();
277
            }
278
        }
279
280
        return $pageRecords;
281
    }
282
283
    public function hasChildren(int $pid): bool
284
    {
285
        $pageRecords = $this->getChildPageRecords([$pid]);
286
287
        return !empty($pageRecords);
288
    }
289
290
    /**
291
     * Fetch all non-deleted pages, regardless of permissions. That's why it's internal.
292
     *
293
     * @return array the full page tree of the whole installation
294
     */
295
    protected function fetchAllPages($dbMounts): array
296
    {
297
        if (!empty($this->fullPageTree)) {
298
            return $this->fullPageTree;
299
        }
300
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
301
            ->getQueryBuilderForTable('pages');
302
        $queryBuilder->getRestrictions()
303
            ->removeAll()
304
            ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
305
            ->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, $this->currentWorkspace));
306
307
        if (!empty($this->additionalQueryRestrictions)) {
308
            foreach ($this->additionalQueryRestrictions as $additionalQueryRestriction) {
309
                $queryBuilder->getRestrictions()->add($additionalQueryRestriction);
310
            }
311
        }
312
313
        $pageRecords = $queryBuilder
314
            ->select(...$this->fields)
315
            ->from('pages')
316
            ->where(
317
                // Only show records in default language
318
                $queryBuilder->expr()->eq('sys_language_uid', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT))
319
            )
320
            ->execute()
321
            ->fetchAll();
322
323
        $ids = array_column($pageRecords, 'uid');
324
        foreach ($dbMounts as $mount) {
325
            $entryPointRootLine = BackendUtility::BEgetRootLine($mount, '', false, $this->fields);
326
            foreach ($entryPointRootLine as $page) {
327
                $pageId = (int)$page['uid'];
328
                if (in_array($pageId, $ids) || $pageId === 0) {
329
                    continue;
330
                }
331
                $pageRecords[] = $page;
332
                $ids[] = $pageId;
333
            }
334
        }
335
336
        $livePagePids = [];
337
        $movePlaceholderData = [];
338
        // This is necessary to resolve all IDs in a workspace
339
        if ($this->currentWorkspace !== 0 && !empty($pageRecords)) {
340
            $livePageIds = [];
341
            foreach ($pageRecords as $pageRecord) {
342
                $livePageIds[] = (int)$pageRecord['uid'];
343
                $livePagePids[(int)$pageRecord['uid']] = (int)$pageRecord['pid'];
344
                if ((int)$pageRecord['t3ver_state'] === VersionState::MOVE_PLACEHOLDER) {
345
                    $movePlaceholderData[$pageRecord['t3ver_move_id']] = [
346
                        'pid' => (int)$pageRecord['pid'],
347
                        'sorting' => (int)$pageRecord['sorting']
348
                    ];
349
                }
350
            }
351
            // Resolve placeholders of workspace versions
352
            $resolver = GeneralUtility::makeInstance(
353
                PlainDataResolver::class,
354
                'pages',
355
                $livePageIds
356
            );
357
            $resolver->setWorkspaceId($this->currentWorkspace);
358
            $resolver->setKeepDeletePlaceholder(false);
359
            $resolver->setKeepMovePlaceholder(false);
360
            $resolver->setKeepLiveIds(false);
361
            $recordIds = $resolver->get();
362
363
            $queryBuilder->getRestrictions()->removeAll();
364
            $pageRecords = $queryBuilder
365
                ->select(...$this->fields)
366
                ->from('pages')
367
                ->where(
368
                    $queryBuilder->expr()->in('uid', $recordIds)
369
                )
370
                ->execute()
371
                ->fetchAll();
372
        }
373
374
        // Now set up sorting, nesting (tree-structure) for all pages based on pid+sorting fields
375
        $groupedAndSortedPagesByPid = [];
376
        foreach ($pageRecords as $pageRecord) {
377
            $parentPageId = (int)$pageRecord['pid'];
378
            // In case this is a record from a workspace
379
            // The uid+pid of the live-version record is fetched
380
            // This is done in order to avoid fetching records again (e.g. via BackendUtility::workspaceOL()
381
            if ((int)$pageRecord['t3ver_oid'] > 0) {
382
                // When a move pointer is found, the pid+sorting of the MOVE_PLACEHOLDER should be used (this is the
383
                // workspace record holding this information), also the t3ver_state is set to the MOVE_PLACEHOLDER
384
                // because the record is then added
385
                if ((int)$pageRecord['t3ver_state'] === VersionState::MOVE_POINTER && !empty($movePlaceholderData[$pageRecord['t3ver_oid']])) {
386
                    $parentPageId = (int)$movePlaceholderData[$pageRecord['t3ver_oid']]['pid'];
387
                    $pageRecord['sorting'] = (int)$movePlaceholderData[$pageRecord['t3ver_oid']]['sorting'];
388
                    $pageRecord['t3ver_state'] = VersionState::MOVE_PLACEHOLDER;
389
                } else {
390
                    // Just a record in a workspace (not moved etc)
391
                    $parentPageId = (int)$livePagePids[$pageRecord['t3ver_oid']];
392
                }
393
                // this is necessary so the links to the modules are still pointing to the live IDs
394
                $pageRecord['uid'] = (int)$pageRecord['t3ver_oid'];
395
                $pageRecord['pid'] = $parentPageId;
396
            }
397
398
            $sorting = (int)$pageRecord['sorting'];
399
            while (isset($groupedAndSortedPagesByPid[$parentPageId][$sorting])) {
400
                $sorting++;
401
            }
402
            $groupedAndSortedPagesByPid[$parentPageId][$sorting] = $pageRecord;
403
        }
404
405
        $this->fullPageTree = [
406
            'uid' => 0,
407
            'title' => $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] ?: 'TYPO3'
408
        ];
409
        $this->addChildrenToPage($this->fullPageTree, $groupedAndSortedPagesByPid);
410
        return $this->fullPageTree;
411
    }
412
413
    /**
414
     * Adds the property "_children" to a page record with the child pages
415
     *
416
     * @param array $page
417
     * @param array[] $groupedAndSortedPagesByPid
418
     */
419
    protected function addChildrenToPage(array &$page, array &$groupedAndSortedPagesByPid)
420
    {
421
        $page['_children'] = $groupedAndSortedPagesByPid[(int)$page['uid']] ?? [];
422
        ksort($page['_children']);
423
        foreach ($page['_children'] as &$child) {
424
            $this->addChildrenToPage($child, $groupedAndSortedPagesByPid);
425
        }
426
    }
427
428
    /**
429
     * Looking for a page by traversing the tree
430
     *
431
     * @param int $pageId the page ID to search for
432
     * @param array $pages the page tree to look for the page
433
     * @return array Array of the tree data, empty array if nothing was found
434
     */
435
    protected function findInPageTree(int $pageId, array $pages): array
436
    {
437
        foreach ($pages['_children'] as $childPage) {
438
            if ((int)$childPage['uid'] === $pageId) {
439
                return $childPage;
440
            }
441
            $result = $this->findInPageTree($pageId, $childPage);
442
            if (!empty($result)) {
443
                return $result;
444
            }
445
        }
446
        return [];
447
    }
448
449
    /**
450
     * Retrieve the page tree based on the given search filter
451
     *
452
     * @param string $searchFilter
453
     * @return array
454
     */
455
    public function fetchFilteredTree(string $searchFilter): array
456
    {
457
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
458
            ->getQueryBuilderForTable('pages');
459
        $queryBuilder->getRestrictions()
460
            ->removeAll()
461
            ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
462
463
        if ($this->getBackendUser()->workspace === 0) {
464
            $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(WorkspaceRestriction::class));
465
        }
466
467
        if (!empty($this->additionalQueryRestrictions)) {
468
            foreach ($this->additionalQueryRestrictions as $additionalQueryRestriction) {
469
                $queryBuilder->getRestrictions()->add($additionalQueryRestriction);
470
            }
471
        }
472
473
        $expressionBuilder = $queryBuilder->expr();
474
475
        $queryBuilder = $queryBuilder
476
            ->select(...$this->fields)
477
            ->from('pages')
478
            ->where(
479
                // Only show records in default language
480
                $expressionBuilder->eq('sys_language_uid', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT))
481
            );
482
483
        $queryBuilder->where(
484
            QueryHelper::stripLogicalOperatorPrefix($this->getBackendUser()->getPagePermsClause(Permission::PAGE_SHOW))
485
        );
486
        $searchParts = $expressionBuilder->orX();
487
        if (is_numeric($searchFilter) && $searchFilter > 0) {
488
            $searchParts->add(
489
                $expressionBuilder->eq('uid', $queryBuilder->createNamedParameter($searchFilter, \PDO::PARAM_INT))
490
            );
491
        }
492
        $searchFilter = '%' . $queryBuilder->escapeLikeWildcards($searchFilter) . '%';
493
494
        $searchWhereAlias = $expressionBuilder->orX(
495
            $expressionBuilder->like(
496
                'nav_title',
497
                $queryBuilder->createNamedParameter($searchFilter, \PDO::PARAM_STR)
498
            ),
499
            $expressionBuilder->like(
500
                'title',
501
                $queryBuilder->createNamedParameter($searchFilter, \PDO::PARAM_STR)
502
            )
503
        );
504
        $searchParts->add($searchWhereAlias);
505
506
        $queryBuilder->andWhere($searchParts);
507
        $pageRecords = $queryBuilder->execute()
508
            ->fetchAll();
509
510
        $pages = [];
511
        foreach ($pageRecords as $page) {
512
            $pages[$page['uid']] = $page;
513
        }
514
515
        if ($this->getBackendUser()->workspace !== 0) {
516
            foreach (array_unique(array_column($pages, 't3ver_oid')) as $t3verOid) {
517
                if ($t3verOid !== 0) {
518
                    unset($pages[$t3verOid]);
519
                }
520
            }
521
        }
522
        unset($pageRecords);
523
524
        $pages = $this->filterPagesOnMountPoints($pages, $this->getAllowedMountPoints());
525
526
        $groupedAndSortedPagesByPid = $this->groupAndSortPages($pages);
527
528
        $this->fullPageTree = [
529
            'uid' => 0,
530
            'title' => $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] ?: 'TYPO3'
531
        ];
532
        $this->addChildrenToPage($this->fullPageTree, $groupedAndSortedPagesByPid);
533
534
        return $this->fullPageTree;
535
    }
536
537
    /**
538
     * Filter all records outside of the allowed mount points
539
     *
540
     * @param array $pages
541
     * @param array $mountPoints
542
     * @return array
543
     */
544
    protected function filterPagesOnMountPoints(array $pages, array $mountPoints): array
545
    {
546
        foreach ($pages as $key => $pageRecord) {
547
            $rootline = BackendUtility::BEgetRootLine(
548
                $pageRecord['uid'],
549
                '',
550
                $this->getBackendUser()->workspace != 0,
551
                $this->fields
552
            );
553
            $rootline = array_reverse($rootline);
554
            if (!in_array(0, $mountPoints, true)) {
555
                $isInsideMountPoints = false;
556
                foreach ($rootline as $rootlineElement) {
557
                    if (in_array((int)$rootlineElement['uid'], $mountPoints, true)) {
558
                        $isInsideMountPoints = true;
559
                        break;
560
                    }
561
                }
562
                if (!$isInsideMountPoints) {
563
                    unset($pages[$key]);
564
                    //skip records outside of the allowed mount points
565
                    continue;
566
                }
567
            }
568
569
            $inFilteredRootline = false;
570
            $amountOfRootlineElements = count($rootline);
571
            for ($i = 0; $i < $amountOfRootlineElements; ++$i) {
572
                $rootlineElement = $rootline[$i];
573
                $rootlineElement['uid'] = (int)$rootlineElement['uid'];
574
                $isInWebMount = false;
575
                if ($rootlineElement['uid'] > 0) {
576
                    $isInWebMount = (int)$this->getBackendUser()->isInWebMount($rootlineElement);
577
                }
578
579
                if (!$isInWebMount
0 ignored issues
show
Bug Best Practice introduced by
The expression $isInWebMount of type false|integer is loosely compared to false; this is ambiguous if the integer can be 0. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
580
                    || ($rootlineElement['uid'] === (int)$mountPoints[0]
581
                        && $rootlineElement['uid'] !== $isInWebMount)
582
                ) {
583
                    continue;
584
                }
585
                if ($this->getBackendUser()->isAdmin() ||($rootlineElement['uid'] === $isInWebMount
586
                        && in_array($rootlineElement['uid'], $mountPoints, true))
587
                ) {
588
                    $inFilteredRootline = true;
589
                }
590
                if (!$inFilteredRootline) {
591
                    continue;
592
                }
593
594
                if (!isset($pages[$rootlineElement['uid']])) {
595
                    $pages[$rootlineElement['uid']] = $rootlineElement;
596
                }
597
            }
598
        }
599
        // Make sure the mountpoints show up in page tree even when parent pages are not accessible pages
600
        foreach ($mountPoints as $mountPoint) {
601
            if ($mountPoint !== 0) {
602
                if (!array_key_exists($mountPoint, $pages)) {
603
                    $pages[$mountPoint] = BackendUtility::getRecord('pages', $mountPoint);
604
                }
605
                $pages[$mountPoint]['pid'] = 0;
606
            }
607
        }
608
609
        return $pages;
610
    }
611
612
    /**
613
     * Group pages by parent page and sort pages based on sorting property
614
     *
615
     * @param array $pages
616
     * @param array $groupedAndSortedPagesByPid
617
     * @return array
618
     */
619
    protected function groupAndSortPages(array $pages, $groupedAndSortedPagesByPid = []): array
620
    {
621
        foreach ($pages as $key => $pageRecord) {
622
            $parentPageId = (int)$pageRecord['pid'];
623
            $sorting = (int)$pageRecord['sorting'];
624
            while (isset($groupedAndSortedPagesByPid[$parentPageId][$sorting])) {
625
                $sorting++;
626
            }
627
            $groupedAndSortedPagesByPid[$parentPageId][$sorting] = $pageRecord;
628
        }
629
630
        return $groupedAndSortedPagesByPid;
631
    }
632
633
    /**
634
     * Get allowed mountpoints. Returns temporary mountpoint when temporary mountpoint is used
635
     * @return array
636
     */
637
    protected function getAllowedMountPoints(): array
638
    {
639
        $mountPoints = (int)($this->getBackendUser()->uc['pageTree_temporaryMountPoint'] ?? 0);
640
        if (!$mountPoints) {
641
            $mountPoints = array_map('intval', $this->getBackendUser()->returnWebmounts());
642
            return array_unique($mountPoints);
643
        }
644
        return [$mountPoints];
645
    }
646
647
    /**
648
     * @return BackendUserAuthentication
649
     */
650
    protected function getBackendUser(): BackendUserAuthentication
651
    {
652
        return $GLOBALS['BE_USER'];
653
    }
654
}
655