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