QueryGenerator   F
last analyzed

Complexity

Total Complexity 323

Size/Duplication

Total Lines 1713
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 323
eloc 973
dl 0
loc 1713
rs 1.627
c 0
b 0
f 0

36 Methods

Rating   Name   Duplication   Size   Complexity  
A getLanguageService() 0 3 1
A setAndCleanUpExternalLists() 0 10 3
A makeFieldList() 0 22 6
D init() 0 127 32
A makeComparisonSelector() 0 12 3
D procesData() 0 98 17
C cleanUpQueryConfig() 0 47 13
A printCodeArray() 0 22 4
D cleanInputVal() 0 26 22
A getSubscript() 0 14 3
F getFormElements() 0 138 39
A mkTableSelect() 0 12 4
C getTreeList() 0 41 12
F makeOptionList() 0 217 58
A getLabelCol() 0 3 1
A userDef() 0 3 1
C getSelectQuery() 0 69 13
A mkTypeSelect() 0 13 5
A verifyComparison() 0 13 4
A mkOperatorSelect() 0 14 6
A verifyType() 0 12 4
A mkCompSelect() 0 12 4
A convertIso8601DatetimeStringToUnixTimestamp() 0 10 3
A getBackendUserAuthentication() 0 3 1
A userDefCleanUp() 0 3 1
A getUserDefQuery() 0 2 1
F makeSelectorTable() 0 137 26
A setFormName() 0 3 1
A getQuery() 0 24 4
C getQuerySingle() 0 42 17
A updateIcon() 0 3 1
A getDateTimePickerField() 0 15 2
A mkFieldToInputSelect() 0 19 5
A initUserDef() 0 2 1
A isDateOfIso8601Format() 0 8 4
A __construct() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like QueryGenerator often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use QueryGenerator, and based on these observations, apply Extract Interface, too.

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\Core\Database;
17
18
use TYPO3\CMS\Backend\Utility\BackendUtility;
19
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
20
use TYPO3\CMS\Core\Database\Query\QueryHelper;
21
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
22
use TYPO3\CMS\Core\Localization\LanguageService;
23
use TYPO3\CMS\Core\Type\Bitmask\Permission;
24
use TYPO3\CMS\Core\Utility\GeneralUtility;
25
use TYPO3\CMS\Core\Utility\MathUtility;
26
use TYPO3\CMS\Core\Utility\StringUtility;
27
28
/**
29
 * Class for generating front end for building queries
30
 *
31
 * @deprecated since v11, will be removed in v12
32
 */
