Passed
Branch master (6c65a4)
by Christian
27:15 queued 11:09
created

QueryGenerator::getDateTimePickerField()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 16
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 14
nc 4
nop 3
dl 0
loc 16
rs 9.4285
c 0
b 0
f 0
1
<?php
2
namespace TYPO3\CMS\Core\Database;
3
4
/*
5
 * This file is part of the TYPO3 CMS project.
6
 *
7
 * It is free software; you can redistribute it and/or modify it under
8
 * the terms of the GNU General Public License, either version 2
9
 * of the License, or any later version.
10
 *
11
 * For the full copyright and license information, please read the
12
 * LICENSE.txt file that was distributed with this source code.
13
 *
14
 * The TYPO3 project - inspiring people to share!
15
 */
16
17
use TYPO3\CMS\Backend\Module\BaseScriptClass;
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\StringUtility;
26
27
/**
28
 * Class for generating front end for building queries
29
 */
30
class QueryGenerator
31
{
32
    /**
33
     * @var array
34
     */
35
    public $lang = [
36
        'OR' => 'or',
37
        'AND' => 'and',
38
        'comparison' => [
39
            // Type = text	offset = 0
40
            '0_' => 'contains',
41
            '1_' => 'does not contain',
42
            '2_' => 'starts with',
43
            '3_' => 'does not start with',
44
            '4_' => 'ends with',
45
            '5_' => 'does not end with',
46
            '6_' => 'equals',
47
            '7_' => 'does not equal',
48
            // Type = number , offset = 32
49
            '32_' => 'equals',
50
            '33_' => 'does not equal',
51
            '34_' => 'is greater than',
52
            '35_' => 'is less than',
53
            '36_' => 'is between',
54
            '37_' => 'is not between',
55
            '38_' => 'is in list',
56
            '39_' => 'is not in list',
57
            '40_' => 'binary AND equals',
58
            '41_' => 'binary AND does not equal',
59
            '42_' => 'binary OR equals',
60
            '43_' => 'binary OR does not equal',
61
            // Type = multiple, relation, files , offset = 64
62
            '64_' => 'equals',
63
            '65_' => 'does not equal',
64
            '66_' => 'contains',
65
            '67_' => 'does not contain',
66
            '68_' => 'is in list',
67
            '69_' => 'is not in list',
68
            '70_' => 'binary AND equals',
69
            '71_' => 'binary AND does not equal',
70
            '72_' => 'binary OR equals',
71
            '73_' => 'binary OR does not equal',
72
            // Type = date,time  offset = 96
73
            '96_' => 'equals',
74
            '97_' => 'does not equal',
75
            '98_' => 'is greater than',
76
            '99_' => 'is less than',
77
            '100_' => 'is between',
78
            '101_' => 'is not between',
79
            '102_' => 'binary AND equals',
80
            '103_' => 'binary AND does not equal',
81
            '104_' => 'binary OR equals',
82
            '105_' => 'binary OR does not equal',
83
            // Type = boolean,  offset = 128
84
            '128_' => 'is True',
85
            '129_' => 'is False',
86
            // Type = binary , offset = 160
87
            '160_' => 'equals',
88
            '161_' => 'does not equal',
89
            '162_' => 'contains',
90
            '163_' => 'does not contain'
91
        ]
92
    ];
93
94
    /**
95
     * @var array
96
     */
97
    public $compSQL = [
98
        // Type = text	offset = 0
99
        '0' => '#FIELD# LIKE \'%#VALUE#%\'',
100
        '1' => '#FIELD# NOT LIKE \'%#VALUE#%\'',
101
        '2' => '#FIELD# LIKE \'#VALUE#%\'',
102
        '3' => '#FIELD# NOT LIKE \'#VALUE#%\'',
103
        '4' => '#FIELD# LIKE \'%#VALUE#\'',
104
        '5' => '#FIELD# NOT LIKE \'%#VALUE#\'',
105
        '6' => '#FIELD# = \'#VALUE#\'',
106
        '7' => '#FIELD# != \'#VALUE#\'',
107
        // Type = number, offset = 32
108
        '32' => '#FIELD# = \'#VALUE#\'',
109
        '33' => '#FIELD# != \'#VALUE#\'',
110
        '34' => '#FIELD# > #VALUE#',
111
        '35' => '#FIELD# < #VALUE#',
112
        '36' => '#FIELD# >= #VALUE# AND #FIELD# <= #VALUE1#',
113
        '37' => 'NOT (#FIELD# >= #VALUE# AND #FIELD# <= #VALUE1#)',
114
        '38' => '#FIELD# IN (#VALUE#)',
115
        '39' => '#FIELD# NOT IN (#VALUE#)',
116
        '40' => '(#FIELD# & #VALUE#)=#VALUE#',
117
        '41' => '(#FIELD# & #VALUE#)!=#VALUE#',
118
        '42' => '(#FIELD# | #VALUE#)=#VALUE#',
119
        '43' => '(#FIELD# | #VALUE#)!=#VALUE#',
120
        // Type = multiple, relation, files , offset = 64
121
        '64' => '#FIELD# = \'#VALUE#\'',
122
        '65' => '#FIELD# != \'#VALUE#\'',
123
        '66' => '#FIELD# LIKE \'%#VALUE#%\' AND #FIELD# LIKE \'%#VALUE1#%\'',
124
        '67' => '(#FIELD# NOT LIKE \'%#VALUE#%\' OR #FIELD# NOT LIKE \'%#VALUE1#%\')',
125
        '68' => '#FIELD# IN (#VALUE#)',
126
        '69' => '#FIELD# NOT IN (#VALUE#)',
127
        '70' => '(#FIELD# & #VALUE#)=#VALUE#',
128
        '71' => '(#FIELD# & #VALUE#)!=#VALUE#',
129
        '72' => '(#FIELD# | #VALUE#)=#VALUE#',
130
        '73' => '(#FIELD# | #VALUE#)!=#VALUE#',
131
        // Type = date, offset = 32
132
        '96' => '#FIELD# = \'#VALUE#\'',
133
        '97' => '#FIELD# != \'#VALUE#\'',
134
        '98' => '#FIELD# > #VALUE#',
135
        '99' => '#FIELD# < #VALUE#',
136
        '100' => '#FIELD# >= #VALUE# AND #FIELD# <= #VALUE1#',
137
        '101' => 'NOT (#FIELD# >= #VALUE# AND #FIELD# <= #VALUE1#)',
138
        '102' => '(#FIELD# & #VALUE#)=#VALUE#',
139
        '103' => '(#FIELD# & #VALUE#)!=#VALUE#',
140
        '104' => '(#FIELD# | #VALUE#)=#VALUE#',
141
        '105' => '(#FIELD# | #VALUE#)!=#VALUE#',
142
        // Type = boolean, offset = 128
143
        '128' => '#FIELD# = \'1\'',
144
        '129' => '#FIELD# != \'1\'',
145
        // Type = binary = 160
146
        '160' => '#FIELD# = \'#VALUE#\'',
147
        '161' => '#FIELD# != \'#VALUE#\'',
148
        '162' => '(#FIELD# & #VALUE#)=#VALUE#',
149
        '163' => '(#FIELD# & #VALUE#)=0'
150
    ];
151
152
    /**
153
     * @var array
154
     */
155
    public $comp_offsets = [
156
        'text' => 0,
157
        'number' => 1,
158
        'multiple' => 2,
159
        'relation' => 2,
160
        'files' => 2,
161
        'date' => 3,
162
        'time' => 3,
163
        'boolean' => 4,
164
        'binary' => 5
165
    ];
166
167
    /**
168
     * @var string
169
     */
170
    public $noWrap = ' nowrap';
171
172
    /**
173
     * Form data name prefix
174
     *
175
     * @var string
176
     */
177
    public $name;
178
179
    /**
180
     * Table for the query
181
     *
182
     * @var string
183
     */
184
    public $table;
185
186
    /**
187
     * @var array
188
     */
189
    public $tableArray;
190
191
    /**
192
     * Field list
193
     *
194
     * @var string
195
     */
196
    public $fieldList;
197
198
    /**
199
     * Array of the fields possible
200
     *
201
     * @var array
202
     */
203
    public $fields = [];
204
205
    /**
206
     * @var array
207
     */
208
    public $extFieldLists = [];
209
210
    /**
211
     * The query config
212
     *
213
     * @var array
214
     */
215
    public $queryConfig = [];
216
217
    /**
218
     * @var bool
219
     */
220
    public $enablePrefix = false;
221
222
    /**
223
     * @var bool
224
     */
225
    public $enableQueryParts = false;
226
227
    /**
228
     * @var string
229
     */
230
    protected $formName = '';
231
232
    /**
233
     * @var int
234
     */
235
    protected $limitBegin;
236
237
    /**
238
     * @var int
239
     */
240
    protected $limitLength;
241
242
    /**
243
     * @var string
244
     */
245
    protected $fieldName;
246
247
    /**
248
     * Make a list of fields for current table
249
     *
250
     * @return string Separated list of fields
251
     */
252
    public function makeFieldList()
253
    {
254
        $fieldListArr = [];
255
        if (is_array($GLOBALS['TCA'][$this->table])) {
256
            $fieldListArr = array_keys($GLOBALS['TCA'][$this->table]['columns']);
257
            $fieldListArr[] = 'uid';
258
            $fieldListArr[] = 'pid';
259
            $fieldListArr[] = 'deleted';
260
            if ($GLOBALS['TCA'][$this->table]['ctrl']['tstamp']) {
261
                $fieldListArr[] = $GLOBALS['TCA'][$this->table]['ctrl']['tstamp'];
262
            }
263
            if ($GLOBALS['TCA'][$this->table]['ctrl']['crdate']) {
264
                $fieldListArr[] = $GLOBALS['TCA'][$this->table]['ctrl']['crdate'];
265
            }
266
            if ($GLOBALS['TCA'][$this->table]['ctrl']['cruser_id']) {
267
                $fieldListArr[] = $GLOBALS['TCA'][$this->table]['ctrl']['cruser_id'];
268
            }
269
            if ($GLOBALS['TCA'][$this->table]['ctrl']['sortby']) {
270
                $fieldListArr[] = $GLOBALS['TCA'][$this->table]['ctrl']['sortby'];
271
            }
272
        }
273
        return implode(',', $fieldListArr);
274
    }
275
276
    /**
277
     * Init function
278
     *
279
     * @param string $name The name
280
     * @param string $table The table name
281
     * @param string $fieldList The field list
282
     */
283
    public function init($name, $table, $fieldList = '')
284
    {
285
        // Analysing the fields in the table.
286
        if (is_array($GLOBALS['TCA'][$table])) {
287
            $this->name = $name;
288
            $this->table = $table;
289
            $this->fieldList = $fieldList ? $fieldList : $this->makeFieldList();
290
            $fieldArr = GeneralUtility::trimExplode(',', $this->fieldList, true);
291
            foreach ($fieldArr as $fieldName) {
292
                $fC = $GLOBALS['TCA'][$this->table]['columns'][$fieldName];
293
                $this->fields[$fieldName] = $fC['config'];
294
                $this->fields[$fieldName]['exclude'] = $fC['exclude'];
295
                if ($this->fields[$fieldName]['type'] === 'user' && !isset($this->fields[$fieldName]['type']['userFunc'])) {
296
                    unset($this->fields[$fieldName]);
297
                    continue;
298
                }
299
                if (is_array($fC) && $fC['label']) {
300
                    $this->fields[$fieldName]['label'] = rtrim(trim($this->getLanguageService()->sL($fC['label'])), ':');
301
                    switch ($this->fields[$fieldName]['type']) {
302
                        case 'input':
303
                            if (preg_match('/int|year/i', $this->fields[$fieldName]['eval'])) {
304
                                $this->fields[$fieldName]['type'] = 'number';
305
                            } elseif (preg_match('/time/i', $this->fields[$fieldName]['eval'])) {
306
                                $this->fields[$fieldName]['type'] = 'time';
307
                            } elseif (preg_match('/date/i', $this->fields[$fieldName]['eval'])) {
308
                                $this->fields[$fieldName]['type'] = 'date';
309
                            } else {
310
                                $this->fields[$fieldName]['type'] = 'text';
311
                            }
312
                            break;
313
                        case 'check':
314
                            if (!$this->fields[$fieldName]['items'] || count($this->fields[$fieldName]['items']) <= 1) {
315
                                $this->fields[$fieldName]['type'] = 'boolean';
316
                            } else {
317
                                $this->fields[$fieldName]['type'] = 'binary';
318
                            }
319
                            break;
320
                        case 'radio':
321
                            $this->fields[$fieldName]['type'] = 'multiple';
322
                            break;
323
                        case 'select':
324
                            $this->fields[$fieldName]['type'] = 'multiple';
325
                            if ($this->fields[$fieldName]['foreign_table']) {
326
                                $this->fields[$fieldName]['type'] = 'relation';
327
                            }
328
                            if ($this->fields[$fieldName]['special']) {
329
                                $this->fields[$fieldName]['type'] = 'text';
330
                            }
331
                            break;
332
                        case 'group':
333
                            $this->fields[$fieldName]['type'] = 'files';
334
                            if ($this->fields[$fieldName]['internal_type'] === 'db') {
335
                                $this->fields[$fieldName]['type'] = 'relation';
336
                            }
337
                            break;
338
                        case 'user':
339
                        case 'flex':
340
                        case 'passthrough':
341
                        case 'none':
342
                        case 'text':
343
                        default:
344
                            $this->fields[$fieldName]['type'] = 'text';
345
                    }
346
                } else {
347
                    $this->fields[$fieldName]['label'] = '[FIELD: ' . $fieldName . ']';
348
                    switch ($fieldName) {
349
                        case 'pid':
350
                            $this->fields[$fieldName]['type'] = 'relation';
351
                            $this->fields[$fieldName]['allowed'] = 'pages';
352
                            break;
353
                        case 'cruser_id':
354
                            $this->fields[$fieldName]['type'] = 'relation';
355
                            $this->fields[$fieldName]['allowed'] = 'be_users';
356
                            break;
357
                        case 'tstamp':
358
                        case 'crdate':
359
                            $this->fields[$fieldName]['type'] = 'time';
360
                            break;
361
                        case 'deleted':
362
                            $this->fields[$fieldName]['type'] = 'boolean';
363
                            break;
364
                        default:
365
                            $this->fields[$fieldName]['type'] = 'number';
366
                    }
367
                }
368
            }
369
        }
370
        /*	// EXAMPLE:
371
        $this->queryConfig = array(
372
        array(
373
        'operator' => 'AND',
374
        'type' => 'FIELD_spaceBefore',
375
        ),
376
        array(
377
        'operator' => 'AND',
378
        'type' => 'FIELD_records',
379
        'negate' => 1,
380
        'inputValue' => 'foo foo'
381
        ),
382
        array(
383
        'type' => 'newlevel',
384
        'nl' => array(
385
        array(
386
        'operator' => 'AND',
387
        'type' => 'FIELD_spaceBefore',
388
        'negate' => 1,
389
        'inputValue' => 'foo foo'
390
        ),
391
        array(
392
        'operator' => 'AND',
393
        'type' => 'FIELD_records',
394
        'negate' => 1,
395
        'inputValue' => 'foo foo'
396
        )
397
        )
398
        ),
399
        array(
400
        'operator' => 'OR',
401
        'type' => 'FIELD_maillist',
402
        )
403
        );
404
         */
405
        $this->initUserDef();
406
    }
407
408
    /**
409
     * Set and clean up external lists
410
     *
411
     * @param string $name The name
412
     * @param string $list The list
413
     * @param string $force
414
     */
415
    public function setAndCleanUpExternalLists($name, $list, $force = '')
416
    {
417
        $fields = array_unique(GeneralUtility::trimExplode(',', $list . ',' . $force, true));
418
        $reList = [];
419
        foreach ($fields as $fieldName) {
420
            if ($this->fields[$fieldName]) {
421
                $reList[] = $fieldName;
422
            }
423
        }
424
        $this->extFieldLists[$name] = implode(',', $reList);
425
    }
426
427
    /**
428
     * Process data
429
     *
430
     * @param string $qC Query config
431
     */
432
    public function procesData($qC = '')
433
    {
434
        $this->queryConfig = $qC;
0 ignored issues
show
Documentation Bug introduced by
It seems like $qC of type string is incompatible with the declared type array of property $queryConfig.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
435
        $POST = GeneralUtility::_POST();
436
        // If delete...
437
        if ($POST['qG_del']) {
438
            // Initialize array to work on, save special parameters
439
            $ssArr = $this->getSubscript($POST['qG_del']);
440
            $workArr = &$this->queryConfig;
441
            $ssArrSize = count($ssArr) - 1;
442
            $i = 0;
443
            for (; $i < $ssArrSize; $i++) {
444
                $workArr = &$workArr[$ssArr[$i]];
445
            }
446
            // Delete the entry and move the other entries
447
            unset($workArr[$ssArr[$i]]);
448
            $workArrSize = count($workArr);
0 ignored issues
show
Bug introduced by
It seems like $workArr can also be of type string; however, parameter $var of count() does only seem to accept Countable|array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

448
            $workArrSize = count(/** @scrutinizer ignore-type */ $workArr);
Loading history...
449
            for ($j = $ssArr[$i]; $j < $workArrSize; $j++) {
450
                $workArr[$j] = $workArr[$j + 1];
451
                unset($workArr[$j + 1]);
452
            }
453
        }
454
        // If insert...
455
        if ($POST['qG_ins']) {
456
            // Initialize array to work on, save special parameters
457
            $ssArr = $this->getSubscript($POST['qG_ins']);
458
            $workArr = &$this->queryConfig;
459
            $ssArrSize = count($ssArr) - 1;
460
            $i = 0;
461
            for (; $i < $ssArrSize; $i++) {
462
                $workArr = &$workArr[$ssArr[$i]];
463
            }
464
            // Move all entries above position where new entry is to be inserted
465
            $workArrSize = count($workArr);
466
            for ($j = $workArrSize; $j > $ssArr[$i]; $j--) {
467
                $workArr[$j] = $workArr[$j - 1];
468
            }
469
            // Clear new entry position
470
            unset($workArr[$ssArr[$i] + 1]);
471
            $workArr[$ssArr[$i] + 1]['type'] = 'FIELD_';
472
        }
473
        // If move up...
474
        if ($POST['qG_up']) {
475
            // Initialize array to work on
476
            $ssArr = $this->getSubscript($POST['qG_up']);
477
            $workArr = &$this->queryConfig;
478
            $ssArrSize = count($ssArr) - 1;
479
            $i = 0;
480
            for (; $i < $ssArrSize; $i++) {
481
                $workArr = &$workArr[$ssArr[$i]];
482
            }
483
            // Swap entries
484
            $qG_tmp = $workArr[$ssArr[$i]];
485
            $workArr[$ssArr[$i]] = $workArr[$ssArr[$i] - 1];
486
            $workArr[$ssArr[$i] - 1] = $qG_tmp;
487
        }
488
        // If new level...
489
        if ($POST['qG_nl']) {
490
            // Initialize array to work on
491
            $ssArr = $this->getSubscript($POST['qG_nl']);
492
            $workArr = &$this->queryConfig;
493
            $ssArraySize = count($ssArr) - 1;
494
            $i = 0;
495
            for (; $i < $ssArraySize; $i++) {
496
                $workArr = &$workArr[$ssArr[$i]];
497
            }
498
            // Do stuff:
499
            $tempEl = $workArr[$ssArr[$i]];
500
            if (is_array($tempEl)) {
501
                if ($tempEl['type'] !== 'newlevel') {
502
                    $workArr[$ssArr[$i]] = [
503
                        'type' => 'newlevel',
504
                        'operator' => $tempEl['operator'],
505
                        'nl' => [$tempEl]
506
                    ];
507
                }
508
            }
509
        }
510
        // If collapse level...
511
        if ($POST['qG_remnl']) {
512
            // Initialize array to work on
513
            $ssArr = $this->getSubscript($POST['qG_remnl']);
514
            $workArr = &$this->queryConfig;
515
            $ssArrSize = count($ssArr) - 1;
516
            $i = 0;
517
            for (; $i < $ssArrSize; $i++) {
518
                $workArr = &$workArr[$ssArr[$i]];
519
            }
520
            // Do stuff:
521
            $tempEl = $workArr[$ssArr[$i]];
522
            if (is_array($tempEl)) {
523
                if ($tempEl['type'] === 'newlevel') {
524
                    $a1 = array_slice($workArr, 0, $ssArr[$i]);
0 ignored issues
show
Bug introduced by
It seems like $workArr can also be of type string; however, parameter $array of array_slice() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

524
                    $a1 = array_slice(/** @scrutinizer ignore-type */ $workArr, 0, $ssArr[$i]);
Loading history...
525
                    $a2 = array_slice($workArr, $ssArr[$i]);
526
                    array_shift($a2);
527
                    $a3 = $tempEl['nl'];
528
                    $a3[0]['operator'] = $tempEl['operator'];
529
                    $workArr = array_merge($a1, $a3, $a2);
530
                }
531
            }
532
        }
533
    }
534
535
    /**
536
     * Clean up query config
537
     *
538
     * @param array $queryConfig Query config
539
     * @return array
540
     */
541
    public function cleanUpQueryConfig($queryConfig)
542
    {
543
        // Since we don't traverse the array using numeric keys in the upcoming while-loop make sure it's fresh and clean before displaying
544
        if (is_array($queryConfig)) {
545
            ksort($queryConfig);
546
        } else {
547
            // queryConfig should never be empty!
548
            if (!isset($queryConfig[0]) || empty($queryConfig[0]['type'])) {
549
                // Make sure queryConfig is an array
550
                $queryConfig = [];
551
                $queryConfig[0] = ['type' => 'FIELD_'];
552
            }
553
        }
554
        // Traverse:
555
        foreach ($queryConfig as $key => $conf) {
556
            $fieldName = '';
557
            if (substr($conf['type'], 0, 6) === 'FIELD_') {
558
                $fieldName = substr($conf['type'], 6);
559
                $fieldType = $this->fields[$fieldName]['type'];
560
            } elseif ($conf['type'] === 'newlevel') {
561
                $fieldType = $conf['type'];
562
            } else {
563
                $fieldType = 'ignore';
564
            }
565
            switch ($fieldType) {
566
                case 'newlevel':
567
                    if (!$queryConfig[$key]['nl']) {
568
                        $queryConfig[$key]['nl'][0]['type'] = 'FIELD_';
569
                    }
570
                    $queryConfig[$key]['nl'] = $this->cleanUpQueryConfig($queryConfig[$key]['nl']);
571
                    break;
572
                case 'userdef':
573
                    $queryConfig[$key] = $this->userDefCleanUp($queryConfig[$key]);
574
                    break;
575
                case 'ignore':
576
                default:
577
                    $verifiedName = $this->verifyType($fieldName);
578
                    $queryConfig[$key]['type'] = 'FIELD_' . $this->verifyType($verifiedName);
579
                    if ($conf['comparison'] >> 5 != $this->comp_offsets[$fieldType]) {
580
                        $conf['comparison'] = $this->comp_offsets[$fieldType] << 5;
581
                    }
582
                    $queryConfig[$key]['comparison'] = $this->verifyComparison($conf['comparison'], $conf['negate'] ? 1 : 0);
583
                    $queryConfig[$key]['inputValue'] = $this->cleanInputVal($queryConfig[$key]);
584
                    $queryConfig[$key]['inputValue1'] = $this->cleanInputVal($queryConfig[$key], 1);
585
            }
586
        }
587
        return $queryConfig;
588
    }
589
590
    /**
591
     * Get form elements
592
     *
593
     * @param int $subLevel
594
     * @param string $queryConfig
595
     * @param string $parent
596
     * @return array
597
     */
598
    public function getFormElements($subLevel = 0, $queryConfig = '', $parent = '')
599
    {
600
        $codeArr = [];
601
        if (!is_array($queryConfig)) {
602
            $queryConfig = $this->queryConfig;
603
        }
604
        $c = 0;
605
        $arrCount = 0;
606
        $loopCount = 0;
607
        foreach ($queryConfig as $key => $conf) {
608
            $fieldName = '';
609
            $subscript = $parent . '[' . $key . ']';
610
            $lineHTML = [];
611
            $lineHTML[] = $this->mkOperatorSelect($this->name . $subscript, $conf['operator'], $c, $conf['type'] !== 'FIELD_');
0 ignored issues
show
Bug introduced by
$c of type integer is incompatible with the type boolean expected by parameter $draw of TYPO3\CMS\Core\Database\...tor::mkOperatorSelect(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

611
            $lineHTML[] = $this->mkOperatorSelect($this->name . $subscript, $conf['operator'], /** @scrutinizer ignore-type */ $c, $conf['type'] !== 'FIELD_');
Loading history...
612
            if (substr($conf['type'], 0, 6) === 'FIELD_') {
613
                $fieldName = substr($conf['type'], 6);
614
                $this->fieldName = $fieldName;
615
                $fieldType = $this->fields[$fieldName]['type'];
616
                if ($conf['comparison'] >> 5 != $this->comp_offsets[$fieldType]) {
617
                    $conf['comparison'] = $this->comp_offsets[$fieldType] << 5;
618
                }
619
                //nasty nasty...
620
                //make sure queryConfig contains _actual_ comparevalue.
621
                //mkCompSelect don't care, but getQuery does.
622
                $queryConfig[$key]['comparison'] += isset($conf['negate']) - $conf['comparison'] % 2;
623
            } elseif ($conf['type'] === 'newlevel') {
624
                $fieldType = $conf['type'];
625
            } else {
626
                $fieldType = 'ignore';
627
            }
628
            $fieldPrefix = htmlspecialchars($this->name . $subscript);
629
            switch ($fieldType) {
630
                case 'ignore':
631
                    break;
632
                case 'newlevel':
633
                    if (!$queryConfig[$key]['nl']) {
634
                        $queryConfig[$key]['nl'][0]['type'] = 'FIELD_';
635
                    }
636
                    $lineHTML[] = '<input type="hidden" name="' . $fieldPrefix . '[type]" value="newlevel">';
637
                    $codeArr[$arrCount]['sub'] = $this->getFormElements($subLevel + 1, $queryConfig[$key]['nl'], $subscript . '[nl]');
638
                    break;
639
                case 'userdef':
640
                    $lineHTML[] = $this->userDef($fieldPrefix, $conf, $fieldName, $fieldType);
641
                    break;
642
                case 'date':
643
                    $lineHTML[] = '<div class="form-inline">';
644
                    $lineHTML[] = $this->makeComparisonSelector($subscript, $fieldName, $conf);
645
                    if ($conf['comparison'] === 100 || $conf['comparison'] === 101) {
646
                        // between
647
                        $lineHTML[] = $this->getDateTimePickerField($fieldPrefix . '[inputValue]', $conf['inputValue'], 'date');
648
                        $lineHTML[] = $this->getDateTimePickerField($fieldPrefix . '[inputValue1]', $conf['inputValue1'], 'date');
649
                    } else {
650
                        $lineHTML[] = $this->getDateTimePickerField($fieldPrefix . '[inputValue]', $conf['inputValue'], 'date');
651
                    }
652
                    $lineHTML[] = '</div>';
653
                    break;
654
                case 'time':
655
                    $lineHTML[] = '<div class="form-inline">';
656
                    $lineHTML[] = $this->makeComparisonSelector($subscript, $fieldName, $conf);
657
                    if ($conf['comparison'] === 100 || $conf['comparison'] === 101) {
658
                        // between:
659
                        $lineHTML[] = $this->getDateTimePickerField($fieldPrefix . '[inputValue]', $conf['inputValue'], 'datetime');
660
                        $lineHTML[] = $this->getDateTimePickerField($fieldPrefix . '[inputValue1]', $conf['inputValue1'], 'datetime');
661
                    } else {
662
                        $lineHTML[] = $this->getDateTimePickerField($fieldPrefix . '[inputValue]', $conf['inputValue'], 'datetime');
663
                    }
664
                    $lineHTML[] = '</div>';
665
                    break;
666
                case 'multiple':
667
                case 'binary':
668
                case 'relation':
669
                    $lineHTML[] = '<div class="form-inline">';
670
                    $lineHTML[] = $this->makeComparisonSelector($subscript, $fieldName, $conf);
671
                    if ($conf['comparison'] === 68 || $conf['comparison'] === 69 || $conf['comparison'] === 162 || $conf['comparison'] === 163) {
672
                        $lineHTML[] = '<select class="form-control" name="' . $fieldPrefix . '[inputValue]' . '[]" multiple="multiple">';
673
                    } elseif ($conf['comparison'] === 66 || $conf['comparison'] === 67) {
674
                        if (is_array($conf['inputValue'])) {
675
                            $conf['inputValue'] = implode(',', $conf['inputValue']);
676
                        }
677
                        $lineHTML[] = '<input class="form-control t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue']) . '" name="' . $fieldPrefix . '[inputValue]' . '">';
678
                    } else {
679
                        $lineHTML[] = '<select class="form-control t3js-submit-change" name="' . $fieldPrefix . '[inputValue]' . '">';
680
                    }
681
                    if ($conf['comparison'] != 66 && $conf['comparison'] != 67) {
682
                        $lineHTML[] = $this->makeOptionList($fieldName, $conf, $this->table);
683
                        $lineHTML[] = '</select>';
684
                    }
685
                    $lineHTML[] = '</div>';
686
                    break;
687
                case 'files':
688
                    $lineHTML[] = '<div class="form-inline">';
689
                    $lineHTML[] = $this->makeComparisonSelector($subscript, $fieldName, $conf);
690
                    if ($conf['comparison'] === 68 || $conf['comparison'] === 69) {
691
                        $lineHTML[] = '<select class="form-control" name="' . $fieldPrefix . '[inputValue]' . '[]" multiple="multiple">';
692
                    } else {
693
                        $lineHTML[] = '<select class="form-control t3js-submit-change" name="' . $fieldPrefix . '[inputValue]' . '">';
694
                    }
695
                    $lineHTML[] = '<option value=""></option>' . $this->makeOptionList($fieldName, $conf, $this->table);
696
                    $lineHTML[] = '</select>';
697
                    if ($conf['comparison'] === 66 || $conf['comparison'] === 67) {
698
                        $lineHTML[] = ' + <input class="form-control t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue1']) . '" name="' . $fieldPrefix . '[inputValue1]' . '">';
699
                    }
700
                    $lineHTML[] = '</div>';
701
                    break;
702
                case 'boolean':
703
                    $lineHTML[] = '<div class="form-inline">';
704
                    $lineHTML[] = $this->makeComparisonSelector($subscript, $fieldName, $conf);
705
                    $lineHTML[] = '<input type="hidden" value="1" name="' . $fieldPrefix . '[inputValue]' . '">';
706
                    $lineHTML[] = '</div>';
707
                    break;
708
                default:
709
                    $lineHTML[] = '<div class="form-inline">';
710
                    $lineHTML[] = $this->makeComparisonSelector($subscript, $fieldName, $conf);
711
                    if ($conf['comparison'] === 37 || $conf['comparison'] === 36) {
712
                        // between:
713
                        $lineHTML[] = '<input class="form-control t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue']) . '" name="' . $fieldPrefix . '[inputValue]' . '">';
