1<?php
2namespace 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
17use TYPO3\CMS\Backend\Module\BaseScriptClass;
18use TYPO3\CMS\Backend\Utility\BackendUtility;
19use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
20use TYPO3\CMS\Core\Core\Environment;
21use TYPO3\CMS\Core\Database\Query\QueryHelper;
22use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
23use TYPO3\CMS\Core\Localization\LanguageService;
24use TYPO3\CMS\Core\Type\Bitmask\Permission;
25use TYPO3\CMS\Core\Utility\GeneralUtility;
26use TYPO3\CMS\Core\Utility\MathUtility;
27use TYPO3\CMS\Core\Utility\StringUtility;
28
29/**
30 * Class for generating front end for building queries
31 */
32class QueryGenerator
33{
34    /**
35     * @var array
36     */
37    public $lang = [
38        'OR' => 'or',
39        'AND' => 'and',
40        'comparison' => [
41            // Type = text	offset = 0
42            '0_' => 'contains',
43            '1_' => 'does not contain',
44            '2_' => 'starts with',
45            '3_' => 'does not start with',
46            '4_' => 'ends with',
47            '5_' => 'does not end with',
48            '6_' => 'equals',
49            '7_' => 'does not equal',
50            // Type = number , offset = 32
51            '32_' => 'equals',
52            '33_' => 'does not equal',
53            '34_' => 'is greater than',
54            '35_' => 'is less than',
55            '36_' => 'is between',
56            '37_' => 'is not between',
57            '38_' => 'is in list',
58            '39_' => 'is not in list',
59            '40_' => 'binary AND equals',
60            '41_' => 'binary AND does not equal',
61            '42_' => 'binary OR equals',
62            '43_' => 'binary OR does not equal',
63            // Type = multiple, relation, files , offset = 64
64            '64_' => 'equals',
65            '65_' => 'does not equal',
66            '66_' => 'contains',
67            '67_' => 'does not contain',
68            '68_' => 'is in list',
69            '69_' => 'is not in list',
70            '70_' => 'binary AND equals',
71            '71_' => 'binary AND does not equal',
72            '72_' => 'binary OR equals',
73            '73_' => 'binary OR does not equal',
74            // Type = date,time  offset = 96
75            '96_' => 'equals',
76            '97_' => 'does not equal',
77            '98_' => 'is greater than',
78            '99_' => 'is less than',
79            '100_' => 'is between',
80            '101_' => 'is not between',
81            '102_' => 'binary AND equals',
82            '103_' => 'binary AND does not equal',
83            '104_' => 'binary OR equals',
84            '105_' => 'binary OR does not equal',
85            // Type = boolean,  offset = 128
86            '128_' => 'is True',
87            '129_' => 'is False',
88            // Type = binary , offset = 160
89            '160_' => 'equals',
90            '161_' => 'does not equal',
91            '162_' => 'contains',
92            '163_' => 'does not contain'
93        ]
94    ];
95
96    /**
97     * @var array
98     */
99    public $compSQL = [
100        // Type = text	offset = 0
101        '0' => '#FIELD# LIKE \'%#VALUE#%\'',
102        '1' => '#FIELD# NOT LIKE \'%#VALUE#%\'',
103        '2' => '#FIELD# LIKE \'#VALUE#%\'',
104        '3' => '#FIELD# NOT LIKE \'#VALUE#%\'',
105        '4' => '#FIELD# LIKE \'%#VALUE#\'',
106        '5' => '#FIELD# NOT LIKE \'%#VALUE#\'',
107        '6' => '#FIELD# = \'#VALUE#\'',
108        '7' => '#FIELD# != \'#VALUE#\'',
109        // Type = number, offset = 32
110        '32' => '#FIELD# = \'#VALUE#\'',
111        '33' => '#FIELD# != \'#VALUE#\'',
112        '34' => '#FIELD# > #VALUE#',
113        '35' => '#FIELD# < #VALUE#',
114        '36' => '#FIELD# >= #VALUE# AND #FIELD# <= #VALUE1#',
115        '37' => 'NOT (#FIELD# >= #VALUE# AND #FIELD# <= #VALUE1#)',
116        '38' => '#FIELD# IN (#VALUE#)',
117        '39' => '#FIELD# NOT IN (#VALUE#)',
118        '40' => '(#FIELD# & #VALUE#)=#VALUE#',
119        '41' => '(#FIELD# & #VALUE#)!=#VALUE#',
120        '42' => '(#FIELD# | #VALUE#)=#VALUE#',
121        '43' => '(#FIELD# | #VALUE#)!=#VALUE#',
122        // Type = multiple, relation, files , offset = 64
123        '64' => '#FIELD# = \'#VALUE#\'',
124        '65' => '#FIELD# != \'#VALUE#\'',
125        '66' => '#FIELD# LIKE \'%#VALUE#%\' AND #FIELD# LIKE \'%#VALUE1#%\'',
126        '67' => '(#FIELD# NOT LIKE \'%#VALUE#%\' OR #FIELD# NOT LIKE \'%#VALUE1#%\')',
127        '68' => '#FIELD# IN (#VALUE#)',
128        '69' => '#FIELD# NOT IN (#VALUE#)',
129        '70' => '(#FIELD# & #VALUE#)=#VALUE#',
130        '71' => '(#FIELD# & #VALUE#)!=#VALUE#',
131        '72' => '(#FIELD# | #VALUE#)=#VALUE#',
132        '73' => '(#FIELD# | #VALUE#)!=#VALUE#',
133        // Type = date, offset = 32
134        '96' => '#FIELD# = \'#VALUE#\'',
135        '97' => '#FIELD# != \'#VALUE#\'',
136        '98' => '#FIELD# > #VALUE#',
137        '99' => '#FIELD# < #VALUE#',
138        '100' => '#FIELD# >= #VALUE# AND #FIELD# <= #VALUE1#',
139        '101' => 'NOT (#FIELD# >= #VALUE# AND #FIELD# <= #VALUE1#)',
140        '102' => '(#FIELD# & #VALUE#)=#VALUE#',
141        '103' => '(#FIELD# & #VALUE#)!=#VALUE#',
142        '104' => '(#FIELD# | #VALUE#)=#VALUE#',
143        '105' => '(#FIELD# | #VALUE#)!=#VALUE#',
144        // Type = boolean, offset = 128
145        '128' => '#FIELD# = \'1\'',
146        '129' => '#FIELD# != \'1\'',
147        // Type = binary = 160
148        '160' => '#FIELD# = \'#VALUE#\'',
149        '161' => '#FIELD# != \'#VALUE#\'',
150        '162' => '(#FIELD# & #VALUE#)=#VALUE#',
151        '163' => '(#FIELD# & #VALUE#)=0'
152    ];
153
154    /**
155     * @var array
156     */
157    public $comp_offsets = [
158        'text' => 0,
159        'number' => 1,
160        'multiple' => 2,
161        'relation' => 2,
162        'files' => 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 int
236     */
237    protected $limitBegin;
238
239    /**
240     * @var int
241     */
242    protected $limitLength;
243
244    /**
245     * @var string
246     */
247    protected $fieldName;
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']) {
263                $fieldListArr[] = $GLOBALS['TCA'][$this->table]['ctrl']['tstamp'];
264            }
265            if ($GLOBALS['TCA'][$this->table]['ctrl']['crdate']) {
266                $fieldListArr[] = $GLOBALS['TCA'][$this->table]['ctrl']['crdate'];
267            }
268            if ($GLOBALS['TCA'][$this->table]['ctrl']['cruser_id']) {
269                $fieldListArr[] = $GLOBALS['TCA'][$this->table]['ctrl']['cruser_id'];
270            }
271            if ($GLOBALS['TCA'][$this->table]['ctrl']['sortby']) {
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     */
285    public function init($name, $table, $fieldList = '')
286    {
287        // Analysing the fields in the table.
288        if (is_array($GLOBALS['TCA'][$table])) {
289            $this->name = $name;
290            $this->table = $table;
291            $this->fieldList = $fieldList ? $fieldList : $this->makeFieldList();
292            $fieldArr = GeneralUtility::trimExplode(',', $this->fieldList, true);
293            foreach ($fieldArr as $fieldName) {
294                $fC = $GLOBALS['TCA'][$this->table]['columns'][$fieldName];
295                $this->fields[$fieldName] = $fC['config'];
296                $this->fields[$fieldName]['exclude'] = $fC['exclude'];
297                if ($this->fields[$fieldName]['type'] === 'user' && !isset($this->fields[$fieldName]['type']['userFunc'])
298                    || $this->fields[$fieldName]['type'] === 'none'
299                ) {
300                    // Do not list type=none "virtual" fields or query them from db,
301                    // and if type is user without defined userFunc
302                    unset($this->fields[$fieldName]);
303                    continue;
304                }
305                if (is_array($fC) && $fC['label']) {
306                    $this->fields[$fieldName]['label'] = rtrim(trim($this->getLanguageService()->sL($fC['label'])), ':');
307                    switch ($this->fields[$fieldName]['type']) {
308                        case 'input':
309                            if (preg_match('/int|year/i', $this->fields[$fieldName]['eval'])) {
310                                $this->fields[$fieldName]['type'] = 'number';
311                            } elseif (preg_match('/time/i', $this->fields[$fieldName]['eval'])) {
312                                $this->fields[$fieldName]['type'] = 'time';
313                            } elseif (preg_match('/date/i', $this->fields[$fieldName]['eval'])) {
314                                $this->fields[$fieldName]['type'] = 'date';
315                            } else {
316                                $this->fields[$fieldName]['type'] = 'text';
317                            }
318                            break;
319                        case 'check':
320                            if (!$this->fields[$fieldName]['items'] || count($this->fields[$fieldName]['items']) <= 1) {
321                                $this->fields[$fieldName]['type'] = 'boolean';
322                            } else {
323                                $this->fields[$fieldName]['type'] = 'binary';
324                            }
325                            break;
326                        case 'radio':
327                            $this->fields[$fieldName]['type'] = 'multiple';
328                            break;
329                        case 'select':
330                            $this->fields[$fieldName]['type'] = 'multiple';
331                            if ($this->fields[$fieldName]['foreign_table']) {
332                                $this->fields[$fieldName]['type'] = 'relation';
333                            }
334                            if ($this->fields[$fieldName]['special']) {
335                                $this->fields[$fieldName]['type'] = 'text';
336                            }
337                            break;
338                        case 'group':
339                            $this->fields[$fieldName]['type'] = 'files';
340                            if ($this->fields[$fieldName]['internal_type'] === 'db') {
341                                $this->fields[$fieldName]['type'] = 'relation';
342                            }
343                            break;
344                        case 'user':
345                        case 'flex':
346                        case 'passthrough':
347                        case 'none':
348                        case 'text':
349                        default:
350                            $this->fields[$fieldName]['type'] = 'text';
351                    }
352                } else {
353                    $this->fields[$fieldName]['label'] = '[FIELD: ' . $fieldName . ']';
354                    switch ($fieldName) {
355                        case 'pid':
356                            $this->fields[$fieldName]['type'] = 'relation';
357                            $this->fields[$fieldName]['allowed'] = 'pages';
358                            break;
359                        case 'cruser_id':
360                            $this->fields[$fieldName]['type'] = 'relation';
361                            $this->fields[$fieldName]['allowed'] = 'be_users';
362                            break;
363                        case 'tstamp':
364                        case 'crdate':
365                            $this->fields[$fieldName]['type'] = 'time';
366                            break;
367                        case 'deleted':
368                            $this->fields[$fieldName]['type'] = 'boolean';
369                            break;
370                        default:
371                            $this->fields[$fieldName]['type'] = 'number';
372                    }
373                }
374            }
375        }
376        /*	// EXAMPLE:
377        $this->queryConfig = array(
378        array(
379        'operator' => 'AND',
380        'type' => 'FIELD_spaceBefore',
381        ),
382        array(
383        'operator' => 'AND',
384        'type' => 'FIELD_records',
385        'negate' => 1,
386        'inputValue' => 'foo foo'
387        ),
388        array(
389        'type' => 'newlevel',
390        'nl' => array(
391        array(
392        'operator' => 'AND',
393        'type' => 'FIELD_spaceBefore',
394        'negate' => 1,
395        'inputValue' => 'foo foo'
396        ),
397        array(
398        'operator' => 'AND',
399        'type' => 'FIELD_records',
400        'negate' => 1,
401        'inputValue' => 'foo foo'
402        )
403        )
404        ),
405        array(
406        'operator' => 'OR',
407        'type' => 'FIELD_maillist',
408        )
409        );
410         */
411        $this->initUserDef();
412    }
413
414    /**
415     * Set and clean up external lists
416     *
417     * @param string $name The name
418     * @param string $list The list
419     * @param string $force
420     */
421    public function setAndCleanUpExternalLists($name, $list, $force = '')
422    {
423        $fields = array_unique(GeneralUtility::trimExplode(',', $list . ',' . $force, true));
424        $reList = [];
425        foreach ($fields as $fieldName) {
426            if ($this->fields[$fieldName]) {
427                $reList[] = $fieldName;
428            }
429        }
430        $this->extFieldLists[$name] = implode(',', $reList);
431    }
432
433    /**
434     * Process data
435     *
436     * @param string $qC Query config
437     */
438    public function procesData($qC = '')
439    {
440        $this->queryConfig = $qC;
441        $POST = GeneralUtility::_POST();
442        // If delete...
443        if ($POST['qG_del']) {
444            // Initialize array to work on, save special parameters
445            $ssArr = $this->getSubscript($POST['qG_del']);
446            $workArr = &$this->queryConfig;
447            $ssArrSize = count($ssArr) - 1;
448            $i = 0;
449            for (; $i < $ssArrSize; $i++) {
450                $workArr = &$workArr[$ssArr[$i]];
451            }
452            // Delete the entry and move the other entries
453            unset($workArr[$ssArr[$i]]);
454            $workArrSize = count($workArr);
455            for ($j = $ssArr[$i]; $j < $workArrSize; $j++) {
456                $workArr[$j] = $workArr[$j + 1];
457                unset($workArr[$j + 1]);
458            }
459        }
460        // If insert...
461        if ($POST['qG_ins']) {
462            // Initialize array to work on, save special parameters
463            $ssArr = $this->getSubscript($POST['qG_ins']);
464            $workArr = &$this->queryConfig;
465            $ssArrSize = count($ssArr) - 1;
466            $i = 0;
467            for (; $i < $ssArrSize; $i++) {
468                $workArr = &$workArr[$ssArr[$i]];
469            }
470            // Move all entries above position where new entry is to be inserted
471            $workArrSize = count($workArr);
472            for ($j = $workArrSize; $j > $ssArr[$i]; $j--) {
473                $workArr[$j] = $workArr[$j - 1];
474            }
475            // Clear new entry position
476            unset($workArr[$ssArr[$i] + 1]);
477            $workArr[$ssArr[$i] + 1]['type'] = 'FIELD_';
478        }
479        // If move up...
480        if ($POST['qG_up']) {
481            // Initialize array to work on
482            $ssArr = $this->getSubscript($POST['qG_up']);
483            $workArr = &$this->queryConfig;
484            $ssArrSize = count($ssArr) - 1;
485            $i = 0;
486            for (; $i < $ssArrSize; $i++) {
487                $workArr = &$workArr[$ssArr[$i]];
488            }
489            // Swap entries
490            $qG_tmp = $workArr[$ssArr[$i]];
491            $workArr[$ssArr[$i]] = $workArr[$ssArr[$i] - 1];
492            $workArr[$ssArr[$i] - 1] = $qG_tmp;
493        }
494        // If new level...
495        if ($POST['qG_nl']) {
496            // Initialize array to work on
497            $ssArr = $this->getSubscript($POST['qG_nl']);
498            $workArr = &$this->queryConfig;
499            $ssArraySize = count($ssArr) - 1;
500            $i = 0;
501            for (; $i < $ssArraySize; $i++) {
502                $workArr = &$workArr[$ssArr[$i]];
503            }
504            // Do stuff:
505            $tempEl = $workArr[$ssArr[$i]];
506            if (is_array($tempEl)) {
507                if ($tempEl['type'] !== 'newlevel') {
508                    $workArr[$ssArr[$i]] = [
509                        'type' => 'newlevel',
510                        'operator' => $tempEl['operator'],
511                        'nl' => [$tempEl]
512                    ];
513                }
514            }
515        }
516        // If collapse level...
517        if ($POST['qG_remnl']) {
518            // Initialize array to work on
519            $ssArr = $this->getSubscript($POST['qG_remnl']);
520            $workArr = &$this->queryConfig;
521            $ssArrSize = count($ssArr) - 1;
522            $i = 0;
523            for (; $i < $ssArrSize; $i++) {
524                $workArr = &$workArr[$ssArr[$i]];
525            }
526            // Do stuff:
527            $tempEl = $workArr[$ssArr[$i]];
528            if (is_array($tempEl)) {
529                if ($tempEl['type'] === 'newlevel') {
530                    $a1 = array_slice($workArr, 0, $ssArr[$i]);
531                    $a2 = array_slice($workArr, $ssArr[$i]);
532                    array_shift($a2);
533                    $a3 = $tempEl['nl'];
534                    $a3[0]['operator'] = $tempEl['operator'];
535                    $workArr = array_merge($a1, $a3, $a2);
536                }
537            }
538        }
539    }
540
541    /**
542     * Clean up query config
543     *
544     * @param array $queryConfig Query config
545     * @return array
546     */
547    public function cleanUpQueryConfig($queryConfig)
548    {
549        // Since we don't traverse the array using numeric keys in the upcoming while-loop make sure it's fresh and clean before displaying
550        if (is_array($queryConfig)) {
551            ksort($queryConfig);
552        } else {
553            // queryConfig should never be empty!
554            if (!isset($queryConfig[0]) || empty($queryConfig[0]['type'])) {
555                // Make sure queryConfig is an array
556                $queryConfig = [];
557                $queryConfig[0] = ['type' => 'FIELD_'];
558            }
559        }
560        // Traverse:
561        foreach ($queryConfig as $key => $conf) {
562            $fieldName = '';
563            if (strpos($conf['type'], 'FIELD_') === 0) {
564                $fieldName = substr($conf['type'], 6);
565                $fieldType = $this->fields[$fieldName]['type'];
566            } elseif ($conf['type'] === 'newlevel') {
567                $fieldType = $conf['type'];
568            } else {
569                $fieldType = 'ignore';
570            }
571            switch ($fieldType) {
572                case 'newlevel':
573                    if (!$queryConfig[$key]['nl']) {
574                        $queryConfig[$key]['nl'][0]['type'] = 'FIELD_';
575                    }
576                    $queryConfig[$key]['nl'] = $this->cleanUpQueryConfig($queryConfig[$key]['nl']);
577                    break;
578                case 'userdef':
579                    $queryConfig[$key] = $this->userDefCleanUp($queryConfig[$key]);
580                    break;
581                case 'ignore':
582                default:
583                    $verifiedName = $this->verifyType($fieldName);
584                    $queryConfig[$key]['type'] = 'FIELD_' . $this->verifyType($verifiedName);
585                    if ($conf['comparison'] >> 5 != $this->comp_offsets[$fieldType]) {
586                        $conf['comparison'] = $this->comp_offsets[$fieldType] << 5;
587                    }
588                    $queryConfig[$key]['comparison'] = $this->verifyComparison($conf['comparison'], $conf['negate'] ? 1 : 0);
589                    $queryConfig[$key]['inputValue'] = $this->cleanInputVal($queryConfig[$key]);
590                    $queryConfig[$key]['inputValue1'] = $this->cleanInputVal($queryConfig[$key], 1);
591            }
592        }
593        return $queryConfig;
594    }
595
596    /**
597     * Get form elements
598     *
599     * @param int $subLevel
600     * @param string $queryConfig
601     * @param string $parent
602     * @return array
603     */
604    public function getFormElements($subLevel = 0, $queryConfig = '', $parent = '')
605    {
606        $codeArr = [];
607        if (!is_array($queryConfig)) {
608            $queryConfig = $this->queryConfig;
609        }
610        $c = 0;
611        $arrCount = 0;
612        $loopCount = 0;
613        foreach ($queryConfig as $key => $conf) {
614            $fieldName = '';
615            $subscript = $parent . '[' . $key . ']';
616            $lineHTML = [];
617            $lineHTML[] = $this->mkOperatorSelect($this->name . $subscript, $conf['operator'], $c, $conf['type'] !== 'FIELD_');
618            if (strpos($conf['type'], 'FIELD_') === 0) {
619                $fieldName = substr($conf['type'], 6);
620                $this->fieldName = $fieldName;
621                $fieldType = $this->fields[$fieldName]['type'];
622                if ($conf['comparison'] >> 5 != $this->comp_offsets[$fieldType]) {
623                    $conf['comparison'] = $this->comp_offsets[$fieldType] << 5;
624                }
625                //nasty nasty...
626                //make sure queryConfig contains _actual_ comparevalue.
627                //mkCompSelect don't care, but getQuery does.
628                $queryConfig[$key]['comparison'] += isset($conf['negate']) - $conf['comparison'] % 2;
629            } elseif ($conf['type'] === 'newlevel') {
630                $fieldType = $conf['type'];
631            } else {
632                $fieldType = 'ignore';
633            }
634            $fieldPrefix = htmlspecialchars($this->name . $subscript);
635            switch ($fieldType) {
636                case 'ignore':
637                    break;
638                case 'newlevel':
639                    if (!$queryConfig[$key]['nl']) {
640                        $queryConfig[$key]['nl'][0]['type'] = 'FIELD_';
641                    }
642                    $lineHTML[] = '<input type="hidden" name="' . $fieldPrefix . '[type]" value="newlevel">';
643                    $codeArr[$arrCount]['sub'] = $this->getFormElements($subLevel + 1, $queryConfig[$key]['nl'], $subscript . '[nl]');
644                    break;
645                case 'userdef':
646                    $lineHTML[] = $this->userDef($fieldPrefix, $conf, $fieldName, $fieldType);
647                    break;
648                case 'date':
649                    $lineHTML[] = '<div class="form-inline">';
650                    $lineHTML[] = $this->makeComparisonSelector($subscript, $fieldName, $conf);
651                    if ($conf['comparison'] === 100 || $conf['comparison'] === 101) {
652                        // between
653                        $lineHTML[] = $this->getDateTimePickerField($fieldPrefix . '[inputValue]', $conf['inputValue'], 'date');
654                        $lineHTML[] = $this->getDateTimePickerField($fieldPrefix . '[inputValue1]', $conf['inputValue1'], 'date');
655                    } else {
656                        $lineHTML[] = $this->getDateTimePickerField($fieldPrefix . '[inputValue]', $conf['inputValue'], 'date');
657                    }
658                    $lineHTML[] = '</div>';
659                    break;
660                case 'time':
661                    $lineHTML[] = '<div class="form-inline">';
662                    $lineHTML[] = $this->makeComparisonSelector($subscript, $fieldName, $conf);
663                    if ($conf['comparison'] === 100 || $conf['comparison'] === 101) {
664                        // between:
665                        $lineHTML[] = $this->getDateTimePickerField($fieldPrefix . '[inputValue]', $conf['inputValue'], 'datetime');
666                        $lineHTML[] = $this->getDateTimePickerField($fieldPrefix . '[inputValue1]', $conf['inputValue1'], 'datetime');
667                    } else {
668                        $lineHTML[] = $this->getDateTimePickerField($fieldPrefix . '[inputValue]', $conf['inputValue'], 'datetime');
669                    }
670                    $lineHTML[] = '</div>';
671                    break;
672                case 'multiple':
673                case 'binary':
674                case 'relation':
675                    $lineHTML[] = '<div class="form-inline">';
676                    $lineHTML[] = $this->makeComparisonSelector($subscript, $fieldName, $conf);
677                    if ($conf['comparison'] === 68 || $conf['comparison'] === 69 || $conf['comparison'] === 162 || $conf['comparison'] === 163) {
678                        $lineHTML[] = '<select class="form-control" name="' . $fieldPrefix . '[inputValue]' . '[]" multiple="multiple">';
679                    } elseif ($conf['comparison'] === 66 || $conf['comparison'] === 67) {
680                        if (is_array($conf['inputValue'])) {
681                            $conf['inputValue'] = implode(',', $conf['inputValue']);
682                        }
683                        $lineHTML[] = '<input class="form-control t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue']) . '" name="' . $fieldPrefix . '[inputValue]">';
684                    } elseif ($conf['comparison'] === 64) {
685                        if (is_array($conf['inputValue'])) {
686                            $conf['inputValue'] = $conf['inputValue'][0];
687                        }
688                        $lineHTML[] = '<select class="form-control t3js-submit-change" name="' . $fieldPrefix . '[inputValue]">';
689                    } else {
690                        $lineHTML[] = '<select class="form-control t3js-submit-change" name="' . $fieldPrefix . '[inputValue]' . '">';
691                    }
692                    if ($conf['comparison'] != 66 && $conf['comparison'] != 67) {
693                        $lineHTML[] = $this->makeOptionList($fieldName, $conf, $this->table);
694                        $lineHTML[] = '</select>';
695                    }
696                    $lineHTML[] = '</div>';
697                    break;
698                case 'files':
699                    $lineHTML[] = '<div class="form-inline">';
700                    $lineHTML[] = $this->makeComparisonSelector($subscript, $fieldName, $conf);
701                    if ($conf['comparison'] === 68 || $conf['comparison'] === 69) {
702                        $lineHTML[] = '<select class="form-control" name="' . $fieldPrefix . '[inputValue]' . '[]" multiple="multiple">';
703                    } else {
704                        $lineHTML[] = '<select class="form-control t3js-submit-change" name="' . $fieldPrefix . '[inputValue]' . '">';
705                    }
706                    $lineHTML[] = '<option value=""></option>' . $this->makeOptionList($fieldName, $conf, $this->table);
707                    $lineHTML[] = '</select>';
708                    if ($conf['comparison'] === 66 || $conf['comparison'] === 67) {
709                        $lineHTML[] = ' + <input class="form-control t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue1']) . '" name="' . $fieldPrefix . '[inputValue1]' . '">';
710                    }
711                    $lineHTML[] = '</div>';
712                    break;
713                case 'boolean':
714                    $lineHTML[] = '<div class="form-inline">';
715                    $lineHTML[] = $this->makeComparisonSelector($subscript, $fieldName, $conf);
716                    $lineHTML[] = '<input type="hidden" value="1" name="' . $fieldPrefix . '[inputValue]' . '">';
717                    $lineHTML[] = '</div>';
718                    break;
719                default:
720                    $lineHTML[] = '<div class="form-inline">';
721                    $lineHTML[] = $this->makeComparisonSelector($subscript, $fieldName, $conf);
722                    if ($conf['comparison'] === 37 || $conf['comparison'] === 36) {
723                        // between:
724                        $lineHTML[] = '<input class="form-control t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue']) . '" name="' . $fieldPrefix . '[inputValue]' . '">';
725                        $lineHTML[] = '<input class="form-control t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue1']) . '" name="' . $fieldPrefix . '[inputValue1]' . '">';
726                    } else {
727                        $lineHTML[] = '<input class="form-control t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue']) . '" name="' . $fieldPrefix . '[inputValue]' . '">';
728                    }
729                    $lineHTML[] = '</div>';
730            }
731            if ($fieldType !== 'ignore') {
732                $lineHTML[] = '<div class="btn-group action-button-group">';
733                $lineHTML[] = $this->updateIcon();
734                if ($loopCount) {
735                    $lineHTML[] = '<button class="btn btn-default" title="Remove condition" name="qG_del' . htmlspecialchars($subscript) . '"><i class="fa fa-trash fa-fw"></i></button>';
736                }
737                $lineHTML[] = '<button class="btn btn-default" title="Add condition" name="qG_ins' . htmlspecialchars($subscript) . '"><i class="fa fa-plus fa-fw"></i></button>';
738                if ($c != 0) {
739                    $lineHTML[] = '<button class="btn btn-default" title="Move up" name="qG_up' . htmlspecialchars($subscript) . '"><i class="fa fa-chevron-up fa-fw"></i></button>';
740                }
741                if ($c != 0 && $fieldType !== 'newlevel') {
742                    $lineHTML[] = '<button class="btn btn-default" title="New level" name="qG_nl' . htmlspecialchars($subscript) . '"><i class="fa fa-chevron-right fa-fw"></i></button>';
743                }
744                if ($fieldType === 'newlevel') {
745                    $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>';
746                }
747                $lineHTML[] = '</div>';
748                $codeArr[$arrCount]['html'] = implode(LF, $lineHTML);
749                $codeArr[$arrCount]['query'] = $this->getQuerySingle($conf, $c > 0 ? 0 : 1);
750                $arrCount++;
751                $c++;
752            }
753            $loopCount = 1;
754        }
755        $this->queryConfig = $queryConfig;
756        return $codeArr;
757    }
758
759    /**
760     * @param string $subscript
761     * @param string $fieldName
762     * @param array $conf
763     *
764     * @return string
765     */
766    protected function makeComparisonSelector($subscript, $fieldName, $conf)
767    {
768        $fieldPrefix = $this->name . $subscript;
769        $lineHTML = [];
770        $lineHTML[] = $this->mkTypeSelect($fieldPrefix . '[type]', $fieldName);
771        $lineHTML[] = '	<div class="input-group">';
772        $lineHTML[] = $this->mkCompSelect($fieldPrefix . '[comparison]', $conf['comparison'], $conf['negate'] ? 1 : 0);
773        $lineHTML[] = '	<div class="input-group-addon">';
774        $lineHTML[] = '		<input type="checkbox" class="checkbox t3js-submit-click"' . ($conf['negate'] ? ' checked' : '') . ' name="' . htmlspecialchars($fieldPrefix) . '[negate]' . '">';
775        $lineHTML[] = '	</div>';
776        $lineHTML[] = '	</div>';
777        return implode(LF, $lineHTML);
778    }
779
780    /**
781     * Make option list
782     *
783     * @param string $fieldName
784     * @param array $conf
785     * @param string $table
786     * @return string
787     */
788    public function makeOptionList($fieldName, $conf, $table)
789    {
790        $out = [];
791        $fieldSetup = $this->fields[$fieldName];
792        $languageService = $this->getLanguageService();
793        if ($fieldSetup['type'] === 'files') {
794            if ($conf['comparison'] === 66 || $conf['comparison'] === 67) {
795                $fileExtArray = explode(',', $fieldSetup['allowed']);
796                natcasesort($fileExtArray);
797                foreach ($fileExtArray as $fileExt) {
798                    if (GeneralUtility::inList($conf['inputValue'], $fileExt)) {
799                        $out[] = '<option value="' . htmlspecialchars($fileExt) . '" selected>.' . htmlspecialchars($fileExt) . '</option>';
800                    } else {
801                        $out[] = '<option value="' . htmlspecialchars($fileExt) . '">.' . htmlspecialchars($fileExt) . '</option>';
802                    }
803                }
804            }
805            $d = dir(Environment::getPublicPath() . '/' . $fieldSetup['uploadfolder']);
806            while (false !== ($entry = $d->read())) {
807                if ($entry === '.' || $entry === '..') {
808                    continue;
809                }
810                $fileArray[] = $entry;
811            }
812            $d->close();
813            natcasesort($fileArray);
814            foreach ($fileArray as $fileName) {
815                if (GeneralUtility::inList($conf['inputValue'], $fileName)) {
816                    $out[] = '<option value="' . htmlspecialchars($fileName) . '" selected>' . htmlspecialchars($fileName) . '</option>';
817                } else {
818                    $out[] = '<option value="' . htmlspecialchars($fileName) . '">' . htmlspecialchars($fileName) . '</option>';
819                }
820            }
821        }
822        if ($fieldSetup['type'] === 'multiple') {
823            $optGroupOpen = false;
824            foreach ($fieldSetup['items'] as $key => $val) {
825                if (strpos($val[0], 'LLL:') === 0) {
826                    $value = $languageService->sL($val[0]);
827                } else {
828                    $value = $val[0];
829                }
830                if ($val[1] === '--div--') {
831                    if ($optGroupOpen) {
832                        $out[] = '</optgroup>';
833                    }
834                    $optGroupOpen = true;
835                    $out[] = '<optgroup label="' . htmlspecialchars($value) . '">';
836                } elseif (GeneralUtility::inList($conf['inputValue'], $val[1])) {
837                    $out[] = '<option value="' . htmlspecialchars($val[1]) . '" selected>' . htmlspecialchars($value) . '</option>';
838                } else {
839                    $out[] = '<option value="' . htmlspecialchars($val[1]) . '">' . htmlspecialchars($value) . '</option>';
840                }
841            }
842            if ($optGroupOpen) {
843                $out[] = '</optgroup>';
844            }
845        }
846        if ($fieldSetup['type'] === 'binary') {
847            foreach ($fieldSetup['items'] as $key => $val) {
848                if (strpos($val[0], 'LLL:') === 0) {
849                    $value = $languageService->sL($val[0]);
850                } else {
851                    $value = $val[0];
852                }
853                if (GeneralUtility::inList($conf['inputValue'], pow(2, $key))) {
854                    $out[] = '<option value="' . pow(2, $key) . '" selected>' . htmlspecialchars($value) . '</option>';
855                } else {
856                    $out[] = '<option value="' . pow(2, $key) . '">' . htmlspecialchars($value) . '</option>';
857                }
858            }
859        }
860        if ($fieldSetup['type'] === 'relation') {
861            $useTablePrefix = 0;
862            $dontPrefixFirstTable = 0;
863            if ($fieldSetup['items']) {
864                foreach ($fieldSetup['items'] as $key => $val) {
865                    if (strpos($val[0], 'LLL:') === 0) {
866                        $value = $languageService->sL($val[0]);
867                    } else {
868                        $value = $val[0];
869                    }
870                    if (GeneralUtility::inList($conf['inputValue'], $val[1])) {
871                        $out[] = '<option value="' . htmlspecialchars($val[1]) . '" selected>' . htmlspecialchars($value) . '</option>';
872                    } else {
873                        $out[] = '<option value="' . htmlspecialchars($val[1]) . '">' . htmlspecialchars($value) . '</option>';
874                    }
875                }
876            }
877            if (stristr($fieldSetup['allowed'], ',')) {
878                $from_table_Arr = explode(',', $fieldSetup['allowed']);
879                $useTablePrefix = 1;
880                if (!$fieldSetup['prepend_tname']) {
881                    $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
882                    $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
883                    $statement = $queryBuilder->select($fieldName)
884                        ->from($table)
885                        ->execute();
886                    while ($row = $statement->fetch()) {
887                        if (stristr($row[$fieldName], ',')) {
888                            $checkContent = explode(',', $row[$fieldName]);
889                            foreach ($checkContent as $singleValue) {
890                                if (!stristr($singleValue, '_')) {
891                                    $dontPrefixFirstTable = 1;
892                                }
893                            }
894                        } else {
895                            $singleValue = $row[$fieldName];
896                            if ($singleValue !== '' && !stristr($singleValue, '_')) {
897                                $dontPrefixFirstTable = 1;
898                            }
899                        }
900                    }
901                }
902            } else {
903                $from_table_Arr[0] = $fieldSetup['allowed'];
904            }
905            if ($fieldSetup['prepend_tname']) {
906                $useTablePrefix = 1;
907            }
908            if ($fieldSetup['foreign_table']) {
909                $from_table_Arr[0] = $fieldSetup['foreign_table'];
910            }
911            $counter = 0;
912            $tablePrefix = '';
913            $backendUserAuthentication = $this->getBackendUserAuthentication();
914            $module = $this->getModule();
915            $outArray = [];
916            $labelFieldSelect = [];
917            foreach ($from_table_Arr as $from_table) {
918                $useSelectLabels = false;
919                $useAltSelectLabels = false;
920                if ($useTablePrefix && !$dontPrefixFirstTable && $counter != 1 || $counter === 1) {
921                    $tablePrefix = $from_table . '_';
922                }
923                $counter = 1;
924                if (is_array($GLOBALS['TCA'][$from_table])) {
925                    $labelField = $GLOBALS['TCA'][$from_table]['ctrl']['label'];
926                    $altLabelField = $GLOBALS['TCA'][$from_table]['ctrl']['label_alt'];
927                    if ($GLOBALS['TCA'][$from_table]['columns'][$labelField]['config']['items']) {
928                        foreach ($GLOBALS['TCA'][$from_table]['columns'][$labelField]['config']['items'] as $labelArray) {
929                            if (strpos($labelArray[0], 'LLL:') === 0) {
930                                $labelFieldSelect[$labelArray[1]] = $languageService->sL($labelArray[0]);
931                            } else {
932                                $labelFieldSelect[$labelArray[1]] = $labelArray[0];
933                            }
934                        }
935                        $useSelectLabels = true;
936                    }
937                    $altLabelFieldSelect = [];
938                    if ($GLOBALS['TCA'][$from_table]['columns'][$altLabelField]['config']['items']) {
939                        foreach ($GLOBALS['TCA'][$from_table]['columns'][$altLabelField]['config']['items'] as $altLabelArray) {
940                            if (strpos($altLabelArray[0], 'LLL:') === 0) {
941                                $altLabelFieldSelect[$altLabelArray[1]] = $languageService->sL($altLabelArray[0]);
942                            } else {
943                                $altLabelFieldSelect[$altLabelArray[1]] = $altLabelArray[0];
944                            }
945                        }
946                        $useAltSelectLabels = true;
947                    }
948
949                    if (!$this->tableArray[$from_table]) {
950                        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($from_table);
951                        if ($module->MOD_SETTINGS['show_deleted']) {
952                            $queryBuilder->getRestrictions()->removeAll();
953                        } else {
954                            $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
955                        }
956                        $selectFields = ['uid', $labelField];
957                        if ($altLabelField) {
958                            $selectFields[] = $altLabelField;
959                        }
960                        $queryBuilder->select(...$selectFields)
961                            ->from($from_table)
962                            ->orderBy('uid');
963                        if (!$backendUserAuthentication->isAdmin() && $GLOBALS['TYPO3_CONF_VARS']['BE']['lockBeUserToDBmounts']) {
964                            $webMounts = $backendUserAuthentication->returnWebmounts();
965                            $perms_clause = $backendUserAuthentication->getPagePermsClause(Permission::PAGE_SHOW);
966                            $webMountPageTree = '';
967                            $webMountPageTreePrefix = '';
968                            foreach ($webMounts as $webMount) {
969                                if ($webMountPageTree) {
970                                    $webMountPageTreePrefix = ',';
971                                }
972                                $webMountPageTree .= $webMountPageTreePrefix
973                                    . $this->getTreeList($webMount, 999, 0, $perms_clause);
974                            }
975                            if ($from_table === 'pages') {
976                                $queryBuilder->where(
977                                    QueryHelper::stripLogicalOperatorPrefix($perms_clause),
978                                    $queryBuilder->expr()->in(
979                                        'uid',
980                                        $queryBuilder->createNamedParameter(
981                                            GeneralUtility::intExplode(',', $webMountPageTree),
982                                            Connection::PARAM_INT_ARRAY
983                                        )
984                                    )
985                                );
986                            } else {
987                                $queryBuilder->where(
988                                    $queryBuilder->expr()->in(
989                                        'pid',
990                                        $queryBuilder->createNamedParameter(
991                                            GeneralUtility::intExplode(',', $webMountPageTree),
992                                            Connection::PARAM_INT_ARRAY
993                                        )
994                                    )
995                                );
996                            }
997                        }
998                        $statement = $queryBuilder->execute();
999                        $this->tableArray[$from_table] = [];
1000                        while ($row = $statement->fetch()) {
1001                            $this->tableArray[$from_table][] = $row;
1002                        }
1003                    }
1004
1005                    foreach ($this->tableArray[$from_table] as $key => $val) {
1006                        if ($useSelectLabels) {
1007                            $outArray[$tablePrefix . $val['uid']] = htmlspecialchars($labelFieldSelect[$val[$labelField]]);
1008                        } elseif ($val[$labelField]) {
1009                            $outArray[$tablePrefix . $val['uid']] = htmlspecialchars($val[$labelField]);
1010                        } elseif ($useAltSelectLabels) {
1011                            $outArray[$tablePrefix . $val['uid']] = htmlspecialchars($altLabelFieldSelect[$val[$altLabelField]]);
1012                        } else {
1013                            $outArray[$tablePrefix . $val['uid']] = htmlspecialchars($val[$altLabelField]);
1014                        }
1015                    }
1016                    if ($module->MOD_SETTINGS['options_sortlabel'] && is_array($outArray)) {
1017                        natcasesort($outArray);
1018                    }
1019                }
1020            }
1021            foreach ($outArray as $key2 => $val2) {
1022                if (GeneralUtility::inList($conf['inputValue'], $key2)) {
1023                    $out[] = '<option value="' . htmlspecialchars($key2) . '" selected>[' . htmlspecialchars($key2) . '] ' . htmlspecialchars($val2) . '</option>';
1024                } else {
1025                    $out[] = '<option value="' . htmlspecialchars($key2) . '">[' . htmlspecialchars($key2) . '] ' . htmlspecialchars($val2) . '</option>';
1026                }
1027            }
1028        }
1029        return implode(LF, $out);
1030    }
1031
1032    /**
1033     * Print code array
1034     *
1035     * @param array $codeArr
1036     * @param int $recursionLevel
1037     * @return string
1038     */
1039    public function printCodeArray($codeArr, $recursionLevel = 0)
1040    {
1041        $indent = 'row-group';
1042        if ($recursionLevel) {
1043            $indent = 'row-group indent indent-' . (int)$recursionLevel;
1044        }
1045        $out = [];
1046        foreach ($codeArr as $k => $v) {
1047            $out[] = '<div class="' . $indent . '">';
1048            $out[] = $v['html'];
1049
1050            if ($this->enableQueryParts) {
1051                $out[] = '<pre>';
1052                $out[] = htmlspecialchars($v['query']);
1053                $out[] = '</pre>';
1054            }
1055            if (is_array($v['sub'])) {
1056                $out[] = '<div class="' . $indent . '">';
1057                $out[] = $this->printCodeArray($v['sub'], $recursionLevel + 1);
1058                $out[] = '</div>';
1059            }
1060
1061            $out[] = '</div>';
1062        }
1063        return implode(LF, $out);
1064    }
1065
1066    /**
1067     * Make operator select
1068     *
1069     * @param string $name
1070     * @param string $op
1071     * @param bool $draw
1072     * @param bool $submit
1073     * @return string
1074     */
1075    public function mkOperatorSelect($name, $op, $draw, $submit)
1076    {
1077        $out = [];
1078        if ($draw) {
1079            $out[] = '<select class="form-control from-control-operator' . ($submit ? ' t3js-submit-change' : '') . '" name="' . htmlspecialchars($name) . '[operator]">';
1080            $out[] = '	<option value="AND"' . (!$op || $op === 'AND' ? ' selected' : '') . '>' . htmlspecialchars($this->lang['AND']) . '</option>';
1081            $out[] = '	<option value="OR"' . ($op === 'OR' ? ' selected' : '') . '>' . htmlspecialchars($this->lang['OR']) . '</option>';
1082            $out[] = '</select>';
1083        } else {
1084            $out[] = '<input type="hidden" value="' . htmlspecialchars($op) . '" name="' . htmlspecialchars($name) . '[operator]">';
1085        }
1086        return implode(LF, $out);
1087    }
1088
1089    /**
1090     * Make type select
1091     *
1092     * @param string $name
1093     * @param string $fieldName
1094     * @param string $prepend
1095     * @return string
1096     */
1097    public function mkTypeSelect($name, $fieldName, $prepend = 'FIELD_')
1098    {
1099        $out = [];
1100        $out[] = '<select class="form-control t3js-submit-change" name="' . htmlspecialchars($name) . '">';
1101        $out[] = '<option value=""></option>';
1102        foreach ($this->fields as $key => $value) {
1103            if (!$value['exclude'] || $this->getBackendUserAuthentication()->check('non_exclude_fields', $this->table . ':' . $key)) {
1104                $label = $this->fields[$key]['label'];
1105                $out[] = '<option value="' . htmlspecialchars($prepend . $key) . '"' . ($key === $fieldName ? ' selected' : '') . '>' . htmlspecialchars($label) . '</option>';
1106            }
1107        }
1108        $out[] = '</select>';
1109        return implode(LF, $out);
1110    }
1111
1112    /**
1113     * Verify type
1114     *
1115     * @param string $fieldName
1116     * @return string
1117     */
1118    public function verifyType($fieldName)
1119    {
1120        $first = '';
1121        foreach ($this->fields as $key => $value) {
1122            if (!$first) {
1123                $first = $key;
1124            }
1125            if ($key === $fieldName) {
1126                return $key;
1127            }
1128        }
1129        return $first;
1130    }
1131
1132    /**
1133     * Verify comparison
1134     *
1135     * @param string $comparison
1136     * @param int $neg
1137     * @return int
1138     */
1139    public function verifyComparison($comparison, $neg)
1140    {
1141        $compOffSet = $comparison >> 5;
1142        $first = -1;
1143        for ($i = 32 * $compOffSet + $neg; $i < 32 * ($compOffSet + 1); $i += 2) {
1144            if ($first === -1) {
1145                $first = $i;
1146            }
1147            if ($i >> 1 === $comparison >> 1) {
1148                return $i;
1149            }
1150        }
1151        return $first;
1152    }
1153
1154    /**
1155     * Make field to input select
1156     *
1157     * @param string $name
1158     * @param string $fieldName
1159     * @return string
1160     */
1161    public function mkFieldToInputSelect($name, $fieldName)
1162    {
1163        $out = [];
1164        $out[] = '<div class="input-group">';
1165        $out[] = '	<div class="input-group-addon">';
1166        $out[] = '		<span class="input-group-btn">';
1167        $out[] = $this->updateIcon();
1168        $out[] = ' 		</span>';
1169        $out[] = ' 	</div>';
1170        $out[] = '	<input type="text" class="form-control t3js-clearable" value="' . htmlspecialchars($fieldName) . '" name="' . htmlspecialchars($name) . '">';
1171        $out[] = '</div>';
1172
1173        $out[] = '<select class="form-control t3js-addfield" name="_fieldListDummy" size="5" data-field="' . htmlspecialchars($name) . '">';
1174        foreach ($this->fields as $key => $value) {
1175            if (!$value['exclude'] || $this->getBackendUserAuthentication()->check('non_exclude_fields', $this->table . ':' . $key)) {
1176                $label = $this->fields[$key]['label'];
1177                $out[] = '<option value="' . htmlspecialchars($key) . '"' . ($key === $fieldName ? ' selected' : '') . '>' . htmlspecialchars($label) . '</option>';
1178            }
1179        }
1180        $out[] = '</select>';
1181        return implode(LF, $out);
1182    }
1183
1184    /**
1185     * Make table select
1186     *
1187     * @param string $name
1188     * @param string $cur
1189     * @return string
1190     */
1191    public function mkTableSelect($name, $cur)
1192    {
1193        $out = [];
1194        $out[] = '<select class="form-control t3js-submit-change" name="' . $name . '">';
1195        $out[] = '<option value=""></option>';
1196        foreach ($GLOBALS['TCA'] as $tN => $value) {
1197            if ($this->getBackendUserAuthentication()->check('tables_select', $tN)) {
1198                $out[] = '<option value="' . htmlspecialchars($tN) . '"' . ($tN === $cur ? ' selected' : '') . '>' . htmlspecialchars($this->getLanguageService()->sL($GLOBALS['TCA'][$tN]['ctrl']['title'])) . '</option>';
1199            }
1200        }
1201        $out[] = '</select>';
1202        return implode(LF, $out);
1203    }
1204
1205    /**
1206     * Make comparison select
1207     *
1208     * @param string $name
1209     * @param string $comparison
1210     * @param int $neg
1211     * @return string
1212     */
1213    public function mkCompSelect($name, $comparison, $neg)
1214    {
1215        $compOffSet = $comparison >> 5;
1216        $out = [];
1217        $out[] = '<select class="form-control t3js-submit-change" name="' . $name . '">';
1218        for ($i = 32 * $compOffSet + $neg; $i < 32 * ($compOffSet + 1); $i += 2) {
1219            if ($this->lang['comparison'][$i . '_']) {
1220                $out[] = '<option value="' . $i . '"' . ($i >> 1 === $comparison >> 1 ? ' selected' : '') . '>' . htmlspecialchars($this->lang['comparison'][$i . '_']) . '</option>';
1221            }
1222        }
1223        $out[] = '</select>';
1224        return implode(LF, $out);
1225    }
1226
1227    /**
1228     * Get subscript
1229     *
1230     * @param array $arr
1231     * @return array
1232     */
1233    public function getSubscript($arr): array
1234    {
1235        $retArr = [];
1236        while (\is_array($arr)) {
1237            reset($arr);
1238            $key = key($arr);
1239            $retArr[] = $key;
1240            if (isset($arr[$key])) {
1241                $arr = $arr[$key];
1242            } else {
1243                break;
1244            }
1245        }
1246        return $retArr;
1247    }
1248
1249    /**
1250     * Init user definition
1251     */
1252    public function initUserDef()
1253    {
1254    }
1255
1256    /**
1257     * User definition
1258     *
1259     * @param string $fieldPrefix
1260     * @param array $conf
1261     * @param string $fieldName
1262     * @param string $fieldType
1263     *
1264     * @return string
1265     */
1266    public function userDef($fieldPrefix, $conf, $fieldName, $fieldType)
1267    {
1268        return '';
1269    }
1270
1271    /**
1272     * User definition clean up
1273     *
1274     * @param array $queryConfig
1275     * @return array
1276     */
1277    public function userDefCleanUp($queryConfig)
1278    {
1279        return $queryConfig;
1280    }
1281
1282    /**
1283     * Get query
1284     *
1285     * @param array $queryConfig
1286     * @param string $pad
1287     * @return string
1288     */
1289    public function getQuery($queryConfig, $pad = '')
1290    {
1291        $qs = '';
1292        // Since we don't traverse the array using numeric keys in the upcoming whileloop make sure it's fresh and clean
1293        ksort($queryConfig);
1294        $first = 1;
1295        foreach ($queryConfig as $key => $conf) {
1296            $conf = $this->convertIso8601DatetimeStringToUnixTimestamp($conf);
1297            switch ($conf['type']) {
1298                case 'newlevel':
1299                    $qs .= LF . $pad . trim($conf['operator']) . ' (' . $this->getQuery(
1300                        $queryConfig[$key]['nl'],
1301                        $pad . '   '
1302                    ) . LF . $pad . ')';
1303                    break;
1304                case 'userdef':
1305                    $qs .= LF . $pad . $this->getUserDefQuery($conf, $first);
1306                    break;
1307                default:
1308                    $qs .= LF . $pad . $this->getQuerySingle($conf, $first);
1309            }
1310            $first = 0;
1311        }
1312        return $qs;
1313    }
1314
1315    /**
1316     * Convert ISO-8601 timestamp (string) into unix timestamp (int)
1317     *
1318     * @param array $conf
1319     * @return array
1320     */
1321    protected function convertIso8601DatetimeStringToUnixTimestamp(array $conf): array
1322    {
1323        if ($this->isDateOfIso8601Format($conf['inputValue'])) {
1324            $conf['inputValue'] = strtotime($conf['inputValue']);
1325            if ($this->isDateOfIso8601Format($conf['inputValue1'])) {
1326                $conf['inputValue1'] = strtotime($conf['inputValue1']);
1327            }
1328        }
1329
1330        return $conf;
1331    }
1332
1333    /**
1334     * Checks if the given value is of the ISO 8601 format.
1335     *
1336     * @param mixed $date
1337     * @return bool
1338     */
1339    protected function isDateOfIso8601Format($date): bool
1340    {
1341        if (!is_int($date) && !is_string($date)) {
1342            return false;
1343        }
1344        $format = 'Y-m-d\\TH:i:s\\Z';
1345        $formattedDate = \DateTime::createFromFormat($format, $date);
1346        return $formattedDate && $formattedDate->format($format) === $date;
1347    }
1348
1349    /**
1350     * Get single query
1351     *
1352     * @param array $conf
1353     * @param bool $first
1354     * @return string
1355     */
1356    public function getQuerySingle($conf, $first)
1357    {
1358        $qs = '';
1359        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($this->table);
1360        $prefix = $this->enablePrefix ? $this->table . '.' : '';
1361        if (!$first) {
1362            // Is it OK to insert the AND operator if none is set?
1363            $operator = strtoupper(trim($conf['operator']));
1364            if (!in_array($operator, ['AND', 'OR'], true)) {
1365                $operator = 'AND';
1366            }
1367            $qs .= $operator . ' ';
1368        }
1369        $qsTmp = str_replace('#FIELD#', $prefix . trim(substr($conf['type'], 6)), $this->compSQL[$conf['comparison']]);
1370        $inputVal = $this->cleanInputVal($conf);
1371        if ($conf['comparison'] === 68 || $conf['comparison'] === 69) {
1372            $inputVal = explode(',', $inputVal);
1373            foreach ($inputVal as $key => $fileName) {
1374                $inputVal[$key] = $queryBuilder->quote($fileName);
1375            }
1376            $inputVal = implode(',', $inputVal);
1377            $qsTmp = str_replace('#VALUE#', $inputVal, $qsTmp);
1378        } elseif ($conf['comparison'] === 162 || $conf['comparison'] === 163) {
1379            $inputValArray = explode(',', $inputVal);
1380            $inputVal = 0;
1381            foreach ($inputValArray as $fileName) {
1382                $inputVal += (int)$fileName;
1383            }
1384            $qsTmp = str_replace('#VALUE#', $inputVal, $qsTmp);
1385        } else {
1386            if (is_array($inputVal)) {
1387                $inputVal = $inputVal[0];
1388            }
1389            $qsTmp = str_replace('#VALUE#', trim($queryBuilder->quote($inputVal), '\''), $qsTmp);
1390        }
1391        if ($conf['comparison'] === 37 || $conf['comparison'] === 36 || $conf['comparison'] === 66 || $conf['comparison'] === 67 || $conf['comparison'] === 100 || $conf['comparison'] === 101) {
1392            // between:
1393            $inputVal = $this->cleanInputVal($conf, '1');
1394            $qsTmp = str_replace('#VALUE1#', trim($queryBuilder->quote($inputVal), '\''), $qsTmp);
1395        }
1396        $qs .= trim($qsTmp);
1397        return $qs;
1398    }
1399
1400    /**
1401     * Clear input value
1402     *
1403     * @param array $conf
1404     * @param string $suffix
1405     * @return string
1406     */
1407    public function cleanInputVal($conf, $suffix = '')
1408    {
1409        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)) {
1410            $inputVal = $conf['inputValue' . $suffix];
1411        } elseif ($conf['comparison'] === 39 || $conf['comparison'] === 38) {
1412            // in list:
1413            $inputVal = implode(',', GeneralUtility::intExplode(',', $conf['inputValue' . $suffix]));
1414        } elseif ($conf['comparison'] === 68 || $conf['comparison'] === 69 || $conf['comparison'] === 162 || $conf['comparison'] === 163) {
1415            // in list:
1416            if (is_array($conf['inputValue' . $suffix])) {
1417                $inputVal = implode(',', $conf['inputValue' . $suffix]);
1418            } elseif ($conf['inputValue' . $suffix]) {
1419                $inputVal = $conf['inputValue' . $suffix];
1420            } else {
1421                $inputVal = 0;
1422            }
1423        } elseif (!is_array($conf['inputValue' . $suffix]) && strtotime($conf['inputValue' . $suffix])) {
1424            $inputVal = $conf['inputValue' . $suffix];
1425        } elseif (!is_array($conf['inputValue' . $suffix]) && MathUtility::canBeInterpretedAsInteger($conf['inputValue' . $suffix])) {
1426            $inputVal = (int)$conf['inputValue' . $suffix];
1427        } else {
1428            // TODO: Six eyes looked at this code and nobody understood completely what is going on here and why we
1429            // fallback to float casting, the whole class smells like it needs a refactoring.
1430            $inputVal = (float)$conf['inputValue' . $suffix];
1431        }
1432        return $inputVal;
1433    }
1434
1435    /**
1436     * Get user definition query
1437     *
1438     * @param array $qcArr
1439     * @param bool $first
1440     */
1441    public function getUserDefQuery($qcArr, $first)
1442    {
1443    }
1444
1445    /**
1446     * Update icon
1447     *
1448     * @return string
1449     */
1450    public function updateIcon()
1451    {
1452        return '<button class="btn btn-default" title="Update" name="just_update"><i class="fa fa-refresh fa-fw"></i></button>';
1453    }
1454
1455    /**
1456     * Get label column
1457     *
1458     * @return string
1459     */
1460    public function getLabelCol()
1461    {
1462        return $GLOBALS['TCA'][$this->table]['ctrl']['label'];
1463    }
1464
1465    /**
1466     * Make selector table
1467     *
1468     * @param array $modSettings
1469     * @param string $enableList
1470     * @return string
1471     */
1472    public function makeSelectorTable($modSettings, $enableList = 'table,fields,query,group,order,limit')
1473    {
1474        $out = [];
1475        $enableArr = explode(',', $enableList);
1476        $userTsConfig = $this->getBackendUserAuthentication()->getTSConfig();
1477
1478        // Make output
1479        if (in_array('table', $enableArr) && !$userTsConfig['mod.']['dbint.']['disableSelectATable']) {
1480            $out[] = '<div class="form-group">';
1481            $out[] = '	<label for="SET[queryTable]">Select a table:</label>';
1482            $out[] =    $this->mkTableSelect('SET[queryTable]', $this->table);
1483            $out[] = '</div>';
1484        }
1485        if ($this->table) {
1486            // Init fields:
1487            $this->setAndCleanUpExternalLists('queryFields', $modSettings['queryFields'], 'uid,' . $this->getLabelCol());
1488            $this->setAndCleanUpExternalLists('queryGroup', $modSettings['queryGroup']);
1489            $this->setAndCleanUpExternalLists('queryOrder', $modSettings['queryOrder'] . ',' . $modSettings['queryOrder2']);
1490            // Limit:
1491            $this->extFieldLists['queryLimit'] = $modSettings['queryLimit'];
1492            if (!$this->extFieldLists['queryLimit']) {
1493                $this->extFieldLists['queryLimit'] = 100;
1494            }
1495            $parts = GeneralUtility::intExplode(',', $this->extFieldLists['queryLimit']);
1496            if ($parts[1]) {
1497                $this->limitBegin = $parts[0];
1498                $this->limitLength = $parts[1];
1499            } else {
1500                $this->limitLength = $this->extFieldLists['queryLimit'];
1501            }
1502            $this->extFieldLists['queryLimit'] = implode(',', array_slice($parts, 0, 2));
1503            // Insert Descending parts
1504            if ($this->extFieldLists['queryOrder']) {
1505                $descParts = explode(',', $modSettings['queryOrderDesc'] . ',' . $modSettings['queryOrder2Desc']);
1506                $orderParts = explode(',', $this->extFieldLists['queryOrder']);
1507                $reList = [];
1508                foreach ($orderParts as $kk => $vv) {
1509                    $reList[] = $vv . ($descParts[$kk] ? ' DESC' : '');
1510                }
1511                $this->extFieldLists['queryOrder_SQL'] = implode(',', $reList);
1512            }
1513            // Query Generator:
1514            $this->procesData($modSettings['queryConfig'] ? unserialize($modSettings['queryConfig'], ['allowed_classes' => false]) : '');
1515            $this->queryConfig = $this->cleanUpQueryConfig($this->queryConfig);
1516            $this->enableQueryParts = (bool)$modSettings['search_query_smallparts'];
1517            $codeArr = $this->getFormElements();
1518            $queryCode = $this->printCodeArray($codeArr);
1519            if (in_array('fields', $enableArr) && !$userTsConfig['mod.']['dbint.']['disableSelectFields']) {
1520                $out[] = '<div class="form-group form-group-with-button-addon">';
1521                $out[] = '	<label for="SET[queryFields]">Select fields:</label>';
1522                $out[] =    $this->mkFieldToInputSelect('SET[queryFields]', $this->extFieldLists['queryFields']);
1523                $out[] = '</div>';
1524            }
1525            if (in_array('query', $enableArr) && !$userTsConfig['mod.']['dbint.']['disableMakeQuery']) {
1526                $out[] = '<div class="form-group">';
1527                $out[] = '	<label>Make Query:</label>';
1528                $out[] =    $queryCode;
1529                $out[] = '</div>';
1530            }
1531            if (in_array('group', $enableArr) && !$userTsConfig['mod.']['dbint.']['disableGroupBy']) {
1532                $out[] = '<div class="form-group form-inline">';
1533                $out[] = '	<label for="SET[queryGroup]">Group By:</label>';
1534                $out[] =     $this->mkTypeSelect('SET[queryGroup]', $this->extFieldLists['queryGroup'], '');
1535                $out[] = '</div>';
1536            }
1537            if (in_array('order', $enableArr) && !$userTsConfig['mod.']['dbint.']['disableOrderBy']) {
1538                $module = $this->getModule();
1539                $orderByArr = explode(',', $this->extFieldLists['queryOrder']);
1540                $orderBy = [];
1541                $orderBy[] = $this->mkTypeSelect('SET[queryOrder]', $orderByArr[0], '');
1542                $orderBy[] = '<div class="checkbox">';
1543                $orderBy[] = '	<label for="checkQueryOrderDesc">';
1544                $orderBy[] =        BackendUtility::getFuncCheck($module->id, 'SET[queryOrderDesc]', $modSettings['queryOrderDesc'], '', '', 'id="checkQueryOrderDesc"') . ' Descending';
1545                $orderBy[] = '	</label>';
1546                $orderBy[] = '</div>';
1547
1548                if ($orderByArr[0]) {
1549                    $orderBy[] = $this->mkTypeSelect('SET[queryOrder2]', $orderByArr[1], '');
1550                    $orderBy[] = '<div class="checkbox">';
1551                    $orderBy[] = '	<label for="checkQueryOrder2Desc">';
1552                    $orderBy[] =        BackendUtility::getFuncCheck($module->id, 'SET[queryOrder2Desc]', $modSettings['queryOrder2Desc'], '', '', 'id="checkQueryOrder2Desc"') . ' Descending';
1553                    $orderBy[] = '	</label>';
1554                    $orderBy[] = '</div>';
1555                }
1556                $out[] = '<div class="form-group form-inline">';
1557                $out[] = '	<label>Order By:</label>';
1558                $out[] =     implode(LF, $orderBy);
1559                $out[] = '</div>';
1560            }
1561            if (in_array('limit', $enableArr) && !$userTsConfig['mod.']['dbint.']['disableLimit']) {
1562                $limit = [];
1563                $limit[] = '<div class="input-group">';
1564                $limit[] = '	<div class="input-group-addon">';
1565                $limit[] = '		<span class="input-group-btn">';
1566                $limit[] = $this->updateIcon();
1567                $limit[] = '		</span>';
1568                $limit[] = '	</div>';
1569                $limit[] = '	<input type="text" class="form-control" value="' . htmlspecialchars($this->extFieldLists['queryLimit']) . '" name="SET[queryLimit]" id="queryLimit">';
1570                $limit[] = '</div>';
1571
1572                $prevLimit = $this->limitBegin - $this->limitLength < 0 ? 0 : $this->limitBegin - $this->limitLength;
1573                $prevButton = '';
1574                $nextButton = '';
1575
1576                if ($this->limitBegin) {
1577                    $prevButton = '<input type="button" class="btn btn-default" value="previous ' . htmlspecialchars($this->limitLength) . '" data-value="' . htmlspecialchars($prevLimit . ',' . $this->limitLength) . '">';
1578                }
1579                if (!$this->limitLength) {
1580                    $this->limitLength = 100;
1581                }
1582
1583                $nextLimit = $this->limitBegin + $this->limitLength;
1584                if ($nextLimit < 0) {
1585                    $nextLimit = 0;
1586                }
1587                if ($nextLimit) {
1588                    $nextButton = '<input type="button" class="btn btn-default" value="next ' . htmlspecialchars($this->limitLength) . '" data-value="' . htmlspecialchars($nextLimit . ',' . $this->limitLength) . '">';
1589                }
1590
1591                $out[] = '<div class="form-group form-group-with-button-addon">';
1592                $out[] = '	<label>Limit:</label>';
1593                $out[] = '	<div class="form-inline">';
1594                $out[] =        implode(LF, $limit);
1595                $out[] = '		<div class="input-group">';
1596                $out[] = '			<div class="btn-group t3js-limit-submit">';
1597                $out[] =                $prevButton;
1598                $out[] =                $nextButton;
1599                $out[] = '			</div>';
1600                $out[] = '			<div class="btn-group t3js-limit-submit">';
1601                $out[] = '				<input type="button" class="btn btn-default" data-value="10" value="10">';
1602                $out[] = '				<input type="button" class="btn btn-default" data-value="20" value="20">';
1603                $out[] = '				<input type="button" class="btn btn-default" data-value="50" value="50">';
1604                $out[] = '				<input type="button" class="btn btn-default" data-value="100" value="100">';
1605                $out[] = '			</div>';
1606                $out[] = '		</div>';
1607                $out[] = '	</div>';
1608                $out[] = '</div>';
1609            }
1610        }
1611        return implode(LF, $out);
1612    }
1613
1614    /**
1615     * Recursively fetch all descendants of a given page
1616     *
1617     * @param int $id uid of the page
1618     * @param int $depth
1619     * @param int $begin
1620     * @param string $permClause
1621     * @return string comma separated list of descendant pages
1622     */
1623    public function getTreeList($id, $depth, $begin = 0, $permClause = '')
1624    {
1625        $depth = (int)$depth;
1626        $begin = (int)$begin;
1627        $id = (int)$id;
1628        if ($id < 0) {
1629            $id = abs($id);
1630        }
1631        if ($begin === 0) {
1632            $theList = $id;
1633        } else {
1634            $theList = '';
1635        }
1636        if ($id && $depth > 0) {
1637            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
1638            $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1639            $queryBuilder->select('uid')
1640                ->from('pages')
1641                ->where(
1642                    $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($id, \PDO::PARAM_INT)),
1643                    $queryBuilder->expr()->eq('sys_language_uid', 0)
1644                )
1645                ->orderBy('uid');
1646            if ($permClause !== '') {
1647                $queryBuilder->andWhere(QueryHelper::stripLogicalOperatorPrefix($permClause));
1648            }
1649            $statement = $queryBuilder->execute();
1650            while ($row = $statement->fetch()) {
1651                if ($begin <= 0) {
1652                    $theList .= ',' . $row['uid'];
1653                }
1654                if ($depth > 1) {
1655                    $theSubList = $this->getTreeList($row['uid'], $depth - 1, $begin - 1, $permClause);
1656                    if (!empty($theList) && !empty($theSubList) && ($theSubList[0] !== ',')) {
1657                        $theList .= ',';
1658                    }
1659                    $theList .= $theSubList;
1660                }
1661            }
1662        }
1663        return $theList;
1664    }
1665
1666    /**
1667     * Get select query
1668     *
1669     * @param string $qString
1670     * @return string
1671     */
1672    public function getSelectQuery($qString = ''): string
1673    {
1674        $backendUserAuthentication = $this->getBackendUserAuthentication();
1675        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->table);
1676        if ($this->getModule()->MOD_SETTINGS['show_deleted']) {
1677            $queryBuilder->getRestrictions()->removeAll();
1678        } else {
1679            $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1680        }
1681        $fieldList = GeneralUtility::trimExplode(
1682            ',',
1683            $this->extFieldLists['queryFields']
1684            . ',pid'
1685            . ($GLOBALS['TCA'][$this->table]['ctrl']['delete'] ? ',' . $GLOBALS['TCA'][$this->table]['ctrl']['delete'] : '')
1686        );
1687        $queryBuilder->select(...$fieldList)
1688            ->from($this->table);
1689
1690        if ($this->extFieldLists['queryGroup']) {
1691            $queryBuilder->groupBy(...QueryHelper::parseGroupBy($this->extFieldLists['queryGroup']));
1692        }
1693        if ($this->extFieldLists['queryOrder']) {
1694            foreach (QueryHelper::parseOrderBy($this->extFieldLists['queryOrder_SQL']) as $orderPair) {
1695                list($fieldName, $order) = $orderPair;
1696                $queryBuilder->addOrderBy($fieldName, $order);
1697            }
1698        }
1699        if ($this->extFieldLists['queryLimit']) {
1700            $queryBuilder->setMaxResults((int)$this->extFieldLists['queryLimit']);
1701        }
1702
1703        if (!$backendUserAuthentication->isAdmin() && $GLOBALS['TYPO3_CONF_VARS']['BE']['lockBeUserToDBmounts']) {
1704            $webMounts = $backendUserAuthentication->returnWebmounts();
1705            $perms_clause = $backendUserAuthentication->getPagePermsClause(Permission::PAGE_SHOW);
1706            $webMountPageTree = '';
1707            $webMountPageTreePrefix = '';
1708            foreach ($webMounts as $webMount) {
1709                if ($webMountPageTree) {
1710                    $webMountPageTreePrefix = ',';
1711                }
1712                $webMountPageTree .= $webMountPageTreePrefix
1713                    . $this->getTreeList($webMount, 999, $begin = 0, $perms_clause);
1714            }
1715            // createNamedParameter() is not used here because the SQL fragment will only include
1716            // the :dcValueX placeholder when the query is returned as a string. The value for the
1717            // placeholder would be lost in the process.
1718            if ($this->table === 'pages') {
1719                $queryBuilder->where(
1720                    QueryHelper::stripLogicalOperatorPrefix($perms_clause),
1721                    $queryBuilder->expr()->in(
1722                        'uid',
1723                        GeneralUtility::intExplode(',', $webMountPageTree)
1724                    )
1725                );
1726            } else {
1727                $queryBuilder->where(
1728                    $queryBuilder->expr()->in(
1729                        'pid',
1730                        GeneralUtility::intExplode(',', $webMountPageTree)
1731                    )
1732                );
1733            }
1734        }
1735        if (!$qString) {
1736            $qString = $this->getQuery($this->queryConfig);
1737        }
1738        $queryBuilder->andWhere(QueryHelper::stripLogicalOperatorPrefix($qString));
1739
1740        return $queryBuilder->getSQL();
1741    }
1742
1743    /**
1744     * @param string $name the field name
1745     * @param string $timestamp ISO-8601 timestamp
1746     * @param string $type [datetime, date, time, timesec, year]
1747     *
1748     * @return string
1749     */
1750    protected function getDateTimePickerField($name, $timestamp, $type)
1751    {
1752        $value = strtotime($timestamp) ? date($GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'] . ' ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'], strtotime($timestamp)) : '';
1753        $id = StringUtility::getUniqueId('dt_');
1754        $html = [];
1755        $html[] = '<div class="input-group" id="' . $id . '-wrapper">';
1756        $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 . '">';
1757        $html[] = '		<input name="' . htmlspecialchars($name) . '" value="' . htmlspecialchars($timestamp) . '" type="hidden">';
1758        $html[] = '		<span class="input-group-btn">';
1759        $html[] = '			<label class="btn btn-default" for="' . $id . '">';
1760        $html[] = '				<span class="fa fa-calendar"></span>';
1761        $html[] = '			</label>';
1762        $html[] = ' 	</span>';
1763        $html[] = '</div>';
1764        return implode(LF, $html);
1765    }
1766
1767    /**
1768     * Sets the current name of the input form.
1769     *
1770     * @param string $formName The name of the form.
1771     */
1772    public function setFormName($formName)
1773    {
1774        $this->formName = trim($formName);
1775    }
1776
1777    /**
1778     * @return BackendUserAuthentication
1779     */
1780    protected function getBackendUserAuthentication()
1781    {
1782        return $GLOBALS['BE_USER'];
1783    }
1784
1785    /**
1786     * @return BaseScriptClass
1787     */
1788    protected function getModule()
1789    {
1790        return $GLOBALS['SOBE'];
1791    }
1792
1793    /**
1794     * @return LanguageService
1795     */
1796    protected function getLanguageService()
1797    {
1798        return $GLOBALS['LANG'];
1799    }
1800}
1801