33
class QueryGenerator
34
{
35
    /**
36
     * @var array
37
     */
38
    public $lang = [
39
        'OR' => 'or',
40
        'AND' => 'and',
41
        'comparison' => [
42
            // Type = text	offset = 0
43
            '0_' => 'contains',
44
            '1_' => 'does not contain',
45
            '2_' => 'starts with',
46
            '3_' => 'does not start with',
47
            '4_' => 'ends with',
48
            '5_' => 'does not end with',
49
            '6_' => 'equals',
50
            '7_' => 'does not equal',
51
            // Type = number , offset = 32
52
            '32_' => 'equals',
53
            '33_' => 'does not equal',
54
            '34_' => 'is greater than',
55
            '35_' => 'is less than',
56
            '36_' => 'is between',
57
            '37_' => 'is not between',
58
            '38_' => 'is in list',
59
            '39_' => 'is not in list',
60
            '40_' => 'binary AND equals',
61
            '41_' => 'binary AND does not equal',
62
            '42_' => 'binary OR equals',
63
            '43_' => 'binary OR does not equal',
64
            // Type = multiple, relation, offset = 64
65
            '64_' => 'equals',
66
            '65_' => 'does not equal',
67
            '66_' => 'contains',
68
            '67_' => 'does not contain',
69
            '68_' => 'is in list',
70
            '69_' => 'is not in list',
71
            '70_' => 'binary AND equals',
72
            '71_' => 'binary AND does not equal',
73
            '72_' => 'binary OR equals',
74
            '73_' => 'binary OR does not equal',
75
            // Type = date,time  offset = 96
76
            '96_' => 'equals',
77
            '97_' => 'does not equal',
78
            '98_' => 'is greater than',
79
            '99_' => 'is less than',
80
            '100_' => 'is between',
81
            '101_' => 'is not between',
82
            '102_' => 'binary AND equals',
83
            '103_' => 'binary AND does not equal',
84
            '104_' => 'binary OR equals',
85
            '105_' => 'binary OR does not equal',
86
            // Type = boolean,  offset = 128
87
            '128_' => 'is True',
88
            '129_' => 'is False',
89
            // Type = binary , offset = 160
90
            '160_' => 'equals',
91
            '161_' => 'does not equal',
92
            '162_' => 'contains',
93
            '163_' => 'does not contain'
94
        ]
95
    ];
96
97
    /**
98
     * @var array
99
     */
100
    public $compSQL = [
101
        // Type = text	offset = 0
102
        '0' => '#FIELD# LIKE \'%#VALUE#%\'',
103
        '1' => '#FIELD# NOT LIKE \'%#VALUE#%\'',
104
        '2' => '#FIELD# LIKE \'#VALUE#%\'',
105
        '3' => '#FIELD# NOT LIKE \'#VALUE#%\'',
106
        '4' => '#FIELD# LIKE \'%#VALUE#\'',
107
        '5' => '#FIELD# NOT LIKE \'%#VALUE#\'',
108
        '6' => '#FIELD# = \'#VALUE#\'',
109
        '7' => '#FIELD# != \'#VALUE#\'',
110
        // Type = number, offset = 32
111
        '32' => '#FIELD# = \'#VALUE#\'',
112
        '33' => '#FIELD# != \'#VALUE#\'',
113
        '34' => '#FIELD# > #VALUE#',
114
        '35' => '#FIELD# < #VALUE#',
115
        '36' => '#FIELD# >= #VALUE# AND #FIELD# <= #VALUE1#',
116
        '37' => 'NOT (#FIELD# >= #VALUE# AND #FIELD# <= #VALUE1#)',
117
        '38' => '#FIELD# IN (#VALUE#)',
118
        '39' => '#FIELD# NOT IN (#VALUE#)',
119
        '40' => '(#FIELD# & #VALUE#)=#VALUE#',
120
        '41' => '(#FIELD# & #VALUE#)!=#VALUE#',
121
        '42' => '(#FIELD# | #VALUE#)=#VALUE#',
122
        '43' => '(#FIELD# | #VALUE#)!=#VALUE#',
123
        // Type = multiple, relation, offset = 64
124
        '64' => '#FIELD# = \'#VALUE#\'',
125
        '65' => '#FIELD# != \'#VALUE#\'',
126
        '66' => '#FIELD# LIKE \'%#VALUE#%\' AND #FIELD# LIKE \'%#VALUE1#%\'',
127
        '67' => '(#FIELD# NOT LIKE \'%#VALUE#%\' OR #FIELD# NOT LIKE \'%#VALUE1#%\')',
128
        '68' => '#FIELD# IN (#VALUE#)',
129
        '69' => '#FIELD# NOT IN (#VALUE#)',
130
        '70' => '(#FIELD# & #VALUE#)=#VALUE#',
131
        '71' => '(#FIELD# & #VALUE#)!=#VALUE#',
132
        '72' => '(#FIELD# | #VALUE#)=#VALUE#',
133
        '73' => '(#FIELD# | #VALUE#)!=#VALUE#',
134
        // Type = date, offset = 32
135
        '96' => '#FIELD# = \'#VALUE#\'',
136
        '97' => '#FIELD# != \'#VALUE#\'',
137
        '98' => '#FIELD# > #VALUE#',
138
        '99' => '#FIELD# < #VALUE#',
139
        '100' => '#FIELD# >= #VALUE# AND #FIELD# <= #VALUE1#',
140
        '101' => 'NOT (#FIELD# >= #VALUE# AND #FIELD# <= #VALUE1#)',
141
        '102' => '(#FIELD# & #VALUE#)=#VALUE#',
142
        '103' => '(#FIELD# & #VALUE#)!=#VALUE#',
143
        '104' => '(#FIELD# | #VALUE#)=#VALUE#',
144
        '105' => '(#FIELD# | #VALUE#)!=#VALUE#',
145
        // Type = boolean, offset = 128
146
        '128' => '#FIELD# = \'1\'',
147
        '129' => '#FIELD# != \'1\'',
148
        // Type = binary = 160
149
        '160' => '#FIELD# = \'#VALUE#\'',
150
        '161' => '#FIELD# != \'#VALUE#\'',
151
        '162' => '(#FIELD# & #VALUE#)=#VALUE#',
152
        '163' => '(#FIELD# & #VALUE#)=0'
153
    ];
154
155
    /**
156
     * @var array
157
     */
158
    public $comp_offsets = [
159
        'text' => 0,
160
        'number' => 1,
161
        'multiple' => 2,
162
        'relation' => 2,
163
        'date' => 3,
164
        'time' => 3,
165
        'boolean' => 4,
166
        'binary' => 5
167
    ];
168
169
    /**
170
     * @var string
171
     */
172
    public $noWrap = ' nowrap';
173
174
    /**
175
     * Form data name prefix
176
     *
177
     * @var string
178
     */
179
    public $name;
180
181
    /**
182
     * Table for the query
183
     *
184
     * @var string
185
     */
186
    public $table;
187
188
    /**
189
     * @var array
190
     */
191
    public $tableArray;
192
193
    /**
194
     * Field list
195
     *
196
     * @var string
197
     */
198
    public $fieldList;
199
200
    /**
201
     * Array of the fields possible
202
     *
203
     * @var array
204
     */
205
    public $fields = [];
206
207
    /**
208
     * @var array
209
     */
210
    public $extFieldLists = [];
211
212
    /**
213
     * The query config
214
     *
215
     * @var array
216
     */
217
    public $queryConfig = [];
218
219
    /**
220
     * @var bool
221
     */
222
    public $enablePrefix = false;
223
224
    /**
225
     * @var bool
226
     */
227
    public $enableQueryParts = false;
228
229
    /**
230
     * @var string
231
     */
232
    protected $formName = '';
233
234
    /**
235
     * @var string
236
     */
237
    protected $fieldName;
238
239
    /**
240
     * @var array Settings, usually from the controller, previously known as MOD_SETTINGS
241
     */
242
    protected $settings = [];
243
244
    public function __construct()
245
    {
246
        trigger_error(__CLASS__ . ' will be removed in TYPO3 v12.', E_USER_DEPRECATED);
247
    }
248
249
    /**
250
     * Make a list of fields for current table
251
     *
252
     * @return string Separated list of fields
253
     */
254
    public function makeFieldList()
255
    {
256
        $fieldListArr = [];
257
        if (is_array($GLOBALS['TCA'][$this->table])) {
258
            $fieldListArr = array_keys($GLOBALS['TCA'][$this->table]['columns'] ?? []);
259
            $fieldListArr[] = 'uid';
260
            $fieldListArr[] = 'pid';
261
            $fieldListArr[] = 'deleted';
262
            if ($GLOBALS['TCA'][$this->table]['ctrl']['tstamp'] ?? false) {
263
                $fieldListArr[] = $GLOBALS['TCA'][$this->table]['ctrl']['tstamp'];
264
            }
265
            if ($GLOBALS['TCA'][$this->table]['ctrl']['crdate'] ?? false) {
266
                $fieldListArr[] = $GLOBALS['TCA'][$this->table]['ctrl']['crdate'];
267
            }
268
            if ($GLOBALS['TCA'][$this->table]['ctrl']['cruser_id'] ?? false) {
269
                $fieldListArr[] = $GLOBALS['TCA'][$this->table]['ctrl']['cruser_id'];
270
            }
271
            if ($GLOBALS['TCA'][$this->table]['ctrl']['sortby'] ?? false) {
272
                $fieldListArr[] = $GLOBALS['TCA'][$this->table]['ctrl']['sortby'];
273
            }
274
        }
275
        return implode(',', $fieldListArr);
276
    }
277
278
    /**
279
     * Init function
280
     *
281
     * @param string $name The name
282
     * @param string $table The table name
283
     * @param string $fieldList The field list
284
     * @param array $settings Module settings like checkboxes in the interface
285
     */
286
    public function init($name, $table, $fieldList = '', array $settings = [])
287
    {
288
        // Analysing the fields in the table.
289
        if (is_array($GLOBALS['TCA'][$table])) {
290
            $this->name = $name;
291
            $this->table = $table;
292
            $this->fieldList = $fieldList ?: $this->makeFieldList();
293
            $this->settings = $settings;
294
            $fieldArr = GeneralUtility::trimExplode(',', $this->fieldList, true);
295
            foreach ($fieldArr as $fieldName) {
296
                $fC = $GLOBALS['TCA'][$this->table]['columns'][$fieldName];
297
                $this->fields[$fieldName] = $fC['config'];
298
                $this->fields[$fieldName]['exclude'] = $fC['exclude'];
299
                if ($this->fields[$fieldName]['type'] === 'user' && !isset($this->fields[$fieldName]['type']['userFunc'])
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: ($this->fields[$fieldNam...ame]['type'] === 'none', Probably Intended Meaning: $this->fields[$fieldName...me]['type'] === 'none')
Loading history...
300
                    || $this->fields[$fieldName]['type'] === 'none'
301
                ) {
302
                    // Do not list type=none "virtual" fields or query them from db,
303
                    // and if type is user without defined userFunc
304
                    unset($this->fields[$fieldName]);
305
                    continue;
306
                }
307
                if (is_array($fC) && $fC['label']) {
308
                    $this->fields[$fieldName]['label'] = rtrim(trim($this->getLanguageService()->sL($fC['label'])), ':');
309
                    switch ($this->fields[$fieldName]['type']) {
310
                        case 'input':
311
                            if (preg_match('/int|year/i', $this->fields[$fieldName]['eval'])) {
312
                                $this->fields[$fieldName]['type'] = 'number';
313
                            } elseif (preg_match('/time/i', $this->fields[$fieldName]['eval'])) {
314
                                $this->fields[$fieldName]['type'] = 'time';
315
                            } elseif (preg_match('/date/i', $this->fields[$fieldName]['eval'])) {
316
                                $this->fields[$fieldName]['type'] = 'date';
317
                            } else {
318
                                $this->fields[$fieldName]['type'] = 'text';
319
                            }
320
                            break;
321
                        case 'check':
322
                            if (!$this->fields[$fieldName]['items'] || count($this->fields[$fieldName]['items']) <= 1) {
323
                                $this->fields[$fieldName]['type'] = 'boolean';
324
                            } else {
325
                                $this->fields[$fieldName]['type'] = 'binary';
326
                            }
327
                            break;
328
                        case 'radio':
329
                            $this->fields[$fieldName]['type'] = 'multiple';
330
                            break;
331
                        case 'select':
332
                            $this->fields[$fieldName]['type'] = 'multiple';
333
                            if ($this->fields[$fieldName]['foreign_table']) {
334
                                $this->fields[$fieldName]['type'] = 'relation';
335
                            }
336
                            if ($this->fields[$fieldName]['special']) {
337
                                $this->fields[$fieldName]['type'] = 'text';
338
                            }
339
                            break;
340
                        case 'group':
341
                            if ($this->fields[$fieldName]['internal_type'] === 'db') {
342
                                $this->fields[$fieldName]['type'] = 'relation';
343
                            }
344
                            break;
345
                        case 'user':
346
                        case 'flex':
347
                        case 'passthrough':
348
                        case 'none':
349
                        case 'text':
350
                        default:
351
                            $this->fields[$fieldName]['type'] = 'text';
352
                    }
353
                } else {
354
                    $this->fields[$fieldName]['label'] = '[FIELD: ' . $fieldName . ']';
355
                    switch ($fieldName) {
356
                        case 'pid':
357
                            $this->fields[$fieldName]['type'] = 'relation';
358
                            $this->fields[$fieldName]['allowed'] = 'pages';
359
                            break;
360
                        case 'cruser_id':
361
                            $this->fields[$fieldName]['type'] = 'relation';
362
                            $this->fields[$fieldName]['allowed'] = 'be_users';
363
                            break;
364
                        case 'tstamp':
365
                        case 'crdate':
366
                            $this->fields[$fieldName]['type'] = 'time';
367
                            break;
368
                        case 'deleted':
369
                            $this->fields[$fieldName]['type'] = 'boolean';
370
                            break;
371
                        default:
372
                            $this->fields[$fieldName]['type'] = 'number';
373
                    }
374
                }
375
            }
376
        }
377
        /*	// EXAMPLE:
378
        $this->queryConfig = array(
379
        array(
380
        'operator' => 'AND',
381
        'type' => 'FIELD_space_before_class',
382
        ),
383
        array(
384
        'operator' => 'AND',
385
        'type' => 'FIELD_records',
386
        'negate' => 1,
387
        'inputValue' => 'foo foo'
388
        ),
389
        array(
390
        'type' => 'newlevel',
391
        'nl' => array(
392
        array(
393
        'operator' => 'AND',
394
        'type' => 'FIELD_space_before_class',
395
        'negate' => 1,
396
        'inputValue' => 'foo foo'
397
        ),
398
        array(
399
        'operator' => 'AND',
400
        'type' => 'FIELD_records',
401
        'negate' => 1,
402
        'inputValue' => 'foo foo'
403
        )
404
        )
405
        ),
406
        array(
407
        'operator' => 'OR',
408
        'type' => 'FIELD_maillist',
409
        )
410
        );
411
         */
412
        $this->initUserDef();
413
    }
414
415
    /**
416
     * Set and clean up external lists
417
     *
418
     * @param string $name The name
419
     * @param string $list The list
420
     * @param string $force
421
     */
422
    public function setAndCleanUpExternalLists($name, $list, $force = '')
423
    {
424
        $fields = array_unique(GeneralUtility::trimExplode(',', $list . ',' . $force, true));
425
        $reList = [];
426
        foreach ($fields as $fieldName) {
427
            if ($this->fields[$fieldName]) {
428
                $reList[] = $fieldName;
429
            }
430
        }
431
        $this->extFieldLists[$name] = implode(',', $reList);
432
    }
433
434
    /**
435
     * Process data
436
     *
437
     * @param array $qC Query config
438
     */
439
    public function procesData($qC = [])
440
    {
441
        $this->queryConfig = $qC;
442
        $POST = GeneralUtility::_POST();
443
        // If delete...
444
        if ($POST['qG_del']) {
445
            // Initialize array to work on, save special parameters
446
            $ssArr = $this->getSubscript($POST['qG_del']);
447
            $workArr = &$this->queryConfig;
448
            $ssArrSize = count($ssArr) - 1;
449
            $i = 0;
450
            for (; $i < $ssArrSize; $i++) {
451
                $workArr = &$workArr[$ssArr[$i]];
452
            }
453
            // Delete the entry and move the other entries
454
            unset($workArr[$ssArr[$i]]);
455
            $workArrSize = count($workArr);
456
            for ($j = $ssArr[$i]; $j < $workArrSize; $j++) {
457
                $workArr[$j] = $workArr[$j + 1];
458
                unset($workArr[$j + 1]);
459
            }
460
        }
461
        // If insert...
462
        if ($POST['qG_ins']) {
463
            // Initialize array to work on, save special parameters
464
            $ssArr = $this->getSubscript($POST['qG_ins']);
465
            $workArr = &$this->queryConfig;
466
            $ssArrSize = count($ssArr) - 1;
467
            $i = 0;
468
            for (; $i < $ssArrSize; $i++) {
469
                $workArr = &$workArr[$ssArr[$i]];
470
            }
471
            // Move all entries above position where new entry is to be inserted
472
            $workArrSize = count($workArr);
473
            for ($j = $workArrSize; $j > $ssArr[$i]; $j--) {
474
                $workArr[$j] = $workArr[$j - 1];
475
            }
476
            // Clear new entry position
477
            unset($workArr[$ssArr[$i] + 1]);
478
            $workArr[$ssArr[$i] + 1]['type'] = 'FIELD_';
479
        }
480
        // If move up...
481
        if ($POST['qG_up']) {
482
            // Initialize array to work on
483
            $ssArr = $this->getSubscript($POST['qG_up']);
484
            $workArr = &$this->queryConfig;
485
            $ssArrSize = count($ssArr) - 1;
486
            $i = 0;
487
            for (; $i < $ssArrSize; $i++) {
488
                $workArr = &$workArr[$ssArr[$i]];
489
            }
490
            // Swap entries
491
            $qG_tmp = $workArr[$ssArr[$i]];
492
            $workArr[$ssArr[$i]] = $workArr[$ssArr[$i] - 1];
493
            $workArr[$ssArr[$i] - 1] = $qG_tmp;
494
        }
495
        // If new level...
496
        if ($POST['qG_nl']) {
497
            // Initialize array to work on
498
            $ssArr = $this->getSubscript($POST['qG_nl']);
499
            $workArr = &$this->queryConfig;
500
            $ssArraySize = count($ssArr) - 1;
501
            $i = 0;
502
            for (; $i < $ssArraySize; $i++) {
503
                $workArr = &$workArr[$ssArr[$i]];
504
            }
505
            // Do stuff:
506
            $tempEl = $workArr[$ssArr[$i]];
507
            if (is_array($tempEl)) {
508
                if ($tempEl['type'] !== 'newlevel') {
509
                    $workArr[$ssArr[$i]] = [
510
                        'type' => 'newlevel',
511
                        'operator' => $tempEl['operator'],
512
                        'nl' => [$tempEl]
513
                    ];
514
                }
515
            }
516
        }
517
        // If collapse level...
518
        if ($POST['qG_remnl']) {
519
            // Initialize array to work on
520
            $ssArr = $this->getSubscript($POST['qG_remnl']);
521
            $workArr = &$this->queryConfig;
522
            $ssArrSize = count($ssArr) - 1;
523
            $i = 0;
524
            for (; $i < $ssArrSize; $i++) {
525
                $workArr = &$workArr[$ssArr[$i]];
526
            }
527
            // Do stuff:
528
            $tempEl = $workArr[$ssArr[$i]];
529
            if (is_array($tempEl)) {
530
                if ($tempEl['type'] === 'newlevel') {
531
                    $a1 = array_slice($workArr, 0, $ssArr[$i]);
532
                    $a2 = array_slice($workArr, $ssArr[$i]);
533
                    array_shift($a2);
534
                    $a3 = $tempEl['nl'];
535
                    $a3[0]['operator'] = $tempEl['operator'];
536
                    $workArr = array_merge($a1, $a3, $a2);
537
                }
538
            }
539
        }
540
    }
541
542
    /**
543
     * Clean up query config
544
     *
545
     * @param array $queryConfig Query config
546
     * @return array
547
     */
548
    public function cleanUpQueryConfig($queryConfig)
549
    {
550
        // Since we don't traverse the array using numeric keys in the upcoming while-loop make sure it's fresh and clean before displaying
551
        if (is_array($queryConfig)) {
0 ignored issues
show
introduced by
The condition is_array($queryConfig) is always true.
Loading history...
552
            ksort($queryConfig);
553
        } else {
554
            // queryConfig should never be empty!
555
            if (!isset($queryConfig[0]) || empty($queryConfig[0]['type'])) {
556
                // Make sure queryConfig is an array
557
                $queryConfig = [];
558
                $queryConfig[0] = ['type' => 'FIELD_'];
559
            }
560
        }
561
        // Traverse:
562
        foreach ($queryConfig as $key => $conf) {
563
            $fieldName = '';
564
            if (strpos($conf['type'], 'FIELD_') === 0) {
565
                $fieldName = substr($conf['type'], 6);
566
                $fieldType = $this->fields[$fieldName]['type'];
567
            } elseif ($conf['type'] === 'newlevel') {
568
                $fieldType = $conf['type'];
569
            } else {
570
                $fieldType = 'ignore';
571
            }
572
            switch ($fieldType) {
573
                case 'newlevel':
574
                    if (!$queryConfig[$key]['nl']) {
575
                        $queryConfig[$key]['nl'][0]['type'] = 'FIELD_';
576
                    }
577
                    $queryConfig[$key]['nl'] = $this->cleanUpQueryConfig($queryConfig[$key]['nl']);
578
                    break;
579
                case 'userdef':
580
                    $queryConfig[$key] = $this->userDefCleanUp($queryConfig[$key]);
581
                    break;
582
                case 'ignore':
583
                default:
584
                    $verifiedName = $this->verifyType($fieldName);
585
                    $queryConfig[$key]['type'] = 'FIELD_' . $this->verifyType($verifiedName);
586
                    if ($conf['comparison'] >> 5 != $this->comp_offsets[$fieldType]) {
587
                        $conf['comparison'] = $this->comp_offsets[$fieldType] << 5;
588
                    }
589
                    $queryConfig[$key]['comparison'] = $this->verifyComparison($conf['comparison'], $conf['negate'] ? 1 : 0);
590
                    $queryConfig[$key]['inputValue'] = $this->cleanInputVal($queryConfig[$key]);
591
                    $queryConfig[$key]['inputValue1'] = $this->cleanInputVal($queryConfig[$key], '1');
592
            }
593
        }
594
        return $queryConfig;
595
    }
596
597
    /**
598
     * Get form elements
599
     *
600
     * @param int $subLevel
601
     * @param string $queryConfig
602
     * @param string $parent
603
     * @return array
604
     */
605
    public function getFormElements($subLevel = 0, $queryConfig = '', $parent = '')
606
    {
607
        $codeArr = [];
608
        if (!is_array($queryConfig)) {
0 ignored issues
show
introduced by
The condition is_array($queryConfig) is always false.
Loading history...
609
            $queryConfig = $this->queryConfig;
610
        }
611
        $c = 0;
612
        $arrCount = 0;
613
        $loopCount = 0;
614
        foreach ($queryConfig as $key => $conf) {
615
            $fieldName = '';
616
            $subscript = $parent . '[' . $key . ']';
617
            $lineHTML = [];
618
            $lineHTML[] = $this->mkOperatorSelect($this->name . $subscript, $conf['operator'], $c > 0, $conf['type'] !== 'FIELD_');
619
            if (strpos($conf['type'], 'FIELD_') === 0) {
620
                $fieldName = substr($conf['type'], 6);
621
                $this->fieldName = $fieldName;
622
                $fieldType = $this->fields[$fieldName]['type'];
623
                if ($conf['comparison'] >> 5 != $this->comp_offsets[$fieldType]) {
624
                    $conf['comparison'] = $this->comp_offsets[$fieldType] << 5;
625
                }
626
                //nasty nasty...
627
                //make sure queryConfig contains _actual_ comparevalue.
628
                //mkCompSelect don't care, but getQuery does.
629
                $queryConfig[$key]['comparison'] += isset($conf['negate']) - $conf['comparison'] % 2;
630
            } elseif ($conf['type'] === 'newlevel') {
631
                $fieldType = $conf['type'];
632
            } else {
633
                $fieldType = 'ignore';
634
            }
635
            $fieldPrefix = htmlspecialchars($this->name . $subscript);
636
            switch ($fieldType) {
637
                case 'ignore':
638
                    break;
639
                case 'newlevel':
640
                    if (!$queryConfig[$key]['nl']) {
641
                        $queryConfig[$key]['nl'][0]['type'] = 'FIELD_';
642
                    }
643
                    $lineHTML[] = '<input type="hidden" name="' . $fieldPrefix . '[type]" value="newlevel">';
644
                    $codeArr[$arrCount]['sub'] = $this->getFormElements($subLevel + 1, $queryConfig[$key]['nl'], $subscript . '[nl]');
645
                    break;
646
                case 'userdef':
647
                    $lineHTML[] = $this->userDef($fieldPrefix, $conf, $fieldName, $fieldType);
648
                    break;
649
                case 'date':
650
                    $lineHTML[] = '<div class="form-inline">';
651
                    $lineHTML[] = $this->makeComparisonSelector($subscript, $fieldName, $conf);
652
                    if ($conf['comparison'] === 100 || $conf['comparison'] === 101) {
653
                        // between
654
                        $lineHTML[] = $this->getDateTimePickerField($fieldPrefix . '[inputValue]', $conf['inputValue'], 'date');
655
                        $lineHTML[] = $this->getDateTimePickerField($fieldPrefix . '[inputValue1]', $conf['inputValue1'], 'date');
656
                    } else {
657
                        $lineHTML[] = $this->getDateTimePickerField($fieldPrefix . '[inputValue]', $conf['inputValue'], 'date');
658
                    }
659
                    $lineHTML[] = '</div>';
660
                    break;
661
                case 'time':
662
                    $lineHTML[] = '<div class="form-inline">';
663
                    $lineHTML[] = $this->makeComparisonSelector($subscript, $fieldName, $conf);
664
                    if ($conf['comparison'] === 100 || $conf['comparison'] === 101) {
665
                        // between:
666
                        $lineHTML[] = $this->getDateTimePickerField($fieldPrefix . '[inputValue]', $conf['inputValue'], 'datetime');
667
                        $lineHTML[] = $this->getDateTimePickerField($fieldPrefix . '[inputValue1]', $conf['inputValue1'], 'datetime');
668
                    } else {
669
                        $lineHTML[] = $this->getDateTimePickerField($fieldPrefix . '[inputValue]', $conf['inputValue'], 'datetime');
670
                    }
671
                    $lineHTML[] = '</div>';
672
                    break;
673
                case 'multiple':
674
                case 'binary':
675
                case 'relation':
676
                    $lineHTML[] = '<div class="form-inline">';
677
                    $lineHTML[] = $this->makeComparisonSelector($subscript, $fieldName, $conf);
678
                    if ($conf['comparison'] === 68 || $conf['comparison'] === 69 || $conf['comparison'] === 162 || $conf['comparison'] === 163) {
679
                        $lineHTML[] = '<select class="form-select" name="' . $fieldPrefix . '[inputValue][]" multiple="multiple">';
680
                    } elseif ($conf['comparison'] === 66 || $conf['comparison'] === 67) {
681
                        if (is_array($conf['inputValue'])) {
682
                            $conf['inputValue'] = implode(',', $conf['inputValue']);
683
                        }
684
                        $lineHTML[] = '<input class="form-control t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue']) . '" name="' . $fieldPrefix . '[inputValue]">';
685
                    } elseif ($conf['comparison'] === 64) {
686
                        if (is_array($conf['inputValue'])) {
687
                            $conf['inputValue'] = $conf['inputValue'][0];
688
                        }
689
                        $lineHTML[] = '<select class="form-select t3js-submit-change" name="' . $fieldPrefix . '[inputValue]">';
690
                    } else {
691
                        $lineHTML[] = '<select class="form-select t3js-submit-change" name="' . $fieldPrefix . '[inputValue]">';
692
                    }
693
                    if ($conf['comparison'] != 66 && $conf['comparison'] != 67) {
694
                        $lineHTML[] = $this->makeOptionList($fieldName, $conf, $this->table);
695
                        $lineHTML[] = '</select>';
696
                    }
697
                    $lineHTML[] = '</div>';
698
                    break;
699
                case 'boolean':
700
                    $lineHTML[] = '<div class="form-inline">';
701
                    $lineHTML[] = $this->makeComparisonSelector($subscript, $fieldName, $conf);
702
                    $lineHTML[] = '<input type="hidden" value="1" name="' . $fieldPrefix . '[inputValue]">';
703
                    $lineHTML[] = '</div>';
704
                    break;
705
                default:
706
                    $lineHTML[] = '<div class="form-inline">';
707
                    $lineHTML[] = $this->makeComparisonSelector($subscript, $fieldName, $conf);
708
                    if ($conf['comparison'] === 37 || $conf['comparison'] === 36) {
709
                        // between:
710
                        $lineHTML[] = '<input class="form-control t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue']) . '" name="' . $fieldPrefix . '[inputValue]">';
711
                        $lineHTML[] = '<input class="form-control t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue1']) . '" name="' . $fieldPrefix . '[inputValue1]">';
