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