Passed
Push — master ( 871442...8c6b9a )
by
unknown
21:50
created

AbstractTreeView::getRootRecord()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of the TYPO3 CMS project.
5
 *
6
 * It is free software; you can redistribute it and/or modify it under
7
 * the terms of the GNU General Public License, either version 2
8
 * of the License, or any later version.
9
 *
10
 * For the full copyright and license information, please read the
11
 * LICENSE.txt file that was distributed with this source code.
12
 *
13
 * The TYPO3 project - inspiring people to share!
14
 */
15
16
namespace TYPO3\CMS\Backend\Tree\View;
17
18
use TYPO3\CMS\Backend\Routing\UriBuilder;
19
use TYPO3\CMS\Backend\Utility\BackendUtility;
20
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
21
use TYPO3\CMS\Core\Database\ConnectionPool;
22
use TYPO3\CMS\Core\Database\Query\QueryHelper;
23
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
24
use TYPO3\CMS\Core\Database\Query\Restriction\WorkspaceRestriction;
25
use TYPO3\CMS\Core\Imaging\Icon;
26
use TYPO3\CMS\Core\Imaging\IconFactory;
27
use TYPO3\CMS\Core\Localization\LanguageService;
28
use TYPO3\CMS\Core\Utility\GeneralUtility;
29
30
/**
31
 * Base class for creating a browsable array/page/folder tree in HTML
32
 */
