‪TYPO3CMS  11.5
QueryGenerator.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of the TYPO3 CMS project.
5  *
6  * It is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License, either version 2
8  * of the License, or any later version.
9  *
10  * For the full copyright and license information, please read the
11  * LICENSE.txt file that was distributed with this source code.
12  *
13  * The TYPO3 project - inspiring people to share!
14  */
15 
17 
18 use Doctrine\DBAL\Exception as DBALException;
19 use Doctrine\DBAL\Platforms\PostgreSqlPlatform;
20 use Doctrine\DBAL\Types\Types;
22 use TYPO3\CMS\Backend\Utility\BackendUtility;
42 
49 {
53  protected ‪$storeList = 'search_query_smallparts,search_result_labels,labels_noprefix,show_deleted,queryConfig,queryTable,queryFields,queryLimit,queryOrder,queryOrderDesc,queryOrder2,queryOrder2Desc,queryGroup,search_query_makeQuery';
54 
58  protected ‪$noDownloadB = 0;
59 
63  protected ‪$hookArray = [];
64 
68  protected ‪$formName = '';
69 
73  protected ‪$iconFactory;
74 
78  protected ‪$tableArray = [];
79 
83  protected ‪$settings = [];
84 
88  protected ‪$menuItems = [];
89 
93  protected ‪$moduleName;
94 
98  protected ‪$lang = [
99  'OR' => 'or',
100  'AND' => 'and',
101  'comparison' => [
102  // Type = text offset = 0
103  '0_' => 'contains',
104  '1_' => 'does not contain',
105  '2_' => 'starts with',
106  '3_' => 'does not start with',
107  '4_' => 'ends with',
108  '5_' => 'does not end with',
109  '6_' => 'equals',
110  '7_' => 'does not equal',
111  // Type = number , offset = 32
112  '32_' => 'equals',
113  '33_' => 'does not equal',
114  '34_' => 'is greater than',
115  '35_' => 'is less than',
116  '36_' => 'is between',
117  '37_' => 'is not between',
118  '38_' => 'is in list',
119  '39_' => 'is not in list',
120  '40_' => 'binary AND equals',
121  '41_' => 'binary AND does not equal',
122  '42_' => 'binary OR equals',
123  '43_' => 'binary OR does not equal',
124  // Type = multiple, relation, offset = 64
125  '64_' => 'equals',
126  '65_' => 'does not equal',
127  '66_' => 'contains',
128  '67_' => 'does not contain',
129  '68_' => 'is in list',
130  '69_' => 'is not in list',
131  '70_' => 'binary AND equals',
132  '71_' => 'binary AND does not equal',
133  '72_' => 'binary OR equals',
134  '73_' => 'binary OR does not equal',
135  // Type = date,time offset = 96
136  '96_' => 'equals',
137  '97_' => 'does not equal',
138  '98_' => 'is greater than',
139  '99_' => 'is less than',
140  '100_' => 'is between',
141  '101_' => 'is not between',
142  '102_' => 'binary AND equals',
143  '103_' => 'binary AND does not equal',
144  '104_' => 'binary OR equals',
145  '105_' => 'binary OR does not equal',
146  // Type = boolean, offset = 128
147  '128_' => 'is True',
148  '129_' => 'is False',
149  // Type = binary , offset = 160
150  '160_' => 'equals',
151  '161_' => 'does not equal',
152  '162_' => 'contains',
153  '163_' => 'does not contain',
154  ],
155  ];
156 
160  protected ‪$compSQL = [
161  // Type = text offset = 0
162  '0' => '#FIELD# LIKE \'%#VALUE#%\'',
163  '1' => '#FIELD# NOT LIKE \'%#VALUE#%\'',
164  '2' => '#FIELD# LIKE \'#VALUE#%\'',
165  '3' => '#FIELD# NOT LIKE \'#VALUE#%\'',
166  '4' => '#FIELD# LIKE \'%#VALUE#\'',
167  '5' => '#FIELD# NOT LIKE \'%#VALUE#\'',
168  '6' => '#FIELD# = \'#VALUE#\'',
169  '7' => '#FIELD# != \'#VALUE#\'',
170  // Type = number, offset = 32
171  '32' => '#FIELD# = \'#VALUE#\'',
172  '33' => '#FIELD# != \'#VALUE#\'',
173  '34' => '#FIELD# > #VALUE#',
174  '35' => '#FIELD# < #VALUE#',
175  '36' => '#FIELD# >= #VALUE# AND #FIELD# <= #VALUE1#',
176  '37' => 'NOT (#FIELD# >= #VALUE# AND #FIELD# <= #VALUE1#)',
177  '38' => '#FIELD# IN (#VALUE#)',
178  '39' => '#FIELD# NOT IN (#VALUE#)',
179  '40' => '(#FIELD# & #VALUE#)=#VALUE#',
180  '41' => '(#FIELD# & #VALUE#)!=#VALUE#',
181  '42' => '(#FIELD# | #VALUE#)=#VALUE#',
182  '43' => '(#FIELD# | #VALUE#)!=#VALUE#',
183  // Type = multiple, relation, offset = 64
184  '64' => '#FIELD# = \'#VALUE#\'',
185  '65' => '#FIELD# != \'#VALUE#\'',
186  '66' => '#FIELD# LIKE \'%#VALUE#%\' AND #FIELD# LIKE \'%#VALUE1#%\'',
187  '67' => '(#FIELD# NOT LIKE \'%#VALUE#%\' OR #FIELD# NOT LIKE \'%#VALUE1#%\')',
188  '68' => '#FIELD# IN (#VALUE#)',
189  '69' => '#FIELD# NOT IN (#VALUE#)',
190  '70' => '(#FIELD# & #VALUE#)=#VALUE#',
191  '71' => '(#FIELD# & #VALUE#)!=#VALUE#',
192  '72' => '(#FIELD# | #VALUE#)=#VALUE#',
193  '73' => '(#FIELD# | #VALUE#)!=#VALUE#',
194  // Type = date, offset = 32
195  '96' => '#FIELD# = \'#VALUE#\'',
196  '97' => '#FIELD# != \'#VALUE#\'',
197  '98' => '#FIELD# > #VALUE#',
198  '99' => '#FIELD# < #VALUE#',
199  '100' => '#FIELD# >= #VALUE# AND #FIELD# <= #VALUE1#',
200  '101' => 'NOT (#FIELD# >= #VALUE# AND #FIELD# <= #VALUE1#)',
201  '102' => '(#FIELD# & #VALUE#)=#VALUE#',
202  '103' => '(#FIELD# & #VALUE#)!=#VALUE#',
203  '104' => '(#FIELD# | #VALUE#)=#VALUE#',
204  '105' => '(#FIELD# | #VALUE#)!=#VALUE#',
205  // Type = boolean, offset = 128
206  '128' => '#FIELD# = \'1\'',
207  '129' => '#FIELD# != \'1\'',
208  // Type = binary = 160
209  '160' => '#FIELD# = \'#VALUE#\'',
210  '161' => '#FIELD# != \'#VALUE#\'',
211  '162' => '(#FIELD# & #VALUE#)=#VALUE#',
212  '163' => '(#FIELD# & #VALUE#)=0',
213  ];
214 
218  protected ‪$comp_offsets = [
219  'text' => 0,
220  'number' => 1,
221  'multiple' => 2,
222  'relation' => 2,
223  'date' => 3,
224  'time' => 3,
225  'boolean' => 4,
226  'binary' => 5,
227  ];
228 
234  protected ‪$name;
235 
241  protected ‪$table;
242 
248  protected ‪$fieldList;
249 
255  protected ‪$fields = [];
256 
260  protected ‪$extFieldLists = [];
261 
267  protected ‪$queryConfig = [];
268 
272  protected ‪$enablePrefix = false;
273 
277  protected ‪$enableQueryParts = false;
278 
282  protected ‪$fieldName;
283 
290  protected ‪$showFieldAndTableNames = false;
291 
292  public function ‪__construct(array ‪$settings, array ‪$menuItems, string ‪$moduleName)
293  {
294  $this->‪getLanguageService()->includeLLFile('EXT:core/Resources/Private/Language/locallang_t3lib_fullsearch.xlf');
295  $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
296  $this->settings = ‪$settings;
297  $this->menuItems = ‪$menuItems;
298  $this->moduleName = ‪$moduleName;
299  $this->showFieldAndTableNames = $this->‪getBackendUserAuthentication()->shallDisplayDebugInformation();
300  }
301 
307  public function ‪form()
308  {
309  $markup = [];
310  $markup[] = '<div class="form-group">';
311  $markup[] = '<input placeholder="Search Word" class="form-control" type="search" name="SET[sword]" value="'
312  . htmlspecialchars($this->settings['sword'] ?? '') . '">';
313  $markup[] = '</div>';
314  $markup[] = '<div class="form-group">';
315  $markup[] = '<input class="btn btn-default" type="submit" name="submit" value="Search All Records">';
316  $markup[] = '</div>';
317  return implode(LF, $markup);
318  }
319 
325  public function ‪queryMaker()
326  {
327  ‪$output = '';
328  $this->hookArray = ‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['t3lib_fullsearch'] ?? [];
329  $msg = $this->‪procesStoreControl();
330  $userTsConfig = $this->‪getBackendUserAuthentication()->getTSConfig();
331  if (!($userTsConfig['mod.']['dbint.']['disableStoreControl'] ?? false)) {
332  ‪$output .= '<h2>Load/Save Query</h2>';
333  ‪$output .= '<div>' . $this->‪makeStoreControl() . '</div>';
334  ‪$output .= $msg;
335  }
336  // Query Maker:
337  $this->‪init('queryConfig', $this->settings['queryTable'] ?? '', '', $this->settings);
338  if ($this->formName) {
339  $this->‪setFormName($this->formName);
340  }
341  $tmpCode = $this->‪makeSelectorTable($this->settings);
342  ‪$output .= '<div id="query"></div><h2>Make query</h2><div>' . $tmpCode . '</div>';
343  $mQ = $this->settings['search_query_makeQuery'];
344  // Make form elements:
345  if ($this->table && is_array(‪$GLOBALS['TCA'][$this->table])) {
346  if ($mQ) {
347  // Show query
348  $this->enablePrefix = true;
349  $queryString = $this->‪getQuery($this->queryConfig);
350  $selectQueryString = $this->‪getSelectQuery($queryString);
351  $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($this->table);
352 
353  $isConnectionMysql = strpos($connection->getServerVersion(), 'MySQL') === 0;
354  $fullQueryString = '';
355  try {
356  if ($mQ === 'explain' && $isConnectionMysql) {
357  // EXPLAIN is no ANSI SQL, for now this is only executed on mysql
358  // @todo: Move away from getSelectQuery() or model differently
359  $fullQueryString = 'EXPLAIN ' . $selectQueryString;
360  $dataRows = $connection->executeQuery('EXPLAIN ' . $selectQueryString)->fetchAllAssociative();
361  } elseif ($mQ === 'count') {
362  $queryBuilder = $connection->createQueryBuilder();
363  $queryBuilder->getRestrictions()->removeAll();
364  if (empty($this->settings['show_deleted'])) {
365  $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
366  }
367  $queryBuilder->count('*')
368  ->from($this->table)
369  ->where(‪QueryHelper::stripLogicalOperatorPrefix($queryString));
370  $fullQueryString = $queryBuilder->getSQL();
371  $dataRows = [$queryBuilder->executeQuery()->fetchOne()];
372  } else {
373  $fullQueryString = $selectQueryString;
374  $dataRows = $connection->executeQuery($selectQueryString)->fetchAllAssociative();
375  }
376  if (!($userTsConfig['mod.']['dbint.']['disableShowSQLQuery'] ?? false)) {
377  ‪$output .= '<h2>SQL query</h2><div><code>' . htmlspecialchars($fullQueryString) . '</code></div>';
378  }
379  $cPR = $this->‪getQueryResultCode($mQ, $dataRows, $this->table);
380  ‪$output .= '<h2>' . ($cPR['header'] ?? '') . '</h2><div>' . $cPR['content'] . '</div>';
381  } catch (DBALException $e) {
382  if (!($userTsConfig['mod.']['dbint.']['disableShowSQLQuery'] ?? false)) {
383  ‪$output .= '<h2>SQL query</h2><div><code>' . htmlspecialchars($fullQueryString) . '</code></div>';
384  }
385  $out = '<p><strong>Error: <span class="text-danger">'
386  . htmlspecialchars($e->getMessage())
387  . '</span></strong></p>';
388  ‪$output .= '<h2>SQL error</h2><div>' . $out . '</div>';
389  }
390  }
391  }
392  return '<div class="database-query-builder">' . ‪$output . '</div>';
393  }
394 
400  public function ‪search()
401  {
402  $swords = $this->settings['sword'] ?? '';
403  $out = '';
404  if ($swords) {
405  foreach (‪$GLOBALS['TCA'] as ‪$table => $value) {
406  // Get fields list
407  $conf = ‪$GLOBALS['TCA'][‪$table];
408  // Avoid querying tables with no columns
409  if (empty($conf['columns'])) {
410  continue;
411  }
412  $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable(‪$table);
413  $tableColumns = $connection->createSchemaManager()->listTableColumns(‪$table);
414  $normalizedTableColumns = [];
415  $fieldsInDatabase = [];
416  foreach ($tableColumns as $column) {
417  $fieldsInDatabase[] = $column->getName();
418  $normalizedTableColumns[trim($column->getName(), $connection->getDatabasePlatform()->getIdentifierQuoteCharacter())] = $column;
419  }
420  ‪$fields = array_intersect(array_keys($conf['columns']), $fieldsInDatabase);
421 
422  $queryBuilder = $connection->createQueryBuilder();
423  $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
424  $queryBuilder->count('*')->from(‪$table);
425  $likes = [];
426  $escapedLikeString = '%' . $queryBuilder->escapeLikeWildcards($swords) . '%';
427  foreach (‪$fields as $field) {
428  $field = trim($field, $connection->getDatabasePlatform()->getIdentifierQuoteCharacter());
429  $quotedField = $queryBuilder->quoteIdentifier($field);
430  $column = $normalizedTableColumns[$field] ?? $normalizedTableColumns[$quotedField] ?? null;
431  if ($column !== null
432  && $connection->getDatabasePlatform() instanceof PostgreSqlPlatform
433  && !in_array($column->getType()->getName(), [Types::STRING, Types::ASCII_STRING, Types::JSON], true)
434  ) {
435  if ($column->getType()->getName() === Types::SMALLINT) {
436  // we need to cast smallint to int first, otherwise text case below won't work
437  $quotedField .= '::int';
438  }
439  $quotedField .= '::text';
440  }
441  $likes[] = $queryBuilder->expr()->comparison(
442  $quotedField,
443  'LIKE',
444  $queryBuilder->createNamedParameter($escapedLikeString, ‪Connection::PARAM_STR)
445  );
446  }
447  $queryBuilder->orWhere(...$likes);
448  $count = $queryBuilder->executeQuery()->fetchOne();
449 
450  if ($count > 0) {
451  $queryBuilder = $connection->createQueryBuilder();
452  $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
453  $queryBuilder->select('uid', $conf['ctrl']['label'])
454  ->from(‪$table)
455  ->setMaxResults(200);
456  $likes = [];
457  foreach (‪$fields as $field) {
458  $field = trim($field, $connection->getDatabasePlatform()->getIdentifierQuoteCharacter());
459  $quotedField = $queryBuilder->quoteIdentifier($field);
460  $column = $normalizedTableColumns[$field] ?? $normalizedTableColumns[$quotedField] ?? null;
461  if ($column !== null
462  && $connection->getDatabasePlatform() instanceof PostgreSqlPlatform
463  && !in_array($column->getType()->getName(), [Types::STRING, Types::ASCII_STRING, Types::JSON], true)
464  ) {
465  if ($column->getType()->getName() === Types::SMALLINT) {
466  // we need to cast smallint to int first, otherwise text case below won't work
467  $quotedField .= '::int';
468  }
469  $quotedField .= '::text';
470  }
471  $likes[] = $queryBuilder->expr()->comparison(
472  $quotedField,
473  'LIKE',
474  $queryBuilder->createNamedParameter($escapedLikeString, ‪Connection::PARAM_STR)
475  );
476  }
477  $statement = $queryBuilder->orWhere(...$likes)->executeQuery();
478  $lastRow = null;
479  $rowArr = [];
480  while ($row = $statement->fetchAssociative()) {
481  $rowArr[] = $this->‪resultRowDisplay($row, $conf, ‪$table);
482  $lastRow = $row;
483  }
484  $markup = [];
485  $markup[] = '<div class="panel panel-default">';
486  $markup[] = ' <div class="panel-heading">';
487  $markup[] = htmlspecialchars($this->‪getLanguageService()->sL($conf['ctrl']['title'])) . ' (' . $count . ')';
488  $markup[] = ' </div>';
489  $markup[] = ' <table class="table table-striped table-hover">';
490  $markup[] = $this->‪resultRowTitles((array)$lastRow, $conf);
491  $markup[] = implode(LF, $rowArr);
492  $markup[] = ' </table>';
493  $markup[] = '</div>';
494 
495  $out .= implode(LF, $markup);
496  }
497  }
498  }
499  return $out;
500  }
501 
507  public function ‪setFormName(‪$formName)
508  {
509  $this->formName = trim(‪$formName);
510  }
511 
517  protected function ‪makeStoreControl()
518  {
519  // Load/Save
520  $storeArray = $this->‪initStoreArray();
521 
522  $opt = [];
523  foreach ($storeArray as $k => $v) {
524  $opt[] = '<option value="' . htmlspecialchars($k) . '">' . htmlspecialchars($v) . '</option>';
525  }
526  // Actions:
527  if (‪ExtensionManagementUtility::isLoaded('sys_action') && $this->‪getBackendUserAuthentication()->isAdmin()) {
528  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_action');
529  $queryBuilder->getRestrictions()->removeAll();
530  $statement = $queryBuilder->select('uid', 'title')
531  ->from('sys_action')
532  ->where($queryBuilder->expr()->eq('type', $queryBuilder->createNamedParameter(2, ‪Connection::PARAM_INT)))
533  ->orderBy('title')
534  ->executeQuery();
535  $opt[] = '<option value="0">__Save to Action:__</option>';
536  while ($row = $statement->fetchAssociative()) {
537  $opt[] = '<option value="-' . (int)$row['uid'] . '">' . htmlspecialchars($row['title']
538  . ' [' . (int)$row['uid'] . ']') . '</option>';
539  }
540  }
541  $markup = [];
542  $markup[] = '<div class="load-queries">';
543  $markup[] = ' <div class="row row-cols-auto">';
544  $markup[] = ' <div class="col">';
545  $markup[] = ' <select class="form-select" name="storeControl[STORE]" data-assign-store-control-title>' . implode(LF, $opt) . '</select>';
546  $markup[] = ' </div>';
547  $markup[] = ' <div class="col">';
548  $markup[] = ' <input class="form-control" name="storeControl[title]" value="" type="text" max="80">';
549  $markup[] = ' </div>';
550  $markup[] = ' <div class="col">';
551  $markup[] = ' <input class="btn btn-default" type="submit" name="storeControl[LOAD]" value="Load">';
552  $markup[] = ' </div>';
553  $markup[] = ' <div class="col">';
554  $markup[] = ' <input class="btn btn-default" type="submit" name="storeControl[SAVE]" value="Save">';
555  $markup[] = ' </div>';
556  $markup[] = ' <div class="col">';
557  $markup[] = ' <input class="btn btn-default" type="submit" name="storeControl[REMOVE]" value="Remove">';
558  $markup[] = ' </div>';
559  $markup[] = ' </div>';
560  $markup[] = '</div>';
561 
562  return implode(LF, $markup);
563  }
564 
570  protected function ‪initStoreArray()
571  {
572  $storeArray = [
573  '0' => '[New]',
574  ];
575  $savedStoreArray = unserialize($this->settings['storeArray'] ?? '', ['allowed_classes' => false]);
576  if (is_array($savedStoreArray)) {
577  $storeArray = array_merge($storeArray, $savedStoreArray);
578  }
579  return $storeArray;
580  }
581 
589  protected function ‪cleanStoreQueryConfigs($storeQueryConfigs, $storeArray)
590  {
591  if (is_array($storeQueryConfigs)) {
592  foreach ($storeQueryConfigs as $k => $v) {
593  if (!isset($storeArray[$k])) {
594  unset($storeQueryConfigs[$k]);
595  }
596  }
597  }
598  return $storeQueryConfigs;
599  }
600 
604  protected function ‪addToStoreQueryConfigs(array $storeQueryConfigs, int $index): array
605  {
606  $keyArr = explode(',', $this->storeList);
607  $storeQueryConfigs[$index] = [];
608  foreach ($keyArr as $k) {
609  $storeQueryConfigs[$index][$k] = $this->settings[$k] ?? null;
610  }
611  return $storeQueryConfigs;
612  }
613 
620  protected function ‪saveQueryInAction($uid)
621  {
622  if (‪ExtensionManagementUtility::isLoaded('sys_action')) {
623  $keyArr = explode(',', $this->storeList);
624  $saveArr = [];
625  foreach ($keyArr as $k) {
626  $saveArr[$k] = $this->settings[$k];
627  }
628  // Show query
629  if ($saveArr['queryTable']) {
630  $this->‪init('queryConfig', $saveArr['queryTable'], '', $this->settings);
631  $this->‪makeSelectorTable($saveArr);
632  $this->enablePrefix = true;
633  $queryString = $this->‪getQuery($this->queryConfig);
634 
635  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
636  ->getQueryBuilderForTable($this->table);
637  $queryBuilder->getRestrictions()->removeAll()
638  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
639  $rowCount = $queryBuilder->count('*')
640  ->from($this->table)
641  ->where(‪QueryHelper::stripLogicalOperatorPrefix($queryString))
642  ->executeQuery()
643  ->fetchOne();
644 
645  $t2DataValue = [
646  'qC' => $saveArr,
647  'qCount' => $rowCount,
648  'qSelect' => $this->‪getSelectQuery($queryString),
649  'qString' => $queryString,
650  ];
651  GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('sys_action')
652  ->update(
653  'sys_action',
654  ['t2_data' => serialize($t2DataValue)],
655  ['uid' => (int)$uid],
656  ['t2_data' => ‪Connection::PARAM_LOB]
657  );
658  }
659  return true;
660  }
661  return false;
662  }
671  protected function ‪loadStoreQueryConfigs($storeQueryConfigs, $storeIndex, $writeArray)
672  {
673  if ($storeQueryConfigs[$storeIndex]) {
674  $keyArr = explode(',', $this->storeList);
675  foreach ($keyArr as $k) {
676  $writeArray[$k] = $storeQueryConfigs[$storeIndex][$k];
677  }
678  }
679  return $writeArray;
680  }
681 
687  protected function ‪procesStoreControl()
688  {
689  $languageService = $this->‪getLanguageService();
690  $flashMessage = null;
691  $storeArray = $this->‪initStoreArray();
692  $storeQueryConfigs = (array)(unserialize($this->settings['storeQueryConfigs'] ?? '', ['allowed_classes' => false]));
693  $storeControl = GeneralUtility::_GP('storeControl');
694  $storeIndex = (int)($storeControl['STORE'] ?? 0);
695  $saveStoreArray = 0;
696  $writeArray = [];
697  $msg = '';
698  if (is_array($storeControl)) {
699  if ($storeControl['LOAD'] ?? false) {
700  if ($storeIndex > 0) {
701  $writeArray = $this->‪loadStoreQueryConfigs($storeQueryConfigs, $storeIndex, $writeArray);
702  $saveStoreArray = 1;
703  $flashMessage = GeneralUtility::makeInstance(
704  FlashMessage::class,
705  sprintf($languageService->getLL('query_loaded'), $storeArray[$storeIndex])
706  );
707  } elseif ($storeIndex < 0 && ‪ExtensionManagementUtility::isLoaded('sys_action')) {
708  $actionRecord = BackendUtility::getRecord('sys_action', abs($storeIndex));
709  if (is_array($actionRecord)) {
710  $dA = unserialize($actionRecord['t2_data'], ['allowed_classes' => false]);
711  $dbSC = [];
712  if (is_array($dA['qC'])) {
713  $dbSC[0] = $dA['qC'];
714  }
715  $writeArray = $this->‪loadStoreQueryConfigs($dbSC, 0, $writeArray);
716  $saveStoreArray = 1;
717  $flashMessage = GeneralUtility::makeInstance(
718  FlashMessage::class,
719  sprintf($languageService->getLL('query_from_action_loaded'), $actionRecord['title'])
720  );
721  }
722  }
723  } elseif ($storeControl['SAVE'] ?? false) {
724  if ($storeIndex < 0) {
725  $qOK = $this->‪saveQueryInAction(abs($storeIndex));
726  if ($qOK) {
727  $flashMessage = GeneralUtility::makeInstance(
728  FlashMessage::class,
729  $languageService->getLL('query_saved')
730  );
731  } else {
732  $flashMessage = GeneralUtility::makeInstance(
733  FlashMessage::class,
734  $languageService->getLL('query_notsaved'),
735  '',
737  );
738  }
739  } else {
740  if (trim($storeControl['title'])) {
741  if ($storeIndex > 0) {
742  $storeArray[$storeIndex] = $storeControl['title'];
743  } else {
744  $storeArray[] = $storeControl['title'];
745  end($storeArray);
746  $storeIndex = key($storeArray);
747  }
748  $storeQueryConfigs = $this->‪addToStoreQueryConfigs($storeQueryConfigs, (int)$storeIndex);
749  $saveStoreArray = 1;
750  $flashMessage = GeneralUtility::makeInstance(
751  FlashMessage::class,
752  $languageService->getLL('query_saved')
753  );
754  }
755  }
756  } elseif ($storeControl['REMOVE'] ?? false) {
757  if ($storeIndex > 0) {
758  $flashMessage = GeneralUtility::makeInstance(
759  FlashMessage::class,
760  sprintf($languageService->getLL('query_removed'), $storeArray[$storeControl['STORE']])
761  );
762  // Removing
763  unset($storeArray[$storeControl['STORE']]);
764  $saveStoreArray = 1;
765  }
766  }
767  if (!empty($flashMessage)) {
768  $msg = GeneralUtility::makeInstance(FlashMessageRendererResolver::class)
769  ->resolve()
770  ->render([$flashMessage]);
771  }
772  }
773  if ($saveStoreArray) {
774  // Making sure, index 0 is not set!
775  unset($storeArray[0]);
776  $writeArray['storeArray'] = serialize($storeArray);
777  $writeArray['storeQueryConfigs'] =
778  serialize($this->‪cleanStoreQueryConfigs($storeQueryConfigs, $storeArray));
779  $this->settings = BackendUtility::getModuleData(
780  $this->menuItems,
781  $writeArray,
782  $this->moduleName,
783  'ses'
784  );
785  }
786  return $msg;
787  }
788 
798  protected function ‪getQueryResultCode($type, array $dataRows, ‪$table)
799  {
800  $out = '';
801  $cPR = [];
802  switch ($type) {
803  case 'count':
804  $cPR['header'] = 'Count';
805  $cPR['content'] = '<br><strong>' . (int)$dataRows[0] . '</strong> records selected.';
806  break;
807  case 'all':
808  $rowArr = [];
809  $dataRow = null;
810  foreach ($dataRows as $dataRow) {
811  $rowArr[] = $this->‪resultRowDisplay($dataRow, ‪$GLOBALS['TCA'][‪$table], ‪$table);
812  }
813  if (is_array($this->hookArray['beforeResultTable'] ?? false)) {
814  foreach ($this->hookArray['beforeResultTable'] as $_funcRef) {
815  $out .= GeneralUtility::callUserFunction($_funcRef, $this->settings);
816  }
817  }
818  if (!empty($rowArr)) {
819  $cPR['header'] = 'Result';
820  $out .= '<table class="table table-striped table-hover">'
821  . $this->‪resultRowTitles((array)$dataRow, ‪$GLOBALS['TCA'][‪$table]) . implode(LF, $rowArr)
822  . '</table>';
823  } else {
825  }
826 
827  $cPR['content'] = $out;
828  break;
829  case 'csv':
830  $rowArr = [];
831  $first = 1;
832  foreach ($dataRows as $dataRow) {
833  if ($first) {
834  $rowArr[] = $this->‪csvValues(array_keys($dataRow));
835  $first = 0;
836  }
837  $rowArr[] = $this->‪csvValues($dataRow, ',', '"', ‪$GLOBALS['TCA'][‪$table], ‪$table);
838  }
839  if (!empty($rowArr)) {
840  $cPR['header'] = 'Result';
841  $out .= '<textarea name="whatever" rows="20" class="text-monospace" style="width:100%">'
842  . htmlspecialchars(implode(LF, $rowArr))
843  . '</textarea>';
844  if (!$this->noDownloadB) {
845  $out .= '<br><input class="btn btn-default" type="submit" name="download_file" '
846  . 'value="Click to download file">';
847  }
848  // Downloads file:
849  // @todo: args. routing anyone?
850  if (GeneralUtility::_GP('download_file')) {
851  $filename = 'TYPO3_' . ‪$table . '_export_' . date('dmy-Hi') . '.csv';
852  $mimeType = 'application/octet-stream';
853  header('Content-Type: ' . $mimeType);
854  header('Content-Disposition: attachment; filename=' . $filename);
855  echo implode(CRLF, $rowArr);
856  die;
857  }
858  } else {
860  }
861  $cPR['content'] = $out;
862  break;
863  case 'explain':
864  default:
865  foreach ($dataRows as $dataRow) {
866  $out .= '<br />' . ‪DebugUtility::viewArray($dataRow);
867  }
868  $cPR['header'] = 'Explain SQL query';
869  $cPR['content'] = $out;
870  }
871  return $cPR;
872  }
883  protected function ‪csvValues($row, $delim = ',', $quote = '"', $conf = [], ‪$table = '')
884  {
885  $valueArray = $row;
886  if (($this->settings['search_result_labels'] ?? false) && ‪$table) {
887  foreach ($valueArray as $key => $val) {
888  $valueArray[$key] = $this->‪getProcessedValueExtra(‪$table, $key, $val, $conf, ';');
889  }
890  }
891  return ‪CsvUtility::csvValues($valueArray, $delim, $quote);
892  }
893 
902  protected function ‪resultRowDisplay($row, $conf, ‪$table)
903  {
904  $languageService = $this->‪getLanguageService();
905  $out = '<tr>';
906  foreach ($row as ‪$fieldName => $fieldValue) {
907  if (GeneralUtility::inList($this->settings['queryFields'] ?? '', ‪$fieldName)
908  || !($this->settings['queryFields'] ?? false)
909  && ‪$fieldName !== 'pid'
910  && ‪$fieldName !== 'deleted'
911  ) {
912  if ($this->settings['search_result_labels'] ?? false) {
913  $fVnew = $this->‪getProcessedValueExtra(‪$table, ‪$fieldName, $fieldValue, $conf, '<br />');
914  } else {
915  $fVnew = htmlspecialchars($fieldValue);
916  }
917  $out .= '<td>' . $fVnew . '</td>';
918  }
919  }
920  $out .= '<td>';
921  $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
922 
923  if (!($row['deleted'] ?? false)) {
924  $out .= '<div class="btn-group" role="group">';
925  $url = (string)$uriBuilder->buildUriFromRoute('record_edit', [
926  'edit' => [
927  ‪$table => [
928  $row['uid'] => 'edit',
929  ],
930  ],
931  'returnUrl' => ‪$GLOBALS['TYPO3_REQUEST']->getAttribute('normalizedParams')->getRequestUri()
932  . ‪HttpUtility::buildQueryString(['SET' => (array)GeneralUtility::_POST('SET')], '&'),
933  ]);
934  $out .= '<a class="btn btn-default" href="' . htmlspecialchars($url) . '">'
935  . $this->iconFactory->getIcon('actions-open', ‪Icon::SIZE_SMALL)->render() . '</a>';
936  $out .= '</div><div class="btn-group" role="group">';
937  $out .= sprintf(
938  '<a class="btn btn-default" href="#" data-dispatch-action="%s" data-dispatch-args-list="%s">%s</a>',
939  'TYPO3.InfoWindow.showItem',
940  htmlspecialchars(‪$table . ',' . $row['uid']),
941  $this->iconFactory->getIcon('actions-document-info', ‪Icon::SIZE_SMALL)->render()
942  );
943  $out .= '</div>';
944  } else {
945  $out .= '<div class="btn-group" role="group">';
946  $out .= '<a class="btn btn-default" href="' . htmlspecialchars((string)$uriBuilder->buildUriFromRoute('tce_db', [
947  'cmd' => [
948  ‪$table => [
949  $row['uid'] => [
950  'undelete' => 1,
951  ],
952  ],
953  ],
954  'redirect' => GeneralUtility::linkThisScript(),
955  ])) . '" title="' . htmlspecialchars($languageService->getLL('undelete_only')) . '">';
956  $out .= $this->iconFactory->getIcon('actions-edit-restore', ‪Icon::SIZE_SMALL)->render() . '</a>';
957  $formEngineParameters = [
958  'edit' => [
959  ‪$table => [
960  $row['uid'] => 'edit',
961  ],
962  ],
963  'returnUrl' => GeneralUtility::linkThisScript(),
964  ];
965  $redirectUrl = (string)$uriBuilder->buildUriFromRoute('record_edit', $formEngineParameters);
966  $out .= '<a class="btn btn-default" href="' . htmlspecialchars((string)$uriBuilder->buildUriFromRoute('tce_db', [
967  'cmd' => [
968  ‪$table => [
969  $row['uid'] => [
970  'undelete' => 1,
971  ],
972  ],
973  ],
974  'redirect' => $redirectUrl,
975  ])) . '" title="' . htmlspecialchars($languageService->getLL('undelete_and_edit')) . '">';
976  $out .= $this->iconFactory->getIcon('actions-delete-edit', ‪Icon::SIZE_SMALL)->render() . '</a>';
977  $out .= '</div>';
978  }
979  $_params = [‪$table => $row];
980  if (is_array($this->hookArray['additionalButtons'] ?? false)) {
981  foreach ($this->hookArray['additionalButtons'] as $_funcRef) {
982  $out .= GeneralUtility::callUserFunction($_funcRef, $_params);
983  }
984  }
985  $out .= '</td></tr>';
986  return $out;
987  }
988 
999  protected function ‪getProcessedValueExtra(‪$table, ‪$fieldName, $fieldValue, $conf, $splitString)
1000  {
1001  $out = '';
1002  ‪$fields = [];
1003  // Analysing the fields in the table.
1004  if (is_array(‪$GLOBALS['TCA'][‪$table] ?? null)) {
1005  $fC = ‪$GLOBALS['TCA'][‪$table]['columns'][‪$fieldName] ?? null;
1006  ‪$fields = $fC['config'] ?? [];
1007  ‪$fields['exclude'] = $fC['exclude'] ?? '';
1008  if (is_array($fC) && $fC['label']) {
1009  ‪$fields['label'] = preg_replace('/:$/', '', trim($this->‪getLanguageService()->sL($fC['label'])));
1010  switch (‪$fields['type']) {
1011  case 'input':
1012  if (preg_match('/int|year/i', ‪$fields['eval'] ?? '')) {
1013  ‪$fields['type'] = 'number';
1014  } elseif (preg_match('/time/i', ‪$fields['eval'] ?? '')) {
1015  ‪$fields['type'] = 'time';
1016  } elseif (preg_match('/date/i', ‪$fields['eval'] ?? '')) {
1017  ‪$fields['type'] = 'date';
1018  } else {
1019  ‪$fields['type'] = 'text';
1020  }
1021  break;
1022  case 'check':
1023  if (!(‪$fields['items'] ?? false)) {
1024  ‪$fields['type'] = 'boolean';
1025  } else {
1026  ‪$fields['type'] = 'binary';
1027  }
1028  break;
1029  case 'radio':
1030  ‪$fields['type'] = 'multiple';
1031  break;
1032  case 'select':
1033  case 'category':
1034  ‪$fields['type'] = 'multiple';
1035  if (‪$fields['foreign_table'] ?? false) {
1036  ‪$fields['type'] = 'relation';
1037  }
1038  if (‪$fields['special'] ?? false) {
1039  ‪$fields['type'] = 'text';
1040  }
1041  break;
1042  case 'group':
1043  if ((‪$fields['internal_type'] ?? '') !== 'folder') {
1044  ‪$fields['type'] = 'relation';
1045  }
1046  break;
1047  case 'user':
1048  case 'flex':
1049  case 'passthrough':
1050  case 'none':
1051  case 'text':
1052  default:
1053  ‪$fields['type'] = 'text';
1054  }
1055  } else {
1056  ‪$fields['label'] = '[FIELD: ' . ‪$fieldName . ']';
1057  switch (‪$fieldName) {
1058  case 'pid':
1059  ‪$fields['type'] = 'relation';
1060  ‪$fields['allowed'] = 'pages';
1061  break;
1062  case 'cruser_id':
1063  ‪$fields['type'] = 'relation';
1064  ‪$fields['allowed'] = 'be_users';
1065  break;
1066  case 'tstamp':
1067  case 'crdate':
1068  ‪$fields['type'] = 'time';
1069  break;
1070  default:
1071  ‪$fields['type'] = 'number';
1072  }
1073  }
1074  }
1075  switch (‪$fields['type']) {
1076  case 'date':
1077  if ($fieldValue != -1) {
1078  // @todo Replace deprecated strftime in php 8.1. Suppress warning in v11.
1079  $out = (string)@strftime('%d-%m-%Y', (int)$fieldValue);
1080  }
1081  break;
1082  case 'time':
1083  if ($fieldValue != -1) {
1084  if ($splitString === '<br />') {
1085  // @todo Replace deprecated strftime in php 8.1. Suppress warning in v11.
1086  $out = (string)@strftime('%H:%M' . $splitString . '%d-%m-%Y', (int)$fieldValue);
1087  } else {
1088  // @todo Replace deprecated strftime in php 8.1. Suppress warning in v11.
1089  $out = (string)@strftime('%H:%M %d-%m-%Y', (int)$fieldValue);
1090  }
1091  }
1092  break;
1093  case 'multiple':
1094  case 'binary':
1095  case 'relation':
1096  $out = $this->‪makeValueList(‪$fieldName, $fieldValue, ‪$fields, ‪$table, $splitString);
1097  break;
1098  case 'boolean':
1099  $out = $fieldValue ? 'True' : 'False';
1100  break;
1101  default:
1102  $out = htmlspecialchars($fieldValue);
1103  }
1104  return $out;
1105  }
1106 
1116  protected function ‪getTreeList($id, $depth, $begin = 0, $permsClause = '')
1117  {
1118  $depth = (int)$depth;
1119  $begin = (int)$begin;
1120  $id = (int)$id;
1121  if ($id < 0) {
1122  $id = abs($id);
1123  }
1124  if ($begin == 0) {
1125  $theList = (string)$id;
1126  } else {
1127  $theList = '';
1128  }
1129  if ($id && $depth > 0) {
1130  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
1131  $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1132  $statement = $queryBuilder->select('uid')
1133  ->from('pages')
1134  ->where(
1135  $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($id, ‪Connection::PARAM_INT)),
1136  $queryBuilder->expr()->eq('sys_language_uid', 0)
1137  )
1138  ->orderBy('uid');
1139  if ($permsClause !== '') {
1140  $queryBuilder->andWhere(‪QueryHelper::stripLogicalOperatorPrefix($permsClause));
1141  }
1142  $statement = $queryBuilder->executeQuery();
1143  while ($row = $statement->fetchAssociative()) {
1144  if ($begin <= 0) {
1145  $theList .= ',' . $row['uid'];
1146  }
1147  if ($depth > 1) {
1148  $theSubList = $this->‪getTreeList($row['uid'], $depth - 1, $begin - 1, $permsClause);
1149  if (!empty($theList) && !empty($theSubList) && ($theSubList[0] !== ',')) {
1150  $theList .= ',';
1151  }
1152  $theList .= $theSubList;
1153  }
1154  }
1155  }
1156  return $theList;
1157  }
1158 
1169  protected function ‪makeValueList(‪$fieldName, $fieldValue, $conf, ‪$table, $splitString)
1170  {
1171  $backendUserAuthentication = $this->‪getBackendUserAuthentication();
1172  $languageService = $this->‪getLanguageService();
1173  $from_table_Arr = [];
1174  $fieldSetup = $conf;
1175  $out = '';
1176  if ($fieldSetup['type'] === 'multiple') {
1177  foreach (($fieldSetup['items'] ?? []) as $key => $val) {
1178  if (strpos($val[0], 'LLL:') === 0) {
1179  $value = $languageService->sL($val[0]);
1180  } else {
1181  $value = $val[0];
1182  }
1183  if (GeneralUtility::inList($fieldValue, $val[1]) || $fieldValue == $val[1]) {
1184  if ($out !== '') {
1185  $out .= $splitString;
1186  }
1187  $out .= htmlspecialchars($value);
1188  }
1189  }
1190  }
1191  if ($fieldSetup['type'] === 'binary') {
1192  foreach ($fieldSetup['items'] as $Key => $val) {
1193  if (strpos($val[0], 'LLL:') === 0) {
1194  $value = $languageService->sL($val[0]);
1195  } else {
1196  $value = $val[0];
1197  }
1198  if ($out !== '') {
1199  $out .= $splitString;
1200  }
1201  $out .= htmlspecialchars($value);
1202  }
1203  }
1204  if ($fieldSetup['type'] === 'relation') {
1205  $dontPrefixFirstTable = 0;
1206  $useTablePrefix = 0;
1207  foreach (($fieldSetup['items'] ?? []) as $val) {
1208  if (strpos($val[0], 'LLL:') === 0) {
1209  $value = $languageService->sL($val[0]);
1210  } else {
1211  $value = $val[0];
1212  }
1213  if (GeneralUtility::inList($fieldValue, $value) || $fieldValue == $value) {
1214  if ($out !== '') {
1215  $out .= $splitString;
1216  }
1217  $out .= htmlspecialchars($value);
1218  }
1219  }
1220  if (str_contains($fieldSetup['allowed'] ?? '', ',')) {
1221  $from_table_Arr = explode(',', $fieldSetup['allowed']);
1222  $useTablePrefix = 1;
1223  if (!$fieldSetup['prepend_tname']) {
1224  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(‪$table);
1225  $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1226  $statement = $queryBuilder->select(‪$fieldName)->from(‪$table)->executeQuery();
1227  while ($row = $statement->fetchAssociative()) {
1228  if (str_contains($row[‪$fieldName], ',')) {
1229  $checkContent = explode(',', $row[‪$fieldName]);
1230  foreach ($checkContent as $singleValue) {
1231  if (!str_contains($singleValue, '_')) {
1232  $dontPrefixFirstTable = 1;
1233  }
1234  }
1235  } else {
1236  $singleValue = $row[‪$fieldName];
1237  if ($singleValue !== '' && !str_contains($singleValue, '_')) {
1238  $dontPrefixFirstTable = 1;
1239  }
1240  }
1241  }
1242  }
1243  } else {
1244  $from_table_Arr[0] = $fieldSetup['allowed'] ?? null;
1245  }
1246  if (!empty($fieldSetup['prepend_tname'])) {
1247  $useTablePrefix = 1;
1248  }
1249  if (!empty($fieldSetup['foreign_table'])) {
1250  $from_table_Arr[0] = $fieldSetup['foreign_table'];
1251  }
1252  $counter = 0;
1253  $useSelectLabels = 0;
1254  $useAltSelectLabels = 0;
1255  $tablePrefix = '';
1256  $labelFieldSelect = [];
1257  foreach ($from_table_Arr as $from_table) {
1258  if ($useTablePrefix && !$dontPrefixFirstTable && $counter != 1 || $counter == 1) {
1259  $tablePrefix = $from_table . '_';
1260  }
1261  $counter = 1;
1262  if (is_array(‪$GLOBALS['TCA'][$from_table] ?? null)) {
1263  $labelField = ‪$GLOBALS['TCA'][$from_table]['ctrl']['label'] ?? '';
1264  $altLabelField = ‪$GLOBALS['TCA'][$from_table]['ctrl']['label_alt'] ?? '';
1265  if (is_array(‪$GLOBALS['TCA'][$from_table]['columns'][$labelField]['config']['items'] ?? false)) {
1266  $items = ‪$GLOBALS['TCA'][$from_table]['columns'][$labelField]['config']['items'];
1267  foreach ($items as $labelArray) {
1268  if (str_starts_with($labelArray[0], 'LLL:')) {
1269  $labelFieldSelect[$labelArray[1]] = $languageService->sL($labelArray[0]);
1270  } else {
1271  $labelFieldSelect[$labelArray[1]] = $labelArray[0];
1272  }
1273  }
1274  $useSelectLabels = 1;
1275  }
1276  $altLabelFieldSelect = [];
1277  if (is_array(‪$GLOBALS['TCA'][$from_table]['columns'][$altLabelField]['config']['items'] ?? false)) {
1278  $items = ‪$GLOBALS['TCA'][$from_table]['columns'][$altLabelField]['config']['items'];
1279  foreach ($items as $altLabelArray) {
1280  if (str_starts_with($altLabelArray[0], 'LLL:')) {
1281  $altLabelFieldSelect[$altLabelArray[1]] = $languageService->sL($altLabelArray[0]);
1282  } else {
1283  $altLabelFieldSelect[$altLabelArray[1]] = $altLabelArray[0];
1284  }
1285  }
1286  $useAltSelectLabels = 1;
1287  }
1288 
1289  if (empty($this->tableArray[$from_table])) {
1290  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($from_table);
1291  $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1292  $selectFields = ['uid', $labelField];
1293  if ($altLabelField) {
1294  $selectFields = array_merge($selectFields, ‪GeneralUtility::trimExplode(',', $altLabelField, true));
1295  }
1296  $queryBuilder->select(...$selectFields)
1297  ->from($from_table)
1298  ->orderBy('uid');
1299  if (!$backendUserAuthentication->isAdmin()) {
1300  $webMounts = $backendUserAuthentication->returnWebmounts();
1301  $perms_clause = $backendUserAuthentication->getPagePermsClause(‪Permission::PAGE_SHOW);
1302  $webMountPageTree = '';
1303  $webMountPageTreePrefix = '';
1304  foreach ($webMounts as $webMount) {
1305  if ($webMountPageTree) {
1306  $webMountPageTreePrefix = ',';
1307  }
1308  $webMountPageTree .= $webMountPageTreePrefix
1309  . $this->‪getTreeList($webMount, 999, 0, $perms_clause);
1310  }
1311  if ($from_table === 'pages') {
1312  $queryBuilder->where(
1314  $queryBuilder->expr()->in(
1315  'uid',
1316  $queryBuilder->createNamedParameter(
1317  ‪GeneralUtility::intExplode(',', $webMountPageTree),
1318  Connection::PARAM_INT_ARRAY
1319  )
1320  )
1321  );
1322  } else {
1323  $queryBuilder->where(
1324  $queryBuilder->expr()->in(
1325  'pid',
1326  $queryBuilder->createNamedParameter(
1327  ‪GeneralUtility::intExplode(',', $webMountPageTree),
1328  Connection::PARAM_INT_ARRAY
1329  )
1330  )
1331  );
1332  }
1333  }
1334  $statement = $queryBuilder->executeQuery();
1335  $this->tableArray[$from_table] = [];
1336  while ($row = $statement->fetchAssociative()) {
1337  $this->tableArray[$from_table][] = $row;
1338  }
1339  }
1340 
1341  foreach ($this->tableArray[$from_table] as $key => $val) {
1342  $this->settings['labels_noprefix'] =
1343  ($this->settings['labels_noprefix'] ?? '') == 1
1344  ? 'on'
1345  : $this->settings['labels_noprefix'];
1346  $prefixString =
1347  $this->settings['labels_noprefix'] === 'on'
1348  ? ''
1349  : ' [' . $tablePrefix . $val['uid'] . '] ';
1350  if ($out !== '') {
1351  $out .= $splitString;
1352  }
1353  if (GeneralUtility::inList($fieldValue, $tablePrefix . $val['uid'])
1354  || $fieldValue == $tablePrefix . $val['uid']) {
1355  if ($useSelectLabels) {
1356  $out .= htmlspecialchars($prefixString . $labelFieldSelect[$val[$labelField]]);
1357  } elseif ($val[$labelField]) {
1358  $out .= htmlspecialchars($prefixString . $val[$labelField]);
1359  } elseif ($useAltSelectLabels) {
1360  $out .= htmlspecialchars($prefixString . $altLabelFieldSelect[$val[$altLabelField]]);
1361  } else {
1362  $out .= htmlspecialchars($prefixString . $val[$altLabelField]);
1363  }
1364  }
1365  }
1366  }
1367  }
1368  }
1369  return $out;
1370  }
1371 
1379  protected function ‪resultRowTitles($row, $conf)
1380  {
1381  $languageService = $this->‪getLanguageService();
1382  $tableHeader = [];
1383  // Start header row
1384  $tableHeader[] = '<thead><tr>';
1385  // Iterate over given columns
1386  foreach ($row as ‪$fieldName => $fieldValue) {
1387  if (GeneralUtility::inList($this->settings['queryFields'] ?? '', ‪$fieldName)
1388  || !($this->settings['queryFields'] ?? false)
1389  && ‪$fieldName !== 'pid'
1390  && ‪$fieldName !== 'deleted'
1391  ) {
1392  if ($this->settings['search_result_labels'] ?? false) {
1393  $title = $languageService->sL(($conf['columns'][‪$fieldName]['label'] ?? false) ?: ‪$fieldName);
1394  } else {
1395  $title = $languageService->sL(‪$fieldName);
1396  }
1397  $tableHeader[] = '<th>' . htmlspecialchars($title) . '</th>';
1398  }
1399  }
1400  // Add empty icon column
1401  $tableHeader[] = '<th></th>';
1402  // Close header row
1403  $tableHeader[] = '</tr></thead>';
1404  return implode(LF, $tableHeader);
1405  }
1410  private function ‪renderNoResultsFoundMessage()
1411  {
1412  $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, 'No rows selected!', '', ‪FlashMessage::INFO);
1413  $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
1414  $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
1415  $defaultFlashMessageQueue->enqueue($flashMessage);
1416  }
1417 
1423  protected function ‪makeFieldList()
1424  {
1425  $fieldListArr = [];
1426  if (is_array(‪$GLOBALS['TCA'][$this->table])) {
1427  $fieldListArr = array_keys(‪$GLOBALS['TCA'][$this->table]['columns'] ?? []);
1428  $fieldListArr[] = 'uid';
1429  $fieldListArr[] = 'pid';
1430  $fieldListArr[] = 'deleted';
1431  if (‪$GLOBALS['TCA'][$this->table]['ctrl']['tstamp'] ?? false) {
1432  $fieldListArr[] = ‪$GLOBALS['TCA'][‪$this->table]['ctrl']['tstamp'];
1433  }
1434  if (‪$GLOBALS['TCA'][$this->table]['ctrl']['crdate'] ?? false) {
1435  $fieldListArr[] = ‪$GLOBALS['TCA'][‪$this->table]['ctrl']['crdate'];
1436  }
1437  if (‪$GLOBALS['TCA'][$this->table]['ctrl']['cruser_id'] ?? false) {
1438  $fieldListArr[] = ‪$GLOBALS['TCA'][‪$this->table]['ctrl']['cruser_id'];
1439  }
1440  if (‪$GLOBALS['TCA'][$this->table]['ctrl']['sortby'] ?? false) {
1441  $fieldListArr[] = ‪$GLOBALS['TCA'][‪$this->table]['ctrl']['sortby'];
1442  }
1443  }
1444  return implode(',', $fieldListArr);
1445  }
1446 
1455  protected function ‪init(‪$name, ‪$table, ‪$fieldList = '', array ‪$settings = [])
1456  {
1457  // Analysing the fields in the table.
1458  if (is_array(‪$GLOBALS['TCA'][‪$table] ?? false)) {
1459  $this->name = ‪$name;
1460  $this->table = ‪$table;
1461  $this->fieldList = ‪$fieldList ?: $this->‪makeFieldList();
1462  $this->settings = ‪$settings;
1463  $fieldArr = ‪GeneralUtility::trimExplode(',', $this->fieldList, true);
1464  foreach ($fieldArr as ‪$fieldName) {
1465  $fC = ‪$GLOBALS['TCA'][‪$this->table]['columns'][‪$fieldName] ?? [];
1466  $this->fields[‪$fieldName] = $fC['config'] ?? [];
1467  $this->fields[‪$fieldName]['exclude'] = $fC['exclude'] ?? '';
1468  if (($this->fields[‪$fieldName]['type'] ?? '') === 'user' && !isset($this->fields[‪$fieldName]['type']['userFunc'])
1469  || ($this->fields[‪$fieldName]['type'] ?? '') === 'none'
1470  ) {
1471  // Do not list type=none "virtual" fields or query them from db,
1472  // and if type is user without defined userFunc
1473  unset($this->fields[‪$fieldName]);
1474  continue;
1475  }
1476  if (is_array($fC) && ($fC['label'] ?? false)) {
1477  $this->fields[‪$fieldName]['label'] = rtrim(trim($this->‪getLanguageService()->sL($fC['label'])), ':');
1478  switch ($this->fields[‪$fieldName]['type']) {
1479  case 'input':
1480  if (preg_match('/int|year/i', ($this->fields[‪$fieldName]['eval'] ?? ''))) {
1481  $this->fields[‪$fieldName]['type'] = 'number';
1482  } elseif (preg_match('/time/i', ($this->fields[‪$fieldName]['eval'] ?? ''))) {
1483  $this->fields[‪$fieldName]['type'] = 'time';
1484  } elseif (preg_match('/date/i', ($this->fields[‪$fieldName]['eval'] ?? ''))) {
1485  $this->fields[‪$fieldName]['type'] = 'date';
1486  } else {
1487  $this->fields[‪$fieldName]['type'] = 'text';
1488  }
1489  break;
1490  case 'check':
1491  if (count($this->fields[‪$fieldName]['items'] ?? []) <= 1) {
1492  $this->fields[‪$fieldName]['type'] = 'boolean';
1493  } else {
1494  $this->fields[‪$fieldName]['type'] = 'binary';
1495  }
1496  break;
1497  case 'radio':
1498  $this->fields[‪$fieldName]['type'] = 'multiple';
1499  break;
1500  case 'select':
1501  case 'category':
1502  $this->fields[‪$fieldName]['type'] = 'multiple';
1503  if ($this->fields[‪$fieldName]['foreign_table'] ?? false) {
1504  $this->fields[‪$fieldName]['type'] = 'relation';
1505  }
1506  if ($this->fields[‪$fieldName]['special'] ?? false) {
1507  $this->fields[‪$fieldName]['type'] = 'text';
1508  }
1509  break;
1510  case 'group':
1511  if (($this->fields[‪$fieldName]['internal_type'] ?? '') !== 'folder') {
1512  $this->fields[‪$fieldName]['type'] = 'relation';
1513  }
1514  break;
1515  case 'user':
1516  case 'flex':
1517  case 'passthrough':
1518  case 'none':
1519  case 'text':
1520  default:
1521  $this->fields[‪$fieldName]['type'] = 'text';
1522  }
1523  } else {
1524  $this->fields[‪$fieldName]['label'] = '[FIELD: ' . ‪$fieldName . ']';
1525  switch (‪$fieldName) {
1526  case 'pid':
1527  $this->fields[‪$fieldName]['type'] = 'relation';
1528  $this->fields[‪$fieldName]['allowed'] = 'pages';
1529  break;
1530  case 'cruser_id':
1531  $this->fields[‪$fieldName]['type'] = 'relation';
1532  $this->fields[‪$fieldName]['allowed'] = 'be_users';
1533  break;
1534  case 'tstamp':
1535  case 'crdate':
1536  $this->fields[‪$fieldName]['type'] = 'time';
1537  break;
1538  case 'deleted':
1539  $this->fields[‪$fieldName]['type'] = 'boolean';
1540  break;
1541  default:
1542  $this->fields[‪$fieldName]['type'] = 'number';
1543  }
1544  }
1545  }
1546  }
1547  /* // EXAMPLE:
1548  $this->queryConfig = array(
1549  array(
1550  'operator' => 'AND',
1551  'type' => 'FIELD_space_before_class',
1552  ),
1553  array(
1554  'operator' => 'AND',
1555  'type' => 'FIELD_records',
1556  'negate' => 1,
1557  'inputValue' => 'foo foo'
1558  ),
1559  array(
1560  'type' => 'newlevel',
1561  'nl' => array(
1562  array(
1563  'operator' => 'AND',
1564  'type' => 'FIELD_space_before_class',
1565  'negate' => 1,
1566  'inputValue' => 'foo foo'
1567  ),
1568  array(
1569  'operator' => 'AND',
1570  'type' => 'FIELD_records',
1571  'negate' => 1,
1572  'inputValue' => 'foo foo'
1573  )
1574  )
1575  ),
1576  array(
1577  'operator' => 'OR',
1578  'type' => 'FIELD_maillist',
1579  )
1580  );
1581  */
1582  }
1583 
1591  protected function ‪setAndCleanUpExternalLists(‪$name, $list, $force = '')
1592  {
1593  ‪$fields = array_unique(‪GeneralUtility::trimExplode(',', $list . ',' . $force, true));
1594  $reList = [];
1595  foreach (‪$fields as ‪$fieldName) {
1596  if (isset($this->fields[‪$fieldName])) {
1597  $reList[] = ‪$fieldName;
1598  }
1599  }
1600  $this->extFieldLists[‪$name] = implode(',', $reList);
1601  }
1602 
1608  protected function ‪procesData($qC = [])
1609  {
1610  $this->queryConfig = $qC;
1611  $POST = GeneralUtility::_POST();
1612  // If delete...
1613  if ($POST['qG_del'] ?? false) {
1614  // Initialize array to work on, save special parameters
1615  $ssArr = $this->‪getSubscript($POST['qG_del']);
1616  $workArr = &‪$this->queryConfig;
1617  $ssArrSize = count($ssArr) - 1;
1618  $i = 0;
1619  for (; $i < $ssArrSize; $i++) {
1620  $workArr = &$workArr[$ssArr[$i]];
1621  }
1622  // Delete the entry and move the other entries
1623  unset($workArr[$ssArr[$i]]);
1624  $workArrSize = count((array)$workArr);
1625  for ($j = $ssArr[$i]; $j < $workArrSize; $j++) {
1626  $workArr[$j] = $workArr[$j + 1];
1627  unset($workArr[$j + 1]);
1628  }
1629  }
1630  // If insert...
1631  if ($POST['qG_ins'] ?? false) {
1632  // Initialize array to work on, save special parameters
1633  $ssArr = $this->‪getSubscript($POST['qG_ins']);
1634  $workArr = &‪$this->queryConfig;
1635  $ssArrSize = count($ssArr) - 1;
1636  $i = 0;
1637  for (; $i < $ssArrSize; $i++) {
1638  $workArr = &$workArr[$ssArr[$i]];
1639  }
1640  // Move all entries above position where new entry is to be inserted
1641  $workArrSize = count((array)$workArr);
1642  for ($j = $workArrSize; $j > $ssArr[$i]; $j--) {
1643  $workArr[$j] = $workArr[$j - 1];
1644  }
1645  // Clear new entry position
1646  unset($workArr[$ssArr[$i] + 1]);
1647  $workArr[$ssArr[$i] + 1]['type'] = 'FIELD_';
1648  }
1649  // If move up...
1650  if ($POST['qG_up'] ?? false) {
1651  // Initialize array to work on
1652  $ssArr = $this->‪getSubscript($POST['qG_up']);
1653  $workArr = &‪$this->queryConfig;
1654  $ssArrSize = count($ssArr) - 1;
1655  $i = 0;
1656  for (; $i < $ssArrSize; $i++) {
1657  $workArr = &$workArr[$ssArr[$i]];
1658  }
1659  // Swap entries
1660  $qG_tmp = $workArr[$ssArr[$i]];
1661  $workArr[$ssArr[$i]] = $workArr[$ssArr[$i] - 1];
1662  $workArr[$ssArr[$i] - 1] = $qG_tmp;
1663  }
1664  // If new level...
1665  if ($POST['qG_nl'] ?? false) {
1666  // Initialize array to work on
1667  $ssArr = $this->‪getSubscript($POST['qG_nl']);
1668  $workArr = &‪$this->queryConfig;
1669  $ssArraySize = count($ssArr) - 1;
1670  $i = 0;
1671  for (; $i < $ssArraySize; $i++) {
1672  $workArr = &$workArr[$ssArr[$i]];
1673  }
1674  // Do stuff:
1675  $tempEl = $workArr[$ssArr[$i]];
1676  if (is_array($tempEl)) {
1677  if ($tempEl['type'] !== 'newlevel') {
1678  $workArr[$ssArr[$i]] = [
1679  'type' => 'newlevel',
1680  'operator' => $tempEl['operator'],
1681  'nl' => [$tempEl],
1682  ];
1683  }
1684  }
1685  }
1686  // If collapse level...
1687  if ($POST['qG_remnl'] ?? false) {
1688  // Initialize array to work on
1689  $ssArr = $this->‪getSubscript($POST['qG_remnl']);
1690  $workArr = &‪$this->queryConfig;
1691  $ssArrSize = count($ssArr) - 1;
1692  $i = 0;
1693  for (; $i < $ssArrSize; $i++) {
1694  $workArr = &$workArr[$ssArr[$i]];
1695  }
1696  // Do stuff:
1697  $tempEl = $workArr[$ssArr[$i]];
1698  if (is_array($tempEl)) {
1699  if ($tempEl['type'] === 'newlevel' && is_array($workArr)) {
1700  $a1 = array_slice($workArr, 0, $ssArr[$i]);
1701  $a2 = array_slice($workArr, $ssArr[$i]);
1702  array_shift($a2);
1703  $a3 = $tempEl['nl'];
1704  $a3[0]['operator'] = $tempEl['operator'];
1705  $workArr = array_merge($a1, $a3, $a2);
1706  }
1707  }
1708  }
1709  }
1710 
1717  protected function ‪cleanUpQueryConfig(‪$queryConfig)
1718  {
1719  // Since we don't traverse the array using numeric keys in the upcoming while-loop make sure it's fresh and clean before displaying
1720  if (!empty(‪$queryConfig) && is_array(‪$queryConfig)) {
1721  ksort(‪$queryConfig);
1722  } else {
1723  // queryConfig should never be empty!
1724  if (!isset(‪$queryConfig[0]) || empty(‪$queryConfig[0]['type'])) {
1725  // Make sure queryConfig is an array
1726  ‪$queryConfig = [];
1727  ‪$queryConfig[0] = ['type' => 'FIELD_'];
1728  }
1729  }
1730  // Traverse:
1731  foreach (‪$queryConfig as $key => $conf) {
1732  ‪$fieldName = '';
1733  if (str_starts_with(($conf['type'] ?? ''), 'FIELD_')) {
1734  ‪$fieldName = substr($conf['type'], 6);
1735  $fieldType = $this->fields[‪$fieldName]['type'] ?? '';
1736  } elseif (($conf['type'] ?? '') === 'newlevel') {
1737  $fieldType = $conf['type'];
1738  } else {
1739  $fieldType = 'ignore';
1740  }
1741  switch ($fieldType) {
1742  case 'newlevel':
1743  if (!‪$queryConfig[$key]['nl']) {
1744  ‪$queryConfig[$key]['nl'][0]['type'] = 'FIELD_';
1745  }
1746  ‪$queryConfig[$key]['nl'] = $this->‪cleanUpQueryConfig(‪$queryConfig[$key]['nl']);
1747  break;
1748  case 'userdef':
1749  break;
1750  case 'ignore':
1751  default:
1752  $verifiedName = $this->‪verifyType($fieldName);
1753  ‪$queryConfig[$key]['type'] = 'FIELD_' . $this->‪verifyType($verifiedName);
1754  if ((int)($conf['comparison'] ?? 0) >> 5 !== (int)($this->comp_offsets[$fieldType] ?? 0)) {
1755  $conf['comparison'] = (int)($this->comp_offsets[$fieldType] ?? 0) << 5;
1756  }
1757  ‪$queryConfig[$key]['comparison'] = $this->‪verifyComparison($conf['comparison'] ?? '0', ($conf['negate'] ?? null) ? 1 : 0);
1758  ‪$queryConfig[$key]['inputValue'] = $this->‪cleanInputVal(‪$queryConfig[$key]);
1759  ‪$queryConfig[$key]['inputValue1'] = $this->‪cleanInputVal(‪$queryConfig[$key], '1');
1760  }
1761  }
1762  return ‪$queryConfig;
1763  }
1764 
1773  protected function ‪getFormElements($subLevel = 0, ‪$queryConfig = '', $parent = '')
1774  {
1775  $codeArr = [];
1776  if (!is_array(‪$queryConfig)) {
1778  }
1779  $c = 0;
1780  $arrCount = 0;
1781  $loopCount = 0;
1782  foreach (‪$queryConfig as $key => $conf) {
1783  ‪$fieldName = '';
1784  $subscript = $parent . '[' . $key . ']';
1785  $lineHTML = [];
1786  $lineHTML[] = $this->‪mkOperatorSelect($this->name . $subscript, ($conf['operator'] ?? ''), (bool)$c, ($conf['type'] ?? '') !== 'FIELD_');
1787  if (str_starts_with(($conf['type'] ?? ''), 'FIELD_')) {
1788  ‪$fieldName = substr($conf['type'], 6);
1789  $this->fieldName = ‪$fieldName;
1790  $fieldType = $this->fields[‪$fieldName]['type'] ?? '';
1791  if ((int)($conf['comparison'] ?? 0) >> 5 !== (int)($this->comp_offsets[$fieldType] ?? 0)) {
1792  $conf['comparison'] = (int)($this->comp_offsets[$fieldType] ?? 0) << 5;
1793  }
1794  //nasty nasty...
1795  //make sure queryConfig contains _actual_ comparevalue.
1796  //mkCompSelect don't care, but getQuery does.
1797  ‪$queryConfig[$key]['comparison'] += isset($conf['negate']) - $conf['comparison'] % 2;
1798  } elseif (($conf['type'] ?? '') === 'newlevel') {
1799  $fieldType = $conf['type'];
1800  } else {
1801  $fieldType = 'ignore';
1802  }
1803  $fieldPrefix = htmlspecialchars($this->name . $subscript);
1804  switch ($fieldType) {
1805  case 'ignore':
1806  break;
1807  case 'newlevel':
1808  if (!‪$queryConfig[$key]['nl']) {
1809  ‪$queryConfig[$key]['nl'][0]['type'] = 'FIELD_';
1810  }
1811  $lineHTML[] = '<input type="hidden" name="' . $fieldPrefix . '[type]" value="newlevel">';
1812  $codeArr[$arrCount]['sub'] = $this->‪getFormElements($subLevel + 1, ‪$queryConfig[$key]['nl'], $subscript . '[nl]');
1813  break;
1814  case 'userdef':
1815  $lineHTML[] = '';
1816  break;
1817  case 'date':
1818  $lineHTML[] = '<div class="row row-cols-auto mb-2 mb-sm-0">';
1819  $lineHTML[] = $this->‪makeComparisonSelector($subscript, ‪$fieldName, $conf);
1820  if ($conf['comparison'] === 100 || $conf['comparison'] === 101) {
1821  // between
1822  $lineHTML[] = $this->‪getDateTimePickerField($fieldPrefix . '[inputValue]', $conf['inputValue'], 'date');
1823  $lineHTML[] = $this->‪getDateTimePickerField($fieldPrefix . '[inputValue1]', $conf['inputValue1'], 'date');
1824  } else {
1825  $lineHTML[] = $this->‪getDateTimePickerField($fieldPrefix . '[inputValue]', $conf['inputValue'], 'date');
1826  }
1827  $lineHTML[] = '</div>';
1828  break;
1829  case 'time':
1830  $lineHTML[] = '<div class="row row-cols-auto mb-2 mb-sm-0">';
1831  $lineHTML[] = $this->‪makeComparisonSelector($subscript, ‪$fieldName, $conf);
1832  if ($conf['comparison'] === 100 || $conf['comparison'] === 101) {
1833  // between:
1834  $lineHTML[] = $this->‪getDateTimePickerField($fieldPrefix . '[inputValue]', $conf['inputValue'], 'datetime');
1835  $lineHTML[] = $this->‪getDateTimePickerField($fieldPrefix . '[inputValue1]', $conf['inputValue1'], 'datetime');
1836  } else {
1837  $lineHTML[] = $this->‪getDateTimePickerField($fieldPrefix . '[inputValue]', $conf['inputValue'], 'datetime');
1838  }
1839  $lineHTML[] = '</div>';
1840  break;
1841  case 'multiple':
1842  case 'binary':
1843  case 'relation':
1844  $lineHTML[] = '<div class="row row-cols-auto mb-2 mb-sm-0">';
1845  $lineHTML[] = $this->‪makeComparisonSelector($subscript, ‪$fieldName, $conf);
1846  $lineHTML[] = '<div class="col mb-sm-2">';
1847  if ($conf['comparison'] === 68 || $conf['comparison'] === 69 || $conf['comparison'] === 162 || $conf['comparison'] === 163) {
1848  $lineHTML[] = '<select class="form-select" name="' . $fieldPrefix . '[inputValue][]" multiple="multiple">';
1849  } elseif ($conf['comparison'] === 66 || $conf['comparison'] === 67) {
1850  if (is_array($conf['inputValue'])) {
1851  $conf['inputValue'] = implode(',', $conf['inputValue']);
1852  }
1853  $lineHTML[] = '<input class="form-control t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue'] ?? '') . '" name="' . $fieldPrefix . '[inputValue]">';
1854  } elseif ($conf['comparison'] === 64) {
1855  if (is_array($conf['inputValue'])) {
1856  $conf['inputValue'] = $conf['inputValue'][0];
1857  }
1858  $lineHTML[] = '<select class="form-select t3js-submit-change" name="' . $fieldPrefix . '[inputValue]">';
1859  } else {
1860  $lineHTML[] = '<select class="form-select t3js-submit-change" name="' . $fieldPrefix . '[inputValue]">';
1861  }
1862  if ($conf['comparison'] != 66 && $conf['comparison'] != 67) {
1863  $lineHTML[] = $this->‪makeOptionList($fieldName, $conf, $this->table);
1864  $lineHTML[] = '</select>';
1865  }
1866  $lineHTML[] = '</div>';
1867  $lineHTML[] = '</div>';
1868  break;
1869  case 'boolean':
1870  $lineHTML[] = '<div class="row row-cols-auto mb-2 mb-sm-0">';
1871  $lineHTML[] = $this->‪makeComparisonSelector($subscript, ‪$fieldName, $conf);
1872  $lineHTML[] = '<input type="hidden" value="1" name="' . $fieldPrefix . '[inputValue]">';
1873  $lineHTML[] = '</div>';
1874  break;
1875  default:
1876  $lineHTML[] = '<div class="row row-cols-auto mb-2 mb-sm-0">';
1877  $lineHTML[] = $this->‪makeComparisonSelector($subscript, ‪$fieldName, $conf);
1878  $lineHTML[] = '<div class="col mb-sm-2">';
1879  if ($conf['comparison'] === 37 || $conf['comparison'] === 36) {
1880  // between:
1881  $lineHTML[] = '<input class="form-control t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue'] ?? '') . '" name="' . $fieldPrefix . '[inputValue]">';
1882  $lineHTML[] = '<input class="form-control t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue1'] ?? '') . '" name="' . $fieldPrefix . '[inputValue1]">';
1883  } else {
1884  $lineHTML[] = '<input class="form-control t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue'] ?? '') . '" name="' . $fieldPrefix . '[inputValue]">';
1885  }
1886  $lineHTML[] = '</div>';
1887  $lineHTML[] = '</div>';
1888  }
1889  if ($fieldType !== 'ignore') {
1890  $lineHTML[] = '<div class="row row-cols-auto mb-2">';
1891  $lineHTML[] = '<div class="btn-group">';
1892  $lineHTML[] = $this->‪updateIcon();
1893  if ($loopCount) {
1894  $lineHTML[] = '<button class="btn btn-default" title="Remove condition" name="qG_del' . htmlspecialchars($subscript) . '"><i class="fa fa-trash fa-fw"></i></button>';
1895  }
1896  $lineHTML[] = '<button class="btn btn-default" title="Add condition" name="qG_ins' . htmlspecialchars($subscript) . '"><i class="fa fa-plus fa-fw"></i></button>';
1897  if ($c != 0) {
1898  $lineHTML[] = '<button class="btn btn-default" title="Move up" name="qG_up' . htmlspecialchars($subscript) . '"><i class="fa fa-chevron-up fa-fw"></i></button>';
1899  }
1900  if ($c != 0 && $fieldType !== 'newlevel') {
1901  $lineHTML[] = '<button class="btn btn-default" title="New level" name="qG_nl' . htmlspecialchars($subscript) . '"><i class="fa fa-chevron-right fa-fw"></i></button>';
1902  }
1903  if ($fieldType === 'newlevel') {
1904  $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>';
1905  }
1906  $lineHTML[] = '</div>';
1907  $lineHTML[] = '</div>';
1908  $codeArr[$arrCount]['html'] = implode(LF, $lineHTML);
1909  $codeArr[$arrCount]['query'] = $this->‪getQuerySingle($conf, $c === 0);
1910  $arrCount++;
1911  $c++;
1912  }
1913  $loopCount = 1;
1914  }
1915  $this->queryConfig = ‪$queryConfig;
1916  return $codeArr;
1917  }
1918 
1926  protected function ‪makeComparisonSelector($subscript, ‪$fieldName, $conf)
1927  {
1928  $fieldPrefix = $this->name . $subscript;
1929  $lineHTML = [];
1930  $lineHTML[] = '<div class="col mb-sm-2">';
1931  $lineHTML[] = $this->‪mkTypeSelect($fieldPrefix . '[type]', ‪$fieldName);
1932  $lineHTML[] = '</div>';
1933  $lineHTML[] = '<div class="col mb-sm-2">';
1934  $lineHTML[] = ' <div class="input-group">';
1935  $lineHTML[] = $this->‪mkCompSelect($fieldPrefix . '[comparison]', $conf['comparison'], ($conf['negate'] ?? null) ? 1 : 0);
1936  $lineHTML[] = ' <span class="input-group-addon">';
1937  $lineHTML[] = ' <input type="checkbox" class="checkbox t3js-submit-click"' . (($conf['negate'] ?? null) ? ' checked' : '') . ' name="' . htmlspecialchars($fieldPrefix) . '[negate]">';
1938  $lineHTML[] = ' </span>';
1939  $lineHTML[] = ' </div>';
1940  $lineHTML[] = ' </div>';
1941  return implode(LF, $lineHTML);
1942  }
1943 
1952  protected function ‪makeOptionList(‪$fieldName, $conf, ‪$table)
1953  {
1954  $backendUserAuthentication = $this->‪getBackendUserAuthentication();
1955  $from_table_Arr = [];
1956  $out = [];
1957  $fieldSetup = $this->fields[‪$fieldName];
1958  $languageService = $this->‪getLanguageService();
1959  if ($fieldSetup['type'] === 'multiple') {
1960  $optGroupOpen = false;
1961  foreach (($fieldSetup['items'] ?? []) as $val) {
1962  if (strpos($val[0], 'LLL:') === 0) {
1963  $value = $languageService->sL($val[0]);
1964  } else {
1965  $value = $val[0];
1966  }
1967  if ($val[1] === '--div--') {
1968  if ($optGroupOpen) {
1969  $out[] = '</optgroup>';
1970  }
1971  $optGroupOpen = true;
1972  $out[] = '<optgroup label="' . htmlspecialchars($value) . '">';
1973  } elseif (GeneralUtility::inList($conf['inputValue'], $val[1])) {
1974  $out[] = '<option value="' . htmlspecialchars($val[1]) . '" selected>' . htmlspecialchars($value) . '</option>';
1975  } else {
1976  $out[] = '<option value="' . htmlspecialchars($val[1]) . '">' . htmlspecialchars($value) . '</option>';
1977  }
1978  }
1979  if ($optGroupOpen) {
1980  $out[] = '</optgroup>';
1981  }
1982  }
1983  if ($fieldSetup['type'] === 'binary') {
1984  foreach ($fieldSetup['items'] as $key => $val) {
1985  if (strpos($val[0], 'LLL:') === 0) {
1986  $value = $languageService->sL($val[0]);
1987  } else {
1988  $value = $val[0];
1989  }
1990  if (GeneralUtility::inList($conf['inputValue'], (string)(2 ** $key))) {
1991  $out[] = '<option value="' . 2 ** $key . '" selected>' . htmlspecialchars($value) . '</option>';
1992  } else {
1993  $out[] = '<option value="' . 2 ** $key . '">' . htmlspecialchars($value) . '</option>';
1994  }
1995  }
1996  }
1997  if ($fieldSetup['type'] === 'relation') {
1998  $useTablePrefix = 0;
1999  $dontPrefixFirstTable = 0;
2000  foreach (($fieldSetup['items'] ?? []) as $val) {
2001  if (strpos($val[0], 'LLL:') === 0) {
2002  $value = $languageService->sL($val[0]);
2003  } else {
2004  $value = $val[0];
2005  }
2006  if (GeneralUtility::inList($conf['inputValue'], $val[1])) {
2007  $out[] = '<option value="' . htmlspecialchars($val[1]) . '" selected>' . htmlspecialchars($value) . '</option>';
2008  } else {
2009  $out[] = '<option value="' . htmlspecialchars($val[1]) . '">' . htmlspecialchars($value) . '</option>';
2010  }
2011  }
2012  $allowedFields = $fieldSetup['allowed'] ?? '';
2013  if (str_contains($allowedFields, ',')) {
2014  $from_table_Arr = explode(',', $allowedFields);
2015  $useTablePrefix = 1;
2016  if (!$fieldSetup['prepend_tname']) {
2017  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(‪$table);
2018  $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
2019  $statement = $queryBuilder->select(‪$fieldName)
2020  ->from(‪$table)
2021  ->executeQuery();
2022  while ($row = $statement->fetchAssociative()) {
2023  if (str_contains($row[‪$fieldName], ',')) {
2024  $checkContent = explode(',', $row[‪$fieldName]);
2025  foreach ($checkContent as $singleValue) {
2026  if (!str_contains($singleValue, '_')) {
2027  $dontPrefixFirstTable = 1;
2028  }
2029  }
2030  } else {
2031  $singleValue = $row[‪$fieldName];
2032  if ($singleValue !== '' && !str_contains($singleValue, '_')) {
2033  $dontPrefixFirstTable = 1;
2034  }
2035  }
2036  }
2037  }
2038  } else {
2039  $from_table_Arr[0] = $allowedFields;
2040  }
2041  if (!empty($fieldSetup['prepend_tname'])) {
2042  $useTablePrefix = 1;
2043  }
2044  if (!empty($fieldSetup['foreign_table'])) {
2045  $from_table_Arr[0] = $fieldSetup['foreign_table'];
2046  }
2047  $counter = 0;
2048  $tablePrefix = '';
2049  $outArray = [];
2050  $labelFieldSelect = [];
2051  foreach ($from_table_Arr as $from_table) {
2052  $useSelectLabels = false;
2053  $useAltSelectLabels = false;
2054  if ($useTablePrefix && !$dontPrefixFirstTable && $counter != 1 || $counter === 1) {
2055  $tablePrefix = $from_table . '_';
2056  }
2057  $counter = 1;
2058  if (is_array(‪$GLOBALS['TCA'][$from_table])) {
2059  $labelField = ‪$GLOBALS['TCA'][$from_table]['ctrl']['label'] ?? '';
2060  $altLabelField = ‪$GLOBALS['TCA'][$from_table]['ctrl']['label_alt'] ?? '';
2061  if (‪$GLOBALS['TCA'][$from_table]['columns'][$labelField]['config']['items'] ?? false) {
2062  foreach (‪$GLOBALS['TCA'][$from_table]['columns'][$labelField]['config']['items'] as $labelArray) {
2063  if (strpos($labelArray[0], 'LLL:') === 0) {
2064  $labelFieldSelect[$labelArray[1]] = $languageService->sL($labelArray[0]);
2065  } else {
2066  $labelFieldSelect[$labelArray[1]] = $labelArray[0];
2067  }
2068  }
2069  $useSelectLabels = true;
2070  }
2071  $altLabelFieldSelect = [];
2072  if (‪$GLOBALS['TCA'][$from_table]['columns'][$altLabelField]['config']['items'] ?? false) {
2073  foreach (‪$GLOBALS['TCA'][$from_table]['columns'][$altLabelField]['config']['items'] as $altLabelArray) {
2074  if (strpos($altLabelArray[0], 'LLL:') === 0) {
2075  $altLabelFieldSelect[$altLabelArray[1]] = $languageService->sL($altLabelArray[0]);
2076  } else {
2077  $altLabelFieldSelect[$altLabelArray[1]] = $altLabelArray[0];
2078  }
2079  }
2080  $useAltSelectLabels = true;
2081  }
2082 
2083  if (!($this->tableArray[$from_table] ?? false)) {
2084  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($from_table);
2085  $queryBuilder->getRestrictions()->removeAll();
2086  if (empty($this->settings['show_deleted'])) {
2087  $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
2088  }
2089  $selectFields = ['uid', $labelField];
2090  if ($altLabelField) {
2091  $selectFields = array_merge($selectFields, ‪GeneralUtility::trimExplode(',', $altLabelField, true));
2092  }
2093  $queryBuilder->select(...$selectFields)
2094  ->from($from_table)
2095  ->orderBy('uid');
2096  if (!$backendUserAuthentication->isAdmin()) {
2097  $webMounts = $backendUserAuthentication->returnWebmounts();
2098  $perms_clause = $backendUserAuthentication->getPagePermsClause(‪Permission::PAGE_SHOW);
2099  $webMountPageTree = '';
2100  $webMountPageTreePrefix = '';
2101  foreach ($webMounts as $webMount) {
2102  if ($webMountPageTree) {
2103  $webMountPageTreePrefix = ',';
2104  }
2105  $webMountPageTree .= $webMountPageTreePrefix
2106  . $this->‪getTreeList($webMount, 999, 0, $perms_clause);
2107  }
2108  if ($from_table === 'pages') {
2109  $queryBuilder->where(
2111  $queryBuilder->expr()->in(
2112  'uid',
2113  $queryBuilder->createNamedParameter(
2114  ‪GeneralUtility::intExplode(',', $webMountPageTree),
2115  Connection::PARAM_INT_ARRAY
2116  )
2117  )
2118  );
2119  } else {
2120  $queryBuilder->where(
2121  $queryBuilder->expr()->in(
2122  'pid',
2123  $queryBuilder->createNamedParameter(
2124  ‪GeneralUtility::intExplode(',', $webMountPageTree),
2125  Connection::PARAM_INT_ARRAY
2126  )
2127  )
2128  );
2129  }
2130  }
2131  $statement = $queryBuilder->executeQuery();
2132  $this->tableArray[$from_table] = $statement->fetchAllAssociative();
2133  }
2134 
2135  foreach (($this->tableArray[$from_table] ?? []) as $val) {
2136  if ($useSelectLabels) {
2137  $outArray[$tablePrefix . $val['uid']] = htmlspecialchars($labelFieldSelect[$val[$labelField]]);
2138  } elseif ($val[$labelField]) {
2139  $outArray[$tablePrefix . $val['uid']] = htmlspecialchars($val[$labelField]);
2140  } elseif ($useAltSelectLabels) {
2141  $outArray[$tablePrefix . $val['uid']] = htmlspecialchars($altLabelFieldSelect[$val[$altLabelField]]);
2142  } else {
2143  $outArray[$tablePrefix . $val['uid']] = htmlspecialchars($val[$altLabelField]);
2144  }
2145  }
2146  if (isset($this->settings['options_sortlabel']) && $this->settings['options_sortlabel'] && is_array($outArray)) {
2147  natcasesort($outArray);
2148  }
2149  }
2150  }
2151  foreach ($outArray as $key2 => $val2) {
2152  if (GeneralUtility::inList($conf['inputValue'], $key2)) {
2153  $out[] = '<option value="' . htmlspecialchars($key2) . '" selected>[' . htmlspecialchars($key2) . '] ' . htmlspecialchars($val2) . '</option>';
2154  } else {
2155  $out[] = '<option value="' . htmlspecialchars($key2) . '">[' . htmlspecialchars($key2) . '] ' . htmlspecialchars($val2) . '</option>';
2156  }
2157  }
2158  }
2159  return implode(LF, $out);
2160  }
2161 
2169  protected function ‪printCodeArray($codeArr, $recursionLevel = 0)
2170  {
2171  $out = [];
2172  foreach (array_values($codeArr) as $queryComponent) {
2173  $out[] = '<div class="card">';
2174  $out[] = '<div class="card-body pb-2">';
2175  $out[] = $queryComponent['html'];
2176 
2177  if ($this->enableQueryParts) {
2178  $out[] = '<div class="row row-cols-auto mb-2">';
2179  $out[] = '<div class="col">';
2180  $out[] = '<code class="m-0">';
2181  $out[] = htmlspecialchars($queryComponent['query']);
2182  $out[] = '</code>';
2183  $out[] = '</div>';
2184  $out[] = '</div>';
2185  }
2186  if (is_array($queryComponent['sub'] ?? null)) {
2187  $out[] = '<div class="mb-2">';
2188  $out[] = $this->‪printCodeArray($queryComponent['sub'], $recursionLevel + 1);
2189  $out[] = '</div>';
2190  }
2191  $out[] = '</div>';
2192  $out[] = '</div>';
2193  }
2194  return implode(LF, $out);
2195  }
2196 
2206  protected function ‪mkOperatorSelect(‪$name, $op, $draw, $submit)
2207  {
2208  $out = [];
2209  if ($draw) {
2210  $out[] = '<div class="row row-cols-auto mb-2">';
2211  $out[] = ' <div class="col">';
2212  $out[] = ' <select class="form-select' . ($submit ? ' t3js-submit-change' : '') . '" name="' . htmlspecialchars(‪$name) . '[operator]">';
2213  $out[] = ' <option value="AND"' . (!$op || $op === 'AND' ? ' selected' : '') . '>' . htmlspecialchars($this->lang['AND']) . '</option>';
2214  $out[] = ' <option value="OR"' . ($op === 'OR' ? ' selected' : '') . '>' . htmlspecialchars($this->lang['OR']) . '</option>';
2215  $out[] = ' </select>';
2216  $out[] = ' </div>';
2217  $out[] = '</div>';
2218  } else {
2219  $out[] = '<input type="hidden" value="' . htmlspecialchars($op) . '" name="' . htmlspecialchars(‪$name) . '[operator]">';
2220  }
2221  return implode(LF, $out);
2222  }
2223 
2232  protected function ‪mkTypeSelect(‪$name, ‪$fieldName, $prepend = 'FIELD_')
2233  {
2234  $out = [];
2235  $out[] = '<select class="form-select t3js-submit-change" name="' . htmlspecialchars(‪$name) . '">';
2236  $out[] = '<option value=""></option>';
2237  foreach ($this->fields as $key => $value) {
2238  if (!($value['exclude'] ?? false) || $this->‪getBackendUserAuthentication()->check('non_exclude_fields', $this->table . ':' . $key)) {
2239  $label = $this->fields[$key]['label'];
2240  if ($this->showFieldAndTableNames) {
2241  $label .= ' [' . $key . ']';
2242  }
2243  $out[] = '<option value="' . htmlspecialchars($prepend . $key) . '"' . ($key === ‪$fieldName ? ' selected' : '') . '>' . htmlspecialchars($label) . '</option>';
2244  }
2245  }
2246  $out[] = '</select>';
2247  return implode(LF, $out);
2248  }
2249 
2256  protected function ‪verifyType(‪$fieldName)
2257  {
2258  $first = '';
2259  foreach ($this->fields as $key => $value) {
2260  if (!$first) {
2261  $first = $key;
2262  }
2263  if ($key === ‪$fieldName) {
2264  return $key;
2265  }
2266  }
2267  return $first;
2268  }
2269 
2277  protected function ‪verifyComparison($comparison, $neg)
2278  {
2279  $compOffSet = $comparison >> 5;
2280  $first = -1;
2281  for ($i = 32 * $compOffSet + $neg; $i < 32 * ($compOffSet + 1); $i += 2) {
2282  if ($first === -1) {
2283  $first = $i;
2284  }
2285  if ($i >> 1 === $comparison >> 1) {
2286  return $i;
2287  }
2288  }
2289  return $first;
2290  }
2291 
2299  protected function ‪mkFieldToInputSelect(‪$name, ‪$fieldName)
2300  {
2301  $out = [];
2302  $out[] = '<div class="input-group mb-2">';
2303  $out[] = ' <span class="input-group-btn">';
2304  $out[] = $this->‪updateIcon();
2305  $out[] = ' </span>';
2306  $out[] = ' <input type="text" class="form-control t3js-clearable" value="' . htmlspecialchars(‪$fieldName) . '" name="' . htmlspecialchars(‪$name) . '">';
2307  $out[] = '</div>';
2309  $out[] = '<select class="form-select t3js-addfield" name="_fieldListDummy" size="5" data-field="' . htmlspecialchars(‪$name) . '">';
2310  foreach ($this->fields as $key => $value) {
2311  if (!$value['exclude'] || $this->‪getBackendUserAuthentication()->check('non_exclude_fields', $this->table . ':' . $key)) {
2312  $label = $this->fields[$key]['label'];
2313  if ($this->showFieldAndTableNames) {
2314  $label .= ' [' . $key . ']';
2315  }
2316  $out[] = '<option value="' . htmlspecialchars($key) . '"' . ($key === ‪$fieldName ? ' selected' : '') . '>' . htmlspecialchars($label) . '</option>';
2317  }
2318  }
2319  $out[] = '</select>';
2320  return implode(LF, $out);
2321  }
2322 
2330  protected function ‪mkTableSelect(‪$name, $cur)
2331  {
2332  $out = [];
2333  $out[] = '<select class="form-select t3js-submit-change" name="' . ‪$name . '">';
2334  $out[] = '<option value=""></option>';
2335  foreach (‪$GLOBALS['TCA'] as $tN => $value) {
2336  if ($this->‪getBackendUserAuthentication()->check('tables_select', $tN)) {
2337  $label = $this->‪getLanguageService()->sL(‪$GLOBALS['TCA'][$tN]['ctrl']['title']);
2338  if ($this->showFieldAndTableNames) {
2339  $label .= ' [' . $tN . ']';
2340  }
2341  $out[] = '<option value="' . htmlspecialchars($tN) . '"' . ($tN === $cur ? ' selected' : '') . '>' . htmlspecialchars($label) . '</option>';
2342  }
2343  }
2344  $out[] = '</select>';
2345  return implode(LF, $out);
2346  }
2347 
2356  protected function ‪mkCompSelect(‪$name, $comparison, $neg)
2357  {
2358  $compOffSet = $comparison >> 5;
2359  $out = [];
2360  $out[] = '<select class="form-select t3js-submit-change" name="' . ‪$name . '">';
2361  for ($i = 32 * $compOffSet + $neg; $i < 32 * ($compOffSet + 1); $i += 2) {
2362  if ($this->lang['comparison'][$i . '_'] ?? false) {
2363  $out[] = '<option value="' . $i . '"' . ($i >> 1 === $comparison >> 1 ? ' selected' : '') . '>' . htmlspecialchars($this->lang['comparison'][$i . '_']) . '</option>';
2364  }
2365  }
2366  $out[] = '</select>';
2367  return implode(LF, $out);
2368  }
2369 
2376  protected function ‪getSubscript($arr): array
2377  {
2378  $retArr = [];
2379  while (\is_array($arr)) {
2380  reset($arr);
2381  $key = key($arr);
2382  $retArr[] = $key;
2383  if (isset($arr[$key])) {
2384  $arr = $arr[$key];
2385  } else {
2386  break;
2387  }
2388  }
2389  return $retArr;
2390  }
2391 
2399  protected function ‪getQuery(‪$queryConfig, $pad = '')
2400  {
2401  $qs = '';
2402  // Since we don't traverse the array using numeric keys in the upcoming whileloop make sure it's fresh and clean
2403  ksort(‪$queryConfig);
2404  $first = true;
2405  foreach (‪$queryConfig as $key => $conf) {
2407  switch ($conf['type']) {
2408  case 'newlevel':
2409  $qs .= LF . $pad . trim($conf['operator']) . ' (' . $this->‪getQuery(
2410  ‪$queryConfig[$key]['nl'],
2411  $pad . ' '
2412  ) . LF . $pad . ')';
2413  break;
2414  default:
2415  $qs .= LF . $pad . $this->‪getQuerySingle($conf, $first);
2416  }
2417  $first = false;
2418  }
2419  return $qs;
2420  }
2421 
2428  protected function ‪convertIso8601DatetimeStringToUnixTimestamp(array $conf): array
2429  {
2430  if ($this->‪isDateOfIso8601Format($conf['inputValue'] ?? '')) {
2431  $conf['inputValue'] = strtotime($conf['inputValue']);
2432  if ($this->‪isDateOfIso8601Format($conf['inputValue1'] ?? '')) {
2433  $conf['inputValue1'] = strtotime($conf['inputValue1']);
2434  }
2435  }
2436 
2437  return $conf;
2438  }
2439 
2446  protected function ‪isDateOfIso8601Format($date): bool
2447  {
2448  if (!is_int($date) && !is_string($date)) {
2449  return false;
2450  }
2451  $format = 'Y-m-d\\TH:i:s\\Z';
2452  $formattedDate = \DateTime::createFromFormat($format, (string)$date);
2453  return $formattedDate && $formattedDate->format($format) === $date;
2454  }
2455 
2463  protected function ‪getQuerySingle($conf, $first)
2464  {
2465  $comparison = (int)($conf['comparison'] ?? 0);
2466  $qs = '';
2467  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($this->table);
2468  $prefix = $this->enablePrefix ? $this->table . '.' : '';
2469  if (!$first) {
2470  // Is it OK to insert the AND operator if none is set?
2471  $operator = strtoupper(trim($conf['operator'] ?? ''));
2472  if (!in_array($operator, ['AND', 'OR'], true)) {
2473  $operator = 'AND';
2474  }
2475  $qs .= $operator . ' ';
2476  }
2477  $qsTmp = str_replace('#FIELD#', $prefix . trim(substr($conf['type'], 6)), $this->compSQL[$comparison] ?? '');
2478  $inputVal = $this->‪cleanInputVal($conf);
2479  if ($comparison === 68 || $comparison === 69) {
2480  $inputVal = explode(',', (string)$inputVal);
2481  foreach ($inputVal as $key => $fileName) {
2482  $inputVal[$key] = $queryBuilder->quote($fileName);
2483  }
2484  $inputVal = implode(',', $inputVal);
2485  $qsTmp = str_replace('#VALUE#', $inputVal, $qsTmp);
2486  } elseif ($comparison === 162 || $comparison === 163) {
2487  $inputValArray = explode(',', (string)$inputVal);
2488  $inputVal = 0;
2489  foreach ($inputValArray as $fileName) {
2490  $inputVal += (int)$fileName;
2491  }
2492  $qsTmp = str_replace('#VALUE#', (string)$inputVal, $qsTmp);
2493  } else {
2494  if (is_array($inputVal)) {
2495  $inputVal = $inputVal[0];
2496  }
2497  // @todo This is weired, as it seems that it quotes the value as string and remove
2498  // quotings using the trim() method. Should be investagated/refactored.
2499  $qsTmp = str_replace('#VALUE#', trim($queryBuilder->quote((string)$inputVal), '\''), $qsTmp);
2500  }
2501  if ($comparison === 37 || $comparison === 36 || $comparison === 66 || $comparison === 67 || $comparison === 100 || $comparison === 101) {
2502  // between:
2503  $inputVal = $this->‪cleanInputVal($conf, '1');
2504  // @todo This is weired, as it seems that it quotes the value as string and remove
2505  // quotings using the trim() method. Should be investagated/refactored.
2506  $qsTmp = str_replace('#VALUE1#', trim($queryBuilder->quote((string)$inputVal), '\''), $qsTmp);
2507  }
2508  $qs .= trim((string)$qsTmp);
2509  return $qs;
2510  }
2511 
2519  protected function ‪cleanInputVal($conf, $suffix = '')
2520  {
2521  $comparison = (int)($conf['comparison'] ?? 0);
2522  if ($comparison >> 5 === 0 || ($comparison === 32 || $comparison === 33 || $comparison === 64 || $comparison === 65 || $comparison === 66 || $comparison === 67 || $comparison === 96 || $comparison === 97)) {
2523  $inputVal = $conf['inputValue' . $suffix] ?? null;
2524  } elseif ($comparison === 39 || $comparison === 38) {
2525  // in list:
2526  $inputVal = implode(',', ‪GeneralUtility::intExplode(',', ($conf['inputValue' . $suffix] ?? '')));
2527  } elseif ($comparison === 68 || $comparison === 69 || $comparison === 162 || $comparison === 163) {
2528  // in list:
2529  if (is_array($conf['inputValue' . $suffix] ?? false)) {
2530  $inputVal = implode(',', $conf['inputValue' . $suffix]);
2531  } elseif ($conf['inputValue' . $suffix] ?? false) {
2532  $inputVal = $conf['inputValue' . $suffix];
2533  } else {
2534  $inputVal = 0;
2535  }
2536  } elseif (!is_array($conf['inputValue' . $suffix] ?? null) && strtotime($conf['inputValue' . $suffix] ?? '')) {
2537  $inputVal = $conf['inputValue' . $suffix];
2538  } elseif (!is_array($conf['inputValue' . $suffix] ?? null) && ‪MathUtility::canBeInterpretedAsInteger($conf['inputValue' . $suffix] ?? null)) {
2539  $inputVal = (int)$conf['inputValue' . $suffix];
2540  } else {
2541  // TODO: Six eyes looked at this code and nobody understood completely what is going on here and why we
2542  // fallback to float casting, the whole class smells like it needs a refactoring.
2543  $inputVal = (float)($conf['inputValue' . $suffix] ?? 0.0);
2544  }
2545  return $inputVal;
2546  }
2547 
2553  protected function ‪updateIcon()
2554  {
2555  return '<button class="btn btn-default" title="Update" name="just_update"><i class="fa fa-refresh fa-fw"></i></button>';
2556  }
2557 
2563  protected function ‪getLabelCol()
2564  {
2565  return ‪$GLOBALS['TCA'][‪$this->table]['ctrl']['label'];
2566  }
2567 
2575  protected function ‪makeSelectorTable($modSettings, $enableList = 'table,fields,query,group,order,limit')
2576  {
2577  $out = [];
2578  $enableArr = explode(',', $enableList);
2579  $userTsConfig = $this->‪getBackendUserAuthentication()->getTSConfig();
2580 
2581  // Make output
2582  if (in_array('table', $enableArr) && !($userTsConfig['mod.']['dbint.']['disableSelectATable'] ?? false)) {
2583  $out[] = '<div class="form-group">';
2584  $out[] = '<label for="SET[queryTable]">Select a table:</label>';
2585  $out[] = '<div class="row row-cols-auto">';
2586  $out[] = '<div class="col">';
2587  $out[] = $this->‪mkTableSelect('SET[queryTable]', $this->table);
2588  $out[] = '</div>';
2589  $out[] = '</div>';
2590  $out[] = '</div>';
2591  }
2592  if ($this->table) {
2593  // Init fields:
2594  $this->‪setAndCleanUpExternalLists('queryFields', $modSettings['queryFields'] ?? '', 'uid,' . $this->‪getLabelCol());
2595  $this->‪setAndCleanUpExternalLists('queryGroup', $modSettings['queryGroup'] ?? '');
2596  $this->‪setAndCleanUpExternalLists('queryOrder', ($modSettings['queryOrder'] ?? '') . ',' . ($modSettings['queryOrder2'] ?? ''));
2597  // Limit:
2598  $this->extFieldLists['queryLimit'] = $modSettings['queryLimit'] ?? '';
2599  if (!$this->extFieldLists['queryLimit']) {
2600  $this->extFieldLists['queryLimit'] = 100;
2601  }
2602  $parts = ‪GeneralUtility::intExplode(',', $this->extFieldLists['queryLimit']);
2603  $limitBegin = 0;
2604  $limitLength = (int)($this->extFieldLists['queryLimit'] ?? 0);
2605  if ($parts[1] ?? null) {
2606  $limitBegin = (int)$parts[0];
2607  $limitLength = (int)$parts[1];
2608  }
2609  $this->extFieldLists['queryLimit'] = implode(',', array_slice($parts, 0, 2));
2610  // Insert Descending parts
2611  if ($this->extFieldLists['queryOrder']) {
2612  $descParts = explode(',', ($modSettings['queryOrderDesc'] ?? '') . ',' . ($modSettings['queryOrder2Desc'] ?? ''));
2613  $orderParts = explode(',', $this->extFieldLists['queryOrder']);
2614  $reList = [];
2615  foreach ($orderParts as $kk => $vv) {
2616  $reList[] = $vv . ($descParts[$kk] ? ' DESC' : '');
2617  }
2618  $this->extFieldLists['queryOrder_SQL'] = implode(',', $reList);
2619  }
2620  // Query Generator:
2621  $this->‪procesData(($modSettings['queryConfig'] ?? false) ? unserialize($modSettings['queryConfig'] ?? '', ['allowed_classes' => false]) : []);
2622  $this->queryConfig = $this->‪cleanUpQueryConfig($this->queryConfig);
2623  $this->enableQueryParts = (bool)($modSettings['search_query_smallparts'] ?? false);
2624  $codeArr = $this->‪getFormElements();
2625  $queryCode = $this->‪printCodeArray($codeArr);
2626  if (in_array('fields', $enableArr) && !($userTsConfig['mod.']['dbint.']['disableSelectFields'] ?? false)) {
2627  $out[] = '<div class="form-group form-group-with-button-addon">';
2628  $out[] = ' <label for="SET[queryFields]">Select fields:</label>';
2629  $out[] = $this->‪mkFieldToInputSelect('SET[queryFields]', $this->extFieldLists['queryFields']);
2630  $out[] = '</div>';
2631  }
2632  if (in_array('query', $enableArr) && !($userTsConfig['mod.']['dbint.']['disableMakeQuery'] ?? false)) {
2633  $out[] = '<div class="form-group">';
2634  $out[] = ' <label>Make Query:</label>';
2635  $out[] = $queryCode;
2636  $out[] = '</div>';
2637  }
2638  if (in_array('group', $enableArr) && !($userTsConfig['mod.']['dbint.']['disableGroupBy'] ?? false)) {
2639  $out[] = '<div class="form-group">';
2640  $out[] = '<label for="SET[queryGroup]">Group By:</label>';
2641  $out[] = '<div class="row row-cols-auto">';
2642  $out[] = '<div class="col">';
2643  $out[] = $this->‪mkTypeSelect('SET[queryGroup]', $this->extFieldLists['queryGroup'], '');
2644  $out[] = '</div>';
2645  $out[] = '</div>';
2646  $out[] = '</div>';
2647  }
2648  if (in_array('order', $enableArr) && !($userTsConfig['mod.']['dbint.']['disableOrderBy'] ?? false)) {
2649  $orderByArr = explode(',', $this->extFieldLists['queryOrder']);
2650  $orderBy = [];
2651  $orderBy[] = '<div class="row row-cols-auto align-items-center">';
2652  $orderBy[] = '<div class="col">';
2653  $orderBy[] = $this->‪mkTypeSelect('SET[queryOrder]', $orderByArr[0], '');
2654  $orderBy[] = '</div>';
2655  $orderBy[] = '<div class="col mt-2">';
2656  $orderBy[] = '<div class="form-check">';
2657  $orderBy[] = BackendUtility::getFuncCheck(0, 'SET[queryOrderDesc]', $modSettings['queryOrderDesc'] ?? '', '', '', 'id="checkQueryOrderDesc"');
2658  $orderBy[] = '<label class="form-check-label" for="checkQueryOrderDesc">Descending</label>';
2659  $orderBy[] = '</div>';
2660  $orderBy[] = '</div>';
2661  $orderBy[] = '</div>';
2662 
2663  if ($orderByArr[0]) {
2664  $orderBy[] = '<div class="row row-cols-auto align-items-center mt-2">';
2665  $orderBy[] = '<div class="col">';
2666  $orderBy[] = '<div class="input-group">';
2667  $orderBy[] = $this->‪mkTypeSelect('SET[queryOrder2]', $orderByArr[1] ?? '', '');
2668  $orderBy[] = '</div>';
2669  $orderBy[] = '</div>';
2670  $orderBy[] = '<div class="col mt-2">';
2671  $orderBy[] = '<div class="form-check">';
2672  $orderBy[] = BackendUtility::getFuncCheck(0, 'SET[queryOrder2Desc]', $modSettings['queryOrder2Desc'] ?? false, '', '', 'id="checkQueryOrder2Desc"');
2673  $orderBy[] = '<label class="form-check-label" for="checkQueryOrder2Desc">Descending</label>';
2674  $orderBy[] = '</div>';
2675  $orderBy[] = '</div>';
2676  $orderBy[] = '</div>';
2677  }
2678  $out[] = '<div class="form-group">';
2679  $out[] = ' <label>Order By:</label>';
2680  $out[] = implode(LF, $orderBy);
2681  $out[] = '</div>';
2682  }
2683  if (in_array('limit', $enableArr) && !($userTsConfig['mod.']['dbint.']['disableLimit'] ?? false)) {
2684  $limit = [];
2685  $limit[] = '<div class="input-group">';
2686  $limit[] = ' <span class="input-group-btn">';
2687  $limit[] = $this->‪updateIcon();
2688  $limit[] = ' </span>';
2689  $limit[] = ' <input type="text" class="form-control" value="' . htmlspecialchars($this->extFieldLists['queryLimit']) . '" name="SET[queryLimit]" id="queryLimit">';
2690  $limit[] = '</div>';
2691 
2692  $prevLimit = $limitBegin - $limitLength < 0 ? 0 : $limitBegin - $limitLength;
2693  $prevButton = '';
2694  $nextButton = '';
2695 
2696  if ($limitBegin) {
2697  $prevButton = '<input type="button" class="btn btn-default" value="previous ' . htmlspecialchars((string)$limitLength) . '" data-value="' . htmlspecialchars($prevLimit . ',' . $limitLength) . '">';
2698  }
2699  if (!$limitLength) {
2700  $limitLength = 100;
2701  }
2702 
2703  $nextLimit = $limitBegin + $limitLength;
2704  if ($nextLimit < 0) {
2705  $nextLimit = 0;
2706  }
2707  if ($nextLimit) {
2708  $nextButton = '<input type="button" class="btn btn-default" value="next ' . htmlspecialchars((string)$limitLength) . '" data-value="' . htmlspecialchars($nextLimit . ',' . $limitLength) . '">';
2709  }
2710 
2711  $out[] = '<div class="form-group">';
2712  $out[] = ' <label>Limit:</label>';
2713  $out[] = ' <div class="row row-cols-auto">';
2714  $out[] = ' <div class="col">';
2715  $out[] = implode(LF, $limit);
2716  $out[] = ' </div>';
2717  $out[] = ' <div class="col">';
2718  $out[] = ' <div class="btn-group t3js-limit-submit">';
2719  $out[] = $prevButton;
2720  $out[] = $nextButton;
2721  $out[] = ' </div>';
2722  $out[] = ' </div>';
2723  $out[] = ' <div class="col">';
2724  $out[] = ' <div class="btn-group t3js-limit-submit">';
2725  $out[] = ' <input type="button" class="btn btn-default" data-value="10" value="10">';
2726  $out[] = ' <input type="button" class="btn btn-default" data-value="20" value="20">';
2727  $out[] = ' <input type="button" class="btn btn-default" data-value="50" value="50">';
2728  $out[] = ' <input type="button" class="btn btn-default" data-value="100" value="100">';
2729  $out[] = ' </div>';
2730  $out[] = ' </div>';
2731  $out[] = ' </div>';
2732  $out[] = '</div>';
2733  }
2734  }
2735  return implode(LF, $out);
2736  }
2737 
2744  protected function ‪getSelectQuery($qString = ''): string
2745  {
2746  $backendUserAuthentication = $this->‪getBackendUserAuthentication();
2747  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->table);
2748  $queryBuilder->getRestrictions()->removeAll();
2749  if (empty($this->settings['show_deleted'])) {
2750  $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
2751  }
2752  $deleteField = ‪$GLOBALS['TCA'][‪$this->table]['ctrl']['delete'] ?? '';
2754  ',',
2755  $this->extFieldLists['queryFields']
2756  . ',pid'
2757  . ($deleteField ? ',' . $deleteField : '')
2758  );
2759  $queryBuilder->select(...‪$fieldList)
2760  ->from($this->table);
2761 
2762  if ($this->extFieldLists['queryGroup']) {
2763  $queryBuilder->groupBy(...‪QueryHelper::parseGroupBy($this->extFieldLists['queryGroup']));
2764  }
2765  if ($this->extFieldLists['queryOrder']) {
2766  foreach (‪QueryHelper::parseOrderBy($this->extFieldLists['queryOrder_SQL']) as $orderPair) {
2767  [‪$fieldName, $order] = $orderPair;
2768  $queryBuilder->addOrderBy(‪$fieldName, $order);
2769  }
2770  }
2771  if ($this->extFieldLists['queryLimit']) {
2772  // Explode queryLimit to fetch the limit and a possible offset
2773  $parts = ‪GeneralUtility::intExplode(',', $this->extFieldLists['queryLimit']);
2774  if ($parts[1] ?? null) {
2775  // Offset and limit are given
2776  $queryBuilder->setFirstResult($parts[0]);
2777  $queryBuilder->setMaxResults($parts[1]);
2778  } else {
2779  // Only the limit is given
2780  $queryBuilder->setMaxResults($parts[0]);
2781  }
2782  }
2783 
2784  if (!$backendUserAuthentication->isAdmin()) {
2785  $webMounts = $backendUserAuthentication->returnWebmounts();
2786  $perms_clause = $backendUserAuthentication->getPagePermsClause(‪Permission::PAGE_SHOW);
2787  $webMountPageTree = '';
2788  $webMountPageTreePrefix = '';
2789  foreach ($webMounts as $webMount) {
2790  if ($webMountPageTree) {
2791  $webMountPageTreePrefix = ',';
2792  }
2793  $webMountPageTree .= $webMountPageTreePrefix
2794  . $this->‪getTreeList($webMount, 999, 0, $perms_clause);
2795  }
2796  // createNamedParameter() is not used here because the SQL fragment will only include
2797  // the :dcValueX placeholder when the query is returned as a string. The value for the
2798  // placeholder would be lost in the process.
2799  if ($this->table === 'pages') {
2800  $queryBuilder->where(
2802  $queryBuilder->expr()->in(
2803  'uid',
2804  ‪GeneralUtility::intExplode(',', $webMountPageTree)
2805  )
2806  );
2807  } else {
2808  $queryBuilder->where(
2809  $queryBuilder->expr()->in(
2810  'pid',
2811  ‪GeneralUtility::intExplode(',', $webMountPageTree)
2812  )
2813  );
2814  }
2815  }
2816  if (!$qString) {
2817  $qString = $this->‪getQuery($this->queryConfig);
2818  }
2819  $queryBuilder->andWhere(‪QueryHelper::stripLogicalOperatorPrefix($qString));
2820 
2821  return $queryBuilder->getSQL();
2822  }
2823 
2831  protected function ‪getDateTimePickerField(‪$name, $timestamp, $type)
2832  {
2833  $value = strtotime($timestamp) ? date(‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'] . ' ' . ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'], (int)strtotime($timestamp)) : '';
2834  $id = ‪StringUtility::getUniqueId('dt_');
2835  $html = [];
2836  $html[] = '<div class="col mb-sm-2">';
2837  $html[] = ' <div class="input-group" id="' . $id . '-wrapper">';
2838  $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 . '">';
2839  $html[] = ' <input name="' . htmlspecialchars(‪$name) . '" value="' . htmlspecialchars($timestamp) . '" type="hidden">';
2840  $html[] = ' <button class="btn btn-default" type="button" data-global-event="click" data-action-focus="#' . $id . '">';
2841  $html[] = ' <span class="fa fa-calendar"></span>';
2842  $html[] = ' </button>';
2843  $html[] = ' </div>';
2844  $html[] = '</div>';
2845  return implode(LF, $html);
2846  }
2847 
2849  {
2850  return ‪$GLOBALS['BE_USER'];
2851  }
2852 
2853  protected function ‪getLanguageService(): LanguageService
2854  {
2855  return ‪$GLOBALS['LANG'];
2856  }
2857 }
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\$iconFactory
‪IconFactory $iconFactory
Definition: QueryGenerator.php:68
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\__construct
‪__construct(array $settings, array $menuItems, string $moduleName)
Definition: QueryGenerator.php:270
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\init
‪init($name, $table, $fieldList='', array $settings=[])
Definition: QueryGenerator.php:1433
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\cleanInputVal
‪string int float null cleanInputVal($conf, $suffix='')
Definition: QueryGenerator.php:2497
‪TYPO3\CMS\Core\Imaging\Icon\SIZE_SMALL
‪const SIZE_SMALL
Definition: Icon.php:30
‪TYPO3\CMS\Core\Database\Query\QueryHelper\parseOrderBy
‪static array array[] parseOrderBy(string $input)
Definition: QueryHelper.php:44
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static list< string > trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
Definition: GeneralUtility.php:999
‪TYPO3\CMS\Core\Database\Connection\PARAM_INT
‪const PARAM_INT
Definition: Connection.php:49
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger($var)
Definition: MathUtility.php:74
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\$table
‪string $table
Definition: QueryGenerator.php:227
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\$compSQL
‪array $compSQL
Definition: QueryGenerator.php:149
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\makeSelectorTable
‪string makeSelectorTable($modSettings, $enableList='table, fields, query, group, order, limit')
Definition: QueryGenerator.php:2553
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\makeFieldList
‪string makeFieldList()
Definition: QueryGenerator.php:1401
‪TYPO3\CMS\Core\Imaging\Icon
Definition: Icon.php:26
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\mkTableSelect
‪string mkTableSelect($name, $cur)
Definition: QueryGenerator.php:2308
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\procesStoreControl
‪string procesStoreControl()
Definition: QueryGenerator.php:665
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\$moduleName
‪string $moduleName
Definition: QueryGenerator.php:84
‪TYPO3\CMS\Core\Utility\CsvUtility\csvValues
‪static string csvValues(array $row, string $delim=',', string $quote='"', int $type = self::TYPE_REMOVE_CONTROLS)
Definition: CsvUtility.php:100
‪TYPO3\CMS\Core\Database\Query\QueryHelper\parseGroupBy
‪static array string[] parseGroupBy(string $input)
Definition: QueryHelper.php:102
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\printCodeArray
‪string printCodeArray($codeArr, $recursionLevel=0)
Definition: QueryGenerator.php:2147
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\verifyComparison
‪int verifyComparison($comparison, $neg)
Definition: QueryGenerator.php:2255
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\resultRowDisplay
‪string resultRowDisplay($row, $conf, $table)
Definition: QueryGenerator.php:880
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\resultRowTitles
‪string resultRowTitles($row, $conf)
Definition: QueryGenerator.php:1357
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\$lang
‪array $lang
Definition: QueryGenerator.php:88
‪TYPO3\CMS\Core\Imaging\IconFactory
Definition: IconFactory.php:34
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\search
‪string search()
Definition: QueryGenerator.php:378
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator
Definition: QueryGenerator.php:49
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\procesData
‪procesData($qC=[])
Definition: QueryGenerator.php:1586
‪TYPO3\CMS\Core\Messaging\FlashMessageRendererResolver
Definition: FlashMessageRendererResolver.php:33
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\$noDownloadB
‪int $noDownloadB
Definition: QueryGenerator.php:56
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\$showFieldAndTableNames
‪bool $showFieldAndTableNames
Definition: QueryGenerator.php:268
‪TYPO3\CMS\Core\Database\Connection\PARAM_STR
‪const PARAM_STR
Definition: Connection.php:54
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\getSelectQuery
‪string getSelectQuery($qString='')
Definition: QueryGenerator.php:2722
‪TYPO3\CMS\Core\Type\Bitmask\Permission
Definition: Permission.php:26
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\setFormName
‪setFormName($formName)
Definition: QueryGenerator.php:485
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility
Definition: ExtensionManagementUtility.php:43
‪TYPO3\CMS\Lowlevel\Database
Definition: QueryGenerator.php:16
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\getSubscript
‪array getSubscript($arr)
Definition: QueryGenerator.php:2354
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\cleanStoreQueryConfigs
‪array cleanStoreQueryConfigs($storeQueryConfigs, $storeArray)
Definition: QueryGenerator.php:567
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\$queryConfig
‪array $queryConfig
Definition: QueryGenerator.php:249
‪TYPO3\CMS\Core\Database\Query\QueryHelper
Definition: QueryHelper.php:32
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\mkCompSelect
‪string mkCompSelect($name, $comparison, $neg)
Definition: QueryGenerator.php:2334
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\makeOptionList
‪string makeOptionList($fieldName, $conf, $table)
Definition: QueryGenerator.php:1930
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\mkFieldToInputSelect
‪string mkFieldToInputSelect($name, $fieldName)
Definition: QueryGenerator.php:2277
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\convertIso8601DatetimeStringToUnixTimestamp
‪array convertIso8601DatetimeStringToUnixTimestamp(array $conf)
Definition: QueryGenerator.php:2406
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\$settings
‪array $settings
Definition: QueryGenerator.php:76
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\$name
‪string $name
Definition: QueryGenerator.php:221
‪TYPO3\CMS\Backend\Routing\UriBuilder
Definition: UriBuilder.php:40
‪TYPO3\CMS\Core\Utility\HttpUtility\buildQueryString
‪static string buildQueryString(array $parameters, string $prependCharacter='', bool $skipEmptyParameters=false)
Definition: HttpUtility.php:171
‪TYPO3\CMS\Core\Utility\DebugUtility\viewArray
‪static string viewArray($array_in)
Definition: DebugUtility.php:205
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\getBackendUserAuthentication
‪getBackendUserAuthentication()
Definition: QueryGenerator.php:2826
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\$hookArray
‪array $hookArray
Definition: QueryGenerator.php:60
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\setAndCleanUpExternalLists
‪setAndCleanUpExternalLists($name, $list, $force='')
Definition: QueryGenerator.php:1569
‪TYPO3\CMS\Core\Utility\CsvUtility
Definition: CsvUtility.php:24
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\$fields
‪array $fields
Definition: QueryGenerator.php:239
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication
Definition: BackendUserAuthentication.php:62
‪TYPO3\CMS\Core\Type\Bitmask\Permission\PAGE_SHOW
‪const PAGE_SHOW
Definition: Permission.php:35
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\saveQueryInAction
‪bool saveQueryInAction($uid)
Definition: QueryGenerator.php:598
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\csvValues
‪string csvValues($row, $delim=',', $quote='"', $conf = [], $table = '')
Definition: QueryGenerator.php:861
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\$fieldList
‪string $fieldList
Definition: QueryGenerator.php:233
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\mkOperatorSelect
‪string mkOperatorSelect($name, $op, $draw, $submit)
Definition: QueryGenerator.php:2184
‪TYPO3\CMS\Core\Utility\DebugUtility
Definition: DebugUtility.php:27
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\getProcessedValueExtra
‪string getProcessedValueExtra($table, $fieldName, $fieldValue, $conf, $splitString)
Definition: QueryGenerator.php:977
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\getDateTimePickerField
‪string getDateTimePickerField($name, $timestamp, $type)
Definition: QueryGenerator.php:2809
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\$extFieldLists
‪array $extFieldLists
Definition: QueryGenerator.php:243
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\cleanUpQueryConfig
‪array cleanUpQueryConfig($queryConfig)
Definition: QueryGenerator.php:1695
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\loadStoreQueryConfigs
‪array loadStoreQueryConfigs($storeQueryConfigs, $storeIndex, $writeArray)
Definition: QueryGenerator.php:649
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\getTreeList
‪string getTreeList($id, $depth, $begin=0, $permsClause='')
Definition: QueryGenerator.php:1094
‪$output
‪$output
Definition: annotationChecker.php:121
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\getQuerySingle
‪string getQuerySingle($conf, $first)
Definition: QueryGenerator.php:2441
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\isDateOfIso8601Format
‪bool isDateOfIso8601Format($date)
Definition: QueryGenerator.php:2424
‪TYPO3\CMS\Core\Database\Connection
Definition: Connection.php:38
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\$menuItems
‪array $menuItems
Definition: QueryGenerator.php:80
‪TYPO3\CMS\Core\Messaging\AbstractMessage\INFO
‪const INFO
Definition: AbstractMessage.php:28
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\mkTypeSelect
‪string mkTypeSelect($name, $fieldName, $prepend='FIELD_')
Definition: QueryGenerator.php:2210
‪TYPO3\CMS\Core\Utility\StringUtility\getUniqueId
‪static string getUniqueId($prefix='')
Definition: StringUtility.php:128
‪TYPO3\CMS\Core\Messaging\FlashMessage
Definition: FlashMessage.php:26
‪TYPO3\CMS\Core\Database\Query\QueryHelper\stripLogicalOperatorPrefix
‪static string stripLogicalOperatorPrefix(string $constraint)
Definition: QueryHelper.php:171
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\$fieldName
‪string $fieldName
Definition: QueryGenerator.php:261
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\form
‪string form()
Definition: QueryGenerator.php:285
‪TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction
Definition: DeletedRestriction.php:28
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\$enablePrefix
‪bool $enablePrefix
Definition: QueryGenerator.php:253
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\queryMaker
‪string queryMaker()
Definition: QueryGenerator.php:303
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\getLanguageService
‪getLanguageService()
Definition: QueryGenerator.php:2831
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\updateIcon
‪string updateIcon()
Definition: QueryGenerator.php:2531
‪TYPO3\CMS\Core\Utility\GeneralUtility\intExplode
‪static int[] intExplode($delimiter, $string, $removeEmptyValues=false, $limit=0)
Definition: GeneralUtility.php:927
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:22
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\getLabelCol
‪string getLabelCol()
Definition: QueryGenerator.php:2541
‪TYPO3\CMS\Core\Utility\HttpUtility
Definition: HttpUtility.php:22
‪TYPO3\CMS\Core\Localization\LanguageService
Definition: LanguageService.php:42
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\verifyType
‪string verifyType($fieldName)
Definition: QueryGenerator.php:2234
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\addToStoreQueryConfigs
‪addToStoreQueryConfigs(array $storeQueryConfigs, int $index)
Definition: QueryGenerator.php:582
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\$tableArray
‪array $tableArray
Definition: QueryGenerator.php:72
‪TYPO3\CMS\Core\Utility\StringUtility
Definition: StringUtility.php:22
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\renderNoResultsFoundMessage
‪renderNoResultsFoundMessage()
Definition: QueryGenerator.php:1388
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\getQuery
‪string getQuery($queryConfig, $pad='')
Definition: QueryGenerator.php:2377
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\makeStoreControl
‪string makeStoreControl()
Definition: QueryGenerator.php:495
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\isLoaded
‪static bool isLoaded($key)
Definition: ExtensionManagementUtility.php:114
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\initStoreArray
‪array initStoreArray()
Definition: QueryGenerator.php:548
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\getQueryResultCode
‪array getQueryResultCode($type, array $dataRows, $table)
Definition: QueryGenerator.php:776
‪TYPO3\CMS\Core\Messaging\FlashMessageService
Definition: FlashMessageService.php:27
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\getFormElements
‪array getFormElements($subLevel=0, $queryConfig='', $parent='')
Definition: QueryGenerator.php:1751
‪TYPO3\CMS\Core\Messaging\AbstractMessage\ERROR
‪const ERROR
Definition: AbstractMessage.php:31
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\$comp_offsets
‪array $comp_offsets
Definition: QueryGenerator.php:206
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\$enableQueryParts
‪bool $enableQueryParts
Definition: QueryGenerator.php:257
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\$storeList
‪string $storeList
Definition: QueryGenerator.php:52
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\$formName
‪string $formName
Definition: QueryGenerator.php:64
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\makeValueList
‪string makeValueList($fieldName, $fieldValue, $conf, $table, $splitString)
Definition: QueryGenerator.php:1147
‪TYPO3\CMS\Core\Database\Connection\PARAM_LOB
‪const PARAM_LOB
Definition: Connection.php:59
‪TYPO3\CMS\Lowlevel\Database\QueryGenerator\makeComparisonSelector
‪string makeComparisonSelector($subscript, $fieldName, $conf)
Definition: QueryGenerator.php:1904