TYPO3CMS  8
 All Classes Namespaces Files Functions Variables Pages
QueryGenerator.php
Go to the documentation of this file.
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 
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 
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 
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 
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 = sizeof($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 = sizeof($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 = sizeof($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 = sizeof($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 = sizeof($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 = sizeof($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 = sizeof($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 
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 
589  public function getFormElements($subLevel = 0, $queryConfig = '', $parent = '')
590  {
591  $codeArr = [];
592  if (!is_array($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 
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 
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(
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
1220  public function initUserDef()
1221  {
1222  }
1223 
1234  public function userDef($fieldPrefix, $conf, $fieldName, $fieldType)
1235  {
1236  return '';
1237  }
1238 
1245  public function userDefCleanUp($queryConfig)
1246  {
1247  return $queryConfig;
1248  }
1249 
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 
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 
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 
1362  public function getUserDefQuery($qcArr, $first)
1363  {
1364  }
1365 
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 
1381  public function getLabelCol()
1382  {
1383  return $GLOBALS['TCA'][$this->table]['ctrl']['label'];
1384  }
1385 
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 
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)),
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 
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(
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 
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 
1687  public function setFormName($formName)
1688  {
1689  $this->formName = trim($formName);
1690  }
1691 
1695  protected function getBackendUserAuthentication()
1696  {
1697  return $GLOBALS['BE_USER'];
1698  }
1699 
1703  protected function getModule()
1704  {
1705  return $GLOBALS['SOBE'];
1706  }
1707 
1711  protected function getLanguageService()
1712  {
1713  return $GLOBALS['LANG'];
1714  }
1715 }
makeSelectorTable($modSettings, $enableList= 'table, fields, query, group, order, limit')
getFormElements($subLevel=0, $queryConfig= '', $parent= '')
makeOptionList($fieldName, $conf, $table)
getDateTimePickerField($name, $timestamp, $type)
userDef($fieldPrefix, $conf, $fieldName, $fieldType)
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
init($name, $table, $fieldList= '')
makeComparisonSelector($subscript, $fieldName, $conf)
mkTypeSelect($name, $fieldName, $prepend= 'FIELD_')
printCodeArray($codeArr, $recursionLevel=0)
mkOperatorSelect($name, $op, $draw, $submit)
setAndCleanUpExternalLists($name, $list, $force= '')
static getFuncCheck($mainParams, $elementName, $currentValue, $script= '', $addParams= '', $tagParams= '')
if(TYPO3_MODE=== 'BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
static makeInstance($className,...$constructorArguments)
static stripLogicalOperatorPrefix(string $constraint)
getTreeList($id, $depth, $begin=0, $permClause)
mkCompSelect($name, $comparison, $neg)
static intExplode($delimiter, $string, $removeEmptyValues=false, $limit=0)