714
                        $lineHTML[] = '<input class="form-control t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue1']) . '" name="' . $fieldPrefix . '[inputValue1]' . '">';
715
                    } else {
716
                        $lineHTML[] = '<input class="form-control t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue']) . '" name="' . $fieldPrefix . '[inputValue]' . '">';
717
                    }
718
                    $lineHTML[] = '</div>';
719
            }
720
            if ($fieldType !== 'ignore') {
721
                $lineHTML[] = '<div class="btn-group action-button-group">';
722
                $lineHTML[] = $this->updateIcon();
723
                if ($loopCount) {
724
                    $lineHTML[] = '<button class="btn btn-default" title="Remove condition" name="qG_del' . htmlspecialchars($subscript) . '"><i class="fa fa-trash fa-fw"></i></button>';
725
                }
726
                $lineHTML[] = '<button class="btn btn-default" title="Add condition" name="qG_ins' . htmlspecialchars($subscript) . '"><i class="fa fa-plus fa-fw"></i></button>';
727
                if ($c != 0) {
728
                    $lineHTML[] = '<button class="btn btn-default" title="Move up" name="qG_up' . htmlspecialchars($subscript) . '"><i class="fa fa-chevron-up fa-fw"></i></button>';
729
                }
730
                if ($c != 0 && $fieldType !== 'newlevel') {
731
                    $lineHTML[] = '<button class="btn btn-default" title="New level" name="qG_nl' . htmlspecialchars($subscript) . '"><i class="fa fa-chevron-right fa-fw"></i></button>';
732
                }
733
                if ($fieldType === 'newlevel') {
734
                    $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>';
735
                }
736
                $lineHTML[] = '</div>';
737
                $codeArr[$arrCount]['html'] = implode(LF, $lineHTML);
738
                $codeArr[$arrCount]['query'] = $this->getQuerySingle($conf, $c > 0 ? 0 : 1);
739
                $arrCount++;
740
                $c++;
741
            }
