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