712
                    } else {
713
                        $lineHTML[] = '<input class="form-control t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue']) . '" name="' . $fieldPrefix . '[inputValue]">';
714
                    }
715
                    $lineHTML[] = '</div>';
716
            }
717
            if ($fieldType !== 'ignore') {
718
                $lineHTML[] = '<div class="btn-group" style="margin-top: .5em;">';
719
                $lineHTML[] = $this->updateIcon();
720
                if ($loopCount) {
721
                    $lineHTML[] = '<button class="btn btn-default" title="Remove condition" name="qG_del' . htmlspecialchars($subscript) . '"><i class="fa fa-trash fa-fw"></i></button>';
722
                }
723
                $lineHTML[] = '<button class="btn btn-default" title="Add condition" name="qG_ins' . htmlspecialchars($subscript) . '"><i class="fa fa-plus fa-fw"></i></button>';
724
                if ($c != 0) {
725
                    $lineHTML[] = '<button class="btn btn-default" title="Move up" name="qG_up' . htmlspecialchars($subscript) . '"><i class="fa fa-chevron-up fa-fw"></i></button>';
726
                }
727
                if ($c != 0 && $fieldType !== 'newlevel') {
728
                    $lineHTML[] = '<button class="btn btn-default" title="New level" name="qG_nl' . htmlspecialchars($subscript) . '"><i class="fa fa-chevron-right fa-fw"></i></button>';
729
                }
730
                if ($fieldType === 'newlevel') {
731
                    $lineHTML[] = '<button class="btn btn-default" title="Collapse new level" name="qG_remnl' . htmlspecialchars($subscript) . '"><i class="fa fa-chevron-left fa-fw"></i></button>';
732
                }
733
                $lineHTML[] = '</div>';
734
                $codeArr[$arrCount]['html'] = implode(LF, $lineHTML);
735
                $codeArr[$arrCount]['query'] = $this->getQuerySingle($conf, $c === 0);
736
                $arrCount++;
737
                $c++;
738
            }