742
            $loopCount = 1;
743
        }
744
        $this->queryConfig = $queryConfig;
745
        return $codeArr;
746
    }
747
748
    /**
749
     * @param string $subscript
750
     * @param string $fieldName
751
     * @param array $conf
752
     *
753
     * @return string
754
     */
755
    protected function makeComparisonSelector($subscript, $fieldName, $conf)
756
    {
757
        $fieldPrefix = $this->name . $subscript;
758
        $lineHTML = [];
759
        $lineHTML[] = $this->mkTypeSelect($fieldPrefix . '[type]', $fieldName);
760
        $lineHTML[] = '	<div class="input-group">';
761
        $lineHTML[] = $this->mkCompSelect($fieldPrefix . '[comparison]', $conf['comparison'], $conf['negate'] ? 1 : 0);
762
        $lineHTML[] = '	<div class="input-group-addon">';
763
        $lineHTML[] = '		<input type="checkbox" class="checkbox t3js-submit-click"' . ($conf['negate'] ? ' checked' : '') . ' name="' . htmlspecialchars($fieldPrefix) . '[negate]' . '">';
764
        $lineHTML[] = '	</div>';
765
        $lineHTML[] = '	</div>';
766
        return implode(LF, $lineHTML);
767
    }
768
769
    /**
770
     * Make option list
771
     *
772
     * @param string $fieldName
773
     * @param array $conf
774
     * @param string $table
775
     * @return string
776
     */
