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