739
            $loopCount = 1;
740
        }
741
        $this->queryConfig = $queryConfig;
742
        return $codeArr;
743
    }
744
745
    /**
746
     * @param string $subscript
747
     * @param string $fieldName
748
     * @param array $conf
749
     *
750
     * @return string
751
     */
752
    protected function makeComparisonSelector($subscript, $fieldName, $conf)
753
    {
754
        $fieldPrefix = $this->name . $subscript;
755
        $lineHTML = [];
756
        $lineHTML[] = $this->mkTypeSelect($fieldPrefix . '[type]', $fieldName);
757
        $lineHTML[] = '	<div class="input-group">';
758
        $lineHTML[] = $this->mkCompSelect($fieldPrefix . '[comparison]', $conf['comparison'], $conf['negate'] ? 1 : 0);
759
        $lineHTML[] = '	<div class="input-group-addon">';
760
        $lineHTML[] = '		<input type="checkbox" class="checkbox t3js-submit-click"' . ($conf['negate'] ? ' checked' : '') . ' name="' . htmlspecialchars($fieldPrefix) . '[negate]">';
761
        $lineHTML[] = '	</div>';
762
        $lineHTML[] = '	</div>';
763
        return implode(LF, $lineHTML);
764
    }
765
766
    /**
767
     * Make option list
768
     *
769
     * @param string $fieldName
770
     * @param array $conf
771
     * @param string $table
772
     * @return string
773
     */
774
    public function makeOptionList($fieldName, $conf, $table)