777
    public function makeOptionList($fieldName, $conf, $table)
778
    {
779
        $out = [];
780
        $fieldSetup = $this->fields[$fieldName];
781
        $languageService = $this->getLanguageService();
782
        if ($fieldSetup['type'] === 'files') {
783
            if ($conf['comparison'] === 66 || $conf['comparison'] === 67) {
784
                $fileExtArray = explode(',', $fieldSetup['allowed']);
785
                natcasesort($fileExtArray);
786
                foreach ($fileExtArray as $fileExt) {
787
                    if (GeneralUtility::inList($conf['inputValue'], $fileExt)) {
788
                        $out[] = '<option value="' . htmlspecialchars($fileExt) . '" selected>.' . htmlspecialchars($fileExt) . '</option>';
789
                    } else {
790
                        $out[] = '<option value="' . htmlspecialchars($fileExt) . '">.' . htmlspecialchars($fileExt) . '</option>';
791
                    }
792
                }
793
            }
794
            $d = dir(PATH_site . $fieldSetup['uploadfolder']);
0 ignored issues
show
Bug introduced by
The call to dir() has too few arguments starting with context. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

794
            $d = /** @scrutinizer ignore-call */ dir(PATH_site . $fieldSetup['uploadfolder']);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
795
            while (false !== ($entry = $d->read())) {
796
                if ($entry === '.' || $entry === '..') {
797
                    continue;
798
                }
799
                $fileArray[] = $entry;
800
            }
801
            $d->close();
802
            natcasesort($fileArray);
803
            foreach ($fileArray as $fileName) {
804
                if (GeneralUtility::inList($conf['inputValue'], $fileName)) {
805
                    $out[] = '<option value="' . htmlspecialchars($fileName) . '" selected>' . htmlspecialchars($fileName) . '</option>';
806
                } else {
807
                    $out[] = '<option value="' . htmlspecialchars($fileName) . '">' . htmlspecialchars($fileName) . '</option>';
808
                }
809
            }
810
        }
811
        if ($fieldSetup['type'] === 'multiple') {
812
            foreach ($fieldSetup['items'] as $key => $val) {
813
                if (substr($val[0], 0, 4) === 'LLL:') {
814
                    $value = $languageService->sL($val[0]);
815
                } else {
816
                    $value = $val[0];
817
                }
818
                if (GeneralUtility::inList($conf['inputValue'], $val[1])) {
819
                    $out[] = '<option value="' . htmlspecialchars($val[1]) . '" selected>' . htmlspecialchars($value) . '</option>';
820
                } else {
821
                    $out[] = '<option value="' . htmlspecialchars($val[1]) . '">' . htmlspecialchars($value) . '</option>';
822
                }
823
            }
824
        }
825
        if ($fieldSetup['type'] === 'binary') {
826
            foreach ($fieldSetup['items'] as $key => $val) {
827
                if (substr($val[0], 0, 4) === 'LLL:') {
828
                    $value = $languageService->sL($val[0]);
829
                } else {
830
                    $value = $val[0];
831
                }
832
                if (GeneralUtility::inList($conf['inputValue'], pow(2, $key))) {
833
                    $out[] = '<option value="' . pow(2, $key) . '" selected>' . htmlspecialchars($value) . '</option>';
834
                } else {
835
                    $out[] = '<option value="' . pow(2, $key) . '">' . htmlspecialchars($value) . '</option>';
836
                }
837
            }
838
        }
839
        if ($fieldSetup['type'] === 'relation') {
840
            $useTablePrefix = 0;
841
            $dontPrefixFirstTable = 0;
842
            if ($fieldSetup['items']) {
843
                foreach ($fieldSetup['items'] as $key => $val) {
844
                    if (substr($val[0], 0, 4) === 'LLL:') {
845
                        $value = $languageService->sL($val[0]);
846
                    } else {
847
                        $value = $val[0];
848
                    }
849
                    if (GeneralUtility::inList($conf['inputValue'], $val[1])) {
850
                        $out[] = '<option value="' . htmlspecialchars($val[1]) . '" selected>' . htmlspecialchars($value) . '</option>';
851
                    } else {
852
                        $out[] = '<option value="' . htmlspecialchars($val[1]) . '">' . htmlspecialchars($value) . '</option>';
853
                    }
854
                }
855
            }
856
            if (stristr($fieldSetup['allowed'], ',')) {
857
                $from_table_Arr = explode(',', $fieldSetup['allowed']);
858
                $useTablePrefix = 1;
859
                if (!$fieldSetup['prepend_tname']) {
860
                    $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
861
                    $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
862
                    $statement = $queryBuilder->select($fieldName)
863
                        ->from($table)
864
                        ->execute();
865
                    while ($row = $statement->fetch()) {
866
                        if (stristr($row[$fieldName], ',')) {
867
                            $checkContent = explode(',', $row[$fieldName]);
868
                            foreach ($checkContent as $singleValue) {
869
                                if (!stristr($singleValue, '_')) {
870
                                    $dontPrefixFirstTable = 1;
871
                                }
872
                            }
873
                        } else {
874
                            $singleValue = $row[$fieldName];
875
                            if ($singleValue !== '' && !stristr($singleValue, '_')) {
876
                                $dontPrefixFirstTable = 1;
877
                            }
878
                        }
879
                    }
880
                }
881
            } else {
882
                $from_table_Arr[0] = $fieldSetup['allowed'];
0 ignored issues
show
Comprehensibility Best Practice introduced by
$from_table_Arr was never initialized. Although not strictly required by PHP, it is generally a good practice to add $from_table_Arr = array(); before regardless.
Loading history...
883
            }
884
            if ($fieldSetup['prepend_tname']) {
885
                $useTablePrefix = 1;
886
            }
887
            if ($fieldSetup['foreign_table']) {
888
                $from_table_Arr[0] = $fieldSetup['foreign_table'];
889
            }
890
            $counter = 0;
891
            $tablePrefix = '';
892
            $backendUserAuthentication = $this->getBackendUserAuthentication();
893
            $module = $this->getModule();
894
            $outArray = [];
895
            $labelFieldSelect = [];
896
            foreach ($from_table_Arr as $from_table) {
897
                $useSelectLabels = false;
898
                $useAltSelectLabels = false;
899
                if ($useTablePrefix && !$dontPrefixFirstTable && $counter != 1 || $counter === 1) {
900
                    $tablePrefix = $from_table . '_';
901
                }
902
                $counter = 1;
903
                if (is_array($GLOBALS['TCA'][$from_table])) {
904
                    $labelField = $GLOBALS['TCA'][$from_table]['ctrl']['label'];
905
                    $altLabelField = $GLOBALS['TCA'][$from_table]['ctrl']['label_alt'];
906
                    if ($GLOBALS['TCA'][$from_table]['columns'][$labelField]['config']['items']) {
907
                        foreach ($GLOBALS['TCA'][$from_table]['columns'][$labelField]['config']['items'] as $labelArray) {
908
                            if (substr($labelArray[0], 0, 4) === 'LLL:') {
909
                                $labelFieldSelect[$labelArray[1]] = $languageService->sL($labelArray[0]);
910
                            } else {
911
                                $labelFieldSelect[$labelArray[1]] = $labelArray[0];
912
                            }
913
                        }
914
                        $useSelectLabels = true;
915
                    }
916
                    $altLabelFieldSelect = [];
917
                    if ($GLOBALS['TCA'][$from_table]['columns'][$altLabelField]['config']['items']) {
918
                        foreach ($GLOBALS['TCA'][$from_table]['columns'][$altLabelField]['config']['items'] as $altLabelArray) {
919
                            if (substr($altLabelArray[0], 0, 4) === 'LLL:') {
920
                                $altLabelFieldSelect[$altLabelArray[1]] = $languageService->sL($altLabelArray[0]);
921
                            } else {
922
                                $altLabelFieldSelect[$altLabelArray[1]] = $altLabelArray[0];
923
                            }
924
                        }
925
                        $useAltSelectLabels = true;
926
                    }
927
928
                    if (!$this->tableArray[$from_table]) {
929
                        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($from_table);
930
                        if ($module->MOD_SETTINGS['show_deleted']) {
931
                            $queryBuilder->getRestrictions()->removeAll();
932
                        } else {
933
                            $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
934
                        }
935
                        $selectFields = ['uid', $labelField];
936
                        if ($altLabelField) {
937
                            $selectFields[] = $altLabelField;
938
                        }
939
                        $queryBuilder->select(...$selectFields)
940
                            ->from($from_table)
941
                            ->orderBy('uid');
942
                        if (!$backendUserAuthentication->isAdmin() && $GLOBALS['TYPO3_CONF_VARS']['BE']['lockBeUserToDBmounts']) {
943
                            $webMounts = $backendUserAuthentication->returnWebmounts();
944
                            $perms_clause = $backendUserAuthentication->getPagePermsClause(Permission::PAGE_SHOW);
945
                            $webMountPageTree = '';
946
                            $webMountPageTreePrefix = '';
947
                            foreach ($webMounts as $webMount) {
948
                                if ($webMountPageTree) {
949
                                    $webMountPageTreePrefix = ',';
950
                                }
951
                                $webMountPageTree .= $webMountPageTreePrefix
952
                                    . $this->getTreeList($webMount, 999, ($begin = 0), $perms_clause);
953
                            }
954
                            if ($from_table === 'pages') {
955
                                $queryBuilder->where(
956
                                    QueryHelper::stripLogicalOperatorPrefix($perms_clause),
957
                                    $queryBuilder->expr()->in(
958
                                        'uid',
959
                                        $queryBuilder->createNamedParameter(
960
                                            GeneralUtility::intExplode(',', $webMountPageTree),
961
                                            Connection::PARAM_INT_ARRAY
962
                                        )
963
                                    )
964
                                );
965
                            } else {
966
                                $queryBuilder->where(
967
                                    $queryBuilder->expr()->in(
968
                                        'pid',
969
                                        $queryBuilder->createNamedParameter(
970
                                            GeneralUtility::intExplode(',', $webMountPageTree),
971
                                            Connection::PARAM_INT_ARRAY
972
                                        )
973
                                    )
974
                                );
975
                            }
976
                        }
977
                        $statement = $queryBuilder->execute();
978
                        $this->tableArray[$from_table] = [];
979
                        while ($row = $statement->fetch()) {
980
                            $this->tableArray[$from_table][] = $row;
981
                        }
982
                    }
983
984
                    foreach ($this->tableArray[$from_table] as $key => $val) {
985
                        if ($useSelectLabels) {
986
                            $outArray[$tablePrefix . $val['uid']] = htmlspecialchars($labelFieldSelect[$val[$labelField]]);
987
                        } elseif ($val[$labelField]) {
988
                            $outArray[$tablePrefix . $val['uid']] = htmlspecialchars($val[$labelField]);
989
                        } elseif ($useAltSelectLabels) {
990
                            $outArray[$tablePrefix . $val['uid']] = htmlspecialchars($altLabelFieldSelect[$val[$altLabelField]]);
991
                        } else {
992
                            $outArray[$tablePrefix . $val['uid']] = htmlspecialchars($val[$altLabelField]);
993
                        }
994
                    }
995
                    if ($module->MOD_SETTINGS['options_sortlabel'] && is_array($outArray)) {
996
                        natcasesort($outArray);
997
                    }
998
                }
999
            }
1000
            foreach ($outArray as $key2 => $val2) {
1001
                if (GeneralUtility::inList($conf['inputValue'], $key2)) {
1002
                    $out[] = '<option value="' . htmlspecialchars($key2) . '" selected>[' . htmlspecialchars($key2) . '] ' . htmlspecialchars($val2) . '</option>';
1003
                } else {
1004
                    $out[] = '<option value="' . htmlspecialchars($key2) . '">[' . htmlspecialchars($key2) . '] ' . htmlspecialchars($val2) . '</option>';
1005
                }
1006
            }
1007
        }
1008
        return implode(LF, $out);
1009
    }
