‪TYPO3CMS  ‪main
SearchController.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 Psr\Http\Message\ResponseInterface;
44 
53 {
59  protected ‪$sword = '';
60 
64  protected ‪$searchWords = [];
65 
69  protected ‪$searchData;
70 
80  protected ‪$searchRootPageIdList = 0;
81 
85  protected ‪$defaultResultNumber = 10;
86 
91 
97  protected ‪$searchRepository;
98 
104  protected ‪$lexerObj;
105 
110  protected ‪$externalParsers = [];
111 
117  protected ‪$firstRow = [];
118 
125  protected ‪$domainRecords = [];
126 
133 
139  protected ‪$resultSections = [];
140 
146  protected ‪$pathCache = [];
147 
153  protected ‪$iconFileNameCache = [];
154 
160  protected ‪$indexerConfig = [];
161 
167  protected ‪$enableMetaphoneSearch = false;
168 
172  protected ‪$typoScriptService;
173 
175  {
176  $this->typoScriptService = ‪$typoScriptService;
177  }
178 
185  protected function ‪initialize(‪$searchData = [])
186  {
187  if (!is_array(‪$searchData)) {
188  ‪$searchData = [];
189  }
190 
191  // Sets availableResultsNumbers - has to be called before request settings are read to avoid DoS attack
192  $this->availableResultsNumbers = array_filter(GeneralUtility::intExplode(',', (string)($this->settings['blind']['numberOfResults'] ?? '')));
193 
194  // Sets default result number if at least one availableResultsNumbers exists
195  if (isset($this->availableResultsNumbers[0])) {
196  $this->defaultResultNumber = $this->availableResultsNumbers[0];
197  }
198 
199  $this->‪loadSettings();
200 
201  // setting default values
202  if (is_array($this->settings['defaultOptions'])) {
203  ‪$searchData = array_merge($this->settings['defaultOptions'], ‪$searchData);
204  }
205  // if "languageUid" was set to "current", take the current site language
206  if ((‪$searchData['languageUid'] ?? '') === 'current') {
207  ‪$searchData['languageUid'] = GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect('language', 'id', 0);
208  }
209 
210  // Indexer configuration from Extension Manager interface:
211  $this->indexerConfig = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('indexed_search');
212  $this->enableMetaphoneSearch = (bool)($this->indexerConfig['enableMetaphoneSearch'] ?? false);
214  // If "_sections" is set, this value overrides any existing value.
215  if (‪$searchData['_sections'] ?? false) {
216  ‪$searchData['sections'] = ‪$searchData['_sections'];
217  }
218  // If "_sections" is set, this value overrides any existing value.
219  if ((‪$searchData['_freeIndexUid'] ?? '') !== '' && (‪$searchData['_freeIndexUid'] ?? '') !== '_') {
220  ‪$searchData['freeIndexUid'] = ‪$searchData['_freeIndexUid'];
221  }
222  ‪$searchData['numberOfResults'] = $this->‪getNumberOfResults(‪$searchData['numberOfResults'] ?? 0);
223  // This gets the search-words into the $searchWordArray
224  $this->‪setSword(‪$searchData['sword'] ?? '');
225  // Add previous search words to current
226  if ((‪$searchData['sword_prev_include'] ?? false) && (‪$searchData['sword_prev'] ?? false)) {
227  $this->‪setSword(trim(‪$searchData['sword_prev']) . ' ' . $this->‪getSword());
228  }
229  // This is the id of the site root.
230  // This value may be a commalist of integer (prepared for this)
231  $this->searchRootPageIdList = (int)‪$GLOBALS['TSFE']->config['rootLine'][0]['uid'];
232  // Setting the list of root PIDs for the search. Notice, these page IDs MUST
233  // have a TypoScript with root flag on them! Basically this list is used
234  // to select on the "rl0" field and page ids are registered as "rl0" only if
235  // a TypoScript record with root flag is there.
236  // This happens AFTER the use of $this->searchRootPageIdList above because
237  // the above will then fetch the menu for the CURRENT site - regardless
238  // of this kind of searching here. Thus a general search will lookup in
239  // the WHOLE database while a specific section search will take the current sections.
240  $rootPidListFromSettings = (string)($this->settings['rootPidList'] ?? '');
241  if ($rootPidListFromSettings) {
242  $this->searchRootPageIdList = implode(',', GeneralUtility::intExplode(',', $rootPidListFromSettings));
243  }
244  $this->searchRepository = GeneralUtility::makeInstance(IndexSearchRepository::class);
245  $this->searchRepository->initialize($this->settings, ‪$searchData, $this->externalParsers, $this->searchRootPageIdList);
246  $this->searchData = ‪$searchData;
247  // $this->searchData is used in $this->getSearchWords
248  $this->searchWords = $this->‪getSearchWords(‪$searchData['defaultOperand']);
249  // Calling hook for modification of initialized content
250  if ($hookObj = $this->‪hookRequest('initialize_postProc')) {
251  $hookObj->initialize_postProc();
252  }
253  return ‪$searchData;
254  }
255 
262  public function ‪searchAction($search = []): ResponseInterface
263  {
264  // check if TypoScript is loaded
265  if (!isset($this->settings['results'])) {
266  return $this->‪redirect('noTypoScript');
267  }
268 
269  ‪$searchData = $this->‪initialize($search);
270  // Find free index uid:
271  $freeIndexUid = ‪$searchData['freeIndexUid'];
272  if ($freeIndexUid == -2) {
273  $freeIndexUid = $this->settings['defaultFreeIndexUidList'];
274  } elseif (!isset(‪$searchData['freeIndexUid'])) {
275  // index configuration is disabled
276  $freeIndexUid = -1;
277  }
278 
279  if (!empty(‪$searchData['extendedSearch'])) {
280  $this->view->assignMultiple($this->‪processExtendedSearchParameters());
281  }
282 
283  $indexCfgs = GeneralUtility::intExplode(',', (string)$freeIndexUid);
284  $resultsets = [];
285  foreach ($indexCfgs as $freeIndexUid) {
286  // Get result rows
287  if ($hookObj = $this->‪hookRequest('getResultRows')) {
288  $resultData = $hookObj->getResultRows($this->searchWords, $freeIndexUid);
289  } else {
290  $resultData = $this->searchRepository->doSearch($this->searchWords, $freeIndexUid);
291  }
292  // Display search results
293  if ($hookObj = $this->‪hookRequest('getDisplayResults')) {
294  $resultsets[$freeIndexUid] = $hookObj->getDisplayResults($this->searchWords, $resultData, $freeIndexUid);
295  } else {
296  $resultsets[$freeIndexUid] = $this->‪getDisplayResults($this->searchWords, $resultData, $freeIndexUid);
297  }
298  // Create header if we are searching more than one indexing configuration
299  if (count($indexCfgs) > 1) {
300  if ($freeIndexUid > 0) {
301  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
302  ->getQueryBuilderForTable('index_config');
303  $indexCfgRec = $queryBuilder
304  ->select('title')
305  ->from('index_config')
306  ->where(
307  $queryBuilder->expr()->eq(
308  'uid',
309  $queryBuilder->createNamedParameter($freeIndexUid, ‪Connection::PARAM_INT)
310  )
311  )
312  ->executeQuery()
313  ->fetchAssociative();
314  $categoryTitle = ‪LocalizationUtility::translate('indexingConfigurationHeader.' . $freeIndexUid, 'IndexedSearch');
315  $categoryTitle = $categoryTitle ?: $indexCfgRec['title'];
316  } else {
317  $categoryTitle = ‪LocalizationUtility::translate('indexingConfigurationHeader.' . $freeIndexUid, 'IndexedSearch');
318  }
319  $resultsets[$freeIndexUid]['categoryTitle'] = $categoryTitle;
320  }
321  // Write search statistics
322  $this->‪writeSearchStat($this->searchWords ?: []);
323  }
324  $this->view->assign('resultsets', $resultsets);
325  $this->view->assign('searchParams', ‪$searchData);
326  $this->view->assign('searchWords', array_map([$this, 'addOperatorLabel'], $this->searchWords));
327 
328  return $this->‪htmlResponse();
329  }
330 
331  /****************************************
332  * functions to make the result rows and result sets
333  * ready for the output
334  ***************************************/
343  protected function ‪getDisplayResults(‪$searchWords, $resultData, $freeIndexUid = -1)
344  {
345  $result = [
346  'count' => $resultData['count'] ?? 0,
347  'searchWords' => ‪$searchWords,
348  ];
349  // Perform display of result rows array
350  if ($resultData) {
351  // Set first selected row (for calculation of ranking later)
352  $this->firstRow = $resultData['firstRow'];
353  // Result display here
354  $result['rows'] = $this->‪compileResultRows($resultData['resultRows'], $freeIndexUid);
355  $result['affectedSections'] = ‪$this->resultSections;
356  // Browsing box
357  if ($resultData['count']) {
358  // could we get this in the view?
359  if (($this->searchData['group'] ?? '') === 'sections' && $freeIndexUid <= 0) {
360  $resultSectionsCount = count($this->resultSections);
361  $result['sectionText'] = sprintf(‪LocalizationUtility::translate('result.' . ($resultSectionsCount > 1 ? 'inNsections' : 'inNsection'), 'IndexedSearch') ?? '', $resultSectionsCount);
362  }
363  }
364  }
365  // Print a message telling which words in which sections we searched for
366  if (str_starts_with($this->searchData['sections'], 'rl')) {
367  $result['searchedInSectionInfo'] = (‪LocalizationUtility::translate('result.inSection', 'IndexedSearch') ?? '') . ' "' . $this->‪getPathFromPageId((int)substr($this->searchData['sections'], 4)) . '"';
368  }
369 
370  if ($hookObj = $this->‪hookRequest('getDisplayResults_postProc')) {
371  $result = $hookObj->getDisplayResults_postProc($result);
372  }
373 
374  return $result;
375  }
376 
385  protected function ‪compileResultRows($resultRows, $freeIndexUid = -1)
386  {
387  $finalResultRows = [];
388  // Transfer result rows to new variable,
389  // performing some mapping of sub-results etc.
390  $newResultRows = [];
391  foreach ($resultRows as $row) {
392  $id = md5((string)$row['phash_grouping']);
393  if (is_array($newResultRows[$id] ?? null)) {
394  // swapping:
395  if (!$newResultRows[$id]['show_resume'] && $row['show_resume']) {
396  // Remove old
397  $subrows = $newResultRows[$id]['_sub'];
398  unset($newResultRows[$id]['_sub']);
399  $subrows[] = $newResultRows[$id];
400  // Insert new:
401  $newResultRows[$id] = $row;
402  $newResultRows[$id]['_sub'] = $subrows;
403  } else {
404  $newResultRows[$id]['_sub'][] = $row;
405  }
406  } else {
407  $newResultRows[$id] = $row;
408  }
409  }
410  $resultRows = $newResultRows;
411  $this->resultSections = [];
412  if ($freeIndexUid <= 0 && ($this->searchData['group'] ?? '') === 'sections') {
413  $rl2flag = str_starts_with($this->searchData['sections'], 'rl');
414  $sections = [];
415  foreach ($resultRows as $row) {
416  $id = $row['rl0'] . '-' . $row['rl1'] . ($rl2flag ? '-' . $row['rl2'] : '');
417  $sections[$id][] = $row;
418  }
419  $this->resultSections = [];
420  foreach ($sections as $id => $resultRows) {
421  $rlParts = explode('-', $id);
422  if ($rlParts[2] ?? false) {
423  $theId = $rlParts[2];
424  $theRLid = 'rl2_' . $rlParts[2];
425  } elseif ($rlParts[1] ?? false) {
426  $theId = $rlParts[1];
427  $theRLid = 'rl1_' . $rlParts[1];
428  } else {
429  $theId = $rlParts[0] ?? 0;
430  $theRLid = '0';
431  }
432  $sectionName = $this->‪getPathFromPageId((int)$theId);
433  $sectionName = ltrim($sectionName, '/');
434  if (!trim($sectionName)) {
435  $sectionTitleLinked = ‪LocalizationUtility::translate('result.unnamedSection', 'IndexedSearch') . ':';
436  } else {
437  $onclick = 'document.forms[\'tx_indexedsearch\'][\'tx_indexedsearch_pi2[search][_sections]\'].value=' . GeneralUtility::quoteJSvalue($theRLid) . ';document.forms[\'tx_indexedsearch\'].submit();return false;';
438  $sectionTitleLinked = '<a href="#" onclick="' . htmlspecialchars($onclick) . '">' . $sectionName . ':</a>';
439  }
440  $resultRowsCount = count($resultRows);
441  $this->resultSections[$id] = [$sectionName, $resultRowsCount];
442  // Add section header
443  $finalResultRows[] = [
444  'isSectionHeader' => true,
445  'numResultRows' => $resultRowsCount,
446  'sectionId' => $id,
447  'sectionTitle' => $sectionTitleLinked,
448  ];
449  // Render result rows
450  foreach ($resultRows as $row) {
451  $finalResultRows[] = $this->‪compileSingleResultRow($row);
452  }
453  }
454  } else {
455  // flat mode or no sections at all
456  foreach ($resultRows as $row) {
457  $finalResultRows[] = $this->‪compileSingleResultRow($row);
458  }
459  }
460  return $finalResultRows;
461  }
462 
470  protected function ‪compileSingleResultRow($row, $headerOnly = 0)
471  {
472  $specRowConf = $this->‪getSpecialConfigurationForResultRow($row);
473  $resultData = $row;
474  $resultData['headerOnly'] = $headerOnly;
475  $resultData['CSSsuffix'] = ($specRowConf['CSSsuffix'] ?? false) ? '-' . $specRowConf['CSSsuffix'] : '';
476  if ($this->‪multiplePagesType($row['item_type']) && isset($row['static_page_arguments'])) {
477  $dat = json_decode($row['static_page_arguments'], true);
478  if (is_array($dat) && is_string($dat['key'] ?? null) && $dat['key'] !== '') {
479  $pp = explode('-', $dat['key']);
480  if ($pp[0] != $pp[1]) {
481  $resultData['titleaddition'] = ', ' . ‪LocalizationUtility::translate('result.pages', 'IndexedSearch') . ' ' . $dat['key'];
482  } else {
483  $resultData['titleaddition'] = ', ' . ‪LocalizationUtility::translate('result.page', 'IndexedSearch') . ' ' . $pp[0];
484  }
485  }
486  }
487  $title = $resultData['item_title'] . ($resultData['titleaddition'] ?? '');
488  $title = ‪GeneralUtility::fixed_lgd_cs($title, (int)$this->settings['results.']['titleCropAfter'], $this->settings['results.']['titleCropSignifier']);
489  // If external media, link to the media-file instead.
490  if ($row['item_type']) {
491  if ($row['show_resume']) {
492  $targetAttribute = '';
493  if (‪$GLOBALS['TSFE']->config['config']['fileTarget'] ?? false) {
494  $targetAttribute = ' target="' . htmlspecialchars(‪$GLOBALS['TSFE']->config['config']['fileTarget']) . '"';
495  }
496  $title = '<a href="' . htmlspecialchars($row['data_filename']) . '"' . $targetAttribute . '>' . htmlspecialchars($title) . '</a>';
497  } else {
498  // Suspicious, so linking to page instead...
499  $copiedRow = $row;
500  unset($copiedRow['static_page_arguments']);
501  $title = $this->‪linkPageATagWrap(
502  htmlspecialchars($title),
503  $this->‪linkPage($row['page_id'], $copiedRow)
504  );
505  }
506  } else {
507  // Else the page
508  $title = $this->‪linkPageATagWrap(
509  htmlspecialchars($title),
510  $this->‪linkPage($row['data_page_id'], $row)
511  );
512  }
513  $resultData['title'] = $title;
514  $resultData['icon'] = $this->‪makeItemTypeIcon($row['item_type'], '', $specRowConf);
515  $resultData['rating'] = $this->‪makeRating($row);
516  $resultData['description'] = $this->‪makeDescription(
517  $row,
518  (bool)!($this->searchData['extResume'] && !$headerOnly),
519  $this->settings['results.']['summaryCropAfter']
520  );
521  $resultData['language'] = $this->‪makeLanguageIndication($row);
522  $resultData['size'] = GeneralUtility::formatSize($row['item_size']);
523  $resultData['created'] = $row['item_crdate'];
524  $resultData['modified'] = $row['item_mtime'];
525  $pI = parse_url($row['data_filename']);
526  if ($pI['scheme'] ?? false) {
527  $targetAttribute = '';
528  if (‪$GLOBALS['TSFE']->config['config']['fileTarget'] ?? false) {
529  $targetAttribute = ' target="' . htmlspecialchars(‪$GLOBALS['TSFE']->config['config']['fileTarget']) . '"';
530  }
531  $resultData['pathTitle'] = $row['data_filename'];
532  $resultData['pathUri'] = $row['data_filename'];
533  $resultData['path'] = '<a href="' . htmlspecialchars($row['data_filename']) . '"' . $targetAttribute . '>' . htmlspecialchars($row['data_filename']) . '</a>';
534  } else {
535  $pathId = $row['data_page_id'] ?: $row['page_id'];
536  $pathMP = $row['data_page_id'] ? $row['data_page_mp'] : '';
537  $pathStr = $this->‪getPathFromPageId($pathId, $pathMP);
538  $pathLinkData = $this->‪linkPage(
539  $pathId,
540  [
541  'data_page_type' => $row['data_page_type'],
542  'data_page_mp' => $pathMP,
543  'sys_language_uid' => $row['sys_language_uid'],
544  'static_page_arguments' => $row['static_page_arguments'],
545  ]
546  );
547 
548  $resultData['pathTitle'] = $pathStr;
549  $resultData['pathUri'] = $pathLinkData['uri'];
550  $resultData['path'] = $this->‪linkPageATagWrap($pathStr, $pathLinkData);
551 
552  // check if the access is restricted
553  if (is_array($this->requiredFrontendUsergroups[$pathId]) && !empty($this->requiredFrontendUsergroups[$pathId])) {
554  $lockedIcon = ‪PathUtility::getPublicResourceWebPath('EXT:indexed_search/Resources/Public/Icons/FileTypes/locked.gif');
555  $resultData['access'] = '<img src="' . htmlspecialchars($lockedIcon) . '"'
556  . ' width="12" height="15" vspace="5" title="'
557  . sprintf(‪LocalizationUtility::translate('result.memberGroups', 'IndexedSearch') ?? '', implode(',', array_unique($this->requiredFrontendUsergroups[$pathId])))
558  . '" alt="" />';
559  }
560  }
561  // If there are subrows (eg. subpages in a PDF-file or if a duplicate page
562  // is selected due to user-login (phash_grouping))
563  if (is_array($row['_sub'] ?? false)) {
564  $resultData['subresults'] = [];
565  if ($this->‪multiplePagesType($row['item_type'])) {
566  $resultData['subresults']['header'] = ‪LocalizationUtility::translate('result.otherMatching', 'IndexedSearch');
567  foreach ($row['_sub'] as $subRow) {
568  $resultData['subresults']['items'][] = $this->‪compileSingleResultRow($subRow, 1);
569  }
570  } else {
571  $resultData['subresults']['header'] = ‪LocalizationUtility::translate('result.otherMatching', 'IndexedSearch');
572  $resultData['subresults']['info'] = ‪LocalizationUtility::translate('result.otherPageAsWell', 'IndexedSearch');
573  }
574  }
575  return $resultData;
576  }
577 
585  protected function ‪getSpecialConfigurationForResultRow($row)
586  {
587  $pathId = $row['data_page_id'] ?: $row['page_id'];
588  $pathMP = $row['data_page_id'] ? $row['data_page_mp'] : '';
589  $specConf = $this->settings['specialConfiguration']['0'] ?? [];
590  try {
591  $rl = GeneralUtility::makeInstance(RootlineUtility::class, $pathId, $pathMP)->get();
592  foreach ($rl as $dat) {
593  if (is_array($this->settings['specialConfiguration'][$dat['uid']] ?? false)) {
594  $specConf = $this->settings['specialConfiguration'][$dat['uid']];
595  $specConf['_pid'] = $dat['uid'];
596  break;
597  }
598  }
599  } catch (RootLineException $e) {
600  // do nothing
601  }
602  return $specConf;
603  }
604 
612  protected function ‪makeRating($row)
613  {
614  $default = ' ';
615  switch ((string)$this->searchData['sortOrder']) {
616  case 'rank_count':
617  return $row['order_val'] . ' ' . ‪LocalizationUtility::translate('result.ratingMatches', 'IndexedSearch');
618  case 'rank_first':
619  return ceil(‪MathUtility::forceIntegerInRange(255 - $row['order_val'], 1, 255) / 255 * 100) . '%';
620  case 'rank_flag':
621  if ($this->firstRow['order_val2'] ?? 0) {
622  // (3 MSB bit, 224 is highest value of order_val1 currently)
623  $base = $row['order_val1'] * 256;
624  // 15-3 MSB = 12
625  $freqNumber = $row['order_val2'] / $this->firstRow['order_val2'] * 2 ** 12;
626  $total = ‪MathUtility::forceIntegerInRange($base + $freqNumber, 0, 32767);
627  return ceil(log($total) / log(32767) * 100) . '%';
628  }
629  return $default;
630  case 'rank_freq':
631  $max = 10000;
632  $total = ‪MathUtility::forceIntegerInRange($row['order_val'], 0, $max);
633  return ceil(log($total) / log($max) * 100) . '%';
634  case 'crdate':
635  return ‪$GLOBALS['TSFE']->cObj->calcAge(‪$GLOBALS['EXEC_TIME'] - $row['item_crdate'], 0);
636  case 'mtime':
637  return ‪$GLOBALS['TSFE']->cObj->calcAge(‪$GLOBALS['EXEC_TIME'] - $row['item_mtime'], 0);
638  default:
639  return $default;
640  }
641  }
642 
649  protected function ‪makeLanguageIndication($row)
650  {
651  ‪$output = '&nbsp;';
652  // If search result is a TYPO3 page:
653  if ((string)$row['item_type'] === '0') {
654  // If TypoScript is used to render the flag:
655  if (is_array($this->settings['flagRendering'] ?? false)) {
656  $cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
657  $cObj->setCurrentVal($row['sys_language_uid']);
658  $typoScriptArray = $this->typoScriptService->convertPlainArrayToTypoScriptArray($this->settings['flagRendering']);
659  ‪$output = $cObj->cObjGetSingle($this->settings['flagRendering']['_typoScriptNodeValue'], $typoScriptArray);
660  }
661  }
662  return ‪$output;
663  }
664 
673  public function ‪makeItemTypeIcon($imageType, $alt, $specRowConf)
674  {
675  // Build compound key if item type is 0, iconRendering is not used
676  // and specialConfiguration.[pid].pageIcon was set in TS
677  if (
678  $imageType === '0' && ($specRowConf['_pid'] ?? false)
679  && is_array($specRowConf['pageIcon'] ?? false)
680  && !is_array($this->settings['iconRendering'] ?? false)
681  ) {
682  $imageType .= ':' . $specRowConf['_pid'];
683  }
684  if (!isset($this->iconFileNameCache[$imageType])) {
685  $this->iconFileNameCache[$imageType] = '';
686  // If TypoScript is used to render the icon:
687  if (is_array($this->settings['iconRendering'] ?? false)) {
688  $cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
689  $cObj->setCurrentVal($imageType);
690  $typoScriptArray = $this->typoScriptService->convertPlainArrayToTypoScriptArray($this->settings['iconRendering']);
691  $this->iconFileNameCache[$imageType] = $cObj->cObjGetSingle($this->settings['iconRendering']['_typoScriptNodeValue'], $typoScriptArray);
692  } else {
693  // Default creation / finding of icon:
694  $icon = '';
695  if ($imageType === '0' || str_starts_with($imageType, '0:')) {
696  if (is_array($specRowConf['pageIcon'] ?? false)) {
697  $this->iconFileNameCache[$imageType] = ‪$GLOBALS['TSFE']->cObj->cObjGetSingle('IMAGE', $specRowConf['pageIcon']);
698  } else {
699  $icon = 'EXT:indexed_search/Resources/Public/Icons/FileTypes/pages.gif';
700  }
701  } elseif ($this->externalParsers[$imageType]) {
702  $icon = $this->externalParsers[$imageType]->getIcon($imageType);
703  }
704  if ($icon) {
705  $fullPath = GeneralUtility::getFileAbsFileName($icon);
706  if ($fullPath) {
707  $imageInfo = GeneralUtility::makeInstance(ImageInfo::class, $fullPath);
708  $iconPath = ‪PathUtility::getAbsoluteWebPath($fullPath);
709  $this->iconFileNameCache[$imageType] = $imageInfo->getWidth() > 0
710  ? '<img src="' . $iconPath
711  . '" width="' . $imageInfo->getWidth()
712  . '" height="' . $imageInfo->getHeight()
713  . '" title="' . htmlspecialchars($alt) . '" alt="" />'
714  : '';
715  }
716  }
717  }
718  }
719  return $this->iconFileNameCache[$imageType];
720  }
721 
731  protected function ‪makeDescription($row, $noMarkup = false, $length = 180)
732  {
733  $markedSW = '';
734  $outputStr = '';
735  if ($row['show_resume']) {
736  if (!$noMarkup) {
737  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_fulltext');
738  $ftdrow = $queryBuilder
739  ->select('*')
740  ->from('index_fulltext')
741  ->where(
742  $queryBuilder->expr()->eq(
743  'phash',
744  $queryBuilder->createNamedParameter($row['phash'], ‪Connection::PARAM_INT)
745  )
746  )
747  ->executeQuery()
748  ->fetchAssociative();
749  if ($ftdrow !== false) {
750  // Cut HTTP references after some length
751  $content = preg_replace('/(http:\\/\\/[^ ]{' . $this->settings['results.']['hrefInSummaryCropAfter'] . '})([^ ]+)/i', '$1...', $ftdrow['fulltextdata']);
752  $markedSW = $this->‪markupSWpartsOfString($content);
753  }
754  }
755  if (!trim($markedSW)) {
756  $outputStr = ‪GeneralUtility::fixed_lgd_cs($row['item_description'], (int)$length, $this->settings['results.']['summaryCropSignifier']);
757  $outputStr = htmlspecialchars($outputStr);
758  }
759  ‪$output = $outputStr ?: $markedSW;
760  } else {
761  ‪$output = '<span class="noResume">' . ‪LocalizationUtility::translate('result.noResume', 'IndexedSearch') . '</span>';
762  }
763  return ‪$output;
764  }
765 
772  protected function ‪markupSWpartsOfString($str)
773  {
774  $htmlParser = GeneralUtility::makeInstance(HtmlParser::class);
775  // Init:
776  $str = str_replace('&nbsp;', ' ', $htmlParser->bidir_htmlspecialchars($str, -1));
777  $str = preg_replace('/\\s\\s+/', ' ', $str);
778  $swForReg = [];
779  // Prepare search words for regex:
780  foreach ($this->searchWords as $d) {
781  $swForReg[] = preg_quote($d['sword'], '/');
782  }
783  $regExString = '(' . implode('|', $swForReg) . ')';
784  // Split and combine:
785  $parts = preg_split('/' . $regExString . '/iu', ' ' . $str . ' ', 20000, PREG_SPLIT_DELIM_CAPTURE);
786  $parts = $parts ?: [];
787  // Constants:
788  $summaryMax = $this->settings['results.']['markupSW_summaryMax'];
789  $postPreLgd = (int)$this->settings['results.']['markupSW_postPreLgd'];
790  $postPreLgd_offset = (int)$this->settings['results.']['markupSW_postPreLgd_offset'];
791  $divider = $this->settings['results.']['markupSW_divider'];
792  $occurrences = (count($parts) - 1) / 2;
793  if ($occurrences) {
794  $postPreLgd = ‪MathUtility::forceIntegerInRange($summaryMax / $occurrences, $postPreLgd, $summaryMax / 2);
795  }
796  // Variable:
797  $summaryLgd = 0;
798  ‪$output = [];
799  // Shorten in-between strings:
800  foreach ($parts as $k => $strP) {
801  if ($k % 2 == 0) {
802  // Find length of the summary part:
803  $strLen = mb_strlen($parts[$k], 'utf-8');
804  ‪$output[$k] = $parts[$k];
805  // Possibly shorten string:
806  if (!$k) {
807  // First entry at all (only cropped on the frontside)
808  if ($strLen > $postPreLgd) {
809  ‪$output[$k] = $divider . preg_replace('/^[^[:space:]]+[[:space:]]/', '', ‪GeneralUtility::fixed_lgd_cs($parts[$k], -($postPreLgd - $postPreLgd_offset)));
810  }
811  } elseif ($summaryLgd > $summaryMax || !isset($parts[$k + 1])) {
812  // In case summary length is exceed OR if there are no more entries at all:
813  if ($strLen > $postPreLgd) {
814  ‪$output[$k] = preg_replace('/[[:space:]][^[:space:]]+$/', '', ‪GeneralUtility::fixed_lgd_cs(
815  $parts[$k],
816  $postPreLgd - $postPreLgd_offset
817  )) . $divider;
818  }
819  } else {
820  if ($strLen > $postPreLgd * 2) {
821  ‪$output[$k] = preg_replace('/[[:space:]][^[:space:]]+$/', '', ‪GeneralUtility::fixed_lgd_cs(
822  $parts[$k],
823  $postPreLgd - $postPreLgd_offset
824  )) . $divider . preg_replace('/^[^[:space:]]+[[:space:]]/', '', ‪GeneralUtility::fixed_lgd_cs($parts[$k], -($postPreLgd - $postPreLgd_offset)));
825  }
826  }
827  $summaryLgd += mb_strlen(‪$output[$k], 'utf-8');
828  // Protect output:
829  ‪$output[$k] = htmlspecialchars(‪$output[$k]);
830  // If summary lgd is exceed, break the process:
831  if ($summaryLgd > $summaryMax) {
832  break;
833  }
834  } else {
835  $summaryLgd += mb_strlen($strP, 'utf-8');
836  ‪$output[$k] = '<strong class="tx-indexedsearch-redMarkup">' . htmlspecialchars($parts[$k]) . '</strong>';
837  }
838  }
839  // Return result:
840  return implode('', ‪$output);
841  }
842 
848  protected function ‪writeSearchStat(array ‪$searchWords): void
849  {
850  if (empty(‪$searchWords)) {
851  return;
852  }
853  $entries = [];
854  foreach (‪$searchWords as $val) {
855  $entries[] = [
856  mb_substr($val['sword'], 0, 50),
857  // Time stamp
858  ‪$GLOBALS['EXEC_TIME'],
859  // search page id for indexed search stats
860  ‪$GLOBALS['TSFE']->id,
861  ];
862  }
863  GeneralUtility::makeInstance(ConnectionPool::class)
864  ->getConnectionForTable('index_stat_word')
865  ->bulkInsert(
866  'index_stat_word',
867  $entries,
868  [ 'word', 'tstamp', 'pageid' ],
870  );
871  }
872 
889  protected function ‪getSearchWords($defaultOperator)
890  {
891  // Shorten search-word string to max 200 bytes - shortening the string here is only a run-away feature!
892  ‪$searchWords = mb_substr($this->‪getSword(), 0, 200);
893  $sWordArray = false;
894  if ($hookObj = $this->‪hookRequest('getSearchWords')) {
895  $sWordArray = $hookObj->getSearchWords_splitSWords(‪$searchWords, $defaultOperator);
896  } else {
897  // sentence
898  if ($this->searchData['searchType'] == 20) {
899  $sWordArray = [
900  [
901  'sword' => trim(‪$searchWords),
902  'oper' => 'AND',
903  ],
904  ];
905  } else {
906  // case-sensitive. Defines the words, which will be
907  // operators between words
908  $operatorTranslateTable = [
909  ['+', 'AND'],
910  ['|', 'OR'],
911  ['-', 'AND NOT'],
912  // Add operators for various languages
913  // Converts the operators to lowercase
914  [mb_strtolower(‪LocalizationUtility::translate('localizedOperandAnd', 'IndexedSearch') ?? '', 'utf-8'), 'AND'],
915  [mb_strtolower(‪LocalizationUtility::translate('localizedOperandOr', 'IndexedSearch') ?? '', 'utf-8'), 'OR'],
916  [mb_strtolower(‪LocalizationUtility::translate('localizedOperandNot', 'IndexedSearch') ?? '', 'utf-8'), 'AND NOT'],
917  ];
918  $swordArray = ‪IndexedSearchUtility::getExplodedSearchString(‪$searchWords, $defaultOperator == 1 ? 'OR' : 'AND', $operatorTranslateTable);
919  if (is_array($swordArray)) {
920  $sWordArray = $this->‪procSearchWordsByLexer($swordArray);
921  }
922  }
923  }
924  return $sWordArray;
925  }
926 
934  protected function ‪procSearchWordsByLexer(‪$searchWords)
935  {
936  $newSearchWords = [];
937  // Init lexer (used to post-processing of search words)
938  $lexerObjectClassName = (‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['indexed_search']['lexer'] ?? false) ? ‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['indexed_search']['lexer'] : Lexer::class;
939 
941  $lexer = GeneralUtility::makeInstance($lexerObjectClassName);
942  $this->lexerObj = $lexer;
943  // Traverse the search word array
944  foreach (‪$searchWords as $wordDef) {
945  // No space in word (otherwise it might be a sentence in quotes like "there is").
946  if (!str_contains($wordDef['sword'], ' ')) {
947  // Split the search word by lexer:
948  $res = $this->lexerObj->split2Words($wordDef['sword']);
949  // Traverse lexer result and add all words again:
950  foreach ($res as $word) {
951  $newSearchWords[] = [
952  'sword' => $word,
953  'oper' => $wordDef['oper'],
954  ];
955  }
956  } else {
957  $newSearchWords[] = $wordDef;
958  }
959  }
960  return $newSearchWords;
961  }
962 
969  public function ‪formAction($search = []): ResponseInterface
970  {
971  // check if TypoScript is loaded
972  if (!isset($this->settings['results'])) {
973  return $this->‪redirect('noTypoScript');
974  }
975 
976  ‪$searchData = $this->‪initialize($search);
977  // Adding search field value
978  $this->view->assign('sword', $this->‪getSword());
979  // Extended search
980  if (!empty(‪$searchData['extendedSearch'])) {
981  $this->view->assignMultiple($this->‪processExtendedSearchParameters());
982  }
983  $this->view->assign('searchParams', ‪$searchData);
984 
985  return $this->‪htmlResponse();
986  }
987 
991  public function ‪noTypoScriptAction(): ResponseInterface
992  {
993  return $this->‪htmlResponse();
994  }
995 
996  /****************************************
997  * building together the available options for every dropdown
998  ***************************************/
1004  protected function ‪getAllAvailableSearchTypeOptions()
1005  {
1006  $allOptions = [];
1007  $types = [0, 1, 2, 3, 10, 20];
1008  $blindSettings = $this->settings['blind'];
1009  if (!$blindSettings['searchType']) {
1010  foreach ($types as $typeNum) {
1011  $allOptions[$typeNum] = ‪LocalizationUtility::translate('searchTypes.' . $typeNum, 'IndexedSearch');
1012  }
1013  }
1014  // Remove this option if metaphone search is disabled)
1015  if (!$this->enableMetaphoneSearch) {
1016  unset($allOptions[10]);
1017  }
1018  // disable single entries by TypoScript
1019  $allOptions = $this->‪removeOptionsFromOptionList($allOptions, $blindSettings['searchType']);
1020  return $allOptions;
1021  }
1022 
1028  protected function ‪getAllAvailableOperandsOptions()
1029  {
1030  $allOptions = [];
1031  $blindSettings = $this->settings['blind'];
1032  if (!$blindSettings['defaultOperand']) {
1033  $allOptions = [
1034  0 => ‪LocalizationUtility::translate('defaultOperands.0', 'IndexedSearch'),
1035  1 => ‪LocalizationUtility::translate('defaultOperands.1', 'IndexedSearch'),
1036  ];
1037  }
1038  // disable single entries by TypoScript
1039  $allOptions = $this->‪removeOptionsFromOptionList($allOptions, $blindSettings['defaultOperand']);
1040  return $allOptions;
1041  }
1042 
1048  protected function ‪getAllAvailableMediaTypesOptions()
1049  {
1050  $allOptions = [];
1051  $mediaTypes = [-1, 0, -2];
1052  $blindSettings = $this->settings['blind'];
1053  if (!$blindSettings['mediaType']) {
1054  foreach ($mediaTypes as $mediaType) {
1055  $allOptions[$mediaType] = ‪LocalizationUtility::translate('mediaTypes.' . $mediaType, 'IndexedSearch');
1056  }
1057  // Add media to search in:
1058  $additionalMedia = trim($this->settings['mediaList']);
1059  if ($additionalMedia !== '') {
1060  $additionalMedia = GeneralUtility::trimExplode(',', $additionalMedia, true);
1061  } else {
1062  $additionalMedia = [];
1063  }
1064  foreach ($this->externalParsers as $extension => $obj) {
1065  // Skip unwanted extensions
1066  if (!empty($additionalMedia) && !in_array($extension, $additionalMedia)) {
1067  continue;
1068  }
1069  if ($name = $obj->searchTypeMediaTitle($extension)) {
1070  $translatedName = ‪LocalizationUtility::translate('mediaTypes.' . $extension, 'IndexedSearch');
1071  $allOptions[$extension] = $translatedName ?: $name;
1072  }
1073  }
1074  }
1075  // disable single entries by TypoScript
1076  $allOptions = $this->‪removeOptionsFromOptionList($allOptions, $blindSettings['mediaType']);
1077  return $allOptions;
1078  }
1079 
1085  protected function ‪getAllAvailableLanguageOptions()
1086  {
1087  $allOptions = [
1088  '-1' => ‪LocalizationUtility::translate('languageUids.-1', 'IndexedSearch'),
1089  ];
1090  $blindSettings = $this->settings['blind'];
1091  if (!$blindSettings['languageUid']) {
1092  try {
1093  $site = GeneralUtility::makeInstance(SiteFinder::class)
1094  ->getSiteByPageId(‪$GLOBALS['TSFE']->id);
1095 
1096  ‪$languages = $site->getLanguages();
1097  foreach (‪$languages as $language) {
1098  $allOptions[$language->getLanguageId()] = $language->getNavigationTitle();
1099  }
1100  } catch (SiteNotFoundException $e) {
1101  // No Site found, no options
1102  $allOptions = [];
1103  }
1105  // disable single entries by TypoScript
1106  $allOptions = $this->‪removeOptionsFromOptionList($allOptions, (array)$blindSettings['languageUid']);
1107  } else {
1108  $allOptions = [];
1109  }
1110  return $allOptions;
1111  }
1112 
1122  protected function ‪getAllAvailableSectionsOptions()
1123  {
1124  $allOptions = [];
1125  $sections = [0, -1, -2, -3];
1126  $blindSettings = $this->settings['blind'];
1127  if (!$blindSettings['sections']) {
1128  foreach ($sections as $section) {
1129  $allOptions[$section] = ‪LocalizationUtility::translate('sections.' . $section, 'IndexedSearch');
1130  }
1131  }
1132  // Creating levels for section menu:
1133  // This selects the first and secondary menus for the "sections" selector - so we can search in sections and sub sections.
1134  if ($this->settings['displayLevel1Sections']) {
1135  $firstLevelMenu = $this->‪getMenuOfPages((int)$this->searchRootPageIdList);
1136  $labelLevel1 = ‪LocalizationUtility::translate('sections.rootLevel1', 'IndexedSearch');
1137  $labelLevel2 = ‪LocalizationUtility::translate('sections.rootLevel2', 'IndexedSearch');
1138  foreach ($firstLevelMenu as $firstLevelKey => $menuItem) {
1139  if (!$menuItem['nav_hide']) {
1140  $allOptions['rl1_' . $menuItem['uid']] = trim($labelLevel1 . ' ' . $menuItem['title']);
1141  if ($this->settings['displayLevel2Sections']) {
1142  $secondLevelMenu = $this->‪getMenuOfPages($menuItem['uid']);
1143  foreach ($secondLevelMenu as $secondLevelKey => $menuItemLevel2) {
1144  if (!$menuItemLevel2['nav_hide']) {
1145  $allOptions['rl2_' . $menuItemLevel2['uid']] = trim($labelLevel2 . ' ' . $menuItemLevel2['title']);
1146  } else {
1147  unset($secondLevelMenu[$secondLevelKey]);
1148  }
1149  }
1150  $allOptions['rl2_' . implode(',', array_keys($secondLevelMenu))] = ‪LocalizationUtility::translate('sections.rootLevel2All', 'IndexedSearch');
1151  }
1152  } else {
1153  unset($firstLevelMenu[$firstLevelKey]);
1154  }
1155  }
1156  $allOptions['rl1_' . implode(',', array_keys($firstLevelMenu))] = ‪LocalizationUtility::translate('sections.rootLevel1All', 'IndexedSearch');
1157  }
1158  // disable single entries by TypoScript
1159  $allOptions = $this->‪removeOptionsFromOptionList($allOptions, $blindSettings['sections']);
1160  return $allOptions;
1161  }
1162 
1169  {
1170  $allOptions = [
1171  '-1' => ‪LocalizationUtility::translate('indexingConfigurations.-1', 'IndexedSearch'),
1172  '-2' => ‪LocalizationUtility::translate('indexingConfigurations.-2', 'IndexedSearch'),
1173  '0' => ‪LocalizationUtility::translate('indexingConfigurations.0', 'IndexedSearch'),
1174  ];
1175  $blindSettings = $this->settings['blind'];
1176  if (!($blindSettings['indexingConfigurations'] ?? false)) {
1177  // add an additional index configuration
1178  $defaultFreeIndexUidList = (string)($this->settings['defaultFreeIndexUidList'] ?? '');
1179  if ($defaultFreeIndexUidList) {
1180  $uidList = GeneralUtility::intExplode(',', $defaultFreeIndexUidList);
1181  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
1182  ->getQueryBuilderForTable('index_config');
1183  $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
1184  $result = $queryBuilder
1185  ->select('uid', 'title')
1186  ->from('index_config')
1187  ->where(
1188  $queryBuilder->expr()->in(
1189  'uid',
1190  $queryBuilder->createNamedParameter($uidList, Connection::PARAM_INT_ARRAY)
1191  )
1192  )
1193  ->executeQuery();
1194 
1195  while ($row = $result->fetchAssociative()) {
1196  $indexId = (int)$row['uid'];
1197  $title = ‪LocalizationUtility::translate('indexingConfigurations.' . $indexId, 'IndexedSearch');
1198  $allOptions[$indexId] = $title ?: $row['title'];
1199  }
1200  }
1201  // disable single entries by TypoScript
1202  $allOptions = $this->‪removeOptionsFromOptionList($allOptions, (array)($blindSettings['indexingConfigurations'] ?? []));
1203  } else {
1204  $allOptions = [];
1205  }
1206  return $allOptions;
1207  }
1208 
1218  protected function ‪getAllAvailableSortOrderOptions()
1219  {
1220  $allOptions = [];
1221  $sortOrders = ['rank_flag', 'rank_freq', 'rank_first', 'rank_count', 'mtime', 'title', 'crdate'];
1222  $blindSettings = $this->settings['blind'];
1223  if (!$blindSettings['sortOrder']) {
1224  foreach ($sortOrders as $order) {
1225  $allOptions[$order] = ‪LocalizationUtility::translate('sortOrders.' . $order, 'IndexedSearch');
1226  }
1227  }
1228  // disable single entries by TypoScript
1229  $allOptions = $this->‪removeOptionsFromOptionList($allOptions, ($blindSettings['sortOrder.'] ?? []));
1230  return $allOptions;
1231  }
1232 
1238  protected function ‪getAllAvailableGroupOptions()
1239  {
1240  $allOptions = [];
1241  $blindSettings = $this->settings['blind'];
1242  if (!($blindSettings['groupBy'] ?? false)) {
1243  $allOptions = [
1244  'sections' => ‪LocalizationUtility::translate('groupBy.sections', 'IndexedSearch'),
1245  'flat' => ‪LocalizationUtility::translate('groupBy.flat', 'IndexedSearch'),
1246  ];
1247  }
1248  // disable single entries by TypoScript
1249  $allOptions = $this->‪removeOptionsFromOptionList($allOptions, ($blindSettings['groupBy.'] ?? []));
1250  return $allOptions;
1251  }
1252 
1258  protected function ‪getAllAvailableSortDescendingOptions()
1259  {
1260  $allOptions = [];
1261  $blindSettings = $this->settings['blind'];
1262  if (!($blindSettings['descending'] ?? false)) {
1263  $allOptions = [
1264  0 => ‪LocalizationUtility::translate('sortOrders.descending', 'IndexedSearch'),
1265  1 => ‪LocalizationUtility::translate('sortOrders.ascending', 'IndexedSearch'),
1266  ];
1267  }
1268  // disable single entries by TypoScript
1269  $allOptions = $this->‪removeOptionsFromOptionList($allOptions, ($blindSettings['descending.'] ?? []));
1270  return $allOptions;
1271  }
1272 
1278  protected function ‪getAllAvailableNumberOfResultsOptions()
1279  {
1280  $allOptions = [];
1281  if (count($this->availableResultsNumbers) > 1) {
1282  $allOptions = array_combine($this->availableResultsNumbers, $this->availableResultsNumbers);
1283  }
1284  // disable single entries by TypoScript
1285  return $this->‪removeOptionsFromOptionList((array)$allOptions, $this->settings['blind']['numberOfResults']);
1286  }
1287 
1295  protected function ‪removeOptionsFromOptionList($allOptions, $blindOptions)
1296  {
1297  if (is_array($blindOptions)) {
1298  foreach ($blindOptions as $key => $val) {
1299  if ($val == 1) {
1300  unset($allOptions[$key]);
1301  }
1302  }
1303  }
1304  return $allOptions;
1305  }
1306 
1314  protected function ‪linkPage($pageUid, $row = [])
1315  {
1316  $pageLanguage = GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect('language', 'contentId', 0);
1317  // Parameters for link
1318  $urlParameters = [];
1319  if ($row['static_page_arguments'] !== null) {
1320  $urlParameters = json_decode($row['static_page_arguments'], true);
1321  }
1322  // Add &type and &MP variable:
1323  if ($row['data_page_mp']) {
1324  $urlParameters['MP'] = $row['data_page_mp'];
1325  }
1326  if (($pageLanguage === 0 && $row['sys_language_uid'] > 0) || $pageLanguage > 0) {
1327  $urlParameters['L'] = (int)$row['sys_language_uid'];
1328  }
1329  // This will make sure that the path is retrieved if it hasn't been
1330  // already. Used only for the sake of the domain_record thing.
1331  $this->‪getPathFromPageId($pageUid);
1332 
1333  return $this->‪preparePageLink($pageUid, $row, $urlParameters);
1334  }
1335 
1342  protected function ‪getMenuOfPages($pageUid)
1343  {
1344  $pageRepository = GeneralUtility::makeInstance(PageRepository::class);
1345  if ($this->settings['displayLevelxAllTypes']) {
1346  return $pageRepository->getMenuForPages([$pageUid]);
1347  }
1348  return $pageRepository->getMenu($pageUid);
1349  }
1350 
1358  protected function ‪getPathFromPageId($id, $pathMP = '')
1359  {
1360  $identStr = $id . '|' . $pathMP;
1361  if (!isset($this->pathCache[$identStr])) {
1362  $this->requiredFrontendUsergroups[$id] = [];
1363  $this->domainRecords[$id] = [];
1364  try {
1365  $rl = GeneralUtility::makeInstance(RootlineUtility::class, $id, $pathMP)->get();
1366  $path = '';
1367  $pageCount = count($rl);
1368  if (!empty($rl)) {
1369  $excludeDoktypesFromPath = GeneralUtility::trimExplode(
1370  ',',
1371  $this->settings['results']['pathExcludeDoktypes'] ?? '',
1372  true
1373  );
1374  $breadcrumbWrap = $this->settings['breadcrumbWrap'] ?? '/';
1375  $breadcrumbWraps = GeneralUtility::makeInstance(TypoScriptService::class)
1376  ->explodeConfigurationForOptionSplit(['wrap' => $breadcrumbWrap], $pageCount);
1377  foreach ($rl as $k => $v) {
1378  if (in_array($v['doktype'], $excludeDoktypesFromPath, false)) {
1379  continue;
1380  }
1381  // Check fe_user
1382  if ($v['fe_group'] && ($v['uid'] == $id || $v['extendToSubpages'])) {
1383  $this->requiredFrontendUsergroups[$id][] = $v['fe_group'];
1384  }
1385  // Check sys_domain
1386  if ($this->settings['detectDomainRecords']) {
1387  $domainName = $this->‪getFirstDomainForPage((int)$v['uid']);
1388  if ($domainName) {
1389  $this->domainRecords[$id][] = $domainName;
1390  // Set path accordingly
1391  $path = $domainName . $path;
1392  break;
1393  }
1394  }
1395  // Stop, if we find that the current id is the current root page.
1396  if ($v['uid'] == ‪$GLOBALS['TSFE']->config['rootLine'][0]['uid']) {
1397  array_pop($breadcrumbWraps);
1398  break;
1399  }
1400  $path = ‪$GLOBALS['TSFE']->cObj->wrap(htmlspecialchars($v['title']), array_pop($breadcrumbWraps)['wrap']) . $path;
1401  }
1402  }
1403  } catch (RootLineException $e) {
1404  $path = '';
1405  }
1406  $this->pathCache[$identStr] = $path;
1407  }
1408  return $this->pathCache[$identStr];
1409  }
1410 
1417  protected function ‪getFirstDomainForPage(int $id): string
1418  {
1419  $domain = '';
1420  try {
1421  $domain = GeneralUtility::makeInstance(SiteFinder::class)
1422  ->getSiteByRootPageId($id)
1423  ->getBase()
1424  ->getHost();
1425  } catch (‪SiteNotFoundException $e) {
1426  // site was not found, we return an empty string as default
1427  }
1428  return $domain;
1429  }
1430 
1435  protected function ‪initializeExternalParsers()
1436  {
1437  // Initialize external document parsers for icon display and other soft operations
1438  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['indexed_search']['external_parsers'] ?? [] as $extension => $className) {
1439  $this->externalParsers[$extension] = GeneralUtility::makeInstance($className);
1440  // Init parser and if it returns FALSE, unset its entry again
1441  if (!$this->externalParsers[$extension]->softInit($extension)) {
1442  unset($this->externalParsers[$extension]);
1443  }
1444  }
1445  }
1446 
1453  protected function ‪hookRequest($functionName)
1454  {
1455  // Hook: menuConfig_preProcessModMenu
1456  if (‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['indexed_search']['pi1_hooks'][$functionName] ?? false) {
1457  $hookObj = GeneralUtility::makeInstance(‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['indexed_search']['pi1_hooks'][$functionName]);
1458  if (method_exists($hookObj, $functionName)) {
1459  $hookObj->pObj = $this;
1460  return $hookObj;
1461  }
1462  }
1463  return null;
1464  }
1465 
1472  protected function ‪multiplePagesType($item_type)
1473  {
1474  return is_object($this->externalParsers[$item_type] ?? false) && $this->externalParsers[$item_type]->isMultiplePageExtension($item_type);
1475  }
1476 
1484  protected function ‪processExtendedSearchParameters()
1485  {
1486  $allSearchTypes = $this->‪getAllAvailableSearchTypeOptions();
1487  $allDefaultOperands = $this->‪getAllAvailableOperandsOptions();
1488  $allMediaTypes = $this->‪getAllAvailableMediaTypesOptions();
1489  $allLanguageUids = $this->‪getAllAvailableLanguageOptions();
1490  $allSortOrders = $this->‪getAllAvailableSortOrderOptions();
1491  $allSortDescendings = $this->‪getAllAvailableSortDescendingOptions();
1492 
1493  return [
1494  'allSearchTypes' => $allSearchTypes,
1495  'allDefaultOperands' => $allDefaultOperands,
1496  'showTypeSearch' => !empty($allSearchTypes) || !empty($allDefaultOperands),
1497  'allMediaTypes' => $allMediaTypes,
1498  'allLanguageUids' => $allLanguageUids,
1499  'showMediaAndLanguageSearch' => !empty($allMediaTypes) || !empty($allLanguageUids),
1500  'allSections' => $this->‪getAllAvailableSectionsOptions(),
1501  'allIndexConfigurations' => $this->‪getAllAvailableIndexConfigurationsOptions(),
1502  'allSortOrders' => $allSortOrders,
1503  'allSortDescendings' => $allSortDescendings,
1504  'showSortOrders' => !empty($allSortOrders) || !empty($allSortDescendings),
1505  'allNumberOfResults' => $this->‪getAllAvailableNumberOfResultsOptions(),
1506  'allGroups' => $this->‪getAllAvailableGroupOptions(),
1507  ];
1508  }
1509 
1513  protected function ‪loadSettings()
1514  {
1515  if (!is_array($this->settings['results.'] ?? false)) {
1516  $this->settings['results.'] = [];
1517  }
1518  $fullTypoScriptArray = $this->typoScriptService->convertPlainArrayToTypoScriptArray($this->settings);
1519  $this->settings['detectDomainRecords'] = $fullTypoScriptArray['detectDomainRecords'] ?? 0;
1520  $this->settings['detectDomainRecords.'] = $fullTypoScriptArray['detectDomainRecords.'] ?? [];
1521  $typoScriptArray = $fullTypoScriptArray['results.'];
1522 
1523  $this->settings['results.']['summaryCropAfter'] = ‪MathUtility::forceIntegerInRange(
1524  ‪$GLOBALS['TSFE']->cObj->stdWrapValue('summaryCropAfter', $typoScriptArray ?? []),
1525  10,
1526  5000,
1527  180
1528  );
1529  $this->settings['results.']['summaryCropSignifier'] = ‪$GLOBALS['TSFE']->cObj->stdWrapValue('summaryCropSignifier', $typoScriptArray ?? []);
1530  $this->settings['results.']['titleCropAfter'] = ‪MathUtility::forceIntegerInRange(
1531  ‪$GLOBALS['TSFE']->cObj->stdWrapValue('titleCropAfter', $typoScriptArray ?? []),
1532  10,
1533  500,
1534  50
1535  );
1536  $this->settings['results.']['titleCropSignifier'] = ‪$GLOBALS['TSFE']->cObj->stdWrapValue('titleCropSignifier', $typoScriptArray ?? []);
1537  $this->settings['results.']['markupSW_summaryMax'] = ‪MathUtility::forceIntegerInRange(
1538  ‪$GLOBALS['TSFE']->cObj->stdWrapValue('markupSW_summaryMax', $typoScriptArray ?? []),
1539  10,
1540  5000,
1541  300
1542  );
1543  $this->settings['results.']['markupSW_postPreLgd'] = ‪MathUtility::forceIntegerInRange(
1544  ‪$GLOBALS['TSFE']->cObj->stdWrapValue('markupSW_postPreLgd', $typoScriptArray ?? []),
1545  1,
1546  500,
1547  60
1548  );
1549  $this->settings['results.']['markupSW_postPreLgd_offset'] = ‪MathUtility::forceIntegerInRange(
1550  ‪$GLOBALS['TSFE']->cObj->stdWrapValue('markupSW_postPreLgd_offset', $typoScriptArray ?? []),
1551  1,
1552  50,
1553  5
1554  );
1555  $this->settings['results.']['markupSW_divider'] = ‪$GLOBALS['TSFE']->cObj->stdWrapValue('markupSW_divider', $typoScriptArray ?? []);
1556  $this->settings['results.']['hrefInSummaryCropAfter'] = ‪MathUtility::forceIntegerInRange(
1557  ‪$GLOBALS['TSFE']->cObj->stdWrapValue('hrefInSummaryCropAfter', $typoScriptArray ?? []),
1558  10,
1559  400,
1560  60
1561  );
1562  $this->settings['results.']['hrefInSummaryCropSignifier'] = ‪$GLOBALS['TSFE']->cObj->stdWrapValue('hrefInSummaryCropSignifier', $typoScriptArray ?? []);
1563  }
1564 
1571  protected function ‪getNumberOfResults($numberOfResults)
1572  {
1573  $numberOfResults = (int)$numberOfResults;
1574 
1575  return in_array($numberOfResults, $this->availableResultsNumbers) ?
1576  $numberOfResults : ‪$this->defaultResultNumber;
1577  }
1578 
1583  protected function ‪preparePageLink(int $pageUid, array $row, array $urlParameters): array
1584  {
1585  $target = '';
1586  $uri = $this->uriBuilder
1587  ->setTargetPageUid($pageUid)
1588  ->setTargetPageType($row['data_page_type'])
1589  ->setArguments($urlParameters)
1590  ->build();
1592  // If external domain, then link to that:
1593  if (!empty($this->domainRecords[$pageUid])) {
1594  $scheme = GeneralUtility::getIndpEnv('TYPO3_SSL') ? 'https://' : 'http://';
1595  $firstDomain = reset($this->domainRecords[$pageUid]);
1596  $uri = $scheme . $firstDomain . $uri;
1597  $target = $this->settings['detectDomainRecords.']['target'] ?? '';
1598  }
1599 
1600  return ['uri' => $uri, 'target' => $target];
1601  }
1602 
1609  protected function ‪linkPageATagWrap(string $linkText, array $linkData): string
1610  {
1611  $attributes = [
1612  'href' => $linkData['uri'],
1613  ];
1614  if (!empty($linkData['target'])) {
1615  $attributes['target'] = $linkData['target'];
1616  }
1617  return sprintf(
1618  '<a %s>%s</a>',
1619  GeneralUtility::implodeAttributes($attributes, true),
1620  $linkText
1621  );
1622  }
1623 
1627  protected function ‪addOperatorLabel(array $searchWord): array
1628  {
1629  if ($searchWord['oper'] ?? false) {
1630  $searchWord['operatorLabel'] = strtolower(str_replace(' ', '', (string)($searchWord['oper'])));
1631  }
1632 
1633  return $searchWord;
1634  }
1635 
1640  public function ‪setSword(‪$sword)
1641  {
1642  $this->sword = (string)‪$sword;
1643  }
1644 
1649  public function ‪getSword()
1650  {
1651  return (string)‪$this->sword;
1652  }
1653 }
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\multiplePagesType
‪bool multiplePagesType($item_type)
Definition: SearchController.php:1454
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\writeSearchStat
‪writeSearchStat(array $searchWords)
Definition: SearchController.php:830
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\markupSWpartsOfString
‪string markupSWpartsOfString($str)
Definition: SearchController.php:754
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\makeDescription
‪string makeDescription($row, $noMarkup=false, $length=180)
Definition: SearchController.php:713
‪TYPO3\CMS\Extbase\Mvc\Controller\ActionController\redirect
‪redirect($actionName, $controllerName=null, $extensionName=null, array $arguments=null, $pageUid=null, $_=null, $statusCode=303)
Definition: ActionController.php:719
‪TYPO3\CMS\IndexedSearch\Controller\SearchController
Definition: SearchController.php:53
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\noTypoScriptAction
‪noTypoScriptAction()
Definition: SearchController.php:973
‪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\Core\Utility\GeneralUtility\fixed_lgd_cs
‪static string fixed_lgd_cs(string $string, int $chars, string $appendString='...')
Definition: GeneralUtility.php:93
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$searchRootPageIdList
‪int string $searchRootPageIdList
Definition: SearchController.php:76
‪TYPO3\CMS\Extbase\Annotation
Definition: IgnoreValidation.php:18
‪TYPO3\CMS\Extbase\Utility\LocalizationUtility
Definition: LocalizationUtility.php:35
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$externalParsers
‪array $externalParsers
Definition: SearchController.php:101
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getAllAvailableOperandsOptions
‪array getAllAvailableOperandsOptions()
Definition: SearchController.php:1010
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$defaultResultNumber
‪int $defaultResultNumber
Definition: SearchController.php:80
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$indexerConfig
‪array $indexerConfig
Definition: SearchController.php:144
‪TYPO3\CMS\Core\Html\HtmlParser
Definition: HtmlParser.php:26
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$requiredFrontendUsergroups
‪array $requiredFrontendUsergroups
Definition: SearchController.php:120
‪TYPO3\CMS\Core\Configuration\ExtensionConfiguration
Definition: ExtensionConfiguration.php:47
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository
Definition: IndexSearchRepository.php:41
‪$languages
‪$languages
Definition: updateIsoDatabase.php:104
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getAllAvailableSearchTypeOptions
‪array getAllAvailableSearchTypeOptions()
Definition: SearchController.php:986
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\formAction
‪formAction($search=[])
Definition: SearchController.php:951
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$searchRepository
‪TYPO3 CMS IndexedSearch Domain Repository IndexSearchRepository $searchRepository
Definition: SearchController.php:90
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getPathFromPageId
‪string getPathFromPageId($id, $pathMP='')
Definition: SearchController.php:1340
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getAllAvailableGroupOptions
‪array getAllAvailableGroupOptions()
Definition: SearchController.php:1220
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$pathCache
‪array $pathCache
Definition: SearchController.php:132
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\compileSingleResultRow
‪array compileSingleResultRow($row, $headerOnly=0)
Definition: SearchController.php:452
‪TYPO3\CMS\Core\Utility\RootlineUtility
Definition: RootlineUtility.php:40
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getFirstDomainForPage
‪string getFirstDomainForPage(int $id)
Definition: SearchController.php:1399
‪TYPO3\CMS\Core\Exception\SiteNotFoundException
Definition: SiteNotFoundException.php:26
‪TYPO3\CMS\Core\Site\SiteFinder
Definition: SiteFinder.php:31
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getMenuOfPages
‪array getMenuOfPages($pageUid)
Definition: SearchController.php:1324
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\preparePageLink
‪preparePageLink(int $pageUid, array $row, array $urlParameters)
Definition: SearchController.php:1565
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\compileResultRows
‪array compileResultRows($resultRows, $freeIndexUid=-1)
Definition: SearchController.php:367
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$iconFileNameCache
‪array $iconFileNameCache
Definition: SearchController.php:138
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getSword
‪string getSword()
Definition: SearchController.php:1631
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\addOperatorLabel
‪addOperatorLabel(array $searchWord)
Definition: SearchController.php:1609
‪TYPO3\CMS\Core\Database\Connection\PARAM_STR
‪const PARAM_STR
Definition: Connection.php:51
‪TYPO3\CMS\Core\Context\Context
Definition: Context.php:55
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\searchAction
‪searchAction($search=[])
Definition: SearchController.php:244
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getAllAvailableIndexConfigurationsOptions
‪array getAllAvailableIndexConfigurationsOptions()
Definition: SearchController.php:1150
‪TYPO3\CMS\Core\Utility\PathUtility\getAbsoluteWebPath
‪static string getAbsoluteWebPath(string $targetPath, bool $prefixWithSitePath=true)
Definition: PathUtility.php:52
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getAllAvailableSortOrderOptions
‪array getAllAvailableSortOrderOptions()
Definition: SearchController.php:1200
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\linkPageATagWrap
‪string linkPageATagWrap(string $linkText, array $linkData)
Definition: SearchController.php:1591
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\hookRequest
‪object null hookRequest($functionName)
Definition: SearchController.php:1435
‪TYPO3\CMS\Extbase\Mvc\Controller\ActionController\htmlResponse
‪htmlResponse(string $html=null)
Definition: ActionController.php:839
‪TYPO3\CMS\IndexedSearch\Controller
Definition: AdministrationController.php:18
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\setSword
‪setSword($sword)
Definition: SearchController.php:1622
‪TYPO3\CMS\Core\Utility\PathUtility\getPublicResourceWebPath
‪static getPublicResourceWebPath(string $resourcePath, bool $prefixWithSitePath=true)
Definition: PathUtility.php:97
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\initialize
‪array initialize($searchData=[])
Definition: SearchController.php:167
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$lexerObj
‪TYPO3 CMS IndexedSearch Lexer $lexerObj
Definition: SearchController.php:96
‪TYPO3\CMS\Extbase\Utility\LocalizationUtility\translate
‪static string null translate(string $key, ?string $extensionName=null, array $arguments=null, Locale|string $languageKey=null)
Definition: LocalizationUtility.php:47
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$searchWords
‪array $searchWords
Definition: SearchController.php:62
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$resultSections
‪array $resultSections
Definition: SearchController.php:126
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$firstRow
‪array $firstRow
Definition: SearchController.php:107
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\makeLanguageIndication
‪string makeLanguageIndication($row)
Definition: SearchController.php:631
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\procSearchWordsByLexer
‪array procSearchWordsByLexer($searchWords)
Definition: SearchController.php:916
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\removeOptionsFromOptionList
‪array removeOptionsFromOptionList($allOptions, $blindOptions)
Definition: SearchController.php:1277
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getAllAvailableMediaTypesOptions
‪array getAllAvailableMediaTypesOptions()
Definition: SearchController.php:1030
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$typoScriptService
‪TYPO3 CMS Core TypoScript TypoScriptService $typoScriptService
Definition: SearchController.php:154
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\injectTypoScriptService
‪injectTypoScriptService(TypoScriptService $typoScriptService)
Definition: SearchController.php:156
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$domainRecords
‪array $domainRecords
Definition: SearchController.php:114
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getSpecialConfigurationForResultRow
‪array getSpecialConfigurationForResultRow($row)
Definition: SearchController.php:567
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$sword
‪string $sword
Definition: SearchController.php:58
‪TYPO3\CMS\Core\Type\File\ImageInfo
Definition: ImageInfo.php:28
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\loadSettings
‪loadSettings()
Definition: SearchController.php:1495
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getDisplayResults
‪array getDisplayResults($searchWords, $resultData, $freeIndexUid=-1)
Definition: SearchController.php:325
‪TYPO3\CMS\Core\TypoScript\TypoScriptService
Definition: TypoScriptService.php:27
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$availableResultsNumbers
‪int[] $availableResultsNumbers
Definition: SearchController.php:84
‪$output
‪$output
Definition: annotationChecker.php:119
‪TYPO3\CMS\Core\Database\Connection
Definition: Connection.php:35
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\makeRating
‪string makeRating($row)
Definition: SearchController.php:594
‪TYPO3\CMS\IndexedSearch\Lexer
Definition: Lexer.php:27
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$searchData
‪array $searchData
Definition: SearchController.php:66
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getNumberOfResults
‪int getNumberOfResults($numberOfResults)
Definition: SearchController.php:1553
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\processExtendedSearchParameters
‪array processExtendedSearchParameters()
Definition: SearchController.php:1466
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Extbase\Mvc\Controller\ActionController
Definition: ActionController.php:61
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:24
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
Definition: ContentObjectRenderer.php:90
‪TYPO3\CMS\Core\Exception\Page\RootLineException
Definition: RootLineException.php:25
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$enableMetaphoneSearch
‪bool $enableMetaphoneSearch
Definition: SearchController.php:150
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getSearchWords
‪array getSearchWords($defaultOperator)
Definition: SearchController.php:871
‪TYPO3\CMS\Core\Domain\Repository\PageRepository
Definition: PageRepository.php:61
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:51
‪TYPO3\CMS\Core\Utility\MathUtility\forceIntegerInRange
‪static int forceIntegerInRange(mixed $theInt, int $min, int $max=2000000000, int $defaultValue=0)
Definition: MathUtility.php:34
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\linkPage
‪array linkPage($pageUid, $row=[])
Definition: SearchController.php:1296
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:51
‪TYPO3\CMS\Core\Database\Query\Restriction\FrontendRestrictionContainer
Definition: FrontendRestrictionContainer.php:30
‪TYPO3\CMS\IndexedSearch\Utility\IndexedSearchUtility\getExplodedSearchString
‪static getExplodedSearchString(string $sword, string $defaultOperator, array $operatorTranslateTable)
Definition: IndexedSearchUtility.php:61
‪TYPO3\CMS\IndexedSearch\Utility\IndexedSearchUtility
Definition: IndexedSearchUtility.php:28
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getAllAvailableLanguageOptions
‪array getAllAvailableLanguageOptions()
Definition: SearchController.php:1067
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\makeItemTypeIcon
‪string makeItemTypeIcon($imageType, $alt, $specRowConf)
Definition: SearchController.php:655
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getAllAvailableSortDescendingOptions
‪array getAllAvailableSortDescendingOptions()
Definition: SearchController.php:1240
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getAllAvailableNumberOfResultsOptions
‪array getAllAvailableNumberOfResultsOptions()
Definition: SearchController.php:1260
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getAllAvailableSectionsOptions
‪array getAllAvailableSectionsOptions()
Definition: SearchController.php:1104
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\initializeExternalParsers
‪initializeExternalParsers()
Definition: SearchController.php:1417