775
    {
776
        $from_table_Arr = [];
777
        $out = [];
778
        $fieldSetup = $this->fields[$fieldName];
779
        $languageService = $this->getLanguageService();
780
        if ($fieldSetup['type'] === 'multiple') {
781
            $optGroupOpen = false;
782
            foreach ($fieldSetup['items'] as $val) {
783
                if (strpos($val[0], 'LLL:') === 0) {
784
                    $value = $languageService->sL($val[0]);
785
                } else {
786
                    $value = $val[0];
787
                }
788
                $itemVal = (string)($val[1] ?? '');
789
                if ($itemVal === '--div--') {
790
                    if ($optGroupOpen) {
791
                        $out[] = '</optgroup>';
792
                    }
793
                    $optGroupOpen = true;
794
                    $out[] = '<optgroup label="' . htmlspecialchars($value) . '">';
795
                } elseif (GeneralUtility::inList($conf['inputValue'], $itemVal)) {
796
                    $out[] = '<option value="' . htmlspecialchars($itemVal) . '" selected>' . htmlspecialchars($value) . '</option>';
797
                } else {
798
                    $out[] = '<option value="' . htmlspecialchars($itemVal) . '">' . htmlspecialchars($value) . '</option>';
799
                }
800
            }
801
            if ($optGroupOpen) {
802
                $out[] = '</optgroup>';
803
            }
804
        }
805
        if ($fieldSetup['type'] === 'binary') {
806
            foreach ($fieldSetup['items'] as $key => $val) {
807
                if (strpos($val[0], 'LLL:') === 0) {
808
                    $value = $languageService->sL($val[0]);
809
                } else {
810
                    $value = $val[0];
811
                }
812
                $itemVal = (string)(2 ** $key);
813
                if (GeneralUtility::inList($conf['inputValue'], $itemVal)) {
814
                    $out[] = '<option value="' . $itemVal . '" selected>' . htmlspecialchars($value) . '</option>';
815
                } else {
816
                    $out[] = '<option value="' . $itemVal . '">' . htmlspecialchars($value) . '</option>';
817
                }
818
            }
819
        }
820
        if ($fieldSetup['type'] === 'relation') {
821
            $useTablePrefix = 0;
822
            $dontPrefixFirstTable = 0;
823
            if ($fieldSetup['items']) {
824
                foreach ($fieldSetup['items'] as $val) {
825
                    if (strpos($val[0], 'LLL:') === 0) {
826
                        $value = $languageService->sL($val[0]);
827
                    } else {
828
                        $value = $val[0];
829
                    }
830
                    $outputValue = (string)($val[1] ?? '');
831
                    if ($outputValue && GeneralUtility::inList($conf['inputValue'], $outputValue)) {
832
                        $out[] = '<option value="' . htmlspecialchars($outputValue) . '" selected>' . htmlspecialchars($value) . '</option>';
833
                    } else {
834
                        $out[] = '<option value="' . htmlspecialchars($outputValue) . '">' . htmlspecialchars($value) . '</option>';
835
                    }
836
                }
837
            }
838
            if (strpos($fieldSetup['allowed'], ',') !== false) {
839
                $from_table_Arr = explode(',', $fieldSetup['allowed']);
840
                $useTablePrefix = 1;
841
                if (!$fieldSetup['prepend_tname']) {
842
                    $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
843
                    $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
844
                    $statement = $queryBuilder->select($fieldName)
845
                        ->from($table)
846
                        ->execute();
847
                    while ($row = $statement->fetch()) {
848
                        if (strpos($row[$fieldName], ',') !== false) {
849
                            $checkContent = explode(',', $row[$fieldName]);
850
                            foreach ($checkContent as $singleValue) {
851
                                if (strpos($singleValue, '_') === false) {
852
                                    $dontPrefixFirstTable = 1;
853
                                }
854
                            }
855
                        } else {
856
                            $singleValue = $row[$fieldName];
857
                            if ($singleValue !== '' && strpos($singleValue, '_') === false) {
858
                                $dontPrefixFirstTable = 1;
859
                            }
860
                        }
861
                    }
862
                }
863
            } else {
864
                $from_table_Arr[0] = $fieldSetup['allowed'];
865
            }
866
            if ($fieldSetup['prepend_tname']) {
867
                $useTablePrefix = 1;
868
            }
869
            if ($fieldSetup['foreign_table']) {
870
                $from_table_Arr[0] = $fieldSetup['foreign_table'];
871
            }
872
            $counter = 0;
873
            $tablePrefix = '';
874
            $backendUserAuthentication = $this->getBackendUserAuthentication();
875
            $outArray = [];
876
            $labelFieldSelect = [];
877
            foreach ($from_table_Arr as $from_table) {
878
                $useSelectLabels = false;
879
                $useAltSelectLabels = false;
880
                if ($useTablePrefix && !$dontPrefixFirstTable && $counter != 1 || $counter === 1) {
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: ($useTablePrefix && ! $d...!= 1) || $counter === 1, Probably Intended Meaning: $useTablePrefix && ! $do...!= 1 || $counter === 1)
Loading history...
881
                    $tablePrefix = $from_table . '_';
882
                }
883
                $counter = 1;
884
                if (is_array($GLOBALS['TCA'][$from_table])) {
885
                    $labelField = $GLOBALS['TCA'][$from_table]['ctrl']['label'];
886
                    $altLabelField = $GLOBALS['TCA'][$from_table]['ctrl']['label_alt'];
887
                    if ($GLOBALS['TCA'][$from_table]['columns'][$labelField]['config']['items']) {
888
                        foreach ($GLOBALS['TCA'][$from_table]['columns'][$labelField]['config']['items'] as $labelArray) {
889
                            if (strpos($labelArray[0], 'LLL:') === 0) {
890
                                $labelFieldSelect[$labelArray[1]] = $languageService->sL($labelArray[0]);
891
                            } else {
892
                                $labelFieldSelect[$labelArray[1]] = $labelArray[0];
893
                            }
894
                        }
895
                        $useSelectLabels = true;
896
                    }
897
                    $altLabelFieldSelect = [];
898
                    if ($GLOBALS['TCA'][$from_table]['columns'][$altLabelField]['config']['items']) {
899
                        foreach ($GLOBALS['TCA'][$from_table]['columns'][$altLabelField]['config']['items'] as $altLabelArray) {
900
                            if (strpos($altLabelArray[0], 'LLL:') === 0) {
901
                                $altLabelFieldSelect[$altLabelArray[1]] = $languageService->sL($altLabelArray[0]);
902
                            } else {
903
                                $altLabelFieldSelect[$altLabelArray[1]] = $altLabelArray[0];
904
                            }
905
                        }
906
                        $useAltSelectLabels = true;
907
                    }
908
909
                    if (!$this->tableArray[$from_table]) {
910
                        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($from_table);
911
                        if (isset($this->settings['show_deleted']) && $this->settings['show_deleted']) {
912
                            $queryBuilder->getRestrictions()->removeAll();
913
                        } else {
914
                            $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
915
                        }
916
                        $selectFields = ['uid', $labelField];
917
                        if ($altLabelField) {
918
                            $selectFields[] = $altLabelField;
919
                        }
920
                        $queryBuilder->select(...$selectFields)
921
                            ->from($from_table)
922
                            ->orderBy('uid');
923
                        if (!$backendUserAuthentication->isAdmin()) {
924
                            $webMounts = $backendUserAuthentication->returnWebmounts();
925
                            $perms_clause = $backendUserAuthentication->getPagePermsClause(Permission::PAGE_SHOW);
926
                            $webMountPageTree = '';
927
                            $webMountPageTreePrefix = '';
928
                            foreach ($webMounts as $webMount) {
929
                                if ($webMountPageTree) {
930
                                    $webMountPageTreePrefix = ',';
931
                                }
932
                                $webMountPageTree .= $webMountPageTreePrefix
933
                                    . $this->getTreeList($webMount, 999, 0, $perms_clause);
934
                            }
935
                            if ($from_table === 'pages') {
936
                                $queryBuilder->where(
937
                                    QueryHelper::stripLogicalOperatorPrefix($perms_clause),
938
                                    $queryBuilder->expr()->in(
939
                                        'uid',
940
                                        $queryBuilder->createNamedParameter(
941
                                            GeneralUtility::intExplode(',', $webMountPageTree),
942
                                            Connection::PARAM_INT_ARRAY
943
                                        )
944
                                    )
945
                                );
946
                            } else {
947
                                $queryBuilder->where(
948
                                    $queryBuilder->expr()->in(
949
                                        'pid',
950
                                        $queryBuilder->createNamedParameter(
951
                                            GeneralUtility::intExplode(',', $webMountPageTree),
952
                                            Connection::PARAM_INT_ARRAY
953
                                        )
954
                                    )
955
                                );
956
                            }
957
                        }
958
                        $statement = $queryBuilder->execute();
959
                        $this->tableArray[$from_table] = [];
960
                        while ($row = $statement->fetch()) {
961
                            $this->tableArray[$from_table][] = $row;
962
                        }
963
                    }
964
965
                    foreach ($this->tableArray[$from_table] as $key => $val) {
966
                        if ($useSelectLabels) {
967
                            $outArray[$tablePrefix . $val['uid']] = htmlspecialchars($labelFieldSelect[$val[$labelField]]);
968
                        } elseif ($val[$labelField]) {
969
                            $outArray[$tablePrefix . $val['uid']] = htmlspecialchars($val[$labelField]);
970
                        } elseif ($useAltSelectLabels) {
971
                            $outArray[$tablePrefix . $val['uid']] = htmlspecialchars($altLabelFieldSelect[$val[$altLabelField]]);
972
                        } else {
973
                            $outArray[$tablePrefix . $val['uid']] = htmlspecialchars($val[$altLabelField]);
974
                        }
975
                    }
976
                    if (isset($this->settings['options_sortlabel']) && $this->settings['options_sortlabel'] && is_array($outArray)) {
977
                        natcasesort($outArray);
978
                    }
979
                }
980
            }
981
            foreach ($outArray as $key2 => $val2) {
982
                $key2 = (string)$key2;
983
                if (GeneralUtility::inList($conf['inputValue'], $key2)) {
984
                    $out[] = '<option value="' . htmlspecialchars($key2) . '" selected>[' . htmlspecialchars($key2) . '] ' . htmlspecialchars($val2) . '</option>';
985
                } else {
986
                    $out[] = '<option value="' . htmlspecialchars($key2) . '">[' . htmlspecialchars($key2) . '] ' . htmlspecialchars($val2) . '</option>';
987
                }
988
            }
989
        }
990
        return implode(LF, $out);
991
    }
992
993
    /**
994
     * Print code array
995
     *
996
     * @param array $codeArr
997
     * @param int $recursionLevel
998
     * @return string
999
     */
1000
    public function printCodeArray($codeArr, $recursionLevel = 0)
1001
    {
1002
        $out = [];
1003
        foreach ($codeArr as $k => $v) {
1004
            $out[] = '<div class="card">';
1005
            $out[] = '<div class="card-content">';
1006
            $out[] = $v['html'];
1007
1008
            if ($this->enableQueryParts) {
1009
                $out[] = '<pre>';
1010
                $out[] = htmlspecialchars($v['query']);
1011
                $out[] = '</pre>';
1012
            }
1013
            if (is_array($v['sub'])) {
1014
                $out[] = '<div>';
1015
                $out[] = $this->printCodeArray($v['sub'], $recursionLevel + 1);
1016
                $out[] = '</div>';
1017
            }
1018
            $out[] = '</div>';
1019
            $out[] = '</div>';
1020
        }
1021
        return implode(LF, $out);
1022
    }
1023
1024
    /**
1025
     * Make operator select
1026
     *
1027
     * @param string $name
1028
     * @param string $op
1029
     * @param bool $draw
1030
     * @param bool $submit
1031
     * @return string
1032
     */
1033
    public function mkOperatorSelect($name, $op, $draw, $submit)
1034
    {
1035
        $out = [];
1036
        if ($draw) {
1037
            $out[] = '<div class="form-inline">';
1038
            $out[] = '<select class="form-select' . ($submit ? ' t3js-submit-change' : '') . '" name="' . htmlspecialchars($name) . '[operator]">';
1039
            $out[] = '	<option value="AND"' . (!$op || $op === 'AND' ? ' selected' : '') . '>' . htmlspecialchars($this->lang['AND']) . '</option>';
1040
            $out[] = '	<option value="OR"' . ($op === 'OR' ? ' selected' : '') . '>' . htmlspecialchars($this->lang['OR']) . '</option>';
1041
            $out[] = '</select>';
1042
            $out[] = '</div>';
1043
        } else {
1044
            $out[] = '<input type="hidden" value="' . htmlspecialchars($op) . '" name="' . htmlspecialchars($name) . '[operator]">';
1045
        }
1046
        return implode(LF, $out);
1047
    }
1048
1049
    /**
1050
     * Make type select
1051
     *
1052
     * @param string $name
1053
     * @param string $fieldName
1054
     * @param string $prepend
1055
     * @return string
1056
     */
1057
    public function mkTypeSelect($name, $fieldName, $prepend = 'FIELD_')
1058
    {
1059
        $out = [];
1060
        $out[] = '<select class="form-select t3js-submit-change" name="' . htmlspecialchars($name) . '">';
1061
        $out[] = '<option value=""></option>';
1062
        foreach ($this->fields as $key => $value) {
1063
            if (!$value['exclude'] || $this->getBackendUserAuthentication()->check('non_exclude_fields', $this->table . ':' . $key)) {
1064
                $label = $this->fields[$key]['label'];
1065
                $out[] = '<option value="' . htmlspecialchars($prepend . $key) . '"' . ($key === $fieldName ? ' selected' : '') . '>' . htmlspecialchars($label) . '</option>';
1066
            }
1067
        }
1068
        $out[] = '</select>';
1069
        return implode(LF, $out);
1070
    }
1071
1072
    /**
1073
     * Verify type
1074
     *
1075
     * @param string $fieldName
1076
     * @return string
1077
     */
1078
    public function verifyType($fieldName)