1010
1011
    /**
1012
     * Print code array
1013
     *
1014
     * @param array $codeArr
1015
     * @param int $recursionLevel
1016
     * @return string
1017
     */
1018
    public function printCodeArray($codeArr, $recursionLevel = 0)
1019
    {
1020
        $indent = 'row-group';
1021
        if ($recursionLevel) {
1022
            $indent = 'row-group indent indent-' . (int)$recursionLevel;
1023
        }
1024
        $out = [];
1025
        foreach ($codeArr as $k => $v) {
1026
            $out[] = '<div class="' . $indent . '">';
1027
            $out[] = $v['html'];
1028
1029
            if ($this->enableQueryParts) {
1030
                $out[] = '<pre>';
1031
                $out[] = htmlspecialchars($v['query']);
1032
                $out[] = '</pre>';
1033
            }
1034
            if (is_array($v['sub'])) {
1035
                $out[] = '<div class="' . $indent . '">';
1036
                $out[] = $this->printCodeArray($v['sub'], ($recursionLevel + 1));
1037
                $out[] = '</div>';
1038
            }
1039
1040
            $out[] = '</div>';
1041
        }
1042
        return implode(LF, $out);
1043
    }
1044
1045
    /**
1046
     * Make operator select
1047
     *
1048
     * @param string $name
1049
     * @param string $op
1050
     * @param bool $draw
1051
     * @param bool $submit
1052
     * @return string
1053
     */
1054
    public function mkOperatorSelect($name, $op, $draw, $submit)
1055
    {
1056
        $out = [];
1057
        if ($draw) {
1058
            $out[] = '<select class="form-control from-control-operator' . ($submit ? ' t3js-submit-change' : '') . '" name="' . htmlspecialchars($name) . '[operator]">';
1059
            $out[] = '	<option value="AND"' . (!$op || $op === 'AND' ? ' selected' : '') . '>' . htmlspecialchars($this->lang['AND']) . '</option>';
1060
            $out[] = '	<option value="OR"' . ($op === 'OR' ? ' selected' : '') . '>' . htmlspecialchars($this->lang['OR']) . '</option>';
1061
            $out[] = '</select>';
1062
        } else {
1063
            $out[] = '<input type="hidden" value="' . htmlspecialchars($op) . '" name="' . htmlspecialchars($name) . '[operator]">';
1064
        }
1065
        return implode(LF, $out);
1066
    }
1067
1068
    /**
1069
     * Make type select
1070
     *
1071
     * @param string $name
1072
     * @param string $fieldName
1073
     * @param string $prepend
1074
     * @return string
1075
     */
1076
    public function mkTypeSelect($name, $fieldName, $prepend = 'FIELD_')
1077
    {
1078
        $out = [];
1079
        $out[] = '<select class="form-control t3js-submit-change" name="' . htmlspecialchars($name) . '">';
1080
        $out[] = '<option value=""></option>';
1081
        foreach ($this->fields as $key => $value) {
1082
            if (!$value['exclude'] || $this->getBackendUserAuthentication()->check('non_exclude_fields', $this->table . ':' . $key)) {
1083
                $label = $this->fields[$key]['label'];
1084
                $out[] = '<option value="' . htmlspecialchars($prepend . $key) . '"' . ($key === $fieldName ? ' selected' : '') . '>' . htmlspecialchars($label) . '</option>';
1085
            }
1086
        }
1087
        $out[] = '</select>';
1088
        return implode(LF, $out);
1089
    }
