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