1079
    {
1080
        $first = '';
1081
        foreach ($this->fields as $key => $value) {
1082
            if (!$first) {
1083
                $first = $key;
1084
            }
1085
            if ($key === $fieldName) {
1086
                return $key;
1087
            }
1088
        }
1089
        return $first;
1090
    }
1091
1092
    /**
1093
     * Verify comparison
1094
     *
1095
     * @param string $comparison
1096
     * @param int $neg
1097
     * @return int
1098
     */
1099
    public function verifyComparison($comparison, $neg)
1100
    {
1101
        $compOffSet = $comparison >> 5;
1102
        $first = -1;
1103
        for ($i = 32 * $compOffSet + $neg; $i < 32 * ($compOffSet + 1); $i += 2) {
1104
            if ($first === -1) {
1105
                $first = $i;
1106
            }
1107
            if ($i >> 1 === $comparison >> 1) {
1108
                return $i;
1109
            }
1110
        }
1111
        return $first;
1112
    }
1113
1114
    /**
1115
     * Make field to input select
1116
     *
1117
     * @param string $name
1118
     * @param string $fieldName
1119
     * @return string
1120
     */
1121
    public function mkFieldToInputSelect($name, $fieldName)
1122
    {
1123
        $out = [];
1124
        $out[] = '<div class="input-group" style="margin-bottom: .5em;">';
1125
        $out[] = '	<span class="input-group-btn">';
1126
        $out[] = $this->updateIcon();
1127
        $out[] = ' 	</span>';
1128
        $out[] = '	<input type="text" class="form-control t3js-clearable" value="' . htmlspecialchars($fieldName) . '" name="' . htmlspecialchars($name) . '">';
1129
        $out[] = '</div>';
1130
1131
        $out[] = '<select class="form-select t3js-addfield" name="_fieldListDummy" size="5" data-field="' . htmlspecialchars($name) . '">';
1132
        foreach ($this->fields as $key => $value) {
1133
            if (!$value['exclude'] || $this->getBackendUserAuthentication()->check('non_exclude_fields', $this->table . ':' . $key)) {
1134
                $label = $this->fields[$key]['label'];
1135
                $out[] = '<option value="' . htmlspecialchars($key) . '"' . ($key === $fieldName ? ' selected' : '') . '>' . htmlspecialchars($label) . '</option>';
1136
            }
1137
        }
1138
        $out[] = '</select>';
1139
        return implode(LF, $out);
1140
    }
1141
1142
    /**
1143
     * Make table select
1144
     *
1145
     * @param string $name
1146
     * @param string $cur
1147
     * @return string
1148
     */
1149
    public function mkTableSelect($name, $cur)
1150
    {
1151
        $out = [];
1152
        $out[] = '<select class="form-select t3js-submit-change" name="' . $name . '">';
1153
        $out[] = '<option value=""></option>';
1154
        foreach ($GLOBALS['TCA'] as $tN => $value) {
1155
            if ($this->getBackendUserAuthentication()->check('tables_select', $tN)) {
1156
                $out[] = '<option value="' . htmlspecialchars($tN) . '"' . ($tN === $cur ? ' selected' : '') . '>' . htmlspecialchars($this->getLanguageService()->sL($GLOBALS['TCA'][$tN]['ctrl']['title'])) . '</option>';
1157
            }
1158
        }
1159
        $out[] = '</select>';
1160
        return implode(LF, $out);
1161
    }
1162
1163
    /**
1164
     * Make comparison select
1165
     *
1166
     * @param string $name
1167
     * @param string $comparison
1168
     * @param int $neg
1169
     * @return string
1170
     */
1171
    public function mkCompSelect($name, $comparison, $neg)
1172
    {
1173
        $compOffSet = $comparison >> 5;
1174
        $out = [];
1175
        $out[] = '<select class="form-select t3js-submit-change" name="' . $name . '">';
1176
        for ($i = 32 * $compOffSet + $neg; $i < 32 * ($compOffSet + 1); $i += 2) {
1177
            if ($this->lang['comparison'][$i . '_']) {
1178
                $out[] = '<option value="' . $i . '"' . ($i >> 1 === $comparison >> 1 ? ' selected' : '') . '>' . htmlspecialchars($this->lang['comparison'][$i . '_']) . '</option>';
1179
            }
1180
        }
1181
        $out[] = '</select>';
1182
        return implode(LF, $out);
1183
    }
1184
1185
    /**
1186
     * Get subscript
1187
     *
1188
     * @param array $arr
1189
     * @return array
1190
     */
1191
    public function getSubscript($arr): array
1192
    {
1193
        $retArr = [];
1194
        while (\is_array($arr)) {
1195
            reset($arr);
1196
            $key = key($arr);
1197
            $retArr[] = $key;
1198
            if (isset($arr[$key])) {
1199
                $arr = $arr[$key];
1200
            } else {
1201
                break;
1202
            }
1203
        }
1204
        return $retArr;
1205
    }
1206
1207
    /**
1208
     * Init user definition
1209
     */
1210
    public function initUserDef()
1211
    {
1212
    }
1213
1214
    /**
1215
     * User definition
1216
     *
1217
     * @param string $fieldPrefix
1218
     * @param array $conf
1219
     * @param string $fieldName
1220
     * @param string $fieldType
1221
     *
1222
     * @return string
1223
     */
1224
    public function userDef($fieldPrefix, $conf, $fieldName, $fieldType)
1225
    {
1226
        return '';
1227
    }
1228
1229
    /**
1230
     * User definition clean up
1231
     *
1232
     * @param array $queryConfig
1233
     * @return array
1234
     */
1235
    public function userDefCleanUp($queryConfig)
1236
    {
1237
        return $queryConfig;
1238
    }
1239
1240
    /**
1241
     * Get query
1242
     *
1243
     * @param array $queryConfig
1244
     * @param string $pad
1245
     * @return string
1246
     */
1247
    public function getQuery($queryConfig, $pad = '')
1248
    {
1249
        $qs = '';
1250
        // Since we don't traverse the array using numeric keys in the upcoming whileloop make sure it's fresh and clean
1251
        ksort($queryConfig);
1252
        $first = true;
1253
        foreach ($queryConfig as $key => $conf) {
1254
            $conf = $this->convertIso8601DatetimeStringToUnixTimestamp($conf);
1255
            switch ($conf['type']) {
1256
                case 'newlevel':
1257
                    $qs .= LF . $pad . trim($conf['operator']) . ' (' . $this->getQuery(
1258
                        $queryConfig[$key]['nl'],
1259
                        $pad . '   '
1260
                    ) . LF . $pad . ')';
1261
                    break;
1262
                case 'userdef':
1263
                    $qs .= LF . $pad . $this->getUserDefQuery($conf, $first);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getUserDefQuery($conf, $first) targeting TYPO3\CMS\Core\Database\...ator::getUserDefQuery() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
1264
                    break;
1265
                default:
1266
                    $qs .= LF . $pad . $this->getQuerySingle($conf, $first);
1267
            }
1268
            $first = false;
1269
        }
1270
        return $qs;
1271
    }
1272
1273
    /**
1274
     * Convert ISO-8601 timestamp (string) into unix timestamp (int)
1275
     *
1276
     * @param array $conf
1277
     * @return array
1278
     */
1279
    protected function convertIso8601DatetimeStringToUnixTimestamp(array $conf): array
1280
    {
1281
        if ($this->isDateOfIso8601Format($conf['inputValue'])) {
1282
            $conf['inputValue'] = strtotime($conf['inputValue']);
1283
            if ($this->isDateOfIso8601Format($conf['inputValue1'])) {
1284
                $conf['inputValue1'] = strtotime($conf['inputValue1']);
1285
            }
1286
        }
1287
1288
        return $conf;
1289
    }
1290
1291
    /**
1292
     * Checks if the given value is of the ISO 8601 format.
1293
     *
1294
     * @param mixed $date
1295
     * @return bool
1296
     */
1297
    protected function isDateOfIso8601Format($date): bool
1298
    {
1299
        if (!is_int($date) && !is_string($date)) {
1300
            return false;
1301
        }
1302
        $format = 'Y-m-d\\TH:i:s\\Z';
1303
        $formattedDate = \DateTime::createFromFormat($format, (string)$date);
1304
        return $formattedDate && $formattedDate->format($format) === $date;
1305
    }
1306
1307
    /**
1308
     * Get single query
1309
     *
1310
     * @param array $conf
1311
     * @param bool $first
1312
     * @return string
1313
     */
1314
    public function getQuerySingle($conf, $first)
1315
    {
1316
        $qs = '';
1317
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($this->table);
1318
        $prefix = $this->enablePrefix ? $this->table . '.' : '';
1319
        if (!$first) {
1320
            // Is it OK to insert the AND operator if none is set?
1321
            $operator = strtoupper(trim($conf['operator']));
1322
            if (!in_array($operator, ['AND', 'OR'], true)) {
1323
                $operator = 'AND';
1324
            }
1325
            $qs .= $operator . ' ';
1326
        }
1327
        $qsTmp = str_replace('#FIELD#', $prefix . trim(substr($conf['type'], 6)), $this->compSQL[$conf['comparison']]);
1328
        $inputVal = $this->cleanInputVal($conf);
1329
        if ($conf['comparison'] === 68 || $conf['comparison'] === 69) {
1330
            $inputVal = explode(',', $inputVal);
1331
            foreach ($inputVal as $key => $fileName) {
1332
                $inputVal[$key] = $queryBuilder->quote($fileName);
1333
            }
1334
            $inputVal = implode(',', $inputVal);
1335
            $qsTmp = str_replace('#VALUE#', $inputVal, $qsTmp);
1336
        } elseif ($conf['comparison'] === 162 || $conf['comparison'] === 163) {
1337
            $inputValArray = explode(',', $inputVal);
1338
            $inputVal = 0;
1339
            foreach ($inputValArray as $fileName) {
1340
                $inputVal += (int)$fileName;
1341
            }
1342
            $qsTmp = str_replace('#VALUE#', (string)$inputVal, $qsTmp);
1343
        } else {
1344
            if (is_array($inputVal)) {
0 ignored issues
show
introduced by
The condition is_array($inputVal) is always false.
Loading history...
1345
                $inputVal = $inputVal[0];
1346
            }
1347
            $qsTmp = str_replace('#VALUE#', trim($queryBuilder->quote($inputVal), '\''), $qsTmp);
1348
        }
1349
        if ($conf['comparison'] === 37 || $conf['comparison'] === 36 || $conf['comparison'] === 66 || $conf['comparison'] === 67 || $conf['comparison'] === 100 || $conf['comparison'] === 101) {
1350
            // between:
1351
            $inputVal = $this->cleanInputVal($conf, '1');
1352
            $qsTmp = str_replace('#VALUE1#', trim($queryBuilder->quote($inputVal), '\''), $qsTmp);
1353
        }
1354
        $qs .= trim((string)$qsTmp);
1355
        return $qs;
1356
    }