1090
1091
    /**
1092
     * Verify type
1093
     *
1094
     * @param string $fieldName
1095
     * @return string
1096
     */
1097
    public function verifyType($fieldName)
1098
    {
1099
        $first = '';
1100
        foreach ($this->fields as $key => $value) {
1101
            if (!$first) {
1102
                $first = $key;
1103
            }
1104
            if ($key === $fieldName) {
1105
                return $key;
1106
            }
1107
        }
1108
        return $first;
1109
    }
1110
1111
    /**
1112
     * Verify comparison
1113
     *
1114
     * @param string $comparison
1115
     * @param int $neg
1116
     * @return int
1117
     */
1118
    public function verifyComparison($comparison, $neg)
1119
    {
1120
        $compOffSet = $comparison >> 5;
1121
        $first = -1;
1122
        for ($i = 32 * $compOffSet + $neg; $i < 32 * ($compOffSet + 1); $i += 2) {
1123
            if ($first === -1) {
1124
                $first = $i;
1125
            }
1126
            if ($i >> 1 === $comparison >> 1) {
1127
                return $i;
1128
            }
1129
        }
1130
        return $first;
1131
    }
1132
1133
    /**
1134
     * Make field to input select
1135
     *
1136
     * @param string $name
1137
     * @param string $fieldName
1138
     * @return string
1139
     */
1140
    public function mkFieldToInputSelect($name, $fieldName)
1141
    {
1142
        $out = [];
1143
        $out[] = '<div class="input-group">';
1144
        $out[] = '	<div class="input-group-addon">';
1145
        $out[] = '		<span class="input-group-btn">';
1146
        $out[] = $this->updateIcon();
1147
        $out[] = ' 		</span>';
1148
        $out[] = ' 	</div>';
1149
        $out[] = '	<input type="text" class="form-control t3js-clearable" value="' . htmlspecialchars($fieldName) . '" name="' . htmlspecialchars($name) . '">';
1150
        $out[] = '</div>';
1151
1152
        $out[] = '<select class="form-control t3js-addfield" name="_fieldListDummy" size="5" data-field="' . htmlspecialchars($name) . '">';
1153
        foreach ($this->fields as $key => $value) {
1154
            if (!$value['exclude'] || $this->getBackendUserAuthentication()->check('non_exclude_fields', $this->table . ':' . $key)) {
1155
                $label = $this->fields[$key]['label'];
1156
                $out[] = '<option value="' . htmlspecialchars($key) . '"' . ($key === $fieldName ? ' selected' : '') . '>' . htmlspecialchars($label) . '</option>';
1157
            }
1158
        }
1159
        $out[] = '</select>';
1160
        return implode(LF, $out);
1161
    }
1162
1163
    /**
1164
     * Make table select
1165
     *
1166
     * @param string $name
1167
     * @param string $cur
1168
     * @return string
1169
     */
1170
    public function mkTableSelect($name, $cur)
1171
    {
1172
        $out = [];
1173
        $out[] = '<select class="form-control t3js-submit-change" name="' . $name . '">';
1174
        $out[] = '<option value=""></option>';
1175
        foreach ($GLOBALS['TCA'] as $tN => $value) {
1176
            if ($this->getBackendUserAuthentication()->check('tables_select', $tN)) {
1177
                $out[] = '<option value="' . htmlspecialchars($tN) . '"' . ($tN === $cur ? ' selected' : '') . '>' . htmlspecialchars($this->getLanguageService()->sL($GLOBALS['TCA'][$tN]['ctrl']['title'])) . '</option>';
1178
            }
1179
        }
1180
        $out[] = '</select>';
1181
        return implode(LF, $out);
1182
    }
1183
1184
    /**
1185
     * Make comparison select
1186
     *
1187
     * @param string $name
1188
     * @param string $comparison
1189
     * @param int $neg
1190
     * @return string
1191
     */
1192
    public function mkCompSelect($name, $comparison, $neg)
1193
    {
1194
        $compOffSet = $comparison >> 5;
1195
        $out = [];
1196
        $out[] = '<select class="form-control t3js-submit-change" name="' . $name . '">';
1197
        for ($i = 32 * $compOffSet + $neg; $i < 32 * ($compOffSet + 1); $i += 2) {
1198
            if ($this->lang['comparison'][$i . '_']) {
1199
                $out[] = '<option value="' . $i . '"' . ($i >> 1 === $comparison >> 1 ? ' selected' : '') . '>' . htmlspecialchars($this->lang['comparison'][$i . '_']) . '</option>';
1200
            }
1201
        }
1202
        $out[] = '</select>';
1203
        return implode(LF, $out);
1204
    }
1205
1206
    /**
1207
     * Get subscript
1208
     *
1209
     * @param array $arr
1210
     * @return array
1211
     */
1212
    public function getSubscript($arr)
1213
    {
1214
        $retArr = [];
1215
        while (is_array($arr)) {
1216
            reset($arr);
1217
            list($key, ) = each($arr);
0 ignored issues
show
Deprecated Code introduced by
The function each() has been deprecated: 7.2 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1217
            list($key, ) = /** @scrutinizer ignore-deprecated */ each($arr);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1218
            $retArr[] = $key;
1219
            $arr = $arr[$key];
1220
        }
1221
        return $retArr;
1222
    }
1223
1224
    /**
1225
     * Init user definition
1226
     */
1227
    public function initUserDef()
1228
    {
1229
    }
1230
1231
    /**
1232
     * User definition
1233
     *
1234
     * @param string $fieldPrefix
1235
     * @param array $conf
1236
     * @param string $fieldName
1237
     * @param string $fieldType
1238
     *
1239
     * @return string
1240
     */
1241
    public function userDef($fieldPrefix, $conf, $fieldName, $fieldType)
0 ignored issues
show
Unused Code introduced by
The parameter $fieldPrefix is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

1241
    public function userDef(/** @scrutinizer ignore-unused */ $fieldPrefix, $conf, $fieldName, $fieldType)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $fieldName is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

1241
    public function userDef($fieldPrefix, $conf, /** @scrutinizer ignore-unused */ $fieldName, $fieldType)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $conf is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

1241
    public function userDef($fieldPrefix, /** @scrutinizer ignore-unused */ $conf, $fieldName, $fieldType)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $fieldType is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

1241
    public function userDef($fieldPrefix, $conf, $fieldName, /** @scrutinizer ignore-unused */ $fieldType)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1242
    {
1243
        return '';
1244
    }
1245
1246
    /**
1247
     * User definition clean up
1248
     *
1249
     * @param array $queryConfig
1250
     * @return array
1251
     */
1252
    public function userDefCleanUp($queryConfig)
1253
    {
1254
        return $queryConfig;
1255
    }
1256
1257
    /**
1258
     * Get query
1259
     *
1260
     * @param array $queryConfig
1261
     * @param string $pad
1262
     * @return string
1263
     */
1264
    public function getQuery($queryConfig, $pad = '')
1265
    {
1266
        $qs = '';
1267
        // Since we don't traverse the array using numeric keys in the upcoming whileloop make sure it's fresh and clean
1268
        ksort($queryConfig);
1269
        $first = 1;
1270
        foreach ($queryConfig as $key => $conf) {
1271
            switch ($conf['type']) {
1272
                case 'newlevel':
1273
                    $qs .= LF . $pad . trim($conf['operator']) . ' (' . $this->getQuery($queryConfig[$key]['nl'], ($pad . '   ')) . LF . $pad . ')';
1274
                    break;
1275
                case 'userdef':
1276
                    $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...
Bug introduced by
$first of type integer is incompatible with the type boolean expected by parameter $first of TYPO3\CMS\Core\Database\...ator::getUserDefQuery(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1276
                    $qs .= LF . $pad . $this->getUserDefQuery($conf, /** @scrutinizer ignore-type */ $first);
Loading history...
1277
                    break;
1278
                default:
1279
                    $qs .= LF . $pad . $this->getQuerySingle($conf, $first);
0 ignored issues
show
Bug introduced by
$first of type integer is incompatible with the type boolean expected by parameter $first of TYPO3\CMS\Core\Database\...rator::getQuerySingle(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1279
                    $qs .= LF . $pad . $this->getQuerySingle($conf, /** @scrutinizer ignore-type */ $first);
Loading history...
1280
            }
1281
            $first = 0;
1282
        }
1283
        return $qs;
1284
    }
1285
1286
    /**
1287
     * Get single query
1288
     *
1289
     * @param array $conf
1290
     * @param bool $first
1291
     * @return string
1292
     */
1293
    public function getQuerySingle($conf, $first)
1294
    {
1295
        $qs = '';
1296
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($this->table);
1297
        $prefix = $this->enablePrefix ? $this->table . '.' : '';
1298
        if (!$first) {
1299
            // Is it OK to insert the AND operator if none is set?
1300
            $qs .= trim($conf['operator'] ?: 'AND') . ' ';
1301
        }
1302
        $qsTmp = str_replace('#FIELD#', $prefix . trim(substr($conf['type'], 6)), $this->compSQL[$conf['comparison']]);
1303
        $inputVal = $this->cleanInputVal($conf);
1304
        if ($conf['comparison'] === 68 || $conf['comparison'] === 69) {
1305
            $inputVal = explode(',', $inputVal);
1306
            foreach ($inputVal as $key => $fileName) {
1307
                $inputVal[$key] = '\'' . $fileName . '\'';
1308
            }
1309
            $inputVal = implode(',', $inputVal);
1310
            $qsTmp = str_replace('#VALUE#', $inputVal, $qsTmp);
1311
        } elseif ($conf['comparison'] === 162 || $conf['comparison'] === 163) {
1312
            $inputValArray = explode(',', $inputVal);
1313
            $inputVal = 0;
1314
            foreach ($inputValArray as $fileName) {
1315
                $inputVal += (int)$fileName;
1316
            }
1317
            $qsTmp = str_replace('#VALUE#', $inputVal, $qsTmp);
1318
        } else {
1319
            if (is_array($inputVal)) {
1320
                $inputVal = $inputVal[0];
1321
            }
1322
            $qsTmp = str_replace('#VALUE#', trim($queryBuilder->quote($inputVal), '\''), $qsTmp);
1323
        }
1324
        if ($conf['comparison'] === 37 || $conf['comparison'] === 36 || $conf['comparison'] === 66 || $conf['comparison'] === 67 || $conf['comparison'] === 100 || $conf['comparison'] === 101) {
1325
            // between:
1326
            $inputVal = $this->cleanInputVal($conf, '1');
1327
            $qsTmp = str_replace('#VALUE1#', trim($queryBuilder->quote($inputVal), '\''), $qsTmp);
1328
        }
1329
        $qs .= trim($qsTmp);
1330
        return $qs;
1331
    }
1332
1333
    /**
1334
     * Clear input value
1335
     *
1336
     * @param array $conf
1337
     * @param string $suffix
1338
     * @return string
1339
     */
1340
    public function cleanInputVal($conf, $suffix = '')
1341
    {
1342
        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)) {
1343
            $inputVal = $conf['inputValue' . $suffix];
1344
        } elseif ($conf['comparison'] === 39 || $conf['comparison'] === 38) {
1345
            // in list:
1346
            $inputVal = implode(',', GeneralUtility::intExplode(',', $conf['inputValue' . $suffix]));
1347
        } elseif ($conf['comparison'] === 68 || $conf['comparison'] === 69 || $conf['comparison'] === 162 || $conf['comparison'] === 163) {
1348
            // in list:
1349
            if (is_array($conf['inputValue' . $suffix])) {
1350
                $inputVal = implode(',', $conf['inputValue' . $suffix]);
1351
            } elseif ($conf['inputValue' . $suffix]) {
1352
                $inputVal = $conf['inputValue' . $suffix];
1353
            } else {
1354
                $inputVal = 0;
1355
            }
1356
        } else {
1357
            $inputVal = (float)$conf['inputValue' . $suffix];
1358
        }
1359
        return $inputVal;
1360
    }