33
abstract class AbstractTreeView
34
{
35
    // EXTERNAL, static:
36
    // If set, the first element in the tree is always expanded.
37
    /**
38
     * @var bool
39
     */
40
    public $expandFirst = false;
41
42
    // If set, then ALL items will be expanded, regardless of stored settings.
43
    /**
44
     * @var bool
45
     */
46
    public $expandAll = false;
47
48
    // Holds the current script to reload to.
49
    /**
50
     * @var string
51
     */
52
    public $thisScript = '';
53
54
    // Which HTML attribute to use: alt/title. See init().
55
    /**
56
     * @var string
57
     */
58
    public $titleAttrib = 'title';
59
60
    /**
61
     * @var bool
62
     */
63
    public $ext_showPathAboveMounts = false;
64
65
    // If set, the id of the mounts will be added to the internal ids array
66
    /**
67
     * @var int
68
     */
69
    public $addSelfId = 0;
70
71
    // Used if the tree is made of records (not folders for ex.)
72
    /**
73
     * @var string
74
     */
75
    public $title = 'no title';
76
77
    // If TRUE, a default title attribute showing the UID of the record is shown.
78
    // This cannot be enabled by default because it will destroy many applications
79
    // where another title attribute is in fact applied later.
80
    /**
81
     * @var bool
82
     */
83
    public $showDefaultTitleAttribute = false;
84
85
    /**
86
     * Needs to be initialized with $GLOBALS['BE_USER']
87
     * Done by default in init()
88
     *
89
     * @var \TYPO3\CMS\Core\Authentication\BackendUserAuthentication|string
90
     */
91
    public $BE_USER = '';
92
93
    /**
94
     * Needs to be initialized with e.g. $GLOBALS['BE_USER']->returnWebmounts()
95
     * Default setting in init() is 0 => 0
96
     * The keys are mount-ids (can be anything basically) and the
97
     * values are the ID of the root element (COULD be zero or anything else.
98
     * For pages that would be the uid of the page, zero for the pagetree root.)
99
     *
100
     * @var array|null
101
     */
102
    public $MOUNTS;
103
104
    /**
105
     * Database table to get the tree data from.
106
     * Leave blank if data comes from an array.
107
     *
108
     * @var string
109
     */
110
    public $table = '';
111
112
    /**
113
     * Defines the field of $table which is the parent id field (like pid for table pages).
114
     *
115
     * @var string
116
     */
117
    public $parentField = 'pid';
118
119
    /**
120
     * WHERE clause used for selecting records for the tree. Is set by function init.
121
     * Only makes sense when $this->table is set.
122
     *
123
     * @see init()
124
     * @var string
125
     */
126
    public $clause = '';
127
128
    /**
129
     * Field for ORDER BY. Is set by function init.
130
     * Only makes sense when $this->table is set.
131
     *
132
     * @see init()
133
     * @var string
134
     */
135
    public $orderByFields = '';
136
137
    /**
138
     * Default set of fields selected from the tree table.
139
     * Make SURE that these fields names listed herein are actually possible to select from $this->table (if that variable is set to a TCA table name)
140
     *
141
     * @see addField()
142
     * @var array
143
     */
144
    public $fieldArray = ['uid', 'pid', 'title', 'is_siteroot'];
145
146
    /**
147
     * List of other fields which are ALLOWED to set (here, based on the "pages" table!)
148
     *
149
     * @see addField()
150
     * @var string
151
     */
152
    public $defaultList = 'uid,pid,tstamp,sorting,deleted,perms_userid,perms_groupid,perms_user,perms_group,perms_everybody,crdate,cruser_id';
153
154
    /**
155
     * Unique name for the tree.
156
     * Used as key for storing the tree into the BE users settings.
157
     * Used as key to pass parameters in links.
158
     * MUST NOT contain underscore chars.
159
     * etc.
160
     *
161
     * @var string
162
     */
163
    public $treeName = '';
164
165
    /**
166
     * A prefix for table cell id's which will be wrapped around an item.
167
     * Can be used for highlighting by JavaScript.
168
     * Needs to be unique if multiple trees are on one HTML page.
169
     *
170
     * @see printTree()
171
     * @var string
172
     */
173
    public $domIdPrefix = 'row';
174
175
    /**
176
     * If 1, HTML code is also accumulated in ->tree array during rendering of the tree
177
     *
178
     * @var int
179
     */
180
    public $makeHTML = 1;
181
182
    /**
183
     * If TRUE, records as selected will be stored internally in the ->recs array
184
     *
185
     * @var int
186
     */
187
    public $setRecs = 0;
188
189
    // *********
190
    // Internal
191
    // *********
192
    // For record trees:
193
    // one-dim array of the uid's selected.
194
    /**
195
     * @var array
196
     */
197
    public $ids = [];
198
199
    // The hierarchy of element uids
200
    /**
201
     * @var array
202
     */
203
    public $ids_hierarchy = [];
204
205
    // The hierarchy of versioned element uids
206
    /**
207
     * @var array
208
     */
209
    public $orig_ids_hierarchy = [];
210
211
    // Temporary, internal array
212
    /**
213
     * @var array
214
     */
215
    public $buffer_idH = [];
216
217
    // For FOLDER trees:
218
    // Special UIDs for folders (integer-hashes of paths)
219
    /**
220
     * @var array
221
     */
222
    public $specUIDmap = [];
223
224
    // For both types
225
    // Tree is accumulated in this variable
226
    /**
227
     * @var array
228
     */
229
    public $tree = [];
230
231
    // Holds (session stored) information about which items in the tree are unfolded and which are not.
232
    /**
233
     * @var array
234
     */
235
    public $stored = [];
236
237
    // Points to the current mountpoint key
238
    /**
239
     * @var int
240
     */
241
    public $bank = 0;
242
243
    // Accumulates the displayed records.
244
    /**
245
     * @var array
246
     */
247
    public $recs = [];
248
249
    /**
250
     * Constructor
251
     */
252
    public function __construct()
253
    {
254
        $this->determineScriptUrl();
255
    }
256
257
    /**
258
     * Sets the script url depending on being a module or script request
259
     */
260
    protected function determineScriptUrl()
261
    {
262
        $this->thisScript = (string)GeneralUtility::makeInstance(UriBuilder::class)->buildUriFromRoutePath(
263
            $GLOBALS['TYPO3_REQUEST']->getAttribute('route')->getPath()
264
        );
265
    }
266
267
    /**
268
     * @return string
269
     */
270
    protected function getThisScript()
271
    {
272
        return strpos($this->thisScript, '?') === false ? $this->thisScript . '?' : $this->thisScript . '&';
273
    }
274
275
    /**
276
     * Initialize the tree class. Needs to be overwritten
277
     *
278
     * @param string $clause Record WHERE clause
279
     * @param string $orderByFields Record ORDER BY field
280
     */
281
    public function init($clause = '', $orderByFields = '')
282
    {
283
        // Setting BE_USER by default
284
        $this->BE_USER = $GLOBALS['BE_USER'];
285
        // Setting clause
286
        if ($clause) {
287
            $this->clause = $clause;
288
        }
289
        if ($orderByFields) {
290
            $this->orderByFields = $orderByFields;
291
        }
292
        if (!is_array($this->MOUNTS)) {
293
            // Dummy
294
            $this->MOUNTS = [0 => 0];
295
        }
296
        // Sets the tree name which is used to identify the tree, used for JavaScript and other things
297
        $this->treeName = str_replace('_', '', $this->treeName ?: $this->table);
298
    }
299
300
    /**
301
     * Adds a fieldname to the internal array ->fieldArray
302
     *
303
     * @param string $field Field name to
304
     * @param bool $noCheck If set, the fieldname will be set no matter what. Otherwise the field name must either be found as key in $GLOBALS['TCA'][$table]['columns'] or in the list ->defaultList
305
     */
306
    public function addField($field, $noCheck = false)
307
    {
308
        if ($noCheck || is_array($GLOBALS['TCA'][$this->table]['columns'][$field]) || GeneralUtility::inList($this->defaultList, $field)) {
309
            $this->fieldArray[] = $field;
310
        }
311
    }
312
313
    /**
314
     * Resets the tree, recs, ids, ids_hierarchy and orig_ids_hierarchy internal variables. Use it if you need it.
315
     */
316
    public function reset()
317
    {
318
        $this->tree = [];
319
        $this->recs = [];
320
        $this->ids = [];
321
        $this->ids_hierarchy = [];
322
        $this->orig_ids_hierarchy = [];
323
    }
324
325
    /*******************************************
326
     *
327
     * rendering parts
328
     *
329
     *******************************************/
330
    /**
331
     * Generate the plus/minus icon for the browsable tree.
332
     *
333
     * @param array $row Record for the entry
334
     * @param int $a The current entry number
335
     * @param int $c The total number of entries. If equal to $a, a "bottom" element is returned.
336
     * @param int $nextCount The number of sub-elements to the current element.
337
     * @param bool $isOpen The element was expanded to render subelements if this flag is set.
338
     * @return string Image tag with the plus/minus icon.
339
     * @internal
340
     * @see \TYPO3\CMS\Backend\Tree\View\PageTreeView::PMicon()
341
     */
342
    public function PMicon($row, $a, $c, $nextCount, $isOpen)
343
    {
344
        if ($nextCount) {
345
            $cmd = $this->bank . '_' . ($isOpen ? '0_' : '1_') . $row['uid'] . '_' . $this->treeName;
346
            $bMark = $this->bank . '_' . $row['uid'];
347
            return $this->PM_ATagWrap('', $cmd, $bMark, $isOpen);
348
        }
349
        return '';
350
    }
351
352
    /**
353
     * Wrap the plus/minus icon in a link
354
     *
355
     * @param string $icon HTML string to wrap, probably an image tag.
356
     * @param string $cmd Command for 'PM' get var
357
     * @param string $bMark If set, the link will have an anchor point (=$bMark) and a name attribute (=$bMark)
358
     * @param bool $isOpen
359
     * @return string Link-wrapped input string
360
     * @internal
361
     */
362
    public function PM_ATagWrap($icon, $cmd, $bMark = '', $isOpen = false)
363
    {
364
        if ($this->thisScript) {
365
            $anchor = $bMark ? '#' . $bMark : '';
366
            $name = $bMark ? ' name="' . $bMark . '"' : '';
367
            $aUrl = $this->getThisScript() . 'PM=' . $cmd . $anchor;
368
            return '<a class="list-tree-control ' . ($isOpen ? 'list-tree-control-open' : 'list-tree-control-closed') . '" href="' . htmlspecialchars($aUrl) . '"' . $name . '><i class="fa"></i></a>';
369
        }
370
        return $icon;
371
    }
372
373
    /**
374
     * Wrapping the image tag, $icon, for the row, $row (except for mount points)
375
     *
376
     * @param string $icon The image tag for the icon
377
     * @param array $row The row for the current element
378
     * @return string The processed icon input value.
379
     * @internal
380
     */
381
    public function wrapIcon($icon, $row)
382
    {
383
        return $icon;
384
    }
385
386
    /**
387
     * Adds attributes to image tag.
388
     *
389
     * @param string $icon Icon image tag
390
     * @param string $attr Attributes to add, eg. ' border="0"'
391
     * @return string Image tag, modified with $attr attributes added.
392
     */
393
    public function addTagAttributes($icon, $attr)
394
    {
395
        return preg_replace('/ ?\\/?>$/', '', $icon) . ' ' . $attr . ' />';
396
    }
397
398
    /*******************************************
399
     *
400
     * tree handling
401
     *
402
     *******************************************/
403
    /**
404
     * Returns TRUE/FALSE if the next level for $id should be expanded - based on
405
     * data in $this->stored[][] and ->expandAll flag.
406
     * Extending parent function
407
     *
408
     * @param int $id Record id/key
409
     * @return bool
410
     * @internal
411
     * @see \TYPO3\CMS\Backend\Tree\View\PageTreeView::expandNext()
412
     */
413
    public function expandNext($id)
414
    {
415
        return !empty($this->stored[$this->bank][$id]) || $this->expandAll;
416
    }
417
418
    /**
419
     * Get stored tree structure AND updating it if needed according to incoming PM GET var.
420
     *
421
     * @internal
422
     */
423
    public function initializePositionSaving()
424
    {
425
        // Get stored tree structure:
426
        $this->stored = json_decode($this->BE_USER->uc['browseTrees'][$this->treeName], true);
427
        // PM action
428
        // (If a plus/minus icon has been clicked, the PM GET var is sent and we
429
        // must update the stored positions in the tree):
430
        // 0: mount key, 1: set/clear boolean, 2: item ID (cannot contain "_"), 3: treeName
431
        $PM = explode('_', GeneralUtility::_GP('PM'));
432
        if (count($PM) === 4 && $PM[3] == $this->treeName) {
433
            if (isset($this->MOUNTS[$PM[0]])) {
434
                // set
435
                if ($PM[1]) {
436
                    $this->stored[$PM[0]][$PM[2]] = 1;
437
                    $this->savePosition();
438
                } else {
439
                    unset($this->stored[$PM[0]][$PM[2]]);
440
                    $this->savePosition();
441
                }
442
            }
443
        }
444
    }
445
446
    /**
447
     * Saves the content of ->stored (keeps track of expanded positions in the tree)
448
     * $this->treeName will be used as key for BE_USER->uc[] to store it in
449
     *
450
     * @internal
451
     */
452
    public function savePosition()
453
    {
454
        $this->BE_USER->uc['browseTrees'][$this->treeName] = json_encode($this->stored);
455
        $this->BE_USER->writeUC();
456
    }
457
458
    /******************************
459
     *
460
     * Functions that might be overwritten by extended classes
461
     *
462
     ********************************/
463
    /**
464
     * Returns the root icon for a tree/mountpoint (defaults to the globe)
465
     *
466
     * @param array $rec Record for root.
467
     * @return string Icon image tag.
468
     */
469
    public function getRootIcon($rec)
470
    {
471
        $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
472
        return $this->wrapIcon($iconFactory->getIcon('apps-pagetree-root', Icon::SIZE_SMALL)->render(), $rec);
473
    }
474
475
    /**
476
     * Get the icon markup for the row
477
     *
478
     * @param array $row The row to get the icon for
479
     * @return string The icon markup, wrapped into a span tag, with the records title as title attribute
480
     */
481
    public function getIcon($row): string
482
    {
483
        if (is_int($row)) {
0 ignored issues
show
introduced by
The condition is_int($row) is always false.
Loading history...
484
            trigger_error(
485
                'Calling ' . __METHOD__ . ' with argument $row containing the records uid is deprecated and will be removed in v12. Use the full row instead.',
486
                E_USER_DEPRECATED
487
            );
488
489
            $row = BackendUtility::getRecord($this->table, $row);
490
            if ($row === null) {
491
                return '';
492
            }
493
        }
494
        $title = $this->showDefaultTitleAttribute ? htmlspecialchars('UID: ' . $row['uid']) : $this->getTitleAttrib($row);
495
        $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
496
        $icon = $row['is_siteroot'] ? $iconFactory->getIcon('apps-pagetree-folder-root', Icon::SIZE_SMALL) : $iconFactory->getIconForRecord($this->table, $row, Icon::SIZE_SMALL);
497
        $icon = '<span title="' . $title . '">' . $icon->render() . '</span>';
498
        return $this->wrapIcon($icon, $row);
499
    }
500
501
    /**
502
     * Returns the title for the input record. If blank, a "no title" label (localized) will be returned.
503
     * Do NOT htmlspecialchar the string from this function - has already been done.
504
     *
505
     * @param array $row The input row array (where the key "title" is used for the title)
506
     * @param int $titleLen Title length (30)
507
     * @return string The title.
508
     */
509
    public function getTitleStr($row, $titleLen = 30)
510
    {
511
        $title = htmlspecialchars(GeneralUtility::fixed_lgd_cs($row['title'], $titleLen));
512
        $title = trim($row['title']) === '' ? '<em>[' . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.no_title')) . ']</em>' : $title;
513
        return $title;
514
    }
