‪TYPO3CMS  ‪main
DatabaseIntegrityController.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use Doctrine\DBAL\Exception as DBALException;
21 use Doctrine\DBAL\Platforms\MariaDBPlatform as DoctrineMariaDBPlatform;
22 use Doctrine\DBAL\Platforms\MySQLPlatform as DoctrineMySQLPlatform;
23 use Doctrine\DBAL\Platforms\PostgreSQLPlatform as DoctrinePostgreSQLPlatform;
24 use Doctrine\DBAL\Types\Type;
25 use Doctrine\DBAL\Types\Types;
26 use Psr\Http\Message\ResponseInterface;
27 use Psr\Http\Message\ServerRequestInterface;
33 use TYPO3\CMS\Backend\Utility\BackendUtility;
42 use TYPO3\CMS\Core\Imaging\IconSize;
61 
67 #[AsController]
69 {
73  protected array ‪$MOD_MENU = [];
74 
78  protected array ‪$MOD_SETTINGS = [];
79 
80  protected string ‪$formName = '';
81  protected string ‪$moduleName = 'system_dbint';
82 
87  protected bool ‪$showFieldAndTableNames = false;
88  protected array ‪$hookArray = [];
89  protected string ‪$table = '';
90  protected bool ‪$enablePrefix = false;
91  protected int ‪$noDownloadB = 0;
92  protected array ‪$tableArray = [];
93  protected array ‪$queryConfig = [];
94  protected array ‪$extFieldLists = [];
95  protected array ‪$fields = [];
96  protected string ‪$storeList = 'search_query_smallparts,search_result_labels,labels_noprefix,show_deleted,queryConfig,queryTable,queryFields,queryLimit,queryOrder,queryOrderDesc,queryOrder2,queryOrder2Desc,queryGroup,search_query_makeQuery';
97  protected bool ‪$enableQueryParts = false;
98  protected array ‪$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  protected string ‪$fieldName = '';
157  protected string ‪$name = '';
158  protected string ‪$fieldList;
159  protected array ‪$comp_offsets = [
160  'text' => 0,
161  'number' => 1,
162  'multiple' => 2,
163  'relation' => 2,
164  'date' => 3,
165  'time' => 3,
166  'boolean' => 4,
167  'binary' => 5,
168  ];
169  protected array ‪$compSQL = [
170  // Type = text offset = 0
171  '0' => '#FIELD# LIKE \'%#VALUE#%\'',
172  '1' => '#FIELD# NOT LIKE \'%#VALUE#%\'',
173  '2' => '#FIELD# LIKE \'#VALUE#%\'',
174  '3' => '#FIELD# NOT LIKE \'#VALUE#%\'',
175  '4' => '#FIELD# LIKE \'%#VALUE#\'',
176  '5' => '#FIELD# NOT LIKE \'%#VALUE#\'',
177  '6' => '#FIELD# = \'#VALUE#\'',
178  '7' => '#FIELD# != \'#VALUE#\'',
179  // Type = number, offset = 32
180  '32' => '#FIELD# = \'#VALUE#\'',
181  '33' => '#FIELD# != \'#VALUE#\'',
182  '34' => '#FIELD# > #VALUE#',
183  '35' => '#FIELD# < #VALUE#',
184  '36' => '#FIELD# >= #VALUE# AND #FIELD# <= #VALUE1#',
185  '37' => 'NOT (#FIELD# >= #VALUE# AND #FIELD# <= #VALUE1#)',
186  '38' => '#FIELD# IN (#VALUE#)',
187  '39' => '#FIELD# NOT IN (#VALUE#)',
188  '40' => '(#FIELD# & #VALUE#)=#VALUE#',
189  '41' => '(#FIELD# & #VALUE#)!=#VALUE#',
190  '42' => '(#FIELD# | #VALUE#)=#VALUE#',
191  '43' => '(#FIELD# | #VALUE#)!=#VALUE#',
192  // Type = multiple, relation, offset = 64
193  '64' => '#FIELD# = \'#VALUE#\'',
194  '65' => '#FIELD# != \'#VALUE#\'',
195  '66' => '#FIELD# LIKE \'%#VALUE#%\' AND #FIELD# LIKE \'%#VALUE1#%\'',
196  '67' => '(#FIELD# NOT LIKE \'%#VALUE#%\' OR #FIELD# NOT LIKE \'%#VALUE1#%\')',
197  '68' => '#FIELD# IN (#VALUE#)',
198  '69' => '#FIELD# NOT IN (#VALUE#)',
199  '70' => '(#FIELD# & #VALUE#)=#VALUE#',
200  '71' => '(#FIELD# & #VALUE#)!=#VALUE#',
201  '72' => '(#FIELD# | #VALUE#)=#VALUE#',
202  '73' => '(#FIELD# | #VALUE#)!=#VALUE#',
203  // Type = date, offset = 32
204  '96' => '#FIELD# = \'#VALUE#\'',
205  '97' => '#FIELD# != \'#VALUE#\'',
206  '98' => '#FIELD# > #VALUE#',
207  '99' => '#FIELD# < #VALUE#',
208  '100' => '#FIELD# >= #VALUE# AND #FIELD# <= #VALUE1#',
209  '101' => 'NOT (#FIELD# >= #VALUE# AND #FIELD# <= #VALUE1#)',
210  '102' => '(#FIELD# & #VALUE#)=#VALUE#',
211  '103' => '(#FIELD# & #VALUE#)!=#VALUE#',
212  '104' => '(#FIELD# | #VALUE#)=#VALUE#',
213  '105' => '(#FIELD# | #VALUE#)!=#VALUE#',
214  // Type = boolean, offset = 128
215  '128' => '#FIELD# = \'1\'',
216  '129' => '#FIELD# != \'1\'',
217  // Type = binary = 160
218  '160' => '#FIELD# = \'#VALUE#\'',
219  '161' => '#FIELD# != \'#VALUE#\'',
220  '162' => '(#FIELD# & #VALUE#)=#VALUE#',
221  '163' => '(#FIELD# & #VALUE#)=0',
222  ];
223 
224  public function ‪__construct(
225  protected ‪IconFactory $iconFactory,
226  protected readonly ‪UriBuilder $uriBuilder,
227  protected readonly ‪ModuleTemplateFactory $moduleTemplateFactory,
228  protected readonly ‪PlatformHelper $platformHelper,
229  ) {}
230 
231  public function ‪handleRequest(ServerRequestInterface $request): ResponseInterface
232  {
233  $languageService = $this->‪getLanguageService();
234 
235  $this->‪menuConfig($request);
236  $moduleTemplate = $this->moduleTemplateFactory->create($request);
237  $this->‪setUpDocHeader($moduleTemplate);
238 
239  $title = $languageService->sL('LLL:EXT:lowlevel/Resources/Private/Language/locallang.xlf:module.dbint.title');
240  switch ($this->MOD_SETTINGS['function']) {
241  case 'search':
242  $moduleTemplate->setTitle($title, $languageService->sL('LLL:EXT:lowlevel/Resources/Private/Language/locallang.xlf:fullSearch'));
243  return $this->‪searchAction($moduleTemplate, $request);
244  case 'records':
245  $moduleTemplate->setTitle($title, $languageService->sL('LLL:EXT:lowlevel/Resources/Private/Language/locallang.xlf:recordStatistics'));
246  return $this->‪recordStatisticsAction($moduleTemplate, $request);
247  case 'relations':
248  $moduleTemplate->setTitle($title, $languageService->sL('LLL:EXT:lowlevel/Resources/Private/Language/locallang.xlf:databaseRelations'));
249  return $this->‪relationsAction($moduleTemplate);
250  default:
251  $moduleTemplate->setTitle($title, $languageService->sL('LLL:EXT:lowlevel/Resources/Private/Language/locallang.xlf:manageRefIndex'));
252  return $this->‪referenceIndexAction($moduleTemplate, $request);
253  }
254  }
255 
259  protected function ‪menuConfig(ServerRequestInterface $request): void
260  {
261  ‪$lang = $this->‪getLanguageService();
262  $parsedBody = $request->getParsedBody();
263  $queryParams = $request->getQueryParams();
264 
265  // MENU-ITEMS:
266  // If array, then it's a selector box menu
267  // If empty string it's just a variable, that'll be saved.
268  // Values NOT in this array will not be saved in the settings-array for the module.
269  $this->MOD_MENU = [
270  'function' => [
271  'refindex' => htmlspecialchars(‪$lang->sL('LLL:EXT:lowlevel/Resources/Private/Language/locallang.xlf:manageRefIndex')),
272  'records' => htmlspecialchars(‪$lang->sL('LLL:EXT:lowlevel/Resources/Private/Language/locallang.xlf:recordStatistics')),
273  'relations' => htmlspecialchars(‪$lang->sL('LLL:EXT:lowlevel/Resources/Private/Language/locallang.xlf:databaseRelations')),
274  'search' => htmlspecialchars(‪$lang->sL('LLL:EXT:lowlevel/Resources/Private/Language/locallang.xlf:fullSearch')),
275  ],
276  'search' => [
277  'raw' => htmlspecialchars(‪$lang->sL('LLL:EXT:lowlevel/Resources/Private/Language/locallang.xlf:rawSearch')),
278  'query' => htmlspecialchars(‪$lang->sL('LLL:EXT:lowlevel/Resources/Private/Language/locallang.xlf:advancedQuery')),
279  ],
280  'search_query_smallparts' => '',
281  'search_result_labels' => '',
282  'labels_noprefix' => '',
283  'options_sortlabel' => '',
284  'show_deleted' => '',
285  'queryConfig' => '',
286  // Current query
287  'queryTable' => '',
288  // Current table
289  'queryFields' => '',
290  // Current tableFields
291  'queryLimit' => '',
292  // Current limit
293  'queryOrder' => '',
294  // Current Order field
295  'queryOrderDesc' => '',
296  // Current Order field descending flag
297  'queryOrder2' => '',
298  // Current Order2 field
299  'queryOrder2Desc' => '',
300  // Current Order2 field descending flag
301  'queryGroup' => '',
302  // Current Group field
303  'storeArray' => '',
304  // Used to store the available Query config memory banks
305  'storeQueryConfigs' => '',
306  // Used to store the available Query configs in memory
307  'search_query_makeQuery' => [
308  'all' => htmlspecialchars(‪$lang->sL('LLL:EXT:lowlevel/Resources/Private/Language/locallang.xlf:selectRecords')),
309  'count' => htmlspecialchars(‪$lang->sL('LLL:EXT:lowlevel/Resources/Private/Language/locallang.xlf:countResults')),
310  'explain' => htmlspecialchars(‪$lang->sL('LLL:EXT:lowlevel/Resources/Private/Language/locallang.xlf:explainQuery')),
311  'csv' => htmlspecialchars(‪$lang->sL('LLL:EXT:lowlevel/Resources/Private/Language/locallang.xlf:csvExport')),
312  ],
313  'sword' => '',
314  ];
315 
316  // EXPLAIN is no ANSI SQL, for now this is only executed on mysql
317  $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionByName(‪ConnectionPool::DEFAULT_CONNECTION_NAME);
318  $platform = $connection->getDatabasePlatform();
319  if (!($platform instanceof DoctrineMariaDBPlatform || $platform instanceof DoctrineMySQLPlatform)) {
320  unset($this->MOD_MENU['search_query_makeQuery']['explain']);
321  }
322 
323  // CLEAN SETTINGS
324  $OLD_MOD_SETTINGS = BackendUtility::getModuleData($this->MOD_MENU, [], $this->moduleName, 'ses');
325  $this->MOD_SETTINGS = BackendUtility::getModuleData($this->MOD_MENU, $parsedBody['SET'] ?? $queryParams['SET'] ?? [], $this->moduleName, 'ses');
326  ‪$queryConfig = $parsedBody['queryConfig'] ?? $queryParams['queryConfig'] ?? false;
327  if (‪$queryConfig) {
328  $this->MOD_SETTINGS = BackendUtility::getModuleData($this->MOD_MENU, ['queryConfig' => serialize(‪$queryConfig)], $this->moduleName, 'ses');
329  }
330  $setLimitToStart = false;
331  foreach ($OLD_MOD_SETTINGS as $key => $val) {
332  if (str_starts_with($key, 'query') && $this->MOD_SETTINGS[$key] != $val && $key !== 'queryLimit' && $key !== 'use_listview') {
333  $setLimitToStart = true;
334  $addConditionCheck = (bool)($parsedBody['qG_ins'] ?? $queryParams['qG_ins'] ?? false);
335  if ($key === 'queryTable' && !$addConditionCheck) {
336  $this->MOD_SETTINGS['queryConfig'] = '';
337  }
338  }
339  if ($key === 'queryTable' && $this->MOD_SETTINGS[$key] != $val) {
340  $this->MOD_SETTINGS['queryFields'] = '';
341  }
342  }
343  if ($setLimitToStart) {
344  $currentLimit = explode(',', $this->MOD_SETTINGS['queryLimit'] ?? '');
345  if (!empty($currentLimit[1] ?? 0)) {
346  $this->MOD_SETTINGS['queryLimit'] = '0,' . $currentLimit[1];
347  } else {
348  $this->MOD_SETTINGS['queryLimit'] = '0';
349  }
350  $this->MOD_SETTINGS = BackendUtility::getModuleData($this->MOD_MENU, $this->MOD_SETTINGS, $this->moduleName, 'ses');
351  }
352  }
353 
357  protected function ‪setUpDocHeader(‪ModuleTemplate $moduleTemplate): void
358  {
359  $buttonBar = $moduleTemplate->‪getDocHeaderComponent()->getButtonBar();
360  $shortCutButton = $buttonBar->makeShortcutButton()
361  ->setRouteIdentifier($this->moduleName)
362  ->setDisplayName($this->MOD_MENU['function'][$this->MOD_SETTINGS['function']])
363  ->setArguments([
364  'SET' => [
365  'function' => $this->MOD_SETTINGS['function'] ?? '',
366  'search' => $this->MOD_SETTINGS['search'] ?? 'raw',
367  'search_query_makeQuery' => $this->MOD_SETTINGS['search_query_makeQuery'] ?? '',
368  ],
369  ]);
370  $buttonBar->addButton($shortCutButton, ‪ButtonBar::BUTTON_POSITION_RIGHT, 2);
371 
372  $menu = $moduleTemplate->‪getDocHeaderComponent()->getMenuRegistry()->makeMenu();
373  $menu->setIdentifier('DatabaseJumpMenu');
374  foreach ($this->MOD_MENU['function'] as $controller => $title) {
375  $item = $menu
376  ->makeMenuItem()
377  ->setHref(
378  (string)$this->uriBuilder->buildUriFromRoute(
379  $this->moduleName,
380  [
381  'id' => 0,
382  'SET' => [
383  'function' => $controller,
384  ],
385  ]
386  )
387  )
388  ->setTitle($title);
389  if ($controller === $this->MOD_SETTINGS['function']) {
390  $item->setActive(true);
391  }
392  $menu->addMenuItem($item);
393  }
394  $moduleTemplate->‪getDocHeaderComponent()->getMenuRegistry()->addMenu($menu);
395  }
396 
400  protected function ‪referenceIndexAction(‪ModuleTemplate $view, ServerRequestInterface $request): ResponseInterface
401  {
402  $isUpdate = $request->getParsedBody()['update'] ?? false;
403  $isCheckOnly = (bool)($request->getParsedBody()['checkOnly'] ?? false);
404  $referenceIndexResult = [];
405  if ($isUpdate || $isCheckOnly) {
406  $referenceIndexResult = GeneralUtility::makeInstance(ReferenceIndex::class)->updateIndex($isCheckOnly);
407  }
408  $readmeLocation = ‪ExtensionManagementUtility::extPath('lowlevel', 'README.rst');
409  $view->‪assignMultiple([
410  'ReadmeLink' => ‪PathUtility::getAbsoluteWebPath($readmeLocation),
411  'ReadmeLocation' => $readmeLocation,
412  'binaryPath' => ‪ExtensionManagementUtility::extPath('core', 'bin/typo3'),
413  'referenceIndexResult' => $referenceIndexResult,
414  ]);
415 
416  return $view->‪renderResponse('ReferenceIndex');
417  }
418 
422  protected function ‪searchAction(‪ModuleTemplate $view, ServerRequestInterface $request): ResponseInterface
423  {
424  ‪$lang = $this->‪getLanguageService();
425  $this->showFieldAndTableNames = $this->‪getBackendUserAuthentication()->shallDisplayDebugInformation();
426  $searchMode = $this->MOD_SETTINGS['search'];
427  $submenu = '';
428  $submenu .= '<div class="form-row">';
429  $submenu .= '<div class="form-group">' . $this->‪getDropdownMenu('SET[search]', $searchMode, $this->MOD_MENU['search'], $request) . '</div>';
430  if ($searchMode === 'query') {
431  $submenu .= '<div class="form-group">' . $this->‪getDropdownMenu('SET[search_query_makeQuery]', $this->MOD_SETTINGS['search_query_makeQuery'], $this->MOD_MENU['search_query_makeQuery'], $request) . '</div>';
432  }
433  $submenu .= '</div>';
434  if ($searchMode === 'query') {
435  $submenu .= '<div class="form-group">';
436  $submenu .= '<div class="form-check">' . $this->‪getFuncCheck('SET[search_query_smallparts]', $this->MOD_SETTINGS['search_query_smallparts'] ?? '', $request, 'id="checkSearch_query_smallparts"') . '<label class="form-check-label" for="checkSearch_query_smallparts">' . ‪$lang->sL('LLL:EXT:lowlevel/Resources/Private/Language/locallang.xlf:showSQL') . '</label></div>';
437  $submenu .= '<div class="form-check">' . $this->‪getFuncCheck('SET[search_result_labels]', $this->MOD_SETTINGS['search_result_labels'] ?? '', $request, 'id="checkSearch_result_labels"') . '<label class="form-check-label" for="checkSearch_result_labels">' . ‪$lang->sL('LLL:EXT:lowlevel/Resources/Private/Language/locallang.xlf:useFormattedStrings') . '</label></div>';
438  $submenu .= '<div class="form-check">' . $this->‪getFuncCheck('SET[labels_noprefix]', $this->MOD_SETTINGS['labels_noprefix'] ?? '', $request, 'id="checkLabels_noprefix"') . '<label class="form-check-label" for="checkLabels_noprefix">' . ‪$lang->sL('LLL:EXT:lowlevel/Resources/Private/Language/locallang.xlf:dontUseOrigValues') . '</label></div>';
439  $submenu .= '<div class="form-check">' . $this->‪getFuncCheck('SET[options_sortlabel]', $this->MOD_SETTINGS['options_sortlabel'] ?? '', $request, 'id="checkOptions_sortlabel"') . '<label class="form-check-label" for="checkOptions_sortlabel">' . ‪$lang->sL('LLL:EXT:lowlevel/Resources/Private/Language/locallang.xlf:sortOptions') . '</label></div>';
440  $submenu .= '<div class="form-check">' . $this->‪getFuncCheck('SET[show_deleted]', $this->MOD_SETTINGS['show_deleted'] ?? 0, $request, 'id="checkShow_deleted"') . '<label class="form-check-label" for="checkShow_deleted">' . ‪$lang->sL('LLL:EXT:lowlevel/Resources/Private/Language/locallang.xlf:showDeleted') . '</label></div>';
441  $submenu .= '</div>';
442  }
443  $view->‪assign('submenu', $submenu);
444  $view->‪assign('searchMode', $searchMode);
445  switch ($searchMode) {
446  case 'query':
447  $view->‪assign('queryMaker', $this->‪queryMaker($request));
448  break;
449  case 'raw':
450  default:
451  $view->‪assign('sword', (string)($this->MOD_SETTINGS['sword'] ?? ''));
452  $view->‪assign('results', $this->‪search($request));
453  $view->‪assign('isSearching', $request->getMethod() === 'POST');
454  }
455 
456  return $view->‪renderResponse('CustomSearch');
457  }
458 
459  protected function ‪queryMaker(ServerRequestInterface $request): string
460  {
461  ‪$output = '';
462  $this->hookArray = ‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['t3lib_fullsearch'] ?? [];
463  $msg = $this->‪procesStoreControl($request);
464  $userTsConfig = $this->‪getBackendUserAuthentication()->getTSConfig();
465  if (!($userTsConfig['mod.']['dbint.']['disableStoreControl'] ?? false)) {
466  ‪$output .= '<h2 class="headline-spaced">Load/Save Query</h2>';
467  ‪$output .= $this->‪makeStoreControl();
468  ‪$output .= $msg;
469  }
470  // Query Maker:
471  $this->‪init('queryConfig', $this->MOD_SETTINGS['queryTable'] ?? '', '', $this->MOD_SETTINGS);
472  ‪$output .= '<h2>Make query</h2>';
473  ‪$output .= $this->‪makeSelectorTable($this->MOD_SETTINGS, $request);
474  $mQ = $this->MOD_SETTINGS['search_query_makeQuery'] ?? '';
475  // Make form elements:
476  if ($this->table && is_array(‪$GLOBALS['TCA'][$this->table])) {
477  if ($mQ) {
478  // Show query
479  $this->enablePrefix = true;
480  $queryString = $this->‪getQuery($this->queryConfig);
481  $selectQueryString = $this->‪getSelectQuery($queryString);
482  $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($this->table);
483  $platform = $connection->getDatabasePlatform();
484 
485  $isConnectionMysql = ($platform instanceof DoctrineMariaDBPlatform || $platform instanceof DoctrineMySQLPlatform);
486  $fullQueryString = '';
487  try {
488  if ($mQ === 'explain' && $isConnectionMysql) {
489  // EXPLAIN is no ANSI SQL, for now this is only executed on mysql
490  // @todo: Move away from getSelectQuery() or model differently
491  $fullQueryString = 'EXPLAIN ' . $selectQueryString;
492  $dataRows = $connection->executeQuery('EXPLAIN ' . $selectQueryString)->fetchAllAssociative();
493  } elseif ($mQ === 'count') {
494  $queryBuilder = $connection->createQueryBuilder();
495  $queryBuilder->getRestrictions()->removeAll();
496  if (empty($this->MOD_SETTINGS['show_deleted'])) {
497  $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
498  }
499  $queryBuilder->count('*')
500  ->from($this->table)
501  ->where(‪QueryHelper::stripLogicalOperatorPrefix($queryString));
502  $fullQueryString = $queryBuilder->getSQL();
503  $dataRows = [$queryBuilder->executeQuery()->fetchOne()];
504  } else {
505  $fullQueryString = $selectQueryString;
506  $dataRows = $connection->executeQuery($selectQueryString)->fetchAllAssociative();
507  }
508  if (!($userTsConfig['mod.']['dbint.']['disableShowSQLQuery'] ?? false)) {
509  ‪$output .= '<h2>SQL query</h2>';
510  ‪$output .= '<pre class="language-sql">';
511  ‪$output .= '<code class="language-sql">';
512  ‪$output .= htmlspecialchars($fullQueryString);
513  ‪$output .= '</code>';
514  ‪$output .= '</pre>';
515  }
516  $cPR = $this->‪getQueryResultCode($mQ, $dataRows, $this->table, $request);
517  if ($cPR['header'] ?? null) {
518  ‪$output .= '<h2>' . $cPR['header'] . '</h2>';
519  }
520  if ($cPR['content'] ?? null) {
521  ‪$output .= $cPR['content'];
522  }
523  } catch (DBALException $e) {
524  if (!($userTsConfig['mod.']['dbint.']['disableShowSQLQuery'] ?? false)) {
525  ‪$output .= '<h2>SQL query</h2>';
526  ‪$output .= '<pre class="language-sql">';
527  ‪$output .= '<code class="language-sql">';
528  ‪$output .= htmlspecialchars($fullQueryString);
529  ‪$output .= '</code>';
530  ‪$output .= '</pre>';
531  }
532  ‪$output .= '<h2>SQL error</h2>';
533  ‪$output .= '<div class="alert alert-danger">';
534  ‪$output .= '<p class="alert-message"><strong>Error:</strong> ' . htmlspecialchars($e->getMessage()) . '</p>';
535  ‪$output .= '</div>';
536  }
537  }
538  }
539 
540  return ‪$output;
541  }
542 
543  protected function ‪getSelectQuery(string $qString = ''): string
544  {
545  $backendUserAuthentication = $this->‪getBackendUserAuthentication();
546  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->table);
547  $queryBuilder->getRestrictions()->removeAll();
548  if (empty($this->MOD_SETTINGS['show_deleted'])) {
549  $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
550  }
551  $deleteField = ‪$GLOBALS['TCA'][‪$this->table]['ctrl']['delete'] ?? '';
553  ',',
554  $this->extFieldLists['queryFields']
555  . ',pid'
556  . ($deleteField ? ',' . $deleteField : '')
557  );
558  $queryBuilder->select(...‪$fieldList)
559  ->from($this->table);
560 
561  if ($this->extFieldLists['queryGroup']) {
562  $queryBuilder->groupBy(...‪QueryHelper::parseGroupBy($this->extFieldLists['queryGroup']));
563  }
564  if ($this->extFieldLists['queryOrder']) {
565  foreach (‪QueryHelper::parseOrderBy($this->extFieldLists['queryOrder_SQL']) as $orderPair) {
566  [‪$fieldName, $order] = $orderPair;
567  $queryBuilder->addOrderBy(‪$fieldName, $order);
568  }
569  }
570  $queryLimit = (string)($this->extFieldLists['queryLimit'] ?? '');
571  if ($queryLimit) {
572  // Explode queryLimit to fetch the limit and a possible offset
573  $parts = ‪GeneralUtility::intExplode(',', $queryLimit);
574  if ($parts[1] ?? null) {
575  // Offset and limit are given
576  $queryBuilder->setFirstResult($parts[0]);
577  $queryBuilder->setMaxResults($parts[1]);
578  } else {
579  // Only the limit is given
580  $queryBuilder->setMaxResults($parts[0]);
581  }
582  }
583 
584  if (!$backendUserAuthentication->isAdmin()) {
585  $webMounts = $backendUserAuthentication->returnWebmounts();
586  $perms_clause = $backendUserAuthentication->getPagePermsClause(‪Permission::PAGE_SHOW);
587  $webMountPageTree = '';
588  $webMountPageTreePrefix = '';
589  foreach ($webMounts as $webMount) {
590  if ($webMountPageTree) {
591  $webMountPageTreePrefix = ',';
592  }
593  $webMountPageTree .= $webMountPageTreePrefix
594  . $this->‪getTreeList($webMount, 999, 0, $perms_clause);
595  }
596  // createNamedParameter() is not used here because the SQL fragment will only include
597  // the :dcValueX placeholder when the query is returned as a string. The value for the
598  // placeholder would be lost in the process.
599  if ($this->table === 'pages') {
600  $queryBuilder->where(
602  $queryBuilder->expr()->in(
603  'uid',
604  ‪GeneralUtility::intExplode(',', $webMountPageTree)
605  )
606  );
607  } else {
608  $queryBuilder->where(
609  $queryBuilder->expr()->in(
610  'pid',
611  ‪GeneralUtility::intExplode(',', $webMountPageTree)
612  )
613  );
614  }
615  }
616  if (!$qString) {
617  $qString = $this->‪getQuery($this->queryConfig);
618  }
619  $queryBuilder->andWhere(‪QueryHelper::stripLogicalOperatorPrefix($qString));
620 
621  return $queryBuilder->getSQL();
622  }
623 
629  protected function ‪getTreeList(int $id, int $depth, int $begin = 0, string $permsClause = ''): string
630  {
631  if ($id < 0) {
632  $id = abs($id);
633  }
634  if ($begin === 0) {
635  $theList = (string)$id;
636  } else {
637  $theList = '';
638  }
639  if ($id && $depth > 0) {
640  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
641  $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
642  $statement = $queryBuilder->select('uid')
643  ->from('pages')
644  ->where(
645  $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($id, ‪Connection::PARAM_INT)),
646  $queryBuilder->expr()->eq('sys_language_uid', 0)
647  )
648  ->orderBy('uid');
649  if ($permsClause !== '') {
650  $queryBuilder->andWhere(‪QueryHelper::stripLogicalOperatorPrefix($permsClause));
651  }
652  $statement = $queryBuilder->executeQuery();
653  while ($row = $statement->fetchAssociative()) {
654  if ($begin <= 0) {
655  $theList .= ',' . $row['uid'];
656  }
657  if ($depth > 1) {
658  $theSubList = $this->‪getTreeList($row['uid'], $depth - 1, $begin - 1, $permsClause);
659  if (!empty($theList) && !empty($theSubList) && ($theSubList[0] !== ',')) {
660  $theList .= ',';
661  }
662  $theList .= $theSubList;
663  }
664  }
665  }
666 
667  return $theList;
668  }
669 
674  protected function ‪getQueryResultCode(string $type, array $dataRows, string ‪$table, ServerRequestInterface $request): array
675  {
676  $out = '';
677  $cPR = [];
678  switch ($type) {
679  case 'count':
680  $cPR['header'] = 'Count';
681  $cPR['content'] = '<p><strong>' . (int)$dataRows[0] . '</strong> records selected.</p>';
682  break;
683  case 'all':
684  $rowArr = [];
685  $dataRow = null;
686  foreach ($dataRows as $dataRow) {
687  $rowArr[] = $this->‪resultRowDisplay($dataRow, ‪$GLOBALS['TCA'][‪$table], ‪$table, $request);
688  }
689  if (is_array($this->hookArray['beforeResultTable'] ?? false)) {
690  foreach ($this->hookArray['beforeResultTable'] as $_funcRef) {
691  $out .= GeneralUtility::callUserFunction($_funcRef, $this->MOD_SETTINGS);
692  }
693  }
694  if (!empty($rowArr)) {
695  $cPR['header'] = 'Result';
696  $out .= '<div class="table-fit">';
697  $out .= '<table class="table table-striped table-hover">';
698  $out .= $this->‪resultRowTitles((array)$dataRow, ‪$GLOBALS['TCA'][‪$table]) . implode(LF, $rowArr);
699  $out .= '</table>';
700  $out .= '</div>';
701  } else {
703  }
704 
705  $cPR['content'] = $out;
706  break;
707  case 'csv':
708  $rowArr = [];
709  $first = 1;
710  foreach ($dataRows as $dataRow) {
711  if ($first) {
712  $rowArr[] = $this->‪csvValues(array_keys($dataRow));
713  $first = 0;
714  }
715  $rowArr[] = $this->‪csvValues($dataRow, ',', '"', ‪$GLOBALS['TCA'][‪$table], ‪$table);
716  }
717  if (!empty($rowArr)) {
718  $cPR['header'] = 'Result';
719  $out .= '<div class="form-group">';
720  $out .= '<textarea class="form-control" name="whatever" rows="20" class="font-monospace" style="width:100%">';
721  $out .= htmlspecialchars(implode(LF, $rowArr));
722  $out .= '</textarea>';
723  $out .= '</div>';
724  if (!$this->noDownloadB) {
725  $out .= '<button class="btn btn-default" type="submit" name="download_file" value="Click to download file">';
726  $out .= $this->iconFactory->getIcon('actions-file-csv-download', IconSize::SMALL)->render();
727  $out .= ' Click to download file';
728  $out .= '</button>';
729  }
730  // Downloads file:
731  // @todo: args. routing anyone?
732  if ($request->getParsedBody()['download_file'] ?? false) {
733  $filename = 'TYPO3_' . ‪$table . '_export_' . date('dmy-Hi') . '.csv';
734  $mimeType = 'application/octet-stream';
735  header('Content-Type: ' . $mimeType);
736  header('Content-Disposition: attachment; filename=' . $filename);
737  echo implode(CRLF, $rowArr);
738  die;
739  }
740  } else {
742  }
743  $cPR['content'] = $out;
744  break;
745  case 'explain':
746  default:
747  foreach ($dataRows as $dataRow) {
748  $out .= ‪DebugUtility::viewArray($dataRow);
749  }
750  $cPR['header'] = 'Explain SQL query';
751  $cPR['content'] = $out;
752  }
753 
754  return $cPR;
755  }
756 
757  protected function ‪csvValues(array $row, string $delim = ',', string $quote = '"', array $conf = [], string ‪$table = ''): string
758  {
759  $valueArray = $row;
760  if (($this->MOD_SETTINGS['search_result_labels'] ?? false) && ‪$table) {
761  foreach ($valueArray as $key => $val) {
762  $valueArray[$key] = $this->‪getProcessedValueExtra($table, $key, (string)$val, $conf, ';');
763  }
764  }
765 
766  return ‪CsvUtility::csvValues($valueArray, $delim, $quote);
767  }
768 
772  protected function ‪resultRowTitles(?array $row, array $conf): string
773  {
774  $languageService = $this->‪getLanguageService();
775  $tableHeader = [];
776  // Start header row
777  $tableHeader[] = '<thead><tr>';
778  // Iterate over given columns
779  foreach ($row ?? [] as ‪$fieldName => $fieldValue) {
780  if (‪GeneralUtility::inList($this->MOD_SETTINGS['queryFields'] ?? '', ‪$fieldName)
781  || !($this->MOD_SETTINGS['queryFields'] ?? false)
782  && ‪$fieldName !== 'pid'
783  && ‪$fieldName !== 'deleted'
784  ) {
785  if ($this->MOD_SETTINGS['search_result_labels'] ?? false) {
786  $title = $languageService->sL(($conf['columns'][‪$fieldName]['label'] ?? false) ?: ‪$fieldName);
787  } else {
788  $title = $languageService->sL(‪$fieldName);
789  }
790  $tableHeader[] = '<th>' . htmlspecialchars($title) . '</th>';
791  }
792  }
793  // Add empty icon column
794  $tableHeader[] = '<th></th>';
795  // Close header row
796  $tableHeader[] = '</tr></thead>';
797 
798  return implode(LF, $tableHeader);
799  }
800 
801  protected function ‪resultRowDisplay(array $row, array $conf, string ‪$table, ServerRequestInterface $request): string
802  {
803  $languageService = $this->‪getLanguageService();
804  $out = '<tr>';
805  foreach ($row as ‪$fieldName => $fieldValue) {
806  if (‪GeneralUtility::inList($this->MOD_SETTINGS['queryFields'] ?? '', ‪$fieldName)
807  || !($this->MOD_SETTINGS['queryFields'] ?? false)
808  && ‪$fieldName !== 'pid'
809  && ‪$fieldName !== 'deleted'
810  ) {
811  if ($this->MOD_SETTINGS['search_result_labels'] ?? false) {
812  $fVnew = $this->‪getProcessedValueExtra($table, ‪$fieldName, (string)$fieldValue, $conf, '<br />');
813  } else {
814  $fVnew = htmlspecialchars((string)$fieldValue);
815  }
816  $out .= '<td>' . $fVnew . '</td>';
817  }
818  }
819  $out .= '<td class="col-control">';
820 
821  if (!($row['deleted'] ?? false)) {
822  $out .= '<div class="btn-group" role="group">';
823  ‪$url = (string)$this->uriBuilder->buildUriFromRoute('record_edit', [
824  'edit' => [
825  ‪$table => [
826  $row['uid'] => 'edit',
827  ],
828  ],
829  'returnUrl' => $request->getAttribute('normalizedParams')->getRequestUri()
830  . ‪HttpUtility::buildQueryString(['SET' => $request->getParsedBody()['SET'] ?? []], '&'),
831  ]);
832  $out .= '<a class="btn btn-default" href="' . htmlspecialchars(‪$url) . '">'
833  . $this->iconFactory->getIcon('actions-open', IconSize::SMALL)->render()
834  . '</a>';
835  $out .= '</div><div class="btn-group" role="group">';
836  $out .= sprintf(
837  '<a class="btn btn-default" href="#" data-dispatch-action="%s" data-dispatch-args-list="%s">%s</a>',
838  'TYPO3.InfoWindow.showItem',
839  htmlspecialchars(‪$table . ',' . $row['uid']),
840  $this->iconFactory->getIcon('actions-document-info', IconSize::SMALL)->render()
841  );
842  $out .= '</div>';
843  } else {
844  $out .= '<div class="btn-group" role="group">';
845  $out .= '<a class="btn btn-default" href="' . htmlspecialchars((string)$this->uriBuilder->buildUriFromRoute('tce_db', [
846  'cmd' => [
847  ‪$table => [
848  $row['uid'] => [
849  'undelete' => 1,
850  ],
851  ],
852  ],
853  'redirect' => (string)$this->uriBuilder->buildUriFromRoute($this->moduleName),
854  ])) . '" title="' . htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_t3lib_fullsearch.xlf:undelete_only')) . '">';
855  $out .= $this->iconFactory->getIcon('actions-edit-restore', IconSize::SMALL)->render() . '</a>';
856  $out .= '</div>';
857  }
858  $_params = [‪$table => $row];
859  if (is_array($this->hookArray['additionalButtons'] ?? false)) {
860  foreach ($this->hookArray['additionalButtons'] as $_funcRef) {
861  $out .= GeneralUtility::callUserFunction($_funcRef, $_params);
862  }
863  }
864  $out .= '</td></tr>';
865 
866  return $out;
867  }
868 
869  protected function ‪getProcessedValueExtra(string ‪$table, string ‪$fieldName, string $fieldValue, array $conf, string $splitString): string
870  {
871  $out = '';
872  ‪$fields = [];
873  $user = $this->‪getBackendUserAuthentication();
874  if ($user->user['lang'] ?? false) {
875  $locale = GeneralUtility::makeInstance(Locales::class)->createLocale($user->user['lang']);
876  } else {
877  $locale = new ‪Locale();
878  }
879  // Analysing the fields in the table.
880  if (is_array(‪$GLOBALS['TCA'][‪$table] ?? null)) {
881  $fC = ‪$GLOBALS['TCA'][‪$table]['columns'][‪$fieldName] ?? null;
882  ‪$fields = $fC['config'] ?? [];
883  ‪$fields['exclude'] = $fC['exclude'] ?? '';
884  if (is_array($fC) && ($fC['label'] ?? false)) {
885  ‪$fields['label'] = preg_replace('/:$/', '', trim($this->‪getLanguageService()->sL($fC['label'])));
886  switch (‪$fields['type']) {
887  case 'input':
888  if (‪GeneralUtility::inList(‪$fields['eval'] ?? '', 'year')) {
889  ‪$fields['type'] = 'number';
890  } else {
891  ‪$fields['type'] = 'text';
892  }
893  break;
894  case 'number':
895  // Empty on purpose, we have to keep the type "number".
896  // Falling back to the "default" case would set the type to "text"
897  break;
898  case 'datetime':
899  if (!in_array(‪$fields['dbType'] ?? '', ‪QueryHelper::getDateTimeTypes(), true)) {
900  ‪$fields['type'] = 'number';
901  } elseif (‪$fields['dbType'] === 'time') {
902  ‪$fields['type'] = 'time';
903  } else {
904  ‪$fields['type'] = 'date';
905  }
906  break;
907  case 'check':
908  if (!(‪$fields['items'] ?? false)) {
909  ‪$fields['type'] = 'boolean';
910  } else {
911  ‪$fields['type'] = 'binary';
912  }
913  break;
914  case 'radio':
915  ‪$fields['type'] = 'multiple';
916  break;
917  case 'select':
918  case 'category':
919  ‪$fields['type'] = 'multiple';
920  if (‪$fields['foreign_table'] ?? false) {
921  ‪$fields['type'] = 'relation';
922  }
923  if (‪$fields['special'] ?? false) {
924  ‪$fields['type'] = 'text';
925  }
926  break;
927  case 'group':
928  ‪$fields['type'] = 'relation';
929  break;
930  case 'user':
931  case 'flex':
932  case 'passthrough':
933  case 'none':
934  case 'text':
935  case 'email':
936  case 'link':
937  case 'password':
938  case 'color':
939  case 'json':
940  case 'uuid':
941  default:
942  ‪$fields['type'] = 'text';
943  }
944  } else {
945  ‪$fields['label'] = '[FIELD: ' . ‪$fieldName . ']';
946  switch (‪$fieldName) {
947  case 'pid':
948  ‪$fields['type'] = 'relation';
949  ‪$fields['allowed'] = 'pages';
950  break;
951  case 'tstamp':
952  case 'crdate':
953  ‪$fields['type'] = 'time';
954  break;
955  default:
956  ‪$fields['type'] = 'number';
957  }
958  }
959  }
960  switch (‪$fields['type']) {
961  case 'date':
962  if ($fieldValue != -1) {
963  $formatter = new ‪DateFormatter();
964  $out = $formatter->format((int)$fieldValue, 'SHORTDATE', $locale);
965  }
966  break;
967  case 'time':
968  if ($fieldValue != -1) {
969  $formatter = new ‪DateFormatter();
970  if ($splitString === '<br />') {
971  $out = $formatter->format((int)$fieldValue, 'HH:mm\'' . $splitString . '\'dd-MM-yyyy', $locale);
972  } else {
973  $out = $formatter->format((int)$fieldValue, 'HH:mm dd-MM-yyyy', $locale);
974  }
975  }
976  break;
977  case 'multiple':
978  case 'binary':
979  case 'relation':
980  $out = $this->makeValueList($fieldName, $fieldValue, $fields, $table, $splitString);
981  break;
982  case 'boolean':
983  $out = $fieldValue ? 'True' : 'False';
984  break;
985  default:
986  $out = htmlspecialchars($fieldValue);
987  }
988 
989  return $out;
990  }
991 
992  protected function makeValueList(string $fieldName, string $fieldValue, array $conf, string $table, string $splitString): string
993  {
994  $backendUserAuthentication = $this->getBackendUserAuthentication();
995  $languageService = $this->getLanguageService();
996  $from_table_Arr = [];
997  $fieldSetup = $conf;
998  $out = '';
999  if ($fieldSetup['type'] === 'multiple') {
1000  foreach (($fieldSetup['items'] ?? []) as $val) {
1001  $value = $languageService->sL($val['label']);
1002  if (GeneralUtility::inList($fieldValue, $val['value']) || $fieldValue == $val['value']) {
1003  if ($out !== '') {
1004  $out .= $splitString;
1005  }
1006  $out .= htmlspecialchars($value);
1007  }
1008  }
1009  }
1010  if ($fieldSetup['type'] === 'binary') {
1011  foreach ($fieldSetup['items'] as $val) {
1012  $value = $languageService->sL($val['label']);
1013  if ($out !== '') {
1014  $out .= $splitString;
1015  }
1016  $out .= htmlspecialchars($value);
1017  }
1018  }
1019  if ($fieldSetup['type'] === 'relation') {
1020  $dontPrefixFirstTable = 0;
1021  $useTablePrefix = 0;
1022  foreach (($fieldSetup['items'] ?? []) as $val) {
1023  if (str_starts_with($val['label'], 'LLL:')) {
1024  $value = $languageService->sL($val['label']);
1025  } else {
1026  $value = $val['label'];
1027  }
1028  if (GeneralUtility::inList($fieldValue, $value) || $fieldValue == $value) {
1029  if ($out !== '') {
1030  $out .= $splitString;
1031  }
1032  $out .= htmlspecialchars($value);
1033  }
1034  }
1035  if (str_contains($fieldSetup['allowed'] ?? '', ',')) {
1036  $from_table_Arr = explode(',', $fieldSetup['allowed']);
1037  $useTablePrefix = 1;
1038  if (!$fieldSetup['prepend_tname']) {
1039  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
1040  $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1041  $statement = $queryBuilder->select($fieldName)->from($table)->executeQuery();
1042  while ($row = $statement->fetchAssociative()) {
1043  if (str_contains($row[$fieldName], ',')) {
1044  $checkContent = explode(',', $row[$fieldName]);
1045  foreach ($checkContent as $singleValue) {
1046  if (!str_contains($singleValue, '_')) {
1047  $dontPrefixFirstTable = 1;
1048  }
1049  }
1050  } else {
1051  $singleValue = $row[$fieldName];
1052  if ($singleValue !== '' && !str_contains($singleValue, '_')) {
1053  $dontPrefixFirstTable = 1;
1054  }
1055  }
1056  }
1057  }
1058  } else {
1059  $from_table_Arr[0] = $fieldSetup['allowed'] ?? null;
1060  }
1061  if (!empty($fieldSetup['prepend_tname'])) {
1062  $useTablePrefix = 1;
1063  }
1064  if (!empty($fieldSetup['foreign_table'])) {
1065  $from_table_Arr[0] = $fieldSetup['foreign_table'];
1066  }
1067  $counter = 0;
1068  $useSelectLabels = 0;
1069  $useAltSelectLabels = 0;
1070  $tablePrefix = '';
1071  $labelFieldSelect = [];
1072  foreach ($from_table_Arr as $from_table) {
1073  if ($useTablePrefix && !$dontPrefixFirstTable && $counter !== 1 || $counter === 1) {
1074  $tablePrefix = $from_table . '_';
1075  }
1076  $counter = 1;
1077  if (is_array($GLOBALS['TCA'][$from_table] ?? null)) {
1078  $labelField = $GLOBALS['TCA'][$from_table]['ctrl']['label'] ?? '';
1079  $altLabelField = $GLOBALS['TCA'][$from_table]['ctrl']['label_alt'] ?? '';
1080  if (is_array($GLOBALS['TCA'][$from_table]['columns'][$labelField]['config']['items'] ?? false)) {
1081  $items = $GLOBALS['TCA'][$from_table]['columns'][$labelField]['config']['items'];
1082  foreach ($items as $labelArray) {
1083  $labelFieldSelect[$labelArray['value']] = $languageService->sL($labelArray['label']);
1084  }
1085  $useSelectLabels = 1;
1086  }
1087  $altLabelFieldSelect = [];
1088  if (is_array($GLOBALS['TCA'][$from_table]['columns'][$altLabelField]['config']['items'] ?? false)) {
1089  $items = $GLOBALS['TCA'][$from_table]['columns'][$altLabelField]['config']['items'];
1090  foreach ($items as $altLabelArray) {
1091  $altLabelFieldSelect[$altLabelArray['value']] = $languageService->sL($altLabelArray['label']);
1092  }
1093  $useAltSelectLabels = 1;
1094  }
1095 
1096  if (empty($this->tableArray[$from_table])) {
1097  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($from_table);
1098  $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1099  $selectFields = ['uid', $labelField];
1100  if ($altLabelField) {
1101  $selectFields = array_merge($selectFields, GeneralUtility::trimExplode(',', $altLabelField, true));
1102  }
1103  $queryBuilder->select(...$selectFields)
1104  ->from($from_table)
1105  ->orderBy('uid');
1106  if (!$backendUserAuthentication->isAdmin()) {
1107  $webMounts = $backendUserAuthentication->returnWebmounts();
1108  $perms_clause = $backendUserAuthentication->getPagePermsClause(Permission::PAGE_SHOW);
1109  $webMountPageTree = '';
1110  $webMountPageTreePrefix = '';
1111  foreach ($webMounts as $webMount) {
1112  if ($webMountPageTree) {
1113  $webMountPageTreePrefix = ',';
1114  }
1115  $webMountPageTree .= $webMountPageTreePrefix
1116  . $this->getTreeList($webMount, 999, 0, $perms_clause);
1117  }
1118  if ($from_table === 'pages') {
1119  $queryBuilder->where(
1120  QueryHelper::stripLogicalOperatorPrefix($perms_clause),
1121  $queryBuilder->expr()->in(
1122  'uid',
1123  $queryBuilder->createNamedParameter(
1124  GeneralUtility::intExplode(',', $webMountPageTree),
1125  Connection::PARAM_INT_ARRAY
1126  )
1127  )
1128  );
1129  } else {
1130  $queryBuilder->where(
1131  $queryBuilder->expr()->in(
1132  'pid',
1133  $queryBuilder->createNamedParameter(
1134  GeneralUtility::intExplode(',', $webMountPageTree),
1135  Connection::PARAM_INT_ARRAY
1136  )
1137  )
1138  );
1139  }
1140  }
1141  $statement = $queryBuilder->executeQuery();
1142  $this->tableArray[$from_table] = [];
1143  while ($row = $statement->fetchAssociative()) {
1144  $this->tableArray[$from_table][] = $row;
1145  }
1146  }
1147 
1148  foreach ($this->tableArray[$from_table] as $key => $val) {
1149  $this->MOD_SETTINGS['labels_noprefix'] =
1150  ($this->MOD_SETTINGS['labels_noprefix'] ?? '') == 1
1151  ? 'on'
1152  : $this->MOD_SETTINGS['labels_noprefix'];
1153  $prefixString =
1154  $this->MOD_SETTINGS['labels_noprefix'] === 'on'
1155  ? ''
1156  : ' [' . $tablePrefix . $val['uid'] . '] ';
1157  if ($out !== '') {
1158  $out .= $splitString;
1159  }
1160  if (GeneralUtility::inList($fieldValue, $tablePrefix . $val['uid'])
1161  || $fieldValue == $tablePrefix . $val['uid']) {
1162  if ($useSelectLabels) {
1163  $out .= htmlspecialchars($prefixString . $labelFieldSelect[$val[$labelField]]);
1164  } elseif ($val[$labelField]) {
1165  $out .= htmlspecialchars($prefixString . $val[$labelField]);
1166  } elseif ($useAltSelectLabels) {
1167  $out .= htmlspecialchars($prefixString . $altLabelFieldSelect[$val[$altLabelField]]);
1168  } else {
1169  $out .= htmlspecialchars($prefixString . $val[$altLabelField]);
1170  }
1171  }
1172  }
1173  }
1174  }
1175  }
1176 
1177  return $out;
1178  }
1179 
1184  private function renderNoResultsFoundMessage(): void
1185  {
1186  $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, 'No rows selected!', '', ContextualFeedbackSeverity::INFO);
1187  $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
1188  $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
1189  $defaultFlashMessageQueue->enqueue($flashMessage);
1190  }
1191 
1192  protected function getQuery(array $queryConfig, string $pad = ''): string
1193  {
1194  $qs = '';
1195  // Since we don't traverse the array using numeric keys in the upcoming whileloop make sure it's fresh and clean
1196  ksort($queryConfig);
1197  $first = true;
1198  foreach ($queryConfig as $key => $conf) {
1199  $conf = $this->convertIso8601DatetimeStringToUnixTimestamp($conf);
1200  switch ($conf['type']) {
1201  case 'newlevel':
1202  $qs .= LF . $pad . trim($conf['operator']) . ' (' . $this->getQuery(
1203  $queryConfig[$key]['nl'],
1204  $pad . ' '
1205  ) . LF . $pad . ')';
1206  break;
1207  default:
1208  $qs .= LF . $pad . $this->getQuerySingle($conf, $first);
1209  }
1210  $first = false;
1211  }
1212 
1213  return $qs;
1214  }
1215 
1216  protected function getQuerySingle(array $conf, bool $first): string
1217  {
1218  $comparison = (int)($conf['comparison'] ?? 0);
1219  $qs = '';
1220  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($this->table);
1221  $prefix = $this->enablePrefix ? $this->table . '.' : '';
1222  if (!$first) {
1223  // Is it OK to insert the AND operator if none is set?
1224  $operator = strtoupper(trim($conf['operator'] ?? ''));
1225  if (!in_array($operator, ['AND', 'OR'], true)) {
1226  $operator = 'AND';
1227  }
1228  $qs .= $operator . ' ';
1229  }
1230  $qsTmp = str_replace('#FIELD#', $prefix . trim(substr($conf['type'], 6)), $this->compSQL[$comparison] ?? '');
1231  $inputVal = $this->cleanInputVal($conf);
1232  if ($comparison === 68 || $comparison === 69) {
1233  $inputVal = explode(',', (string)$inputVal);
1234  foreach ($inputVal as $key => $fileName) {
1235  $inputVal[$key] = $queryBuilder->quote($fileName);
1236  }
1237  $inputVal = implode(',', $inputVal);
1238  $qsTmp = str_replace('#VALUE#', $inputVal, $qsTmp);
1239  } elseif ($comparison === 162 || $comparison === 163) {
1240  $inputValArray = explode(',', (string)$inputVal);
1241  $inputVal = 0;
1242  foreach ($inputValArray as $fileName) {
1243  $inputVal += (int)$fileName;
1244  }
1245  $qsTmp = str_replace('#VALUE#', (string)$inputVal, $qsTmp);
1246  } else {
1247  if (is_array($inputVal)) {
1248  $inputVal = $inputVal[0];
1249  }
1250  // @todo This is weired, as it seems that it quotes the value as string and remove
1251  // quotings using the trim() method. Should be investagated/refactored.
1252  $qsTmp = str_replace('#VALUE#', trim($queryBuilder->quote((string)$inputVal), '\''), $qsTmp);
1253  }
1254  if ($comparison === 37 || $comparison === 36 || $comparison === 66 || $comparison === 67 || $comparison === 100 || $comparison === 101) {
1255  // between:
1256  $inputVal = $this->‪cleanInputVal($conf, '1');
1257  // @todo This is weired, as it seems that it quotes the value as string and remove
1258  // quotings using the trim() method. Should be investagated/refactored.
1259  $qsTmp = str_replace('#VALUE1#', trim($queryBuilder->quote((string)$inputVal), '\''), $qsTmp);
1260  }
1261  $qs .= trim((string)$qsTmp);
1262 
1263  return $qs;
1264  }
1265 
1269  protected function ‪cleanInputVal(array $conf, string $suffix = '')
1270  {
1271  $comparison = (int)($conf['comparison'] ?? 0);
1272  $var = $conf['inputValue' . $suffix] ?? '';
1273  if ($comparison >> 5 === 0 || ($comparison === 32 || $comparison === 33 || $comparison === 64 || $comparison === 65 || $comparison === 66 || $comparison === 67 || $comparison === 96 || $comparison === 97)) {
1274  $inputVal = $var ?? null;
1275  } elseif ($comparison === 39 || $comparison === 38) {
1276  // in list:
1277  $inputVal = implode(',', ‪GeneralUtility::intExplode(',', (string)($var ?? '')));
1278  } elseif ($comparison === 68 || $comparison === 69 || $comparison === 162 || $comparison === 163) {
1279  // in list:
1280  if (is_array($var ?? false)) {
1281  $inputVal = implode(',', $var);
1282  } elseif ($var ?? false) {
1283  $inputVal = $var;
1284  } else {
1285  $inputVal = 0;
1286  }
1287  } elseif (!is_array($var) && strtotime((string)$var)) {
1288  $inputVal = $var;
1289  } elseif (!is_array($var) && ‪MathUtility::canBeInterpretedAsInteger($var)) {
1290  $inputVal = (int)$var;
1291  } else {
1292  // TODO: Six eyes looked at this code and nobody understood completely what is going on here and why we
1293  // fallback to float casting, the whole class smells like it needs a refactoring.
1294  $inputVal = (float)($var ?? 0.0);
1295  }
1296 
1297  return $inputVal;
1298  }
1299 
1300  protected function ‪convertIso8601DatetimeStringToUnixTimestamp(array $conf): array
1301  {
1302  if ($this->‪isDateOfIso8601Format($conf['inputValue'] ?? '')) {
1303  $conf['inputValue'] = strtotime($conf['inputValue']);
1304  if ($this->‪isDateOfIso8601Format($conf['inputValue1'] ?? '')) {
1305  $conf['inputValue1'] = strtotime($conf['inputValue1']);
1306  }
1307  }
1308 
1309  return $conf;
1310  }
1311 
1315  protected function ‪isDateOfIso8601Format(mixed $date): bool
1316  {
1317  if (!is_int($date) && !is_string($date)) {
1318  return false;
1319  }
1320  $format = 'Y-m-d\\TH:i:s\\Z';
1321  $formattedDate = \DateTime::createFromFormat($format, (string)$date);
1322 
1323  return $formattedDate && $formattedDate->format($format) === $date;
1324  }
1325 
1326  protected function ‪makeSelectorTable(array $modSettings, ServerRequestInterface $request, string $enableList = 'table,fields,query,group,order,limit'): string
1327  {
1328  $out = [];
1329  $enableArr = explode(',', $enableList);
1330  $userTsConfig = $this->‪getBackendUserAuthentication()->getTSConfig();
1331 
1332  // Make output
1333  if (in_array('table', $enableArr) && !($userTsConfig['mod.']['dbint.']['disableSelectATable'] ?? false)) {
1334  $out[] = '<div class="form-group">';
1335  $out[] = '<label class="form-label" for="SET[queryTable]">Select a table:</label>';
1336  $out[] = $this->‪mkTableSelect('SET[queryTable]', $this->table);
1337  $out[] = '</div>';
1338  }
1339  if ($this->table) {
1340  // Init fields:
1341  $this->‪setAndCleanUpExternalLists('queryFields', $modSettings['queryFields'] ?? '', 'uid,' . $this->‪getLabelCol());
1342  $this->‪setAndCleanUpExternalLists('queryGroup', $modSettings['queryGroup'] ?? '');
1343  $this->‪setAndCleanUpExternalLists('queryOrder', ($modSettings['queryOrder'] ?? '') . ',' . ($modSettings['queryOrder2'] ?? ''));
1344  // Limit:
1345  $this->extFieldLists['queryLimit'] = $modSettings['queryLimit'] ?? '';
1346  if (!$this->extFieldLists['queryLimit']) {
1347  $this->extFieldLists['queryLimit'] = 100;
1348  }
1349  $parts = ‪GeneralUtility::intExplode(',', (string)$this->extFieldLists['queryLimit']);
1350  $limitBegin = 0;
1351  $limitLength = (int)($this->extFieldLists['queryLimit']);
1352  if ($parts[1] ?? null) {
1353  $limitBegin = (int)$parts[0];
1354  $limitLength = (int)$parts[1];
1355  }
1356  $this->extFieldLists['queryLimit'] = implode(',', array_slice($parts, 0, 2));
1357  // Insert Descending parts
1358  if ($this->extFieldLists['queryOrder']) {
1359  $descParts = explode(',', ($modSettings['queryOrderDesc'] ?? '') . ',' . ($modSettings['queryOrder2Desc'] ?? ''));
1360  $orderParts = explode(',', $this->extFieldLists['queryOrder']);
1361  $reList = [];
1362  foreach ($orderParts as $kk => $vv) {
1363  $reList[] = $vv . ($descParts[$kk] ? ' DESC' : '');
1364  }
1365  $this->extFieldLists['queryOrder_SQL'] = implode(',', $reList);
1366  }
1367  // Query Generator:
1368  $this->‪procesData($request, ($modSettings['queryConfig'] ?? '') ? unserialize((string)$modSettings['queryConfig'], ['allowed_classes' => false]) : []);
1369  $this->queryConfig = $this->‪cleanUpQueryConfig($this->queryConfig);
1370  $this->enableQueryParts = (bool)($modSettings['search_query_smallparts'] ?? false);
1371  $codeArr = $this->‪getFormElements();
1372  $queryCode = $this->‪printCodeArray($codeArr);
1373  if (in_array('fields', $enableArr) && !($userTsConfig['mod.']['dbint.']['disableSelectFields'] ?? false)) {
1374  $out[] = '<div class="form-group">';
1375  $out[] = '<label class="form-label" for="SET[queryFields]">Select fields:</label>';
1376  $out[] = $this->‪mkFieldToInputSelect('SET[queryFields]', $this->extFieldLists['queryFields']);
1377  $out[] = '</div>';
1378  }
1379  if (in_array('query', $enableArr) && !($userTsConfig['mod.']['dbint.']['disableMakeQuery'] ?? false)) {
1380  $out[] = '<div class="form-group">';
1381  $out[] = '<label class="form-label">Make Query:</label>';
1382  $out[] = $queryCode;
1383  $out[] = '</div>';
1384  }
1385  if (in_array('group', $enableArr) && !($userTsConfig['mod.']['dbint.']['disableGroupBy'] ?? false)) {
1386  $out[] = '<div class="form-group">';
1387  $out[] = '<label class="form-label" for="SET[queryGroup]">Group By:</label>';
1388  $out[] = $this->‪mkTypeSelect('SET[queryGroup]', $this->extFieldLists['queryGroup'], '');
1389  $out[] = '</div>';
1390  }
1391  if (in_array('order', $enableArr) && !($userTsConfig['mod.']['dbint.']['disableOrderBy'] ?? false)) {
1392  $orderByArr = explode(',', $this->extFieldLists['queryOrder']);
1393  $orderBy = [];
1394  $orderBy[] = '<div class="form-group">';
1395  $orderBy[] = '<div class="input-group">';
1396  $orderBy[] = $this->‪mkTypeSelect('SET[queryOrder]', $orderByArr[0], '');
1397  $orderBy[] = '<div class="input-group-text">';
1398  $orderBy[] = '<div class="form-check form-check-type-toggle">';
1399  $orderBy[] = $this->‪getFuncCheck('SET[queryOrderDesc]', $modSettings['queryOrderDesc'] ?? '', $request, 'id="checkQueryOrderDesc"');
1400  $orderBy[] = '<label class="form-check-label" for="checkQueryOrderDesc">Descending</label>';
1401  $orderBy[] = '</div>';
1402  $orderBy[] = '</div>';
1403  $orderBy[] = '</div>';
1404  $orderBy[] = '</div>';
1405 
1406  if ($orderByArr[0]) {
1407  $orderBy[] = '<div class="form-group">';
1408  $orderBy[] = '<div class="input-group">';
1409  $orderBy[] = $this->‪mkTypeSelect('SET[queryOrder2]', $orderByArr[1] ?? '', '');
1410  $orderBy[] = '<div class="input-group-text">';
1411  $orderBy[] = '<div class="form-check form-check-type-toggle">';
1412  $orderBy[] = $this->‪getFuncCheck('SET[queryOrder2Desc]', $modSettings['queryOrder2Desc'] ?? false, $request, 'id="checkQueryOrder2Desc"');
1413  $orderBy[] = '<label class="form-check-label" for="checkQueryOrder2Desc">Descending</label>';
1414  $orderBy[] = '</div>';
1415  $orderBy[] = '</div>';
1416  $orderBy[] = '</div>';
1417  $orderBy[] = '</div>';
1418  }
1419  $out[] = '<div class="form-group">';
1420  $out[] = ' <label class="form-label">Order By:</label>';
1421  $out[] = implode(LF, $orderBy);
1422  $out[] = '</div>';
1423  }
1424  if (in_array('limit', $enableArr) && !($userTsConfig['mod.']['dbint.']['disableLimit'] ?? false)) {
1425  $limit = [];
1426  $limit[] = '<div class="input-group">';
1427  $limit[] = $this->‪updateIcon();
1428  $limit[] = '<input type="text" class="form-control" value="' . htmlspecialchars($this->extFieldLists['queryLimit']) . '" name="SET[queryLimit]" id="queryLimit">';
1429  $limit[] = '</div>';
1430 
1431  $prevLimit = $limitBegin - $limitLength < 0 ? 0 : $limitBegin - $limitLength;
1432  $prevButton = '';
1433  $nextButton = '';
1434 
1435  if ($limitBegin) {
1436  $prevButton = '<input type="button" class="btn btn-default" value="previous ' . htmlspecialchars((string)$limitLength) . '" data-value="' . htmlspecialchars($prevLimit . ',' . $limitLength) . '">';
1437  }
1438  if (!$limitLength) {
1439  $limitLength = 100;
1440  }
1441 
1442  $nextLimit = $limitBegin + $limitLength;
1443  if ($nextLimit < 0) {
1444  $nextLimit = 0;
1445  }
1446  if ($nextLimit) {
1447  $nextButton = '<input type="button" class="btn btn-default" value="next ' . htmlspecialchars((string)$limitLength) . '" data-value="' . htmlspecialchars($nextLimit . ',' . $limitLength) . '">';
1448  }
1449 
1450  $out[] = '<div class="form-group">';
1451  $out[] = ' <label class="form-label">Limit:</label>';
1452  $out[] = ' <div class="form-row">';
1453  $out[] = ' <div class="form-group">';
1454  $out[] = implode(LF, $limit);
1455  $out[] = ' </div>';
1456  $out[] = ' <div class="form-group">';
1457  $out[] = ' <div class="btn-group t3js-limit-submit">';
1458  $out[] = $prevButton;
1459  $out[] = $nextButton;
1460  $out[] = ' </div>';
1461  $out[] = ' </div>';
1462  $out[] = ' <div class="form-group">';
1463  $out[] = ' <div class="btn-group t3js-limit-submit">';
1464  $out[] = ' <input type="button" class="btn btn-default" data-value="10" value="10">';
1465  $out[] = ' <input type="button" class="btn btn-default" data-value="20" value="20">';
1466  $out[] = ' <input type="button" class="btn btn-default" data-value="50" value="50">';
1467  $out[] = ' <input type="button" class="btn btn-default" data-value="100" value="100">';
1468  $out[] = ' </div>';
1469  $out[] = ' </div>';
1470  $out[] = ' </div>';
1471  $out[] = '</div>';
1472  }
1473  }
1474 
1475  return implode(LF, $out);
1476  }
1477 
1478  protected function ‪cleanUpQueryConfig(array ‪$queryConfig): array
1479  {
1480  // Since we don't traverse the array using numeric keys in the upcoming while-loop make sure it's fresh and clean before displaying
1481  if (!empty(‪$queryConfig) && is_array(‪$queryConfig)) {
1482  ksort(‪$queryConfig);
1483  } elseif (empty(‪$queryConfig[0]['type'])) {
1484  // Make sure queryConfig is an array
1485  ‪$queryConfig = [];
1486  ‪$queryConfig[0] = ['type' => 'FIELD_'];
1487  }
1488  // Traverse:
1489  foreach (‪$queryConfig as $key => $conf) {
1490  ‪$fieldName = '';
1491  if (str_starts_with(($conf['type'] ?? ''), 'FIELD_')) {
1492  ‪$fieldName = substr($conf['type'], 6);
1493  $fieldType = $this->fields[‪$fieldName]['type'] ?? '';
1494  } elseif (($conf['type'] ?? '') === 'newlevel') {
1495  $fieldType = $conf['type'];
1496  } else {
1497  $fieldType = 'ignore';
1498  }
1499  switch ($fieldType) {
1500  case 'newlevel':
1501  if (!‪$queryConfig[$key]['nl']) {
1502  ‪$queryConfig[$key]['nl'][0]['type'] = 'FIELD_';
1503  }
1504  ‪$queryConfig[$key]['nl'] = $this->‪cleanUpQueryConfig($queryConfig[$key]['nl']);
1505  break;
1506  case 'userdef':
1507  break;
1508  case 'ignore':
1509  default:
1510  $verifiedName = $this->‪verifyType($fieldName);
1511  ‪$queryConfig[$key]['type'] = 'FIELD_' . $this->‪verifyType($verifiedName);
1512  if ((int)($conf['comparison'] ?? 0) >> 5 !== (int)($this->comp_offsets[$fieldType] ?? 0)) {
1513  $conf['comparison'] = (int)($this->comp_offsets[$fieldType] ?? 0) << 5;
1514  }
1515  ‪$queryConfig[$key]['comparison'] = $this->‪verifyComparison($conf['comparison'] ?? '' ? (string)$conf['comparison'] : '0', ($conf['negate'] ?? null) ? 1 : 0);
1516  ‪$queryConfig[$key]['inputValue'] = $this->‪cleanInputVal($queryConfig[$key]);
1517  ‪$queryConfig[$key]['inputValue1'] = $this->‪cleanInputVal($queryConfig[$key], '1');
1518  }
1519  }
1520 
1521  return ‪$queryConfig;
1522  }
1523 
1524  protected function ‪verifyType(string ‪$fieldName): string
1525  {
1526  $first = '';
1527  foreach ($this->fields as $key => $value) {
1528  if (!$first) {
1529  $first = $key;
1530  }
1531  if ($key === ‪$fieldName) {
1532  return $key;
1533  }
1534  }
1535 
1536  return $first;
1537  }
1538 
1542  protected function ‪verifyComparison($comparison, int $neg): int
1543  {
1544  $compOffSet = $comparison >> 5;
1545  $first = -1;
1546  for ($i = 32 * $compOffSet + $neg; $i < 32 * ($compOffSet + 1); $i += 2) {
1547  if ($first === -1) {
1548  $first = $i;
1549  }
1550  if ($i >> 1 === $comparison >> 1) {
1551  return $i;
1552  }
1553  }
1554 
1555  return $first;
1556  }
1557 
1561  protected function ‪getFormElements(int $subLevel = 0, ‪$queryConfig = '', string $parent = ''): array
1562  {
1563  $codeArr = [];
1564  if (!is_array(‪$queryConfig)) {
1566  }
1567  $c = 0;
1568  $arrCount = 0;
1569  $loopCount = 0;
1570  foreach (‪$queryConfig as $key => $conf) {
1571  ‪$fieldName = '';
1572  $subscript = $parent . '[' . $key . ']';
1573  $lineHTML = [];
1574  $lineHTML[] = $this->‪mkOperatorSelect($this->name . $subscript, ($conf['operator'] ?? ''), (bool)$c, ($conf['type'] ?? '') !== 'FIELD_');
1575  if (str_starts_with(($conf['type'] ?? ''), 'FIELD_')) {
1576  ‪$fieldName = substr($conf['type'], 6);
1577  $this->fieldName = ‪$fieldName;
1578  $fieldType = $this->fields[‪$fieldName]['type'] ?? '';
1579  if ((int)($conf['comparison'] ?? 0) >> 5 !== (int)($this->comp_offsets[$fieldType] ?? 0)) {
1580  $conf['comparison'] = (int)($this->comp_offsets[$fieldType] ?? 0) << 5;
1581  }
1582  //nasty nasty...
1583  //make sure queryConfig contains _actual_ comparevalue.
1584  //mkCompSelect don't care, but getQuery does.
1585  ‪$queryConfig[$key]['comparison'] += isset($conf['negate']) - $conf['comparison'] % 2;
1586  } elseif (($conf['type'] ?? '') === 'newlevel') {
1587  $fieldType = $conf['type'];
1588  } else {
1589  $fieldType = 'ignore';
1590  }
1591  $fieldPrefix = htmlspecialchars($this->name . $subscript);
1592  switch ($fieldType) {
1593  case 'ignore':
1594  break;
1595  case 'newlevel':
1596  if (!‪$queryConfig[$key]['nl']) {
1597  ‪$queryConfig[$key]['nl'][0]['type'] = 'FIELD_';
1598  }
1599  $lineHTML[] = '<input type="hidden" name="' . $fieldPrefix . '[type]" value="newlevel">';
1600  $codeArr[$arrCount]['sub'] = $this->‪getFormElements($subLevel + 1, ‪$queryConfig[$key]['nl'], $subscript . '[nl]');
1601  break;
1602  case 'userdef':
1603  $lineHTML[] = '';
1604  break;
1605  case 'date':
1606  $lineHTML[] = '<div class="form-row">';
1607  $lineHTML[] = $this->‪makeComparisonSelector($subscript, ‪$fieldName, $conf);
1608  if ($conf['comparison'] === 100 || $conf['comparison'] === 101) {
1609  // between
1610  $lineHTML[] = $this->‪getDateTimePickerField($fieldPrefix . '[inputValue]', (string)$conf['inputValue'], 'date');
1611  $lineHTML[] = $this->‪getDateTimePickerField($fieldPrefix . '[inputValue1]', (string)$conf['inputValue1'], 'date');
1612  } else {
1613  $lineHTML[] = $this->‪getDateTimePickerField($fieldPrefix . '[inputValue]', (string)$conf['inputValue'], 'date');
1614  }
1615  $lineHTML[] = '</div>';
1616  break;
1617  case 'time':
1618  $lineHTML[] = '<div class="form-row">';
1619  $lineHTML[] = $this->‪makeComparisonSelector($subscript, ‪$fieldName, $conf);
1620  if ($conf['comparison'] === 100 || $conf['comparison'] === 101) {
1621  // between:
1622  $lineHTML[] = $this->‪getDateTimePickerField($fieldPrefix . '[inputValue]', (string)$conf['inputValue'], 'datetime');
1623  $lineHTML[] = $this->‪getDateTimePickerField($fieldPrefix . '[inputValue1]', (string)$conf['inputValue1'], 'datetime');
1624  } else {
1625  $lineHTML[] = $this->‪getDateTimePickerField($fieldPrefix . '[inputValue]', (string)$conf['inputValue'], 'datetime');
1626  }
1627  $lineHTML[] = '</div>';
1628  break;
1629  case 'multiple':
1630  case 'binary':
1631  case 'relation':
1632  $lineHTML[] = '<div class="form-row">';
1633  $lineHTML[] = $this->‪makeComparisonSelector($subscript, ‪$fieldName, $conf);
1634  $lineHTML[] = '<div class="form-group">';
1635  if ($conf['comparison'] === 68 || $conf['comparison'] === 69 || $conf['comparison'] === 162 || $conf['comparison'] === 163) {
1636  $lineHTML[] = '<select class="form-select" name="' . $fieldPrefix . '[inputValue][]" multiple="multiple">';
1637  } elseif ($conf['comparison'] === 66 || $conf['comparison'] === 67) {
1638  if (is_array($conf['inputValue'])) {
1639  $conf['inputValue'] = implode(',', $conf['inputValue']);
1640  }
1641  $lineHTML[] = '<input class="form-control form-control-clearable t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue'] ?? '') . '" name="' . $fieldPrefix . '[inputValue]">';
1642  } elseif ($conf['comparison'] === 64) {
1643  if (is_array($conf['inputValue'])) {
1644  $conf['inputValue'] = $conf['inputValue'][0];
1645  }
1646  $lineHTML[] = '<select class="form-select t3js-submit-change" name="' . $fieldPrefix . '[inputValue]">';
1647  } else {
1648  $lineHTML[] = '<select class="form-select t3js-submit-change" name="' . $fieldPrefix . '[inputValue]">';
1649  }
1650  if ($conf['comparison'] != 66 && $conf['comparison'] != 67) {
1651  $lineHTML[] = $this->‪makeOptionList($fieldName, $conf, $this->table);
1652  $lineHTML[] = '</select>';
1653  }
1654  $lineHTML[] = '</div>';
1655  $lineHTML[] = '</div>';
1656  break;
1657  case 'boolean':
1658  $lineHTML[] = '<div class="form-row">';
1659  $lineHTML[] = $this->‪makeComparisonSelector($subscript, ‪$fieldName, $conf);
1660  $lineHTML[] = '<input type="hidden" value="1" name="' . $fieldPrefix . '[inputValue]">';
1661  $lineHTML[] = '</div>';
1662  break;
1663  default:
1664  $lineHTML[] = '<div class="form-row">';
1665  $lineHTML[] = $this->‪makeComparisonSelector($subscript, ‪$fieldName, $conf);
1666  if ($conf['comparison'] === 37 || $conf['comparison'] === 36) {
1667  // between:
1668  $lineHTML[] = '<div class="form-group">';
1669  $lineHTML[] = ' <input class="form-control form-control-clearable t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue'] ?? '') . '" name="' . $fieldPrefix . '[inputValue]">';
1670  $lineHTML[] = '</div>';
1671  $lineHTML[] = '<div class="form-group">';
1672  $lineHTML[] = ' <input class="form-control form-control-clearable t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue1'] ?? '') . '" name="' . $fieldPrefix . '[inputValue1]">';
1673  $lineHTML[] = '</div>';
1674  } else {
1675  $lineHTML[] = '<div class="form-group">';
1676  $lineHTML[] = ' <input class="form-control form-control-clearable t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue'] ?? '') . '" name="' . $fieldPrefix . '[inputValue]">';
1677  $lineHTML[] = '</div>';
1678  }
1679  $lineHTML[] = '</div>';
1680  }
1681  if ($fieldType !== 'ignore') {
1682  $lineHTML[] = '<div class="form-row">';
1683  $lineHTML[] = '<div class="btn-group">';
1684  $lineHTML[] = $this->‪updateIcon();
1685  if ($loopCount) {
1686  $lineHTML[] = ''
1687  . '<button class="btn btn-default" title="Remove condition" name="qG_del' . htmlspecialchars($subscript) . '">'
1688  . $this->iconFactory->getIcon('actions-delete', IconSize::SMALL)->render()
1689  . '</button>';
1690  }
1691  $lineHTML[] = ''
1692  . '<button class="btn btn-default" title="Add condition" name="qG_ins' . htmlspecialchars($subscript) . '">'
1693  . $this->iconFactory->getIcon('actions-plus', IconSize::SMALL)->render()
1694  . '</button>';
1695  if ($c != 0) {
1696  $lineHTML[] = ''
1697  . '<button class="btn btn-default" title="Move up" name="qG_up' . htmlspecialchars($subscript) . '">'
1698  . $this->iconFactory->getIcon('actions-chevron-up', IconSize::SMALL)->render()
1699  . '</button>';
1700  }
1701  if ($c != 0 && $fieldType !== 'newlevel') {
1702  $lineHTML[] = ''
1703  . '<button class="btn btn-default" title="New level" name="qG_nl' . htmlspecialchars($subscript) . '">'
1704  . $this->iconFactory->getIcon('actions-chevron-right', IconSize::SMALL)->render()
1705  . '</button>';
1706  }
1707  if ($fieldType === 'newlevel') {
1708  $lineHTML[] = ''
1709  . '<button class="btn btn-default" title="Collapse new level" name="qG_remnl' . htmlspecialchars($subscript) . '">'
1710  . $this->iconFactory->getIcon('actions-chevron-left', IconSize::SMALL)->render()
1711  . '</button>';
1712  }
1713  $lineHTML[] = '</div>';
1714  $lineHTML[] = '</div>';
1715  $codeArr[$arrCount]['html'] = implode(LF, $lineHTML);
1716  $codeArr[$arrCount]['query'] = $this->‪getQuerySingle($conf, $c === 0);
1717  $arrCount++;
1718  $c++;
1719  }
1720  $loopCount = 1;
1721  }
1722  $this->queryConfig = ‪$queryConfig;
1723 
1724  return $codeArr;
1725  }
1726 
1727  protected function ‪getDateTimePickerField(string ‪$name, string $timestamp, string $type): string
1728  {
1729  $value = strtotime($timestamp) ? date(‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'] . ' ' . ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'], (int)strtotime($timestamp)) : '';
1730  $id = ‪StringUtility::getUniqueId('dt_');
1731  $html = [];
1732  $html[] = '<div class="form-group">';
1733  $html[] = ' <div class="input-group" id="' . $id . '-wrapper">';
1734  $html[] = ' <input data-formengine-input-name="' . htmlspecialchars(‪$name) . '" value="' . $value . '" class="form-control form-control-clearable t3js-datetimepicker t3js-clearable" data-date-type="' . htmlspecialchars($type) . '" type="text" id="' . $id . '">';
1735  $html[] = ' <input name="' . htmlspecialchars(‪$name) . '" value="' . htmlspecialchars($timestamp) . '" type="hidden">';
1736  $html[] = ' <button class="btn btn-default" type="button" data-global-event="click" data-action-focus="#' . $id . '">';
1737  $html[] = $this->iconFactory->getIcon('actions-calendar-alternative', IconSize::SMALL)->render();
1738  $html[] = ' </button>';
1739  $html[] = ' </div>';
1740  $html[] = '</div>';
1741 
1742  return implode(LF, $html);
1743  }
1744 
1745  protected function ‪makeOptionList(string ‪$fieldName, array $conf, string ‪$table): string
1746  {
1747  $backendUserAuthentication = $this->‪getBackendUserAuthentication();
1748  $from_table_Arr = [];
1749  $out = [];
1750  $fieldSetup = $this->fields[‪$fieldName];
1751  $languageService = $this->‪getLanguageService();
1752  if ($fieldSetup['type'] === 'multiple') {
1753  $optGroupOpen = false;
1754  foreach (($fieldSetup['items'] ?? []) as $val) {
1755  $value = $languageService->sL($val['label']);
1756  if ($val['value'] === '--div--') {
1757  if ($optGroupOpen) {
1758  $out[] = '</optgroup>';
1759  }
1760  $optGroupOpen = true;
1761  $out[] = '<optgroup label="' . htmlspecialchars($value) . '">';
1762  } elseif (‪GeneralUtility::inList($conf['inputValue'], (string)$val['value'])) {
1763  $out[] = '<option value="' . htmlspecialchars((string)$val['value']) . '" selected>' . htmlspecialchars($value) . '</option>';
1764  } else {
1765  $out[] = '<option value="' . htmlspecialchars((string)$val['value']) . '">' . htmlspecialchars($value) . '</option>';
1766  }
1767  }
1768  if ($optGroupOpen) {
1769  $out[] = '</optgroup>';
1770  }
1771  }
1772  if ($fieldSetup['type'] === 'binary') {
1773  foreach ($fieldSetup['items'] as $key => $val) {
1774  $value = $languageService->sL($val['label']);
1775  if (‪GeneralUtility::inList($conf['inputValue'], (string)(2 ** $key))) {
1776  $out[] = '<option value="' . 2 ** $key . '" selected>' . htmlspecialchars($value) . '</option>';
1777  } else {
1778  $out[] = '<option value="' . 2 ** $key . '">' . htmlspecialchars($value) . '</option>';
1779  }
1780  }
1781  }
1782  if ($fieldSetup['type'] === 'relation') {
1783  $useTablePrefix = 0;
1784  $dontPrefixFirstTable = 0;
1785  foreach (($fieldSetup['items'] ?? []) as $val) {
1786  $value = $languageService->sL($val['label']);
1787  if (‪GeneralUtility::inList($conf['inputValue'], (string)$val['value'])) {
1788  $out[] = '<option value="' . htmlspecialchars((string)$val['value']) . '" selected>' . htmlspecialchars($value) . '</option>';
1789  } else {
1790  $out[] = '<option value="' . htmlspecialchars((string)$val['value']) . '">' . htmlspecialchars($value) . '</option>';
1791  }
1792  }
1793  $allowedFields = $fieldSetup['allowed'] ?? '';
1794  if (str_contains($allowedFields, ',')) {
1795  $from_table_Arr = explode(',', $allowedFields);
1796  $useTablePrefix = 1;
1797  if (!$fieldSetup['prepend_tname']) {
1798  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(‪$table);
1799  $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1800  $statement = $queryBuilder->select(‪$fieldName)
1801  ->from(‪$table)
1802  ->executeQuery();
1803  while ($row = $statement->fetchAssociative()) {
1804  if (str_contains($row[‪$fieldName], ',')) {
1805  $checkContent = explode(',', $row[‪$fieldName]);
1806  foreach ($checkContent as $singleValue) {
1807  if (!str_contains($singleValue, '_')) {
1808  $dontPrefixFirstTable = 1;
1809  }
1810  }
1811  } else {
1812  $singleValue = $row[‪$fieldName];
1813  if ($singleValue !== '' && !str_contains($singleValue, '_')) {
1814  $dontPrefixFirstTable = 1;
1815  }
1816  }
1817  }
1818  }
1819  } else {
1820  $from_table_Arr[0] = $allowedFields;
1821  }
1822  if (!empty($fieldSetup['prepend_tname'])) {
1823  $useTablePrefix = 1;
1824  }
1825  if (!empty($fieldSetup['foreign_table'])) {
1826  $from_table_Arr[0] = $fieldSetup['foreign_table'];
1827  }
1828  $counter = 0;
1829  $tablePrefix = '';
1830  $outArray = [];
1831  $labelFieldSelect = [];
1832  foreach ($from_table_Arr as $from_table) {
1833  $useSelectLabels = false;
1834  $useAltSelectLabels = false;
1835  if ($useTablePrefix && !$dontPrefixFirstTable && $counter !== 1 || $counter === 1) {
1836  $tablePrefix = $from_table . '_';
1837  }
1838  $counter = 1;
1839  if (is_array(‪$GLOBALS['TCA'][$from_table])) {
1840  $labelField = ‪$GLOBALS['TCA'][$from_table]['ctrl']['label'] ?? '';
1841  $altLabelField = ‪$GLOBALS['TCA'][$from_table]['ctrl']['label_alt'] ?? '';
1842  if (‪$GLOBALS['TCA'][$from_table]['columns'][$labelField]['config']['items'] ?? false) {
1843  foreach (‪$GLOBALS['TCA'][$from_table]['columns'][$labelField]['config']['items'] as $labelArray) {
1844  $labelFieldSelect[$labelArray[1]] = $languageService->sL($labelArray[0]);
1845  }
1846  $useSelectLabels = true;
1847  }
1848  $altLabelFieldSelect = [];
1849  if (‪$GLOBALS['TCA'][$from_table]['columns'][$altLabelField]['config']['items'] ?? false) {
1850  foreach (‪$GLOBALS['TCA'][$from_table]['columns'][$altLabelField]['config']['items'] as $altLabelArray) {
1851  $altLabelFieldSelect[$altLabelArray[1]] = $languageService->sL($altLabelArray[0]);
1852  }
1853  $useAltSelectLabels = true;
1854  }
1855 
1856  if (!($this->tableArray[$from_table] ?? false)) {
1857  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($from_table);
1858  $queryBuilder->getRestrictions()->removeAll();
1859  if (empty($this->MOD_SETTINGS['show_deleted'])) {
1860  $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1861  }
1862  $selectFields = ['uid', $labelField];
1863  if ($altLabelField) {
1864  $selectFields = array_merge($selectFields, ‪GeneralUtility::trimExplode(',', $altLabelField, true));
1865  }
1866  $queryBuilder->select(...$selectFields)
1867  ->from($from_table)
1868  ->orderBy('uid');
1869  if (!$backendUserAuthentication->isAdmin()) {
1870  $webMounts = $backendUserAuthentication->returnWebmounts();
1871  $perms_clause = $backendUserAuthentication->getPagePermsClause(‪Permission::PAGE_SHOW);
1872  $webMountPageTree = '';
1873  $webMountPageTreePrefix = '';
1874  foreach ($webMounts as $webMount) {
1875  if ($webMountPageTree) {
1876  $webMountPageTreePrefix = ',';
1877  }
1878  $webMountPageTree .= $webMountPageTreePrefix
1879  . $this->‪getTreeList($webMount, 999, 0, $perms_clause);
1880  }
1881  if ($from_table === 'pages') {
1882  $queryBuilder->where(
1884  $queryBuilder->expr()->in(
1885  'uid',
1886  $queryBuilder->createNamedParameter(
1887  ‪GeneralUtility::intExplode(',', $webMountPageTree),
1889  )
1890  )
1891  );
1892  } else {
1893  $queryBuilder->where(
1894  $queryBuilder->expr()->in(
1895  'pid',
1896  $queryBuilder->createNamedParameter(
1897  ‪GeneralUtility::intExplode(',', $webMountPageTree),
1899  )
1900  )
1901  );
1902  }
1903  }
1904  $statement = $queryBuilder->executeQuery();
1905  $this->tableArray[$from_table] = $statement->fetchAllAssociative();
1906  }
1907 
1908  foreach (($this->tableArray[$from_table] ?? []) as $val) {
1909  if ($useSelectLabels) {
1910  $outArray[$tablePrefix . $val['uid']] = htmlspecialchars($labelFieldSelect[$val[$labelField]]);
1911  } elseif ($val[$labelField]) {
1912  $outArray[$tablePrefix . $val['uid']] = htmlspecialchars($val[$labelField]);
1913  } elseif ($useAltSelectLabels) {
1914  $outArray[$tablePrefix . $val['uid']] = htmlspecialchars($altLabelFieldSelect[$val[$altLabelField]]);
1915  } else {
1916  $outArray[$tablePrefix . $val['uid']] = htmlspecialchars($val[$altLabelField]);
1917  }
1918  }
1919  if (isset($this->MOD_SETTINGS['options_sortlabel']) && $this->MOD_SETTINGS['options_sortlabel'] && is_array($outArray)) {
1920  natcasesort($outArray);
1921  }
1922  }
1923  }
1924  foreach ($outArray as $key2 => $val2) {
1925  $key2 = (string)$key2;
1926  $val2 = (string)$val2;
1927  if (‪GeneralUtility::inList($conf['inputValue'], $key2)) {
1928  $out[] = '<option value="' . htmlspecialchars($key2) . '" selected>[' . htmlspecialchars($key2) . '] ' . htmlspecialchars($val2) . '</option>';
1929  } else {
1930  $out[] = '<option value="' . htmlspecialchars($key2) . '">[' . htmlspecialchars($key2) . '] ' . htmlspecialchars($val2) . '</option>';
1931  }
1932  }
1933  }
1934 
1935  return implode(LF, $out);
1936  }
1937 
1938  protected function ‪mkOperatorSelect(string ‪$name, string $op, bool $draw, bool $submit): string
1939  {
1940  $out = [];
1941  if ($draw) {
1942  $out[] = '<div class="form-group">';
1943  $out[] = ' <select class="form-select' . ($submit ? ' t3js-submit-change' : '') . '" name="' . htmlspecialchars(‪$name) . '[operator]">';
1944  $out[] = ' <option value="AND"' . (!$op || $op === 'AND' ? ' selected' : '') . '>' . htmlspecialchars($this->lang['AND']) . '</option>';
1945  $out[] = ' <option value="OR"' . ($op === 'OR' ? ' selected' : '') . '>' . htmlspecialchars($this->lang['OR']) . '</option>';
1946  $out[] = ' </select>';
1947  $out[] = '</div>';
1948  } else {
1949  $out[] = '<input type="hidden" value="' . htmlspecialchars($op) . '" name="' . htmlspecialchars(‪$name) . '[operator]">';
1950  }
1951 
1952  return implode(LF, $out);
1953  }
1954 
1955  protected function ‪makeComparisonSelector(string $subscript, string ‪$fieldName, array $conf): string
1956  {
1957  $fieldPrefix = $this->name . $subscript;
1958  $lineHTML = [];
1959  $lineHTML[] = '<div class="form-group">';
1960  $lineHTML[] = $this->‪mkTypeSelect($fieldPrefix . '[type]', ‪$fieldName);
1961  $lineHTML[] = '</div>';
1962  $lineHTML[] = '<div class="form-group">';
1963  $lineHTML[] = ' <div class="input-group">';
1964  $lineHTML[] = $this->‪mkCompSelect($fieldPrefix . '[comparison]', (string)$conf['comparison'], ($conf['negate'] ?? null) ? 1 : 0);
1965  $lineHTML[] = ' <span class="input-group-text">';
1966  $lineHTML[] = ' <div class="form-check form-check-type-toggle">';
1967  $lineHTML[] = ' <input type="checkbox" class="form-check-input t3js-submit-click"' . (($conf['negate'] ?? null) ? ' checked' : '') . ' name="' . htmlspecialchars($fieldPrefix) . '[negate]">';
1968  $lineHTML[] = ' </div>';
1969  $lineHTML[] = ' </span>';
1970  $lineHTML[] = ' </div>';
1971  $lineHTML[] = '</div>';
1972 
1973  return implode(LF, $lineHTML);
1974  }
1975 
1976  protected function ‪mkCompSelect(string ‪$name, string $comparison, int $neg): string
1977  {
1978  $compOffSet = $comparison >> 5;
1979  $out = [];
1980  $out[] = '<select class="form-select t3js-submit-change" name="' . ‪$name . '">';
1981  for ($i = 32 * $compOffSet + $neg; $i < 32 * ($compOffSet + 1); $i += 2) {
1982  if ($this->lang['comparison'][$i . '_'] ?? false) {
1983  $out[] = '<option value="' . $i . '"' . ($i >> 1 === $comparison >> 1 ? ' selected' : '') . '>' . htmlspecialchars($this->lang['comparison'][$i . '_']) . '</option>';
1984  }
1985  }
1986  $out[] = '</select>';
1987 
1988  return implode(LF, $out);
1989  }
1990 
1991  protected function ‪printCodeArray(array $codeArr, int $recursionLevel = 0): string
1992  {
1993  $out = [];
1994  foreach (array_values($codeArr) as $queryComponent) {
1995  $out[] = '<div class="card">';
1996  $out[] = '<div class="card-body">';
1997  $out[] = $queryComponent['html'];
1998 
1999  if ($this->enableQueryParts) {
2000  $out[] = '<pre class="language-sql">';
2001  $out[] = '<code class="language-sql">';
2002  $out[] = htmlspecialchars($queryComponent['query']);
2003  $out[] = '</code>';
2004  $out[] = '</pre>';
2005  }
2006  if (is_array($queryComponent['sub'] ?? null)) {
2007  $out[] = $this->‪printCodeArray($queryComponent['sub'], $recursionLevel + 1);
2008  }
2009  $out[] = '</div>';
2010  $out[] = '</div>';
2011  }
2012 
2013  return implode(LF, $out);
2014  }
2015 
2016  protected function ‪mkFieldToInputSelect(string ‪$name, string ‪$fieldName): string
2017  {
2018  $out = [];
2019  $out[] = '<div class="input-group mb-1">';
2020  $out[] = $this->‪updateIcon();
2021  $out[] = '<input type="text" class="form-control form-control-clearable t3js-clearable" value="' . htmlspecialchars(‪$fieldName) . '" name="' . htmlspecialchars(‪$name) . '">';
2022  $out[] = '</div>';
2023  $out[] = '<select class="form-select t3js-addfield" name="_fieldListDummy" size="5" data-field="' . htmlspecialchars(‪$name) . '">';
2024  foreach ($this->fields as $key => $value) {
2025  if (!$value['exclude'] || $this->‪getBackendUserAuthentication()->check('non_exclude_fields', $this->table . ':' . $key)) {
2026  $label = $this->fields[$key]['label'];
2027  if ($this->showFieldAndTableNames) {
2028  $label .= ' [' . $key . ']';
2029  }
2030  $out[] = '<option value="' . htmlspecialchars($key) . '"' . ($key === ‪$fieldName ? ' selected' : '') . '>' . htmlspecialchars($label) . '</option>';
2031  }
2032  }
2033  $out[] = '</select>';
2034 
2035  return implode(LF, $out);
2036  }
2037 
2038  protected function ‪procesData(ServerRequestInterface $request, array $qC = []): void
2039  {
2040  $this->queryConfig = $qC;
2041  $POST = $request->getParsedBody();
2042  // If delete...
2043  if ($POST['qG_del'] ?? false) {
2044  // Initialize array to work on, save special parameters
2045  $ssArr = $this->‪getSubscript($POST['qG_del']);
2046  $workArr = &‪$this->queryConfig;
2047  $ssArrSize = count($ssArr) - 1;
2048  $i = 0;
2049  for (; $i < $ssArrSize; $i++) {
2050  $workArr = &$workArr[$ssArr[$i]];
2051  }
2052  // Delete the entry and move the other entries
2053  unset($workArr[$ssArr[$i]]);
2054  $workArrSize = count((array)$workArr);
2055  for ($j = $ssArr[$i]; $j < $workArrSize; $j++) {
2056  $workArr[$j] = $workArr[$j + 1];
2057  unset($workArr[$j + 1]);
2058  }
2059  }
2060  // If insert...
2061  if ($POST['qG_ins'] ?? false) {
2062  // Initialize array to work on, save special parameters
2063  $ssArr = $this->‪getSubscript($POST['qG_ins']);
2064  $workArr = &‪$this->queryConfig;
2065  $ssArrSize = count($ssArr) - 1;
2066  $i = 0;
2067  for (; $i < $ssArrSize; $i++) {
2068  $workArr = &$workArr[$ssArr[$i]];
2069  }
2070  // Move all entries above position where new entry is to be inserted
2071  $workArrSize = count((array)$workArr);
2072  for ($j = $workArrSize; $j > $ssArr[$i]; $j--) {
2073  $workArr[$j] = $workArr[$j - 1];
2074  }
2075  // Clear new entry position
2076  unset($workArr[$ssArr[$i] + 1]);
2077  $workArr[$ssArr[$i] + 1]['type'] = 'FIELD_';
2078  }
2079  // If move up...
2080  if ($POST['qG_up'] ?? false) {
2081  // Initialize array to work on
2082  $ssArr = $this->‪getSubscript($POST['qG_up']);
2083  $workArr = &‪$this->queryConfig;
2084  $ssArrSize = count($ssArr) - 1;
2085  $i = 0;
2086  for (; $i < $ssArrSize; $i++) {
2087  $workArr = &$workArr[$ssArr[$i]];
2088  }
2089  // Swap entries
2090  $qG_tmp = $workArr[$ssArr[$i]];
2091  $workArr[$ssArr[$i]] = $workArr[$ssArr[$i] - 1];
2092  $workArr[$ssArr[$i] - 1] = $qG_tmp;
2093  }
2094  // If new level...
2095  if ($POST['qG_nl'] ?? false) {
2096  // Initialize array to work on
2097  $ssArr = $this->‪getSubscript($POST['qG_nl']);
2098  $workArr = &‪$this->queryConfig;
2099  $ssArraySize = count($ssArr) - 1;
2100  $i = 0;
2101  for (; $i < $ssArraySize; $i++) {
2102  $workArr = &$workArr[$ssArr[$i]];
2103  }
2104  // Do stuff:
2105  $tempEl = $workArr[$ssArr[$i]];
2106  if (is_array($tempEl)) {
2107  if ($tempEl['type'] !== 'newlevel') {
2108  $workArr[$ssArr[$i]] = [
2109  'type' => 'newlevel',
2110  'operator' => $tempEl['operator'],
2111  'nl' => [$tempEl],
2112  ];
2113  }
2114  }
2115  }
2116  // If collapse level...
2117  if ($POST['qG_remnl'] ?? false) {
2118  // Initialize array to work on
2119  $ssArr = $this->‪getSubscript($POST['qG_remnl']);
2120  $workArr = &‪$this->queryConfig;
2121  $ssArrSize = count($ssArr) - 1;
2122  $i = 0;
2123  for (; $i < $ssArrSize; $i++) {
2124  $workArr = &$workArr[$ssArr[$i]];
2125  }
2126  // Do stuff:
2127  $tempEl = $workArr[$ssArr[$i]];
2128  if (is_array($tempEl)) {
2129  if ($tempEl['type'] === 'newlevel' && is_array($workArr)) {
2130  $a1 = array_slice($workArr, 0, $ssArr[$i]);
2131  $a2 = array_slice($workArr, $ssArr[$i]);
2132  array_shift($a2);
2133  $a3 = $tempEl['nl'];
2134  $a3[0]['operator'] = $tempEl['operator'];
2135  $workArr = array_merge($a1, $a3, $a2);
2136  }
2137  }
2138  }
2139  }
2140 
2141  protected function ‪getSubscript($arr): array
2142  {
2143  $retArr = [];
2144  while (\is_array($arr)) {
2145  reset($arr);
2146  $key = key($arr);
2147  $retArr[] = $key;
2148  if (isset($arr[$key])) {
2149  $arr = $arr[$key];
2150  } else {
2151  break;
2152  }
2153  }
2154 
2155  return $retArr;
2156  }
2157 
2158  protected function ‪getLabelCol(): string
2159  {
2160  return ‪$GLOBALS['TCA'][‪$this->table]['ctrl']['label'];
2161  }
2162 
2163  protected function ‪mkTypeSelect(string ‪$name, string ‪$fieldName, string $prepend = 'FIELD_'): string
2164  {
2165  $out = [];
2166  $out[] = '<select class="form-select t3js-submit-change" name="' . htmlspecialchars(‪$name) . '">';
2167  $out[] = '<option value=""></option>';
2168  foreach ($this->fields as $key => $value) {
2169  if (!($value['exclude'] ?? false) || $this->‪getBackendUserAuthentication()->check('non_exclude_fields', $this->table . ':' . $key)) {
2170  $label = $this->fields[$key]['label'];
2171  if ($this->showFieldAndTableNames) {
2172  $label .= ' [' . $key . ']';
2173  }
2174  $out[] = '<option value="' . htmlspecialchars($prepend . $key) . '"' . ($key === ‪$fieldName ? ' selected' : '') . '>' . htmlspecialchars($label) . '</option>';
2175  }
2176  }
2177  $out[] = '</select>';
2178 
2179  return implode(LF, $out);
2180  }
2181 
2182  protected function ‪updateIcon(): string
2183  {
2184  return '<button class="btn btn-default" title="Update" name="just_update">' . $this->iconFactory->getIcon('actions-refresh', IconSize::SMALL)->render() . '</button>';
2185  }
2186 
2187  protected function ‪setAndCleanUpExternalLists(string ‪$name, string $list, string $force = ''): void
2188  {
2189  ‪$fields = array_unique(‪GeneralUtility::trimExplode(',', $list . ',' . $force, true));
2190  $reList = [];
2191  foreach (‪$fields as ‪$fieldName) {
2192  if (isset($this->fields[‪$fieldName])) {
2193  $reList[] = ‪$fieldName;
2194  }
2195  }
2196  $this->extFieldLists[‪$name] = implode(',', $reList);
2197  }
2198 
2199  protected function ‪mkTableSelect(string ‪$name, string $cur): string
2200  {
2201  $tables = [];
2202  foreach (‪$GLOBALS['TCA'] as $tableName => $value) {
2203  $tableTitle = $this->‪getLanguageService()->sL(‪$GLOBALS['TCA'][$tableName]['ctrl']['title'] ?? '');
2204  if (!$tableTitle || $this->showFieldAndTableNames) {
2205  $tableTitle .= ' [' . $tableName . ']';
2206  }
2207  $tables[$tableName] = trim($tableTitle);
2208  }
2209  asort($tables);
2210 
2211  $out = [];
2212  $out[] = '<select class="form-select t3js-submit-change" name="' . ‪$name . '">';
2213  $out[] = '<option value=""></option>';
2214  foreach ($tables as $tableName => $label) {
2215  if ($this->‪getBackendUserAuthentication()->check('tables_select', $tableName)) {
2216  $out[] = '<option value="' . htmlspecialchars($tableName) . '"' . ($tableName === $cur ? ' selected' : '') . '>' . htmlspecialchars($label) . '</option>';
2217  }
2218  }
2219  $out[] = '</select>';
2220 
2221  return implode(LF, $out);
2222  }
2223 
2227  protected function ‪init(string ‪$name, string ‪$table, string ‪$fieldList = '', array $settings = []): void
2228  {
2229  // Analysing the fields in the table.
2230  if (is_array(‪$GLOBALS['TCA'][‪$table] ?? false)) {
2231  $this->name = ‪$name;
2232  $this->table = ‪$table;
2233  $this->fieldList = ‪$fieldList ?: $this->‪makeFieldList();
2234  $this->MOD_SETTINGS = $settings;
2235  $fieldArr = ‪GeneralUtility::trimExplode(',', $this->fieldList, true);
2236  foreach ($fieldArr as ‪$fieldName) {
2237  $fieldConfig = ‪$GLOBALS['TCA'][‪$this->table]['columns'][‪$fieldName] ?? [];
2238  $this->fields[‪$fieldName] = $fieldConfig['config'] ?? [];
2239  $this->fields[‪$fieldName]['exclude'] = $fieldConfig['exclude'] ?? '';
2240  if (((($this->fields[‪$fieldName]['type'] ?? '') === 'user') && (!isset($this->fields[‪$fieldName]['type']['userFunc'])))
2241  || ($this->fields[‪$fieldName]['type'] ?? '') === 'none'
2242  ) {
2243  // Do not list type=none "virtual" fields or query them from db,
2244  // and if type is user without defined userFunc
2245  unset($this->fields[‪$fieldName]);
2246  continue;
2247  }
2248  if (is_array($fieldConfig) && ($fieldConfig['label'] ?? false)) {
2249  $this->fields[‪$fieldName]['label'] = rtrim(trim($this->‪getLanguageService()->sL($fieldConfig['label'])), ':');
2250  switch ($this->fields[‪$fieldName]['type']) {
2251  case 'input':
2252  if (preg_match('/int|year/i', ($this->fields[‪$fieldName]['eval'] ?? ''))) {
2253  $this->fields[‪$fieldName]['type'] = 'number';
2254  } else {
2255  $this->fields[‪$fieldName]['type'] = 'text';
2256  }
2257  break;
2258  case 'number':
2259  // Empty on purpose, we have to keep the type "number".
2260  // Falling back to the "default" case would set the type to "text"
2261  break;
2262  case 'datetime':
2263  if (!in_array($this->fields[‪$fieldName]['dbType'] ?? '', ‪QueryHelper::getDateTimeTypes(), true)) {
2264  $this->fields[‪$fieldName]['type'] = 'number';
2265  } elseif ($this->fields[‪$fieldName]['dbType'] === 'time') {
2266  $this->fields[‪$fieldName]['type'] = 'time';
2267  } else {
2268  $this->fields[‪$fieldName]['type'] = 'date';
2269  }
2270  break;
2271  case 'check':
2272  if (count($this->fields[‪$fieldName]['items'] ?? []) <= 1) {
2273  $this->fields[‪$fieldName]['type'] = 'boolean';
2274  } else {
2275  $this->fields[‪$fieldName]['type'] = 'binary';
2276  }
2277  break;
2278  case 'radio':
2279  $this->fields[‪$fieldName]['type'] = 'multiple';
2280  break;
2281  case 'select':
2282  case 'category':
2283  $this->fields[‪$fieldName]['type'] = 'multiple';
2284  if ($this->fields[‪$fieldName]['foreign_table'] ?? false) {
2285  $this->fields[‪$fieldName]['type'] = 'relation';
2286  }
2287  if ($this->fields[‪$fieldName]['special'] ?? false) {
2288  $this->fields[‪$fieldName]['type'] = 'text';
2289  }
2290  break;
2291  case 'group':
2292  $this->fields[‪$fieldName]['type'] = 'relation';
2293  break;
2294  case 'user':
2295  case 'flex':
2296  case 'passthrough':
2297  case 'none':
2298  case 'text':
2299  case 'email':
2300  case 'link':
2301  case 'password':
2302  case 'color':
2303  case 'json':
2304  case 'uuid':
2305  default:
2306  $this->fields[‪$fieldName]['type'] = 'text';
2307  }
2308  } else {
2309  $this->fields[‪$fieldName]['label'] = '[FIELD: ' . ‪$fieldName . ']';
2310  switch (‪$fieldName) {
2311  case 'pid':
2312  $this->fields[‪$fieldName]['type'] = 'relation';
2313  $this->fields[‪$fieldName]['allowed'] = 'pages';
2314  break;
2315  case 'tstamp':
2316  case 'crdate':
2317  $this->fields[‪$fieldName]['type'] = 'time';
2318  break;
2319  case 'deleted':
2320  $this->fields[‪$fieldName]['type'] = 'boolean';
2321  break;
2322  default:
2323  $this->fields[‪$fieldName]['type'] = 'number';
2324  }
2325  }
2326 
2327  uasort($this->fields, static fn($fieldA, $fieldB) => strcmp($fieldA['label'], $fieldB['label']));
2328  }
2329  }
2330  /* // EXAMPLE:
2331  $this->queryConfig = array(
2332  array(
2333  'operator' => 'AND',
2334  'type' => 'FIELD_space_before_class',
2335  ),
2336  array(
2337  'operator' => 'AND',
2338  'type' => 'FIELD_records',
2339  'negate' => 1,
2340  'inputValue' => 'foo foo'
2341  ),
2342  array(
2343  'type' => 'newlevel',
2344  'nl' => array(
2345  array(
2346  'operator' => 'AND',
2347  'type' => 'FIELD_space_before_class',
2348  'negate' => 1,
2349  'inputValue' => 'foo foo'
2350  ),
2351  array(
2352  'operator' => 'AND',
2353  'type' => 'FIELD_records',
2354  'negate' => 1,
2355  'inputValue' => 'foo foo'
2356  )
2357  )
2358  ),
2359  array(
2360  'operator' => 'OR',
2361  'type' => 'FIELD_maillist',
2362  )
2363  );
2364  */
2365  }
2366 
2367  protected function ‪makeFieldList(): string
2368  {
2369  $fieldListArr = [];
2370  if (is_array(‪$GLOBALS['TCA'][$this->table])) {
2371  $fieldListArr = array_keys(‪$GLOBALS['TCA'][$this->table]['columns'] ?? []);
2372  $fieldListArr[] = 'uid';
2373  $fieldListArr[] = 'pid';
2374  $fieldListArr[] = 'deleted';
2375  if (‪$GLOBALS['TCA'][$this->table]['ctrl']['tstamp'] ?? false) {
2376  $fieldListArr[] = ‪$GLOBALS['TCA'][‪$this->table]['ctrl']['tstamp'];
2377  }
2378  if (‪$GLOBALS['TCA'][$this->table]['ctrl']['crdate'] ?? false) {
2379  $fieldListArr[] = ‪$GLOBALS['TCA'][‪$this->table]['ctrl']['crdate'];
2380  }
2381  if (‪$GLOBALS['TCA'][$this->table]['ctrl']['sortby'] ?? false) {
2382  $fieldListArr[] = ‪$GLOBALS['TCA'][‪$this->table]['ctrl']['sortby'];
2383  }
2384  }
2385 
2386  return implode(',', $fieldListArr);
2387  }
2388 
2389  protected function ‪makeStoreControl(): string
2390  {
2391  // Load/Save
2392  $storeArray = $this->‪initStoreArray();
2393 
2394  $opt = [];
2395  foreach ($storeArray as $k => $v) {
2396  $opt[] = '<option value="' . htmlspecialchars((string)$k) . '">' . htmlspecialchars((string)$v) . '</option>';
2397  }
2398 
2399  $markup = [];
2400  $markup[] = '<div class="form-row">';
2401  $markup[] = ' <div class="form-group">';
2402  $markup[] = ' <select class="form-select" name="storeControl[STORE]" data-assign-store-control-title>' . implode(LF, $opt) . '</select>';
2403  $markup[] = ' </div>';
2404  $markup[] = ' <div class="form-group">';
2405  $markup[] = ' <input class="form-control" name="storeControl[title]" value="" type="text" max="80">';
2406  $markup[] = ' </div>';
2407  $markup[] = ' <div class="form-group">';
2408  $markup[] = ' <button class="btn btn-default" type="submit" name="storeControl[LOAD]" value="Load">';
2409  $markup[] = $this->iconFactory->getIcon('actions-upload', IconSize::SMALL)->render();
2410  $markup[] = ' Load';
2411  $markup[] = ' </button>';
2412  $markup[] = ' <button class="btn btn-default" type="submit" name="storeControl[SAVE]" value="Save">';
2413  $markup[] = $this->iconFactory->getIcon('actions-save', IconSize::SMALL)->render();
2414  $markup[] = ' Save';
2415  $markup[] = ' </button>';
2416  $markup[] = ' <button class="btn btn-default" type="submit" name="storeControl[REMOVE]" value="Remove">';
2417  $markup[] = $this->iconFactory->getIcon('actions-delete', IconSize::SMALL)->render();
2418  $markup[] = ' Remove';
2419  $markup[] = ' </button>';
2420  $markup[] = ' </div>';
2421  $markup[] = '</div>';
2422 
2423  return implode(LF, $markup);
2424  }
2425 
2426  protected function ‪procesStoreControl(ServerRequestInterface $request): string
2427  {
2428  $languageService = $this->‪getLanguageService();
2429  $flashMessage = null;
2430  $storeArray = $this->‪initStoreArray();
2431  $storeQueryConfigs = (array)(unserialize($this->MOD_SETTINGS['storeQueryConfigs'] ?? '', ['allowed_classes' => false]));
2432  $storeControl = $request->getParsedBody()['storeControl'] ?? [];
2433  $storeIndex = (int)($storeControl['STORE'] ?? 0);
2434  $saveStoreArray = 0;
2435  $writeArray = [];
2436  $msg = '';
2437  if (is_array($storeControl)) {
2438  if ($storeControl['LOAD'] ?? false) {
2439  if ($storeIndex > 0) {
2440  $writeArray = $this->‪loadStoreQueryConfigs($storeQueryConfigs, $storeIndex, $writeArray);
2441  $saveStoreArray = 1;
2442  $flashMessage = GeneralUtility::makeInstance(
2443  FlashMessage::class,
2444  sprintf($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_t3lib_fullsearch.xlf:query_loaded'), $storeArray[$storeIndex])
2445  );
2446  }
2447  } elseif ($storeControl['SAVE'] ?? false) {
2448  if (trim($storeControl['title'])) {
2449  if ($storeIndex > 0) {
2450  $storeArray[$storeIndex] = $storeControl['title'];
2451  } else {
2452  $storeArray[] = $storeControl['title'];
2453  end($storeArray);
2454  $storeIndex = key($storeArray);
2455  }
2456  $storeQueryConfigs = $this->‪addToStoreQueryConfigs($storeQueryConfigs, (int)$storeIndex);
2457  $saveStoreArray = 1;
2458  $flashMessage = GeneralUtility::makeInstance(
2459  FlashMessage::class,
2460  $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_t3lib_fullsearch.xlf:query_saved')
2461  );
2462  }
2463  } elseif ($storeControl['REMOVE'] ?? false) {
2464  if ($storeIndex > 0) {
2465  $flashMessage = GeneralUtility::makeInstance(
2466  FlashMessage::class,
2467  sprintf($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_t3lib_fullsearch.xlf:query_removed'), $storeArray[$storeControl['STORE']])
2468  );
2469  // Removing
2470  unset($storeArray[$storeControl['STORE']]);
2471  $saveStoreArray = 1;
2472  }
2473  }
2474  if (!empty($flashMessage)) {
2475  $msg = GeneralUtility::makeInstance(FlashMessageRendererResolver::class)
2476  ->resolve()
2477  ->render([$flashMessage]);
2478  }
2479  }
2480  if ($saveStoreArray) {
2481  // Making sure, index 0 is not set!
2482  unset($storeArray[0]);
2483  $writeArray['storeArray'] = serialize($storeArray);
2484  $writeArray['storeQueryConfigs'] =
2485  serialize($this->‪cleanStoreQueryConfigs($storeQueryConfigs, $storeArray));
2486  $this->MOD_SETTINGS = BackendUtility::getModuleData(
2487  $this->MOD_MENU,
2488  $writeArray,
2489  $this->moduleName,
2490  'ses'
2491  );
2492  }
2493 
2494  return $msg;
2495  }
2496 
2497  protected function ‪cleanStoreQueryConfigs(array $storeQueryConfigs, array $storeArray): array
2498  {
2499  if (is_array($storeQueryConfigs)) {
2500  foreach ($storeQueryConfigs as $k => $v) {
2501  if (!isset($storeArray[$k])) {
2502  unset($storeQueryConfigs[$k]);
2503  }
2504  }
2505  }
2506 
2507  return $storeQueryConfigs;
2508  }
2509 
2510  protected function ‪addToStoreQueryConfigs(array $storeQueryConfigs, int $index): array
2511  {
2512  $keyArr = explode(',', $this->storeList);
2513  $storeQueryConfigs[$index] = [];
2514  foreach ($keyArr as $k) {
2515  $storeQueryConfigs[$index][$k] = $this->MOD_SETTINGS[$k] ?? null;
2516  }
2517 
2518  return $storeQueryConfigs;
2519  }
2520 
2521  protected function ‪loadStoreQueryConfigs(array $storeQueryConfigs, int $storeIndex, array $writeArray): array
2522  {
2523  if ($storeQueryConfigs[$storeIndex]) {
2524  $keyArr = explode(',', $this->storeList);
2525  foreach ($keyArr as $k) {
2526  $writeArray[$k] = $storeQueryConfigs[$storeIndex][$k];
2527  }
2528  }
2529 
2530  return $writeArray;
2531  }
2532 
2533  protected function ‪initStoreArray(): array
2534  {
2535  $storeArray = [
2536  '0' => '[New]',
2537  ];
2538  $savedStoreArray = unserialize($this->MOD_SETTINGS['storeArray'] ?? '', ['allowed_classes' => false]);
2539  if (is_array($savedStoreArray)) {
2540  $storeArray = array_merge($storeArray, $savedStoreArray);
2541  }
2542 
2543  return $storeArray;
2544  }
2545 
2546  protected function ‪search(ServerRequestInterface $request): string
2547  {
2548  $swords = $this->MOD_SETTINGS['sword'] ?? '';
2549  $out = '';
2550  if ($swords) {
2551  foreach (‪$GLOBALS['TCA'] as ‪$table => $value) {
2552  // Get fields list
2553  $conf = ‪$GLOBALS['TCA'][‪$table];
2554  // Avoid querying tables with no columns
2555  if (empty($conf['columns'])) {
2556  continue;
2557  }
2558  $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable(‪$table);
2559  $identifierQuoteCharacter = $this->platformHelper->getIdentifierQuoteCharacter($connection->getDatabasePlatform());
2560  $tableColumns = $connection->createSchemaManager()->listTableColumns(‪$table);
2561  $normalizedTableColumns = [];
2562  $fieldsInDatabase = [];
2563  foreach ($tableColumns as $column) {
2564  $fieldsInDatabase[] = $column->getName();
2565  $normalizedTableColumns[trim($column->getName(), $identifierQuoteCharacter)] = $column;
2566  }
2567  ‪$fields = array_intersect(array_keys($conf['columns']), $fieldsInDatabase);
2568 
2569  $queryBuilder = $connection->createQueryBuilder();
2570  $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
2571  $queryBuilder->count('*')->from(‪$table);
2572  $likes = [];
2573  $escapedLikeString = '%' . $queryBuilder->escapeLikeWildcards($swords) . '%';
2574  foreach (‪$fields as $field) {
2575  $field = trim($field, $identifierQuoteCharacter);
2576  $quotedField = $queryBuilder->quoteIdentifier($field);
2577  $column = $normalizedTableColumns[$field] ?? $normalizedTableColumns[$quotedField] ?? null;
2578  if ($column !== null
2579  && $connection->getDatabasePlatform() instanceof DoctrinePostgreSQLPlatform
2580  && !in_array(Type::getTypeRegistry()->lookupName($column->getType()), [Types::STRING, Types::ASCII_STRING, Types::JSON], true)
2581  ) {
2582  if (Type::getTypeRegistry()->lookupName($column->getType()) === Types::SMALLINT) {
2583  // we need to cast smallint to int first, otherwise text case below won't work
2584  $quotedField .= '::int';
2585  }
2586  $quotedField .= '::text';
2587  }
2588  $likes[] = $queryBuilder->expr()->comparison(
2589  $quotedField,
2590  'LIKE',
2591  $queryBuilder->createNamedParameter($escapedLikeString)
2592  );
2593  }
2594  $queryBuilder->orWhere(...$likes);
2595  $count = $queryBuilder->executeQuery()->fetchOne();
2596 
2597  if ($count > 0) {
2598  $queryBuilder = $connection->createQueryBuilder();
2599  $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
2600  $queryBuilder->select('uid', $conf['ctrl']['label'])
2601  ->from(‪$table)
2602  ->setMaxResults(200);
2603  $likes = [];
2604  foreach (‪$fields as $field) {
2605  $field = trim($field, $identifierQuoteCharacter);
2606  $quotedField = $queryBuilder->quoteIdentifier($field);
2607  $column = $normalizedTableColumns[$field] ?? $normalizedTableColumns[$quotedField] ?? null;
2608  if ($column !== null
2609  && $connection->getDatabasePlatform() instanceof DoctrinePostgreSQLPlatform
2610  && !in_array(Type::getTypeRegistry()->lookupName($column->getType()), [Types::STRING, Types::ASCII_STRING, Types::JSON], true)
2611  ) {
2612  if (Type::getTypeRegistry()->lookupName($column->getType()) === Types::SMALLINT) {
2613  // we need to cast smallint to int first, otherwise text case below won't work
2614  $quotedField .= '::int';
2615  }
2616  $quotedField .= '::text';
2617  }
2618  $likes[] = $queryBuilder->expr()->comparison(
2619  $quotedField,
2620  'LIKE',
2621  $queryBuilder->createNamedParameter($escapedLikeString)
2622  );
2623  }
2624  $statement = $queryBuilder->orWhere(...$likes)->executeQuery();
2625  $lastRow = null;
2626  $rowArr = [];
2627  while ($row = $statement->fetchAssociative()) {
2628  $rowArr[] = $this->‪resultRowDisplay($row, $conf, ‪$table, $request);
2629  $lastRow = $row;
2630  }
2631  $markup = [];
2632  $markup[] = '<div class="panel panel-default">';
2633  $markup[] = ' <div class="panel-heading">';
2634  $markup[] = htmlspecialchars($this->‪getLanguageService()->sL($conf['ctrl']['title'])) . ' (' . $count . ')';
2635  $markup[] = ' </div>';
2636  $markup[] = ' <div class="table-fit">';
2637  $markup[] = ' <table class="table table-striped table-hover">';
2638  $markup[] = $this->‪resultRowTitles((array)$lastRow, $conf);
2639  $markup[] = implode(LF, $rowArr);
2640  $markup[] = ' </table>';
2641  $markup[] = ' </div>';
2642  $markup[] = '</div>';
2643 
2644  $out .= implode(LF, $markup);
2645  }
2646  }
2647  }
2648 
2649  return $out;
2650  }
2651 
2655  protected function ‪recordStatisticsAction(‪ModuleTemplate $view, ServerRequestInterface $request): ResponseInterface
2656  {
2657  $languageService = $this->‪getLanguageService();
2658  $databaseIntegrityCheck = GeneralUtility::makeInstance(DatabaseIntegrityCheck::class);
2659  $databaseIntegrityCheck->genTree(0);
2660 
2661  // Page stats
2662  $pageStatistic = [
2663  'total_pages' => [
2664  'icon' => $this->iconFactory->getIconForRecord('pages', [], IconSize::SMALL)->render(),
2665  'count' => count($databaseIntegrityCheck->getPageIdArray()),
2666  ],
2667  'translated_pages' => [
2668  'icon' => $this->iconFactory->getIconForRecord('pages', [], IconSize::SMALL)->render(),
2669  'count' => count($databaseIntegrityCheck->getPageTranslatedPageIDArray()),
2670  ],
2671  'hidden_pages' => [
2672  'icon' => $this->iconFactory->getIconForRecord('pages', ['hidden' => 1], IconSize::SMALL)->render(),
2673  'count' => $databaseIntegrityCheck->getRecStats()['hidden'] ?? 0,
2674  ],
2675  'deleted_pages' => [
2676  'icon' => $this->iconFactory->getIconForRecord('pages', ['deleted' => 1], IconSize::SMALL)->render(),
2677  'count' => isset($databaseIntegrityCheck->getRecStats()['deleted']['pages']) ? count($databaseIntegrityCheck->getRecStats()['deleted']['pages']) : 0,
2678  ],
2679  ];
2680 
2681  // doktypes stats
2682  $doktypes = [];
2683  $doktype = ‪$GLOBALS['TCA']['pages']['columns']['doktype']['config']['items'];
2684  if (is_array($doktype)) {
2685  foreach ($doktype as $setup) {
2686  if ($setup['value'] !== '--div--') {
2687  $doktypes[] = [
2688  'icon' => $this->iconFactory->getIconForRecord('pages', ['doktype' => $setup['value']], IconSize::SMALL)->render(),
2689  'title' => $languageService->sL($setup['label']) . ' (' . $setup['value'] . ')',
2690  'count' => (int)($databaseIntegrityCheck->getRecStats()['doktype'][$setup['value']] ?? 0),
2691  ];
2692  }
2693  }
2694  }
2695 
2696  // Tables and lost records
2697  $id_list = '-1,0,' . implode(',', array_keys($databaseIntegrityCheck->getPageIdArray()));
2698  $id_list = rtrim($id_list, ',');
2699  $databaseIntegrityCheck->lostRecords($id_list);
2700 
2701  // Fix a lost record if requested
2702  $fixSingleLostRecordTableName = (string)($request->getQueryParams()['fixLostRecords_table'] ?? '');
2703  $fixSingleLostRecordUid = (int)($request->getQueryParams()['fixLostRecords_uid'] ?? 0);
2704  if (!empty($fixSingleLostRecordTableName) && $fixSingleLostRecordUid
2705  && $databaseIntegrityCheck->fixLostRecord($fixSingleLostRecordTableName, $fixSingleLostRecordUid)
2706  ) {
2707  $databaseIntegrityCheck = GeneralUtility::makeInstance(DatabaseIntegrityCheck::class);
2708  $databaseIntegrityCheck->genTree(0);
2709  $id_list = '-1,0,' . implode(',', array_keys($databaseIntegrityCheck->getPageIdArray()));
2710  $id_list = rtrim($id_list, ',');
2711  $databaseIntegrityCheck->lostRecords($id_list);
2712  }
2713 
2714  $tableStatistic = [];
2715  $countArr = $databaseIntegrityCheck->countRecords($id_list);
2716  if (is_array(‪$GLOBALS['TCA'])) {
2717  foreach (‪$GLOBALS['TCA'] as $t => $value) {
2718  if (‪$GLOBALS['TCA'][$t]['ctrl']['hideTable'] ?? false) {
2719  continue;
2720  }
2721  if ($t === 'pages' && $databaseIntegrityCheck->getLostPagesList() !== '') {
2722  $lostRecordCount = count(explode(',', $databaseIntegrityCheck->getLostPagesList()));
2723  } else {
2724  $lostRecordCount = isset($databaseIntegrityCheck->getLRecords()[$t]) ? count($databaseIntegrityCheck->getLRecords()[$t]) : 0;
2725  }
2726  $recordCount = 0;
2727  if ($countArr['all'][$t] ?? false) {
2728  $recordCount = (int)($countArr['non_deleted'][$t] ?? 0) . '/' . $lostRecordCount;
2729  }
2730  $lostRecordList = [];
2731  if (is_array($databaseIntegrityCheck->getLRecords()[$t] ?? false)) {
2732  foreach ($databaseIntegrityCheck->getLRecords()[$t] as $data) {
2733  if (!‪GeneralUtility::inList($databaseIntegrityCheck->getLostPagesList(), $data['pid'])) {
2734  $fixLink = (string)$this->uriBuilder->buildUriFromRoute(
2735  $this->moduleName,
2736  ['SET' => ['function' => 'records'], 'fixLostRecords_table' => $t, 'fixLostRecords_uid' => $data['uid']]
2737  );
2738  $lostRecordList[] =
2739  '<div class="record">' .
2740  '<a href="' . htmlspecialchars($fixLink) . '" title="' . htmlspecialchars($languageService->sL('LLL:EXT:lowlevel/Resources/Private/Language/locallang.xlf:fixLostRecord')) . '">' .
2741  $this->iconFactory->getIcon('status-dialog-error', IconSize::SMALL)->render() .
2742  '</a>uid:' . $data['uid'] . ', pid:' . $data['pid'] . ', ' . htmlspecialchars(‪GeneralUtility::fixed_lgd_cs(strip_tags($data['title']), 20)) .
2743  '</div>';
2744  } else {
2745  $lostRecordList[] =
2746  '<div class="record-noicon">' .
2747  'uid:' . $data['uid'] . ', pid:' . $data['pid'] . ', ' . htmlspecialchars(‪GeneralUtility::fixed_lgd_cs(strip_tags($data['title']), 20)) .
2748  '</div>';
2749  }
2750  }
2751  }
2752  $tableStatistic[$t] = [
2753  'icon' => $this->iconFactory->getIconForRecord($t, [], IconSize::SMALL)->render(),
2754  'title' => $languageService->sL(‪$GLOBALS['TCA'][$t]['ctrl']['title']),
2755  'count' => $recordCount,
2756  'lostRecords' => implode(LF, $lostRecordList),
2757  ];
2758  }
2759  }
2760 
2761  $view->‪assignMultiple([
2762  'pages' => $pageStatistic,
2763  'doktypes' => $doktypes,
2764  'tables' => $tableStatistic,
2765  ]);
2766 
2767  return $view->‪renderResponse('RecordStatistics');
2768  }
2769 
2773  protected function ‪relationsAction(‪ModuleTemplate $view): ResponseInterface
2774  {
2775  $databaseIntegrityCheck = GeneralUtility::makeInstance(DatabaseIntegrityCheck::class);
2776  $databaseIntegrityCheck->selectNonEmptyRecordsWithFkeys();
2777  $view->‪assignMultiple([
2778  'select_db' => $databaseIntegrityCheck->testDBRefs($databaseIntegrityCheck->getCheckSelectDBRefs()),
2779  'group_db' => $databaseIntegrityCheck->testDBRefs($databaseIntegrityCheck->getCheckGroupDBRefs()),
2780  ]);
2781 
2782  return $view->‪renderResponse('Relations');
2783  }
2784 
2786  {
2787  return ‪$GLOBALS['BE_USER'];
2788  }
2789 
2791  {
2792  return ‪$GLOBALS['LANG'];
2793  }
2794 
2795  //################################
2796  // copied over from BackendUtility to enable deprecation of the original method
2797  // @todo finish fluidification of template and remove HTML generation from controller
2798  //################################
2799 
2808  protected function ‪getDropdownMenu(
2809  string $elementName,
2810  string|int $currentValue,
2811  mixed $menuItems,
2812  ServerRequestInterface $request
2813  ) {
2814  if (!is_array($menuItems) || count($menuItems) <= 1) {
2815  return '';
2816  }
2817  $scriptUrl = $this->uriBuilder->buildUriFromRequest($request);
2818  $options = [];
2819  foreach ($menuItems as $value => $label) {
2820  $options[] = '<option value="'
2821  . htmlspecialchars($value) . '"'
2822  . ((string)$currentValue === (string)$value ? ' selected="selected"' : '') . '>'
2823  . htmlspecialchars($label, ENT_COMPAT, 'UTF-8', false) . '</option>';
2824  }
2825  $dataMenuIdentifier = str_replace(['SET[', ']'], '', $elementName);
2826  $dataMenuIdentifier = ‪GeneralUtility::camelCaseToLowerCaseUnderscored($dataMenuIdentifier);
2827  $dataMenuIdentifier = str_replace('_', '-', $dataMenuIdentifier);
2828  // relies on module 'TYPO3/CMS/Backend/ActionDispatcher'
2829  $attributes = GeneralUtility::implodeAttributes([
2830  'name' => $elementName,
2831  'data-menu-identifier' => $dataMenuIdentifier,
2832  'data-global-event' => 'change',
2833  'data-action-navigate' => '$data=~s/$value/',
2834  'data-navigate-value' => $scriptUrl . '&' . $elementName . '=${value}',
2835  ], true);
2836 
2837  return '
2838  <select class="form-select" ' . $attributes . '>
2839  ' . implode(LF, $options) . '
2840  </select>';
2841  }
2842 
2851  protected function ‪getFuncCheck(
2852  string $elementName,
2853  string|bool|int $currentValue,
2854  ServerRequestInterface $request,
2855  string $tagParams = ''
2856  ) {
2857  // relies on module 'TYPO3/CMS/Backend/ActionDispatcher'
2858  $scriptUrl = $this->uriBuilder->buildUriFromRequest($request);
2859  $attributes = GeneralUtility::implodeAttributes([
2860  'type' => 'checkbox',
2861  'class' => 'form-check-input',
2862  'name' => $elementName,
2863  'value' => '1',
2864  'data-global-event' => 'change',
2865  'data-action-navigate' => '$data=~s/$value/',
2866  'data-navigate-value' => sprintf('%s&%s=${value}', $scriptUrl, $elementName),
2867  'data-empty-value' => '0',
2868  ], true);
2869 
2870  return
2871  '<input ' . $attributes .
2872  ($currentValue ? ' checked="checked"' : '') .
2873  ($tagParams ? ' ' . $tagParams : '') .
2874  ' />';
2875  }
2876 }
‪TYPO3\CMS\Core\Database\Query\QueryHelper\parseOrderBy
‪static array array[] parseOrderBy(string $input)
Definition: QueryHelper.php:44
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\$formName
‪string $formName
Definition: DatabaseIntegrityController.php:80
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\search
‪search(ServerRequestInterface $request)
Definition: DatabaseIntegrityController.php:2546
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\procesStoreControl
‪procesStoreControl(ServerRequestInterface $request)
Definition: DatabaseIntegrityController.php:2426
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:27
‪TYPO3\CMS\Core\Database\Connection\PARAM_INT
‪const PARAM_INT
Definition: Connection.php:52
‪TYPO3\CMS\Backend\Template\Components\ButtonBar
Definition: ButtonBar.php:33
‪TYPO3\CMS\Core\Utility\GeneralUtility\fixed_lgd_cs
‪static string fixed_lgd_cs(string $string, int $chars, string $appendString='...')
Definition: GeneralUtility.php:92
‪TYPO3\CMS\Backend\Template\ModuleTemplate\assignMultiple
‪assignMultiple(array $values)
Definition: ModuleTemplate.php:103
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\resultRowTitles
‪resultRowTitles(?array $row, array $conf)
Definition: DatabaseIntegrityController.php:772
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\getQuerySingle
‪getQuerySingle(array $conf, bool $first)
Definition: DatabaseIntegrityController.php:1216
‪TYPO3\CMS\Backend\Template\ModuleTemplateFactory
Definition: ModuleTemplateFactory.php:33
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\makeFieldList
‪makeFieldList()
Definition: DatabaseIntegrityController.php:2367
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\handleRequest
‪handleRequest(ServerRequestInterface $request)
Definition: DatabaseIntegrityController.php:231
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\$tableArray
‪array $tableArray
Definition: DatabaseIntegrityController.php:92
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\makeStoreControl
‪makeStoreControl()
Definition: DatabaseIntegrityController.php:2389
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\isDateOfIso8601Format
‪isDateOfIso8601Format(mixed $date)
Definition: DatabaseIntegrityController.php:1315
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\verifyType
‪verifyType(string $fieldName)
Definition: DatabaseIntegrityController.php:1524
‪TYPO3\CMS\Core\Database\ReferenceIndex
Definition: ReferenceIndex.php:40
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\$lang
‪array $lang
Definition: DatabaseIntegrityController.php:98
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\setAndCleanUpExternalLists
‪setAndCleanUpExternalLists(string $name, string $list, string $force='')
Definition: DatabaseIntegrityController.php:2187
‪TYPO3\CMS\Core\Database\Query\QueryHelper\parseGroupBy
‪static array string[] parseGroupBy(string $input)
Definition: QueryHelper.php:102
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\$fieldName
‪string $fieldName
Definition: DatabaseIntegrityController.php:156
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\mkTableSelect
‪mkTableSelect(string $name, string $cur)
Definition: DatabaseIntegrityController.php:2199
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\mkTypeSelect
‪mkTypeSelect(string $name, string $fieldName, string $prepend='FIELD_')
Definition: DatabaseIntegrityController.php:2163
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\$queryConfig
‪array $queryConfig
Definition: DatabaseIntegrityController.php:93
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\relationsAction
‪relationsAction(ModuleTemplate $view)
Definition: DatabaseIntegrityController.php:2773
‪TYPO3\CMS\Backend\Template\ModuleTemplate\renderResponse
‪renderResponse(string $templateFileName='')
Definition: ModuleTemplate.php:122
‪TYPO3\CMS\Core\Utility\DebugUtility\viewArray
‪static string viewArray(mixed $array_in)
Definition: DebugUtility.php:113
‪TYPO3\CMS\Core\Localization\Locales
Definition: Locales.php:36
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\$MOD_SETTINGS
‪array $MOD_SETTINGS
Definition: DatabaseIntegrityController.php:78
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\$enableQueryParts
‪bool $enableQueryParts
Definition: DatabaseIntegrityController.php:97
‪TYPO3\CMS\Core\Imaging\IconFactory
Definition: IconFactory.php:34
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\$storeList
‪string $storeList
Definition: DatabaseIntegrityController.php:96
‪TYPO3\CMS\Core\Utility\GeneralUtility\camelCaseToLowerCaseUnderscored
‪static string camelCaseToLowerCaseUnderscored($string)
Definition: GeneralUtility.php:678
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\$moduleName
‪string $moduleName
Definition: DatabaseIntegrityController.php:81
‪TYPO3\CMS\Core\Messaging\FlashMessageRendererResolver
Definition: FlashMessageRendererResolver.php:33
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\cleanUpQueryConfig
‪cleanUpQueryConfig(array $queryConfig)
Definition: DatabaseIntegrityController.php:1478
‪TYPO3\CMS\Backend\Template\ModuleTemplate
Definition: ModuleTemplate.php:46
‪TYPO3\CMS\Core\Database\ConnectionPool\DEFAULT_CONNECTION_NAME
‪const DEFAULT_CONNECTION_NAME
Definition: ConnectionPool.php:50
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\makeSelectorTable
‪makeSelectorTable(array $modSettings, ServerRequestInterface $request, string $enableList='table, fields, query, group, order, limit')
Definition: DatabaseIntegrityController.php:1326
‪TYPO3\CMS\Core\Type\Bitmask\Permission
Definition: Permission.php:26
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\searchAction
‪searchAction(ModuleTemplate $view, ServerRequestInterface $request)
Definition: DatabaseIntegrityController.php:422
‪TYPO3\CMS\Core\Localization\DateFormatter
Definition: DateFormatter.php:27
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility
Definition: ExtensionManagementUtility.php:32
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\$enablePrefix
‪bool $enablePrefix
Definition: DatabaseIntegrityController.php:90
‪TYPO3\CMS\Core\Type\ContextualFeedbackSeverity
‪ContextualFeedbackSeverity
Definition: ContextualFeedbackSeverity.php:25
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\getBackendUserAuthentication
‪getBackendUserAuthentication()
Definition: DatabaseIntegrityController.php:2785
‪TYPO3\CMS\Core\Utility\PathUtility\getAbsoluteWebPath
‪static string getAbsoluteWebPath(string $targetPath, bool $prefixWithSitePath=true)
Definition: PathUtility.php:52
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger(mixed $var)
Definition: MathUtility.php:69
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\convertIso8601DatetimeStringToUnixTimestamp
‪convertIso8601DatetimeStringToUnixTimestamp(array $conf)
Definition: DatabaseIntegrityController.php:1300
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\$noDownloadB
‪int $noDownloadB
Definition: DatabaseIntegrityController.php:91
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\extPath
‪static extPath(string $key, string $script='')
Definition: ExtensionManagementUtility.php:82
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\getTreeList
‪string getTreeList(int $id, int $depth, int $begin=0, string $permsClause='')
Definition: DatabaseIntegrityController.php:629
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\resultRowDisplay
‪resultRowDisplay(array $row, array $conf, string $table, ServerRequestInterface $request)
Definition: DatabaseIntegrityController.php:801
‪TYPO3\CMS\Core\Database\Query\QueryHelper
Definition: QueryHelper.php:32
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\cleanInputVal
‪mixed cleanInputVal(array $conf, string $suffix='')
Definition: DatabaseIntegrityController.php:1269
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\getLabelCol
‪getLabelCol()
Definition: DatabaseIntegrityController.php:2158
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\$fieldList
‪string $fieldList
Definition: DatabaseIntegrityController.php:158
‪TYPO3\CMS\Core\Database\Query\QueryHelper\getDateTimeTypes
‪static array getDateTimeTypes()
Definition: QueryHelper.php:211
‪TYPO3\CMS\Backend\Routing\UriBuilder
Definition: UriBuilder.php:44
‪TYPO3\CMS\Core\Utility\HttpUtility\buildQueryString
‪static string buildQueryString(array $parameters, string $prependCharacter='', bool $skipEmptyParameters=false)
Definition: HttpUtility.php:124
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\addToStoreQueryConfigs
‪addToStoreQueryConfigs(array $storeQueryConfigs, int $index)
Definition: DatabaseIntegrityController.php:2510
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\procesData
‪procesData(ServerRequestInterface $request, array $qC=[])
Definition: DatabaseIntegrityController.php:2038
‪TYPO3\CMS\Core\Utility\CsvUtility
Definition: CsvUtility.php:26
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\mkFieldToInputSelect
‪mkFieldToInputSelect(string $name, string $fieldName)
Definition: DatabaseIntegrityController.php:2016
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication
Definition: BackendUserAuthentication.php:61
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\csvValues
‪csvValues(array $row, string $delim=',', string $quote='"', array $conf = [], string $table = '')
Definition: DatabaseIntegrityController.php:757
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\getDateTimePickerField
‪getDateTimePickerField(string $name, string $timestamp, string $type)
Definition: DatabaseIntegrityController.php:1727
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\$fields
‪array $fields
Definition: DatabaseIntegrityController.php:95
‪TYPO3\CMS\Core\Type\Bitmask\Permission\PAGE_SHOW
‪const PAGE_SHOW
Definition: Permission.php:35
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\referenceIndexAction
‪referenceIndexAction(ModuleTemplate $view, ServerRequestInterface $request)
Definition: DatabaseIntegrityController.php:400
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\$extFieldLists
‪array $extFieldLists
Definition: DatabaseIntegrityController.php:94
‪TYPO3\CMS\Core\Utility\DebugUtility
Definition: DebugUtility.php:27
‪TYPO3\CMS\Lowlevel\Controller
Definition: ConfigurationController.php:18
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\getFuncCheck
‪string getFuncCheck(string $elementName, string|bool|int $currentValue, ServerRequestInterface $request, string $tagParams='')
Definition: DatabaseIntegrityController.php:2851
‪$output
‪$output
Definition: annotationChecker.php:114
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\init
‪init(string $name, string $table, string $fieldList='', array $settings=[])
Definition: DatabaseIntegrityController.php:2227
‪TYPO3\CMS\Core\Database\Connection
Definition: Connection.php:41
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\initStoreArray
‪initStoreArray()
Definition: DatabaseIntegrityController.php:2533
‪TYPO3\CMS\Webhooks\Message\$url
‪identifier readonly UriInterface $url
Definition: LoginErrorOccurredMessage.php:36
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\updateIcon
‪updateIcon()
Definition: DatabaseIntegrityController.php:2182
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\getFormElements
‪getFormElements(int $subLevel=0, $queryConfig='', string $parent='')
Definition: DatabaseIntegrityController.php:1561
‪TYPO3\CMS\Backend\Template\ModuleTemplate\getDocHeaderComponent
‪getDocHeaderComponent()
Definition: ModuleTemplate.php:181
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\makeComparisonSelector
‪makeComparisonSelector(string $subscript, string $fieldName, array $conf)
Definition: DatabaseIntegrityController.php:1955
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\cleanStoreQueryConfigs
‪cleanStoreQueryConfigs(array $storeQueryConfigs, array $storeArray)
Definition: DatabaseIntegrityController.php:2497
‪TYPO3\CMS\Lowlevel\Integrity\DatabaseIntegrityCheck
Definition: DatabaseIntegrityCheck.php:43
‪TYPO3\CMS\Core\Messaging\FlashMessage
Definition: FlashMessage.php:27
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\$showFieldAndTableNames
‪bool $showFieldAndTableNames
Definition: DatabaseIntegrityController.php:87
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\getDropdownMenu
‪string getDropdownMenu(string $elementName, string|int $currentValue, mixed $menuItems, ServerRequestInterface $request)
Definition: DatabaseIntegrityController.php:2808
‪TYPO3\CMS\Core\Database\Query\QueryHelper\stripLogicalOperatorPrefix
‪static string stripLogicalOperatorPrefix(string $constraint)
Definition: QueryHelper.php:171
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\mkOperatorSelect
‪mkOperatorSelect(string $name, string $op, bool $draw, bool $submit)
Definition: DatabaseIntegrityController.php:1938
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\$comp_offsets
‪array $comp_offsets
Definition: DatabaseIntegrityController.php:159
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\$table
‪string $table
Definition: DatabaseIntegrityController.php:89
‪TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction
Definition: DeletedRestriction.php:28
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\mkCompSelect
‪mkCompSelect(string $name, string $comparison, int $neg)
Definition: DatabaseIntegrityController.php:1976
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\$hookArray
‪array $hookArray
Definition: DatabaseIntegrityController.php:88
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\makeOptionList
‪makeOptionList(string $fieldName, array $conf, string $table)
Definition: DatabaseIntegrityController.php:1745
‪TYPO3\CMS\Core\Localization\Locale
Definition: Locale.php:30
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:24
‪TYPO3\CMS\Backend\Template\ModuleTemplate\assign
‪assign(string $key, mixed $value)
Definition: ModuleTemplate.php:94
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\$name
‪string $name
Definition: DatabaseIntegrityController.php:157
‪TYPO3\CMS\Core\Utility\GeneralUtility\inList
‪static bool inList($list, $item)
Definition: GeneralUtility.php:422
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\setUpDocHeader
‪setUpDocHeader(ModuleTemplate $moduleTemplate)
Definition: DatabaseIntegrityController.php:357
‪TYPO3\CMS\Backend\Attribute\AsController
Definition: AsController.php:25
‪TYPO3\CMS\Core\Utility\HttpUtility
Definition: HttpUtility.php:24
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\recordStatisticsAction
‪recordStatisticsAction(ModuleTemplate $view, ServerRequestInterface $request)
Definition: DatabaseIntegrityController.php:2655
‪TYPO3\CMS\Core\Localization\LanguageService
Definition: LanguageService.php:46
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\getQueryResultCode
‪array getQueryResultCode(string $type, array $dataRows, string $table, ServerRequestInterface $request)
Definition: DatabaseIntegrityController.php:674
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\$MOD_MENU
‪array $MOD_MENU
Definition: DatabaseIntegrityController.php:73
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\getSubscript
‪getSubscript($arr)
Definition: DatabaseIntegrityController.php:2141
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\getProcessedValueExtra
‪getProcessedValueExtra(string $table, string $fieldName, string $fieldValue, array $conf, string $splitString)
Definition: DatabaseIntegrityController.php:869
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\verifyComparison
‪verifyComparison($comparison, int $neg)
Definition: DatabaseIntegrityController.php:1542
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\renderNoResultsFoundMessage
‪renderNoResultsFoundMessage()
Definition: DatabaseIntegrityController.php:1184
‪TYPO3\CMS\Core\Utility\StringUtility
Definition: StringUtility.php:24
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\queryMaker
‪queryMaker(ServerRequestInterface $request)
Definition: DatabaseIntegrityController.php:459
‪TYPO3\CMS\Backend\Template\Components\ButtonBar\BUTTON_POSITION_RIGHT
‪const BUTTON_POSITION_RIGHT
Definition: ButtonBar.php:42
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\menuConfig
‪menuConfig(ServerRequestInterface $request)
Definition: DatabaseIntegrityController.php:259
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController
Definition: DatabaseIntegrityController.php:69
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\getSelectQuery
‪getSelectQuery(string $qString='')
Definition: DatabaseIntegrityController.php:543
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\getQuery
‪getQuery(array $queryConfig, string $pad='')
Definition: DatabaseIntegrityController.php:1192
‪TYPO3\CMS\Core\Utility\GeneralUtility\intExplode
‪static list< int > intExplode(string $delimiter, string $string, bool $removeEmptyValues=false)
Definition: GeneralUtility.php:751
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\__construct
‪__construct(protected IconFactory $iconFactory, protected readonly UriBuilder $uriBuilder, protected readonly ModuleTemplateFactory $moduleTemplateFactory, protected readonly PlatformHelper $platformHelper,)
Definition: DatabaseIntegrityController.php:224
‪TYPO3\CMS\Core\Database\Connection\PARAM_INT_ARRAY
‪const PARAM_INT_ARRAY
Definition: Connection.php:72
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\$compSQL
‪array $compSQL
Definition: DatabaseIntegrityController.php:169
‪TYPO3\CMS\Core\Messaging\FlashMessageService
Definition: FlashMessageService.php:27
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\loadStoreQueryConfigs
‪loadStoreQueryConfigs(array $storeQueryConfigs, int $storeIndex, array $writeArray)
Definition: DatabaseIntegrityController.php:2521
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\printCodeArray
‪printCodeArray(array $codeArr, int $recursionLevel=0)
Definition: DatabaseIntegrityController.php:1991
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static list< string > trimExplode(string $delim, string $string, bool $removeEmptyValues=false, int $limit=0)
Definition: GeneralUtility.php:817
‪TYPO3\CMS\Core\Utility\StringUtility\getUniqueId
‪static getUniqueId(string $prefix='')
Definition: StringUtility.php:57
‪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\Platform\PlatformHelper
Definition: PlatformHelper.php:26
‪TYPO3\CMS\Lowlevel\Controller\DatabaseIntegrityController\getLanguageService
‪getLanguageService()
Definition: DatabaseIntegrityController.php:2790