1357
1358
    /**
1359
     * Clear input value
1360
     *
1361
     * @param array $conf
1362
     * @param string $suffix
1363
     * @return string
1364
     */
1365
    public function cleanInputVal($conf, $suffix = '')
1366
    {
1367
        if ($conf['comparison'] >> 5 === 0 || ($conf['comparison'] === 32 || $conf['comparison'] === 33 || $conf['comparison'] === 64 || $conf['comparison'] === 65 || $conf['comparison'] === 66 || $conf['comparison'] === 67 || $conf['comparison'] === 96 || $conf['comparison'] === 97)) {
1368
            $inputVal = $conf['inputValue' . $suffix];
1369
        } elseif ($conf['comparison'] === 39 || $conf['comparison'] === 38) {
1370
            // in list:
1371
            $inputVal = implode(',', GeneralUtility::intExplode(',', $conf['inputValue' . $suffix]));
1372
        } elseif ($conf['comparison'] === 68 || $conf['comparison'] === 69 || $conf['comparison'] === 162 || $conf['comparison'] === 163) {
1373
            // in list:
1374
            if (is_array($conf['inputValue' . $suffix])) {
1375
                $inputVal = implode(',', $conf['inputValue' . $suffix]);
1376
            } elseif ($conf['inputValue' . $suffix]) {
1377
                $inputVal = $conf['inputValue' . $suffix];
1378
            } else {
1379
                $inputVal = 0;
1380
            }
1381
        } elseif (!is_array($conf['inputValue' . $suffix]) && strtotime($conf['inputValue' . $suffix])) {
1382
            $inputVal = $conf['inputValue' . $suffix];
1383
        } elseif (!is_array($conf['inputValue' . $suffix]) && MathUtility::canBeInterpretedAsInteger($conf['inputValue' . $suffix])) {
1384
            $inputVal = (int)$conf['inputValue' . $suffix];
1385
        } else {
1386
            // TODO: Six eyes looked at this code and nobody understood completely what is going on here and why we
1387
            // fallback to float casting, the whole class smells like it needs a refactoring.
1388
            $inputVal = (float)$conf['inputValue' . $suffix];
1389
        }
1390
        return $inputVal;
1391
    }
1392
1393
    /**
1394
     * Get user definition query
1395
     *
1396
     * @param array $qcArr
1397
     * @param bool $first
1398
     */
1399
    public function getUserDefQuery($qcArr, $first)
1400
    {
1401
    }
1402
1403
    /**
1404
     * Update icon
1405
     *
1406
     * @return string
1407
     */
1408
    public function updateIcon()
1409
    {
1410
        return '<button class="btn btn-default" title="Update" name="just_update"><i class="fa fa-refresh fa-fw"></i></button>';
1411
    }
1412
1413
    /**
1414
     * Get label column
1415
     *
1416
     * @return string
1417
     */
1418
    public function getLabelCol()
1419
    {
1420
        return $GLOBALS['TCA'][$this->table]['ctrl']['label'];
1421
    }
1422
1423
    /**
1424
     * Make selector table
1425
     *
1426
     * @param array $modSettings
1427
     * @param string $enableList
1428
     * @return string
1429
     */
1430
    public function makeSelectorTable($modSettings, $enableList = 'table,fields,query,group,order,limit')
1431
    {
1432
        $out = [];
1433
        $enableArr = explode(',', $enableList);
1434
        $userTsConfig = $this->getBackendUserAuthentication()->getTSConfig();
1435
1436
        // Make output
1437
        if (in_array('table', $enableArr) && !$userTsConfig['mod.']['dbint.']['disableSelectATable']) {
1438
            $out[] = '<div class="form-group">';
1439
            $out[] = '	<label for="SET[queryTable]">Select a table:</label>';
1440
            $out[] =    $this->mkTableSelect('SET[queryTable]', $this->table);
1441
            $out[] = '</div>';
1442
        }
1443
        if ($this->table) {
1444
            // Init fields:
1445
            $this->setAndCleanUpExternalLists('queryFields', $modSettings['queryFields'], 'uid,' . $this->getLabelCol());
1446
            $this->setAndCleanUpExternalLists('queryGroup', $modSettings['queryGroup']);
1447
            $this->setAndCleanUpExternalLists('queryOrder', $modSettings['queryOrder'] . ',' . $modSettings['queryOrder2']);
1448
            // Limit:
1449
            $this->extFieldLists['queryLimit'] = $modSettings['queryLimit'];
1450
            if (!$this->extFieldLists['queryLimit']) {
1451
                $this->extFieldLists['queryLimit'] = 100;
1452
            }
1453
            $parts = GeneralUtility::intExplode(',', $this->extFieldLists['queryLimit']);
1454
            $limitBegin = 0;
1455
            $limitLength = (int)($this->extFieldLists['queryLimit'] ?? 0);
1456
            if ($parts[1]) {
1457
                $limitBegin = (int)$parts[0];
1458
                $limitLength = (int)$parts[1];
1459
            }
1460
            $this->extFieldLists['queryLimit'] = implode(',', array_slice($parts, 0, 2));
1461
            // Insert Descending parts
1462
            if ($this->extFieldLists['queryOrder']) {
1463
                $descParts = explode(',', $modSettings['queryOrderDesc'] . ',' . $modSettings['queryOrder2Desc']);
1464
                $orderParts = explode(',', $this->extFieldLists['queryOrder']);
1465
                $reList = [];
1466
                foreach ($orderParts as $kk => $vv) {
1467
                    $reList[] = $vv . ($descParts[$kk] ? ' DESC' : '');
1468
                }
1469
                $this->extFieldLists['queryOrder_SQL'] = implode(',', $reList);
1470
            }
1471
            // Query Generator:
1472
            $this->procesData($modSettings['queryConfig'] ? unserialize($modSettings['queryConfig'], ['allowed_classes' => false]) : []);
1473
            $this->queryConfig = $this->cleanUpQueryConfig($this->queryConfig);
1474
            $this->enableQueryParts = (bool)$modSettings['search_query_smallparts'];
1475
            $codeArr = $this->getFormElements();
1476
            $queryCode = $this->printCodeArray($codeArr);
1477
            if (in_array('fields', $enableArr) && !$userTsConfig['mod.']['dbint.']['disableSelectFields']) {
1478
                $out[] = '<div class="form-group form-group-with-button-addon">';
1479
                $out[] = '	<label for="SET[queryFields]">Select fields:</label>';
1480
                $out[] =    $this->mkFieldToInputSelect('SET[queryFields]', $this->extFieldLists['queryFields']);
1481
                $out[] = '</div>';
1482
            }
1483
            if (in_array('query', $enableArr) && !$userTsConfig['mod.']['dbint.']['disableMakeQuery']) {
1484
                $out[] = '<div class="form-group">';
1485
                $out[] = '	<label>Make Query:</label>';
1486
                $out[] =    $queryCode;
1487
                $out[] = '</div>';
1488
            }
1489
            if (in_array('group', $enableArr) && !$userTsConfig['mod.']['dbint.']['disableGroupBy']) {
1490
                $out[] = '<div class="form-group form-inline">';
1491
                $out[] = '	<label for="SET[queryGroup]">Group By:</label>';
1492
                $out[] =     $this->mkTypeSelect('SET[queryGroup]', $this->extFieldLists['queryGroup'], '');
1493
                $out[] = '</div>';
1494
            }
1495
            if (in_array('order', $enableArr) && !$userTsConfig['mod.']['dbint.']['disableOrderBy']) {
1496
                $orderByArr = explode(',', $this->extFieldLists['queryOrder']);
1497
                $orderBy = [];
1498
                $orderBy[] = $this->mkTypeSelect('SET[queryOrder]', $orderByArr[0], '');
1499
                $orderBy[] = '<div class="form-check">';
1500
                $orderBy[] = BackendUtility::getFuncCheck(0, 'SET[queryOrderDesc]', $modSettings['queryOrderDesc'], '', '', 'id="checkQueryOrderDesc"');
1501
                $orderBy[] = '	<label class="form-check-label" for="checkQueryOrderDesc">';
1502
                $orderBy[] =        'Descending';
1503
                $orderBy[] = '	</label>';
1504
                $orderBy[] = '</div>';
1505
1506
                if ($orderByArr[0]) {
1507
                    $orderBy[] = $this->mkTypeSelect('SET[queryOrder2]', $orderByArr[1], '');
1508
                    $orderBy[] = '<div class="form-check">';
1509
                    $orderBy[] = BackendUtility::getFuncCheck(0, 'SET[queryOrder2Desc]', $modSettings['queryOrder2Desc'], '', '', 'id="checkQueryOrder2Desc"');
1510
                    $orderBy[] = '	<label class="form-check-label" for="checkQueryOrder2Desc">';
1511
                    $orderBy[] =        'Descending';
1512
                    $orderBy[] = '	</label>';
1513
                    $orderBy[] = '</div>';
1514
                }
1515
                $out[] = '<div class="form-group form-inline">';
1516
                $out[] = '	<label>Order By:</label>';
1517
                $out[] =     implode(LF, $orderBy);
1518
                $out[] = '</div>';
1519
            }
1520
            if (in_array('limit', $enableArr) && !$userTsConfig['mod.']['dbint.']['disableLimit']) {
1521
                $limit = [];
1522
                $limit[] = '<div class="input-group">';
1523
                $limit[] = '	<span class="input-group-btn">';
1524
                $limit[] = $this->updateIcon();
1525
                $limit[] = '	</span>';
1526
                $limit[] = '	<input type="text" class="form-control" value="' . htmlspecialchars($this->extFieldLists['queryLimit']) . '" name="SET[queryLimit]" id="queryLimit">';
1527
                $limit[] = '</div>';
1528
1529
                $prevLimit = $limitBegin - $limitLength < 0 ? 0 : $limitBegin - $limitLength;
1530
                $prevButton = '';
1531
                $nextButton = '';
1532
1533
                if ($limitBegin) {
1534
                    $prevButton = '<input type="button" class="btn btn-default" value="previous ' . htmlspecialchars((string)$limitLength) . '" data-value="' . htmlspecialchars($prevLimit . ',' . $limitLength) . '">';
1535
                }
1536
                if (!$limitLength) {
1537
                    $limitLength = 100;
1538
                }
1539
1540
                $nextLimit = $limitBegin + $limitLength;
1541
                if ($nextLimit < 0) {
1542
                    $nextLimit = 0;
1543
                }
1544
                if ($nextLimit) {
1545
                    $nextButton = '<input type="button" class="btn btn-default" value="next ' . htmlspecialchars((string)$limitLength) . '" data-value="' . htmlspecialchars($nextLimit . ',' . $limitLength) . '">';
1546
                }
1547
1548
                $out[] = '<div class="form-group">';
1549
                $out[] = '	<label>Limit:</label>';
1550
                $out[] = '	<div class="form-inline">';
1551
                $out[] =        implode(LF, $limit);
1552
                $out[] = '		<div class="btn-group t3js-limit-submit">';
1553
                $out[] =            $prevButton;
1554
                $out[] =            $nextButton;
1555
                $out[] = '		</div>';
1556
                $out[] = '		<div class="btn-group t3js-limit-submit">';
1557
                $out[] = '			<input type="button" class="btn btn-default" data-value="10" value="10">';
1558
                $out[] = '			<input type="button" class="btn btn-default" data-value="20" value="20">';
1559
                $out[] = '			<input type="button" class="btn btn-default" data-value="50" value="50">';
1560
                $out[] = '			<input type="button" class="btn btn-default" data-value="100" value="100">';
1561
                $out[] = '		</div>';
1562
                $out[] = '	</div>';
1563
                $out[] = '</div>';
1564
            }
1565
        }
1566
        return implode(LF, $out);
1567
    }