515
516
    /**
517
     * Returns the value for the image "title" attribute
518
     *
519
     * @param array $row The input row array (where the key "title" is used for the title)
520
     * @return string The attribute value (is htmlspecialchared() already)
521
     * @see wrapIcon()
522
     */
523
    public function getTitleAttrib($row)
524
    {
525
        return htmlspecialchars($row['title']);
526
    }
527
528
    /**
529
     * Returns the id from the record (typ. uid)
530
     *
531
     * @param array $row Record array
532
     * @return int The "uid" field value.
533
     */
534
    public function getId($row)
535
    {
536
        return $row['uid'];
537
    }
538
539
    /********************************
540
     *
541
     * tree data building
542
     *
543
     ********************************/
544
    /**
545
     * Fetches the data for the tree
546
     *
547
     * @param int $uid item id for which to select subitems (parent id)
548
     * @param int $depth Max depth (recursivity limit)
549
     * @param string $depthData HTML-code prefix for recursive calls.
550
     * @return int The count of items on the level
551
     */
552
    public function getTree($uid, $depth = 999, $depthData = '')
553
    {
554
        // Buffer for id hierarchy is reset:
555
        $this->buffer_idH = [];
556
        // Init vars
557
        $depth = (int)$depth;
558
        $HTML = '';
559
        $a = 0;
560
        $res = $this->getDataInit($uid);
561
        $c = $this->getDataCount($res);
562
        $crazyRecursionLimiter = 9999;
563
        $idH = [];
564
        // Traverse the records:
565
        while ($crazyRecursionLimiter > 0 && ($row = $this->getDataNext($res))) {
566
            /** @var array $row */
567
            if (!$this->getBackendUser()->isInWebMount($this->table === 'pages' ? $row : $row['pid'])) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->getBackendUser()-...' ? $row : $row['pid']) of type integer|null is loosely compared to false; this is ambiguous if the integer can be 0. You might want to explicitly use === null 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...
568
                // Current record is not within web mount => skip it
569
                continue;
570
            }
571
572
            $a++;
573
            $crazyRecursionLimiter--;
574
            $newID = $row['uid'];
575
            if ($newID == 0) {
576
                throw new \RuntimeException('Endless recursion detected: TYPO3 has detected an error in the database. Please fix it manually (e.g. using phpMyAdmin) and change the UID of ' . $this->table . ':0 to a new value. See https://forge.typo3.org/issues/16150 to get more information about a possible cause.', 1294586383);
577
            }
578
            // Reserve space.
579
            $this->tree[] = [];
580
            end($this->tree);
581
            // Get the key for this space
582
            $treeKey = key($this->tree);
583
            // If records should be accumulated, do so
584
            if ($this->setRecs) {
585
                $this->recs[$row['uid']] = $row;
586
            }
587
            // Accumulate the id of the element in the internal arrays
588
            $this->ids[] = ($idH[$row['uid']]['uid'] = $row['uid']);
589
            $this->ids_hierarchy[$depth][] = $row['uid'];
590
            $this->orig_ids_hierarchy[$depth][] = $row['_ORIG_uid'] ?: $row['uid'];
591
592
            // Make a recursive call to the next level
593
            $nextLevelDepthData = $depthData . '<span class="treeline-icon treeline-icon-' . ($a === $c ? 'clear' : 'line') . '"></span>';
594
            $hasSub = $this->expandNext($newID) && !$row['php_tree_stop'];
595
            if ($depth > 1 && $hasSub) {
596
                $nextCount = $this->getTree($newID, $depth - 1, $nextLevelDepthData);
597
                if (!empty($this->buffer_idH)) {
598
                    $idH[$row['uid']]['subrow'] = $this->buffer_idH;
599
                }
600
                // Set "did expand" flag
601
                $isOpen = true;
602
            } else {
603
                $nextCount = $this->getCount($newID);
604
                // Clear "did expand" flag
605
                $isOpen = false;
606
            }
607
            // Set HTML-icons, if any:
608
            if ($this->makeHTML) {
609
                $HTML = $this->PMicon($row, $a, $c, $nextCount, $isOpen);
610
            }
611
            // Finally, add the row/HTML content to the ->tree array in the reserved key.
612
            $this->tree[$treeKey] = [
613
                'row' => $row,
614
                'HTML' => $HTML,
615
                'invertedDepth' => $depth,
616
                'depthData' => $depthData,
617
                'bank' => $this->bank,
618
                'hasSub' => $nextCount && $hasSub,
619
                'isFirst' => $a === 1,
620
                'isLast' => $a === $c,
621
            ];
622
        }
