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