1361
1362
    /**
1363
     * Get user definition query
1364
     *
1365
     * @param array $qcArr
1366
     * @param bool $first
1367
     */
1368
    public function getUserDefQuery($qcArr, $first)
0 ignored issues
show
Unused Code introduced by
The parameter $first is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

1368
    public function getUserDefQuery($qcArr, /** @scrutinizer ignore-unused */ $first)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $qcArr is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

1368
    public function getUserDefQuery(/** @scrutinizer ignore-unused */ $qcArr, $first)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1369
    {
1370
    }
1371
1372
    /**
1373
     * Update icon
1374
     *
1375
     * @return string
1376
     */
1377
    public function updateIcon()
1378
    {
1379
        return '<button class="btn btn-default" title="Update" name="just_update"><i class="fa fa-refresh fa-fw"></i></button>';
1380
    }
1381
1382
    /**
1383
     * Get label column
1384
     *
1385
     * @return string
1386
     */
1387
    public function getLabelCol()
1388
    {
1389
        return $GLOBALS['TCA'][$this->table]['ctrl']['label'];
1390
    }
1391
1392
    /**
1393
     * Make selector table
1394
     *
1395
     * @param array $modSettings
1396
     * @param string $enableList
1397
     * @return string
1398
     */
1399
    public function makeSelectorTable($modSettings, $enableList = 'table,fields,query,group,order,limit')
1400
    {
1401
        $out = [];
1402
        $enableArr = explode(',', $enableList);
1403
        $backendUserAuthentication = $this->getBackendUserAuthentication();
1404
        // Make output
1405
        if (in_array('table', $enableArr) && !$backendUserAuthentication->userTS['mod.']['dbint.']['disableSelectATable']) {
1406
            $out[] = '<div class="form-group">';
1407
            $out[] = '	<label for="SET[queryTable]">Select a table:</label>';
1408
            $out[] =    $this->mkTableSelect('SET[queryTable]', $this->table);
1409
            $out[] = '</div>';
1410
        }
1411
        if ($this->table) {
1412
            // Init fields:
1413
            $this->setAndCleanUpExternalLists('queryFields', $modSettings['queryFields'], 'uid,' . $this->getLabelCol());
1414
            $this->setAndCleanUpExternalLists('queryGroup', $modSettings['queryGroup']);
1415
            $this->setAndCleanUpExternalLists('queryOrder', $modSettings['queryOrder'] . ',' . $modSettings['queryOrder2']);
1416
            // Limit:
1417
            $this->extFieldLists['queryLimit'] = $modSettings['queryLimit'];
1418
            if (!$this->extFieldLists['queryLimit']) {
1419
                $this->extFieldLists['queryLimit'] = 100;
1420
            }
1421
            $parts = GeneralUtility::intExplode(',', $this->extFieldLists['queryLimit']);
1422
            if ($parts[1]) {
1423
                $this->limitBegin = $parts[0];
1424
                $this->limitLength = $parts[1];
1425
            } else {
1426
                $this->limitLength = $this->extFieldLists['queryLimit'];
1427
            }
1428
            $this->extFieldLists['queryLimit'] = implode(',', array_slice($parts, 0, 2));
1429
            // Insert Descending parts
1430
            if ($this->extFieldLists['queryOrder']) {
1431
                $descParts = explode(',', $modSettings['queryOrderDesc'] . ',' . $modSettings['queryOrder2Desc']);
1432
                $orderParts = explode(',', $this->extFieldLists['queryOrder']);
1433
                $reList = [];
1434
                foreach ($orderParts as $kk => $vv) {
1435
                    $reList[] = $vv . ($descParts[$kk] ? ' DESC' : '');
1436
                }
1437
                $this->extFieldLists['queryOrder_SQL'] = implode(',', $reList);
1438
            }
1439
            // Query Generator:
1440
            $this->procesData($modSettings['queryConfig'] ? unserialize($modSettings['queryConfig']) : '');
1441
            $this->queryConfig = $this->cleanUpQueryConfig($this->queryConfig);
0 ignored issues
show
Bug introduced by
It seems like $this->queryConfig can also be of type string; however, parameter $queryConfig of TYPO3\CMS\Core\Database\...r::cleanUpQueryConfig() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1441
            $this->queryConfig = $this->cleanUpQueryConfig(/** @scrutinizer ignore-type */ $this->queryConfig);
Loading history...
1442
            $this->enableQueryParts = (bool)$modSettings['search_query_smallparts'];
1443
            $codeArr = $this->getFormElements();
1444
            $queryCode = $this->printCodeArray($codeArr);
1445
            if (in_array('fields', $enableArr) && !$backendUserAuthentication->userTS['mod.']['dbint.']['disableSelectFields']) {
1446
                $out[] = '<div class="form-group form-group-with-button-addon">';
1447
                $out[] = '	<label for="SET[queryFields]">Select fields:</label>';
1448
                $out[] =    $this->mkFieldToInputSelect('SET[queryFields]', $this->extFieldLists['queryFields']);
1449
                $out[] = '</div>';
1450
            }
1451
            if (in_array('query', $enableArr) && !$backendUserAuthentication->userTS['mod.']['dbint.']['disableMakeQuery']) {
1452
                $out[] = '<div class="form-group">';
1453
                $out[] = '	<label>Make Query:</label>';
1454
                $out[] =    $queryCode;
1455
                $out[] = '</div>';
1456
            }
1457
            if (in_array('group', $enableArr) && !$backendUserAuthentication->userTS['mod.']['dbint.']['disableGroupBy']) {
1458
                $out[] = '<div class="form-group form-inline">';
1459
                $out[] = '	<label for="SET[queryGroup]">Group By:</label>';
1460
                $out[] =     $this->mkTypeSelect('SET[queryGroup]', $this->extFieldLists['queryGroup'], '');
1461
                $out[] = '</div>';
1462
            }
1463
            if (in_array('order', $enableArr) && !$backendUserAuthentication->userTS['mod.']['dbint.']['disableOrderBy']) {
1464
                $module = $this->getModule();
1465
                $orderByArr = explode(',', $this->extFieldLists['queryOrder']);
1466
                $orderBy = [];
1467
                $orderBy[] = $this->mkTypeSelect('SET[queryOrder]', $orderByArr[0], '');
1468
                $orderBy[] = '<div class="checkbox">';
1469
                $orderBy[] = '	<label for="checkQueryOrderDesc">';
1470
                $orderBy[] =        BackendUtility::getFuncCheck($module->id, 'SET[queryOrderDesc]', $modSettings['queryOrderDesc'], '', '', 'id="checkQueryOrderDesc"') . ' Descending';
1471
                $orderBy[] = '	</label>';
1472
                $orderBy[] = '</div>';
1473
1474
                if ($orderByArr[0]) {
1475
                    $orderBy[] = $this->mkTypeSelect('SET[queryOrder2]', $orderByArr[1], '');
1476
                    $orderBy[] = '<div class="checkbox">';
1477
                    $orderBy[] = '	<label for="checkQueryOrder2Desc">';
1478
                    $orderBy[] =        BackendUtility::getFuncCheck($module->id, 'SET[queryOrder2Desc]', $modSettings['queryOrder2Desc'], '', '', 'id="checkQueryOrder2Desc"') . ' Descending';
1479
                    $orderBy[] = '	</label>';
1480
                    $orderBy[] = '</div>';
1481
                }
1482
                $out[] = '<div class="form-group form-inline">';
1483
                $out[] = '	<label>Order By:</label>';
1484
                $out[] =     implode(LF, $orderBy);
1485
                $out[] = '</div>';
1486
            }
1487
            if (in_array('limit', $enableArr) && !$backendUserAuthentication->userTS['mod.']['dbint.']['disableLimit']) {
1488
                $limit = [];
1489
                $limit[] = '<div class="input-group">';
1490
                $limit[] = '	<div class="input-group-addon">';
1491
                $limit[] = '		<span class="input-group-btn">';
1492
                $limit[] = $this->updateIcon();
1493
                $limit[] = '		</span>';
1494
                $limit[] = '	</div>';
1495
                $limit[] = '	<input type="text" class="form-control" value="' . htmlspecialchars($this->extFieldLists['queryLimit']) . '" name="SET[queryLimit]" id="queryLimit">';
1496
                $limit[] = '</div>';
1497
1498
                $prevLimit = $this->limitBegin - $this->limitLength < 0 ? 0 : $this->limitBegin - $this->limitLength;
1499
                $prevButton = '';
1500
                $nextButton = '';
1501
1502
                if ($this->limitBegin) {
1503
                    $prevButton = '<input type="button" class="btn btn-default" value="previous ' . htmlspecialchars($this->limitLength) . '" data-value="' . htmlspecialchars($prevLimit . ',' . $this->limitLength) . '">';
1504
                }
1505
                if (!$this->limitLength) {
1506
                    $this->limitLength = 100;
1507
                }
1508
1509
                $nextLimit = $this->limitBegin + $this->limitLength;
1510
                if ($nextLimit < 0) {
1511
                    $nextLimit = 0;
1512
                }
1513
                if ($nextLimit) {
1514
                    $nextButton = '<input type="button" class="btn btn-default" value="next ' . htmlspecialchars($this->limitLength) . '" data-value="' . htmlspecialchars($nextLimit . ',' . $this->limitLength) . '">';
1515
                }
1516
1517
                $out[] = '<div class="form-group form-group-with-button-addon">';
1518
                $out[] = '	<label>Limit:</label>';
1519
                $out[] = '	<div class="form-inline">';
1520
                $out[] =        implode(LF, $limit);
1521
                $out[] = '		<div class="input-group">';
1522
                $out[] = '			<div class="btn-group t3js-limit-submit">';
1523
                $out[] =                $prevButton;
1524
                $out[] =                $nextButton;
1525
                $out[] = '			</div>';
1526
                $out[] = '			<div class="btn-group t3js-limit-submit">';
1527
                $out[] = '				<input type="button" class="btn btn-default" data-value="10" value="10">';
1528
                $out[] = '				<input type="button" class="btn btn-default" data-value="20" value="20">';
1529
                $out[] = '				<input type="button" class="btn btn-default" data-value="50" value="50">';
1530
                $out[] = '				<input type="button" class="btn btn-default" data-value="100" value="100">';
1531
                $out[] = '			</div>';
1532
                $out[] = '		</div>';
1533
                $out[] = '	</div>';
1534
                $out[] = '</div>';
1535
            }
1536
        }
1537
        return implode(LF, $out);
1538
    }