623
624
        $this->getDataFree($res);
625
        $this->buffer_idH = $idH;
626
        return $c;
627
    }
628
629
    /********************************
630
     *
631
     * Data handling
632
     * Works with records and arrays
633
     *
634
     ********************************/
635
    /**
636
     * Returns the number of records having the parent id, $uid
637
     *
638
     * @param int $uid Id to count subitems for
639
     * @return int
640
     * @internal
641
     */
642
    public function getCount($uid)
643
    {
644
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->table);
645
        $queryBuilder->getRestrictions()
646
                ->removeAll()
647
                ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
648
                ->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, (int)$this->BE_USER->workspace));
649
        $count = $queryBuilder
650
                ->count('uid')
651
                ->from($this->table)
652
                ->where(
653
                    $queryBuilder->expr()->eq(
654
                        $this->parentField,
655
                        $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)
656
                    ),
657
                    QueryHelper::stripLogicalOperatorPrefix($this->clause)
658
                )
659
                ->execute()
660
                ->fetchColumn();
661
662
        return (int)$count;
663
    }
664
665
    /**
666
     * Returns root record for uid (<=0)
667
     *
668
     * @return array Array with title/uid keys with values of $this->title/0 (zero)
669
     */
670
    public function getRootRecord()
671
    {
672
        return ['title' => $this->title, 'uid' => 0];
673
    }