1568
1569
    /**
1570
     * Recursively fetch all descendants of a given page
1571
     *
1572
     * @param int $id uid of the page
1573
     * @param int $depth
1574
     * @param int $begin
1575
     * @param string $permClause
1576
     * @return string comma separated list of descendant pages
1577
     */
1578
    public function getTreeList($id, $depth, $begin = 0, $permClause = '')
1579
    {
1580
        $depth = (int)$depth;
1581
        $begin = (int)$begin;
1582
        $id = (int)$id;
1583
        if ($id < 0) {
1584
            $id = abs($id);
1585
        }
1586
        if ($begin === 0) {
1587
            $theList = $id;
1588
        } else {
1589
            $theList = '';
1590
        }
1591
        if ($id && $depth > 0) {
1592
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
1593
            $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1594
            $queryBuilder->select('uid')
1595
                ->from('pages')
1596
                ->where(
1597
                    $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($id, \PDO::PARAM_INT)),
1598
                    $queryBuilder->expr()->eq('sys_language_uid', 0)
1599
                )
1600
                ->orderBy('uid');
1601
            if ($permClause !== '') {
1602
                $queryBuilder->andWhere(QueryHelper::stripLogicalOperatorPrefix($permClause));
1603
            }
1604
            $statement = $queryBuilder->execute();
1605
            while ($row = $statement->fetch()) {
1606
                if ($begin <= 0) {
1607
                    $theList .= ',' . $row['uid'];
1608
                }
1609
                if ($depth > 1) {
1610
                    $theSubList = $this->getTreeList($row['uid'], $depth - 1, $begin - 1, $permClause);
1611
                    if (!empty($theList) && !empty($theSubList) && ($theSubList[0] !== ',')) {
1612
                        $theList .= ',';
1613
                    }
1614
                    $theList .= $theSubList;
1615
                }
1616
            }
1617
        }
1618
        return $theList;
1619
    }
1620
1621
    /**
1622
     * Get select query
1623
     *
1624
     * @param string $qString
1625
     * @return string
1626
     */
1627
    public function getSelectQuery($qString = ''): string
1628
    {
1629
        $backendUserAuthentication = $this->getBackendUserAuthentication();
1630
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->table);
1631
        if (isset($this->settings['show_deleted']) && $this->settings['show_deleted']) {
1632
            $queryBuilder->getRestrictions()->removeAll();
1633
        } else {
1634
            $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1635
        }
1636
        $fieldList = GeneralUtility::trimExplode(
1637
            ',',
1638
            $this->extFieldLists['queryFields']
1639
            . ',pid'
1640
            . ($GLOBALS['TCA'][$this->table]['ctrl']['delete'] ? ',' . $GLOBALS['TCA'][$this->table]['ctrl']['delete'] : '')
1641
        );
1642
        $queryBuilder->select(...$fieldList)
1643
            ->from($this->table);
1644
1645
        if ($this->extFieldLists['queryGroup']) {
1646
            $queryBuilder->groupBy(...QueryHelper::parseGroupBy($this->extFieldLists['queryGroup']));
1647
        }
1648
        if ($this->extFieldLists['queryOrder']) {
1649
            foreach (QueryHelper::parseOrderBy($this->extFieldLists['queryOrder_SQL']) as $orderPair) {
1650
                [$fieldName, $order] = $orderPair;
1651
                $queryBuilder->addOrderBy($fieldName, $order);
1652
            }
1653
        }
1654
        if ($this->extFieldLists['queryLimit']) {
1655
            $queryBuilder->setMaxResults((int)$this->extFieldLists['queryLimit']);
1656
        }
1657
1658
        if (!$backendUserAuthentication->isAdmin()) {
1659
            $webMounts = $backendUserAuthentication->returnWebmounts();
1660
            $perms_clause = $backendUserAuthentication->getPagePermsClause(Permission::PAGE_SHOW);
1661
            $webMountPageTree = '';
1662
            $webMountPageTreePrefix = '';
1663
            foreach ($webMounts as $webMount) {
1664
                if ($webMountPageTree) {
1665
                    $webMountPageTreePrefix = ',';
1666
                }
1667
                $webMountPageTree .= $webMountPageTreePrefix
1668
                    . $this->getTreeList($webMount, 999, 0, $perms_clause);
1669
            }
1670
            // createNamedParameter() is not used here because the SQL fragment will only include
1671
            // the :dcValueX placeholder when the query is returned as a string. The value for the
1672
            // placeholder would be lost in the process.
1673
            if ($this->table === 'pages') {
1674
                $queryBuilder->where(
1675
                    QueryHelper::stripLogicalOperatorPrefix($perms_clause),
1676
                    $queryBuilder->expr()->in(
1677
                        'uid',
1678
                        GeneralUtility::intExplode(',', $webMountPageTree)
1679
                    )
1680
                );
1681
            } else {
1682
                $queryBuilder->where(
1683
                    $queryBuilder->expr()->in(
1684
                        'pid',
1685
                        GeneralUtility::intExplode(',', $webMountPageTree)
1686
                    )
1687
                );
1688
            }
1689
        }
1690
        if (!$qString) {
1691
            $qString = $this->getQuery($this->queryConfig);
1692
        }
1693
        $queryBuilder->andWhere(QueryHelper::stripLogicalOperatorPrefix($qString));
1694
1695
        return $queryBuilder->getSQL();
1696
    }
1697
1698
    /**
1699
     * @param string $name the field name
1700
     * @param string $timestamp ISO-8601 timestamp
1701
     * @param string $type [datetime, date, time, timesec, year]
1702
     *
1703
     * @return string
1704
     */
1705
    protected function getDateTimePickerField($name, $timestamp, $type)
1706
    {
1707
        $value = strtotime($timestamp) ? date($GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'] . ' ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'], (int)strtotime($timestamp)) : '';
1708
        $id = StringUtility::getUniqueId('dt_');
1709
        $html = [];
1710
        $html[] = '<div class="input-group" id="' . $id . '-wrapper">';
1711
        $html[] = '		<input data-formengine-input-name="' . htmlspecialchars($name) . '" value="' . $value . '" class="form-control t3js-datetimepicker t3js-clearable" data-date-type="' . htmlspecialchars($type) . '" type="text" id="' . $id . '">';
1712
        $html[] = '		<input name="' . htmlspecialchars($name) . '" value="' . htmlspecialchars($timestamp) . '" type="hidden">';
1713
        $html[] = '		<span class="input-group-btn">';
1714
        $html[] = '			<label class="btn btn-default" for="' . $id . '">';
1715
        $html[] = '				<span class="fa fa-calendar"></span>';
1716
        $html[] = '			</label>';
1717
        $html[] = ' 	</span>';
1718
        $html[] = '</div>';
1719
        return implode(LF, $html);
1720
    }
1721
1722
    /**
1723
     * Sets the current name of the input form.
1724
     *
1725
     * @param string $formName The name of the form.
1726
     */
1727
    public function setFormName($formName)
1728
    {
1729
        $this->formName = trim($formName);
1730
    }
1731
1732
    /**
1733
     * @return BackendUserAuthentication
1734
     */
1735
    protected function getBackendUserAuthentication()
1736
    {
1737
        return $GLOBALS['BE_USER'];
1738
    }
1739
1740
    /**
1741
     * @return LanguageService
1742
     */
1743
    protected function getLanguageService()
1744
    {
1745
        return $GLOBALS['LANG'];
1746
    }
1747
}
1748