1539
1540
    /**
1541
     * Get tree list
1542
     *
1543
     * @param int $id
1544
     * @param int $depth
1545
     * @param int $begin
1546
     * @param string $permClause
1547
     * @return string
1548
     */
1549
    public function getTreeList($id, $depth, $begin = 0, $permClause = '')
1550
    {
1551
        $depth = (int)$depth;
1552
        $begin = (int)$begin;
1553
        $id = (int)$id;
1554
        if ($id < 0) {
1555
            $id = abs($id);
1556
        }
1557
        if ($begin === 0) {
1558
            $theList = $id;
1559
        } else {
1560
            $theList = '';
1561
        }
1562
        if ($id && $depth > 0) {
1563
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
1564
            $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1565
            $queryBuilder->select('uid')
1566
                ->from('pages')
1567
                ->where($queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($id, \PDO::PARAM_INT)));
1568
            if ($permClause !== '') {
1569
                $queryBuilder->andWhere(QueryHelper::stripLogicalOperatorPrefix($permClause));
1570
            }
1571
            $statement = $queryBuilder->execute();
1572
            while ($row = $statement->fetch()) {
1573
                if ($begin <= 0) {
1574
                    $theList .= ',' . $row['uid'];
1575
                }
1576
                if ($depth > 1) {
1577
                    $theList .= $this->getTreeList($row['uid'], $depth - 1, $begin - 1, $permClause);
1578
                }
1579
            }
1580
        }
1581
        return $theList;
1582
    }
1583
1584
    /**
1585
     * Get select query
1586
     *
1587
     * @param string $qString
1588
     * @return string
1589
     */
1590
    public function getSelectQuery($qString = ''): string
1591
    {
1592
        $backendUserAuthentication = $this->getBackendUserAuthentication();
1593
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->table);
1594
        if ($this->getModule()->MOD_SETTINGS['show_deleted']) {
1595
            $queryBuilder->getRestrictions()->removeAll();
1596
        } else {
1597
            $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1598
        }
1599
        $fieldList = GeneralUtility::trimExplode(
1600
            ',',
1601
            $this->extFieldLists['queryFields']
1602
            . ',pid'
1603
            . ($GLOBALS['TCA'][$this->table]['ctrl']['delete'] ? ',' . $GLOBALS['TCA'][$this->table]['ctrl']['delete'] : '')
1604
        );
1605
        $queryBuilder->select(...$fieldList)
1606
            ->from($this->table);
1607
1608
        if ($this->extFieldLists['queryGroup']) {
1609
            $queryBuilder->groupBy(...QueryHelper::parseGroupBy($this->extFieldLists['queryGroup']));
1610
        }
1611
        if ($this->extFieldLists['queryOrder']) {
1612
            foreach (QueryHelper::parseOrderBy($this->extFieldLists['queryOrder_SQL']) as $orderPair) {
1613
                list($fieldName, $order) = $orderPair;
1614
                $queryBuilder->addOrderBy($fieldName, $order);
1615
            }
1616
        }
1617
        if ($this->extFieldLists['queryLimit']) {
1618
            $queryBuilder->setMaxResults((int)$this->extFieldLists['queryLimit']);
1619
        }
1620
1621
        if (!$backendUserAuthentication->isAdmin() && $GLOBALS['TYPO3_CONF_VARS']['BE']['lockBeUserToDBmounts']) {
1622
            $webMounts = $backendUserAuthentication->returnWebmounts();
1623
            $perms_clause = $backendUserAuthentication->getPagePermsClause(Permission::PAGE_SHOW);
1624
            $webMountPageTree = '';
1625
            $webMountPageTreePrefix = '';
1626
            foreach ($webMounts as $webMount) {
1627
                if ($webMountPageTree) {
1628
                    $webMountPageTreePrefix = ',';
1629
                }
1630
                $webMountPageTree .= $webMountPageTreePrefix
1631
                    . $this->getTreeList($webMount, 999, ($begin = 0), $perms_clause);
1632
            }
1633
            // createNamedParameter() is not used here because the SQL fragment will only include
1634
            // the :dcValueX placeholder when the query is returned as a string. The value for the
1635
            // placeholder would be lost in the process.
1636
            if ($this->table === 'pages') {
1637
                $queryBuilder->where(
1638
                    QueryHelper::stripLogicalOperatorPrefix($perms_clause),
1639
                    $queryBuilder->expr()->in(
1640
                        'uid',
1641
                        GeneralUtility::intExplode(',', $webMountPageTree)
1642
                    )
1643
                );
1644
            } else {
1645
                $queryBuilder->where(
1646
                    $queryBuilder->expr()->in(
1647
                        'pid',
1648
                        GeneralUtility::intExplode(',', $webMountPageTree)
1649
                    )
1650
                );
1651
            }
1652
        }
1653
        if (!$qString) {
1654
            $qString = $this->getQuery($this->queryConfig);
1655
        }
1656
        $queryBuilder->andWhere(QueryHelper::stripLogicalOperatorPrefix($qString));
1657
1658
        return $queryBuilder->getSQL();
1659
    }
1660
1661
    /**
1662
     * @param string $name the field name
1663
     * @param int $timestamp the unix timestamp
1664
     * @param string $type [datetime, date, time, timesec, year]
1665
     *
1666
     * @return string
1667
     */
1668
    protected function getDateTimePickerField($name, $timestamp, $type)
1669
    {
1670
        $dateFormat = $GLOBALS['TYPO3_CONF_VARS']['SYS']['USdateFormat'] ? '%H:%M %m-%d-%Y' : '%H:%M %d-%m-%Y';
1671
        $value = ($timestamp > 0 ? strftime($dateFormat, $timestamp) : '');
1672
        $id = StringUtility::getUniqueId('dt_');
1673
        $html = [];
1674
        $html[] = '<div class="input-group" id="' . $id . '-wrapper">';
1675
        $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 . '">';
1676
        $html[] = '		<input name="' . htmlspecialchars($name) . '" value="' . (int)$timestamp . '" type="hidden">';
1677
        $html[] = '		<span class="input-group-btn">';
1678
        $html[] = '			<label class="btn btn-default" for="' . $id . '">';
1679
        $html[] = '				<span class="fa fa-calendar"></span>';
1680
        $html[] = '			</label>';
1681
        $html[] = ' 	</span>';
1682
        $html[] = '</div>';
1683
        return implode(LF, $html);
1684
    }
1685
1686
    /**
1687
     * Sets the current name of the input form.
1688
     *
1689
     * @param string $formName The name of the form.
1690
     */
1691
    public function setFormName($formName)
1692
    {
1693
        $this->formName = trim($formName);
1694
    }
1695
1696
    /**
1697
     * @return BackendUserAuthentication
1698
     */
1699
    protected function getBackendUserAuthentication()
1700
    {
1701
        return $GLOBALS['BE_USER'];
1702
    }
1703
1704
    /**
1705
     * @return BaseScriptClass
1706
     */
1707
    protected function getModule()
1708
    {
1709
        return $GLOBALS['SOBE'];
1710
    }
1711
1712
    /**
1713
     * @return LanguageService
1714
     */
1715
    protected function getLanguageService()
1716
    {
1717
        return $GLOBALS['LANG'];
1718
    }
1719
}
1720