674
675
    /**
676
     * Returns the record for a uid.
677
     * For tables: Looks up the record in the database.
678
     * For arrays: Returns the fake record for uid id.
679
     *
680
     * @param int $uid UID to look up
681
     * @return array The record
682
     */
683
    public function getRecord($uid)
684
    {
685
        return BackendUtility::getRecordWSOL($this->table, $uid);
686
    }
687
688
    /**
689
     * Getting the tree data: Selecting/Initializing data pointer to items for a certain parent id.
690
     * For tables: This will make a database query to select all children to "parent"
691
     * For arrays: This will return key to the ->dataLookup array
692
     *
693
     * @param int $parentId parent item id
694
     *
695
     * @return mixed Data handle (Tables: An sql-resource, arrays: A parentId integer. -1 is returned if there were NO subLevel.)
696
     * @internal
697
     */
698
    public function getDataInit($parentId)
699
    {
700
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->table);
701
        $queryBuilder->getRestrictions()
702
                ->removeAll()
703
                ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
704
                ->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, (int)$this->BE_USER->workspace));
705
        $queryBuilder
706
                ->select(...$this->fieldArray)
707
                ->from($this->table)
708
                ->where(
709
                    $queryBuilder->expr()->eq(
710
                        $this->parentField,
711
                        $queryBuilder->createNamedParameter($parentId, \PDO::PARAM_INT)
712
                    ),
713
                    QueryHelper::stripLogicalOperatorPrefix($this->clause)
714
                );
715
716
        foreach (QueryHelper::parseOrderBy($this->orderByFields) as $orderPair) {
717
            [$fieldName, $order] = $orderPair;
718
            $queryBuilder->addOrderBy($fieldName, $order);
719
        }
720
721
        return $queryBuilder->execute();
722
    }
723
724
    /**
725
     * Getting the tree data: Counting elements in resource
726
     *
727
     * @param mixed $res Data handle
728
     * @return int number of items
729
     * @internal
730
     * @see getDataInit()
731
     */
732
    public function getDataCount(&$res)
733
    {
734
        return $res->rowCount();
735
    }
736
737
    /**
738
     * Getting the tree data: next entry
739
     *
740
     * @param mixed $res Data handle
741
     *
742
     * @return array|bool item data array OR FALSE if end of elements.
743
     * @internal
744
     * @see getDataInit()
745
     */
746
    public function getDataNext(&$res)
747
    {
748
        while ($row = $res->fetch()) {
749
            BackendUtility::workspaceOL($this->table, $row, $this->BE_USER->workspace, true);
750
            if (is_array($row)) {
751
                break;
752
            }
753
        }
754
        return $row;
755
    }
756
757
    /**
758
     * Getting the tree data: frees data handle
759
     *
760
     * @param mixed $res Data handle
761
     * @internal
762
     */
763
    public function getDataFree(&$res)
764
    {
765
        $res->closeCursor();
766
    }
767
768
    /**
769
     * @return LanguageService
770
     */
771
    protected function getLanguageService()
772
    {
773
        return $GLOBALS['LANG'];
774
    }
775
776
    /**
777
     * @return BackendUserAuthentication
778
     */
779
    protected function getBackendUser()
780
    {
781
        return $GLOBALS['BE_USER'];
782
    }
783
}
784