‪TYPO3CMS  11.5
SearchController.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of the TYPO3 CMS project.
5  *
6  * It is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License, either version 2
8  * of the License, or any later version.
9  *
10  * For the full copyright and license information, please read the
11  * LICENSE.txt file that was distributed with this source code.
12  *
13  * The TYPO3 project - inspiring people to share!
14  */
15 
17 
18 use Psr\Http\Message\ResponseInterface;
38 use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
42 
51 {
57  protected ‪$sword = '';
58 
62  protected ‪$searchWords = [];
63 
67  protected ‪$searchData;
68 
78  protected ‪$searchRootPageIdList = 0;
79 
83  protected ‪$defaultResultNumber = 10;
84 
89 
95  protected ‪$searchRepository;
96 
102  protected ‪$lexerObj;
103 
108  protected ‪$externalParsers = [];
109 
115  protected ‪$firstRow = [];
116 
123  protected ‪$domainRecords = [];
124 
131 
137  protected ‪$resultSections = [];
138 
144  protected ‪$pathCache = [];
145 
151  protected ‪$iconFileNameCache = [];
152 
158  protected ‪$indexerConfig = [];
159 
165  protected ‪$enableMetaphoneSearch = false;
166 
170  protected ‪$typoScriptService;
171 
176  {
177  $this->typoScriptService = ‪$typoScriptService;
178  }
179 
186  public function ‪initialize(‪$searchData = [])
187  {
188  if (!is_array(‪$searchData)) {
189  ‪$searchData = [];
190  }
191 
192  // check if TypoScript is loaded
193  if (!isset($this->settings['results'])) {
194  $this->‪redirect('noTypoScript');
195  }
196 
197  // Sets availableResultsNumbers - has to be called before request settings are read to avoid DoS attack
198  $this->availableResultsNumbers = array_filter(‪GeneralUtility::intExplode(',', $this->settings['blind']['numberOfResults']));
199 
200  // Sets default result number if at least one availableResultsNumbers exists
201  if (isset($this->availableResultsNumbers[0])) {
202  $this->defaultResultNumber = $this->availableResultsNumbers[0];
203  }
204 
205  $this->‪loadSettings();
206 
207  // setting default values
208  if (is_array($this->settings['defaultOptions'])) {
209  ‪$searchData = array_merge($this->settings['defaultOptions'], ‪$searchData);
210  }
211  // if "languageUid" was set to "current", take the current site language
212  if ((‪$searchData['languageUid'] ?? '') === 'current') {
213  ‪$searchData['languageUid'] = GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect('language', 'id', 0);
214  }
215 
216  // Indexer configuration from Extension Manager interface:
217  $this->indexerConfig = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('indexed_search');
218  $this->enableMetaphoneSearch = (bool)($this->indexerConfig['enableMetaphoneSearch'] ?? false);
220  // If "_sections" is set, this value overrides any existing value.
221  if (‪$searchData['_sections'] ?? false) {
222  ‪$searchData['sections'] = ‪$searchData['_sections'];
223  }
224  // If "_sections" is set, this value overrides any existing value.
225  if ((‪$searchData['_freeIndexUid'] ?? '') !== '' && (‪$searchData['_freeIndexUid'] ?? '') !== '_') {
226  ‪$searchData['freeIndexUid'] = ‪$searchData['_freeIndexUid'];
227  }
228  ‪$searchData['numberOfResults'] = $this->‪getNumberOfResults(‪$searchData['numberOfResults'] ?? 0);
229  // This gets the search-words into the $searchWordArray
230  $this->‪setSword(‪$searchData['sword'] ?? '');
231  // Add previous search words to current
232  if ((‪$searchData['sword_prev_include'] ?? false) && (‪$searchData['sword_prev'] ?? false)) {
233  $this->‪setSword(trim(‪$searchData['sword_prev']) . ' ' . $this->‪getSword());
234  }
235  // This is the id of the site root.
236  // This value may be a commalist of integer (prepared for this)
237  $this->searchRootPageIdList = (int)‪$GLOBALS['TSFE']->config['rootLine'][0]['uid'];
238  // Setting the list of root PIDs for the search. Notice, these page IDs MUST
239  // have a TypoScript template with root flag on them! Basically this list is used
240  // to select on the "rl0" field and page ids are registered as "rl0" only if
241  // a TypoScript template record with root flag is there.
242  // This happens AFTER the use of $this->searchRootPageIdList above because
243  // the above will then fetch the menu for the CURRENT site - regardless
244  // of this kind of searching here. Thus a general search will lookup in
245  // the WHOLE database while a specific section search will take the current sections.
246  if ($this->settings['rootPidList']) {
247  $this->searchRootPageIdList = implode(',', ‪GeneralUtility::intExplode(',', $this->settings['rootPidList']));
248  }
249  $this->searchRepository = GeneralUtility::makeInstance(IndexSearchRepository::class);
250  $this->searchRepository->initialize($this->settings, ‪$searchData, $this->externalParsers, $this->searchRootPageIdList);
251  $this->searchData = ‪$searchData;
252  // $this->searchData is used in $this->getSearchWords
253  $this->searchWords = $this->‪getSearchWords(‪$searchData['defaultOperand']);
254  // Calling hook for modification of initialized content
255  if ($hookObj = $this->‪hookRequest('initialize_postProc')) {
256  $hookObj->initialize_postProc();
257  }
258  return ‪$searchData;
259  }
260 
267  public function ‪searchAction($search = []): ResponseInterface
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(',', $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 (strpos($this->searchData['sections'], 'rl') === 0) {
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($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 = strpos($this->searchData['sections'], 'rl') === 0;
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, $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  // Prepare search words for markup in content:
509  $markUpSwParams = [];
510  if ($this->settings['forwardSearchWordsInResultLink']['_typoScriptNodeValue']) {
511  // @deprecated: this feature will have no effect anymore in TYPO3 v12, and will be disabled
512  if ($this->settings['forwardSearchWordsInResultLink']['no_cache']) {
513  $markUpSwParams = ['no_cache' => 1];
514  }
515  foreach ($this->searchWords as $d) {
516  $markUpSwParams['sword_list'][] = $d['sword'];
517  }
518  }
519  $title = $this->‪linkPageATagWrap(
520  htmlspecialchars($title),
521  $this->‪linkPage($row['data_page_id'], $row, $markUpSwParams)
522  );
523  }
524  $resultData['title'] = $title;
525  $resultData['icon'] = $this->‪makeItemTypeIcon($row['item_type'], '', $specRowConf);
526  $resultData['rating'] = $this->‪makeRating($row);
527  $resultData['description'] = $this->‪makeDescription(
528  $row,
529  (bool)!($this->searchData['extResume'] && !$headerOnly),
530  $this->settings['results.']['summaryCropAfter']
531  );
532  $resultData['language'] = $this->‪makeLanguageIndication($row);
533  $resultData['size'] = GeneralUtility::formatSize($row['item_size']);
534  $resultData['created'] = $row['item_crdate'];
535  $resultData['modified'] = $row['item_mtime'];
536  $pI = parse_url($row['data_filename']);
537  if ($pI['scheme'] ?? false) {
538  $targetAttribute = '';
539  if (‪$GLOBALS['TSFE']->config['config']['fileTarget'] ?? false) {
540  $targetAttribute = ' target="' . htmlspecialchars(‪$GLOBALS['TSFE']->config['config']['fileTarget']) . '"';
541  }
542  $resultData['pathTitle'] = $row['data_filename'];
543  $resultData['pathUri'] = $row['data_filename'];
544  $resultData['path'] = '<a href="' . htmlspecialchars($row['data_filename']) . '"' . $targetAttribute . '>' . htmlspecialchars($row['data_filename']) . '</a>';
545  } else {
546  $pathId = $row['data_page_id'] ?: $row['page_id'];
547  $pathMP = $row['data_page_id'] ? $row['data_page_mp'] : '';
548  $pathStr = $this->‪getPathFromPageId($pathId, $pathMP);
549  $pathLinkData = $this->‪linkPage(
550  $pathId,
551  [
552  'data_page_type' => $row['data_page_type'],
553  'data_page_mp' => $pathMP,
554  'sys_language_uid' => $row['sys_language_uid'],
555  'static_page_arguments' => $row['static_page_arguments'],
556  ]
557  );
558 
559  $resultData['pathTitle'] = $pathStr;
560  $resultData['pathUri'] = $pathLinkData['uri'];
561  $resultData['path'] = $this->‪linkPageATagWrap($pathStr, $pathLinkData);
562 
563  // check if the access is restricted
564  if (is_array($this->requiredFrontendUsergroups[$pathId]) && !empty($this->requiredFrontendUsergroups[$pathId])) {
565  $lockedIcon = ‪PathUtility::getPublicResourceWebPath('EXT:indexed_search/Resources/Public/Icons/FileTypes/locked.gif');
566  $resultData['access'] = '<img src="' . htmlspecialchars($lockedIcon) . '"'
567  . ' width="12" height="15" vspace="5" title="'
568  . sprintf(‪LocalizationUtility::translate('result.memberGroups', 'IndexedSearch') ?? '', implode(',', array_unique($this->requiredFrontendUsergroups[$pathId])))
569  . '" alt="" />';
570  }
571  }
572  // If there are subrows (eg. subpages in a PDF-file or if a duplicate page
573  // is selected due to user-login (phash_grouping))
574  if (is_array($row['_sub'] ?? false)) {
575  $resultData['subresults'] = [];
576  if ($this->‪multiplePagesType($row['item_type'])) {
577  $resultData['subresults']['header'] = ‪LocalizationUtility::translate('result.otherMatching', 'IndexedSearch');
578  foreach ($row['_sub'] as $subRow) {
579  $resultData['subresults']['items'][] = $this->‪compileSingleResultRow($subRow, 1);
580  }
581  } else {
582  $resultData['subresults']['header'] = ‪LocalizationUtility::translate('result.otherMatching', 'IndexedSearch');
583  $resultData['subresults']['info'] = ‪LocalizationUtility::translate('result.otherPageAsWell', 'IndexedSearch');
584  }
585  }
586  return $resultData;
587  }
588 
596  protected function ‪getSpecialConfigurationForResultRow($row)
597  {
598  $pathId = $row['data_page_id'] ?: $row['page_id'];
599  $pathMP = $row['data_page_id'] ? $row['data_page_mp'] : '';
600  $specConf = $this->settings['specialConfiguration']['0'] ?? [];
601  try {
602  $rl = GeneralUtility::makeInstance(RootlineUtility::class, $pathId, $pathMP)->get();
603  foreach ($rl as $dat) {
604  if (is_array($this->settings['specialConfiguration'][$dat['uid']] ?? false)) {
605  $specConf = $this->settings['specialConfiguration'][$dat['uid']];
606  $specConf['_pid'] = $dat['uid'];
607  break;
608  }
609  }
610  } catch (RootLineException $e) {
611  // do nothing
612  }
613  return $specConf;
614  }
615 
623  protected function ‪makeRating($row)
624  {
625  $default = ' ';
626  switch ((string)$this->searchData['sortOrder']) {
627  case 'rank_count':
628  return $row['order_val'] . ' ' . ‪LocalizationUtility::translate('result.ratingMatches', 'IndexedSearch');
629  case 'rank_first':
630  return ceil(‪MathUtility::forceIntegerInRange(255 - $row['order_val'], 1, 255) / 255 * 100) . '%';
631  case 'rank_flag':
632  if ($this->firstRow['order_val2'] ?? 0) {
633  // (3 MSB bit, 224 is highest value of order_val1 currently)
634  $base = $row['order_val1'] * 256;
635  // 15-3 MSB = 12
636  $freqNumber = $row['order_val2'] / $this->firstRow['order_val2'] * 2 ** 12;
637  $total = ‪MathUtility::forceIntegerInRange($base + $freqNumber, 0, 32767);
638  return ceil(log($total) / log(32767) * 100) . '%';
639  }
640  return $default;
641  case 'rank_freq':
642  $max = 10000;
643  $total = ‪MathUtility::forceIntegerInRange($row['order_val'], 0, $max);
644  return ceil(log($total) / log($max) * 100) . '%';
645  case 'crdate':
646  return ‪$GLOBALS['TSFE']->cObj->calcAge(‪$GLOBALS['EXEC_TIME'] - $row['item_crdate'], 0);
647  case 'mtime':
648  return ‪$GLOBALS['TSFE']->cObj->calcAge(‪$GLOBALS['EXEC_TIME'] - $row['item_mtime'], 0);
649  default:
650  return $default;
651  }
652  }
653 
660  protected function ‪makeLanguageIndication($row)
661  {
662  ‪$output = '&nbsp;';
663  // If search result is a TYPO3 page:
664  if ((string)$row['item_type'] === '0') {
665  // If TypoScript is used to render the flag:
666  if (is_array($this->settings['flagRendering'] ?? false)) {
667  $cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
668  $cObj->setCurrentVal($row['sys_language_uid']);
669  $typoScriptArray = $this->typoScriptService->convertPlainArrayToTypoScriptArray($this->settings['flagRendering']);
670  ‪$output = $cObj->cObjGetSingle($this->settings['flagRendering']['_typoScriptNodeValue'], $typoScriptArray);
671  }
672  }
673  return ‪$output;
674  }
675 
684  public function ‪makeItemTypeIcon($imageType, $alt, $specRowConf)
685  {
686  // Build compound key if item type is 0, iconRendering is not used
687  // and specialConfiguration.[pid].pageIcon was set in TS
688  if (
689  $imageType === '0' && ($specRowConf['_pid'] ?? false)
690  && is_array($specRowConf['pageIcon'] ?? false)
691  && !is_array($this->settings['iconRendering'] ?? false)
692  ) {
693  $imageType .= ':' . $specRowConf['_pid'];
694  }
695  if (!isset($this->iconFileNameCache[$imageType])) {
696  $this->iconFileNameCache[$imageType] = '';
697  // If TypoScript is used to render the icon:
698  if (is_array($this->settings['iconRendering'] ?? false)) {
699  $cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
700  $cObj->setCurrentVal($imageType);
701  $typoScriptArray = $this->typoScriptService->convertPlainArrayToTypoScriptArray($this->settings['iconRendering']);
702  $this->iconFileNameCache[$imageType] = $cObj->cObjGetSingle($this->settings['iconRendering']['_typoScriptNodeValue'], $typoScriptArray);
703  } else {
704  // Default creation / finding of icon:
705  $icon = '';
706  if ($imageType === '0' || strpos($imageType, '0:') === 0) {
707  if (is_array($specRowConf['pageIcon'] ?? false)) {
708  $this->iconFileNameCache[$imageType] = ‪$GLOBALS['TSFE']->cObj->cObjGetSingle('IMAGE', $specRowConf['pageIcon']);
709  } else {
710  $icon = 'EXT:indexed_search/Resources/Public/Icons/FileTypes/pages.gif';
711  }
712  } elseif ($this->externalParsers[$imageType]) {
713  $icon = $this->externalParsers[$imageType]->getIcon($imageType);
714  }
715  if ($icon) {
716  $fullPath = GeneralUtility::getFileAbsFileName($icon);
717  if ($fullPath) {
718  $imageInfo = GeneralUtility::makeInstance(ImageInfo::class, $fullPath);
719  $iconPath = ‪PathUtility::getAbsoluteWebPath($fullPath);
720  $this->iconFileNameCache[$imageType] = $imageInfo->getWidth()
721  ? '<img src="' . $iconPath
722  . '" width="' . $imageInfo->getWidth()
723  . '" height="' . $imageInfo->getHeight()
724  . '" title="' . htmlspecialchars($alt) . '" alt="" />'
725  : '';
726  }
727  }
728  }
729  }
730  return $this->iconFileNameCache[$imageType];
731  }
732 
742  protected function ‪makeDescription($row, $noMarkup = false, $length = 180)
743  {
744  $markedSW = '';
745  $outputStr = '';
746  if ($row['show_resume']) {
747  if (!$noMarkup) {
748  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_fulltext');
749  $ftdrow = $queryBuilder
750  ->select('*')
751  ->from('index_fulltext')
752  ->where(
753  $queryBuilder->expr()->eq(
754  'phash',
755  $queryBuilder->createNamedParameter($row['phash'], ‪Connection::PARAM_INT)
756  )
757  )
758  ->executeQuery()
759  ->fetchAssociative();
760  if ($ftdrow !== false) {
761  // Cut HTTP references after some length
762  $content = preg_replace('/(http:\\/\\/[^ ]{' . $this->settings['results.']['hrefInSummaryCropAfter'] . '})([^ ]+)/i', '$1...', $ftdrow['fulltextdata']);
763  $markedSW = $this->‪markupSWpartsOfString($content);
764  }
765  }
766  if (!trim($markedSW)) {
767  $outputStr = GeneralUtility::fixed_lgd_cs($row['item_description'], $length, $this->settings['results.']['summaryCropSignifier']);
768  $outputStr = htmlspecialchars($outputStr);
769  }
770  ‪$output = $outputStr ?: $markedSW;
771  } else {
772  ‪$output = '<span class="noResume">' . ‪LocalizationUtility::translate('result.noResume', 'IndexedSearch') . '</span>';
773  }
774  return ‪$output;
775  }
776 
783  protected function ‪markupSWpartsOfString($str)
784  {
785  $htmlParser = GeneralUtility::makeInstance(HtmlParser::class);
786  // Init:
787  $str = str_replace('&nbsp;', ' ', $htmlParser->bidir_htmlspecialchars($str, -1));
788  $str = preg_replace('/\\s\\s+/', ' ', $str);
789  $swForReg = [];
790  // Prepare search words for regex:
791  foreach ($this->searchWords as $d) {
792  $swForReg[] = preg_quote($d['sword'], '/');
793  }
794  $regExString = '(' . implode('|', $swForReg) . ')';
795  // Split and combine:
796  $parts = preg_split('/' . $regExString . '/iu', ' ' . $str . ' ', 20000, PREG_SPLIT_DELIM_CAPTURE);
797  $parts = $parts ?: [];
798  // Constants:
799  $summaryMax = $this->settings['results.']['markupSW_summaryMax'];
800  $postPreLgd = (int)$this->settings['results.']['markupSW_postPreLgd'];
801  $postPreLgd_offset = (int)$this->settings['results.']['markupSW_postPreLgd_offset'];
802  $divider = $this->settings['results.']['markupSW_divider'];
803  $occurrences = (count($parts) - 1) / 2;
804  if ($occurrences) {
805  $postPreLgd = ‪MathUtility::forceIntegerInRange($summaryMax / $occurrences, $postPreLgd, $summaryMax / 2);
806  }
807  // Variable:
808  $summaryLgd = 0;
809  ‪$output = [];
810  // Shorten in-between strings:
811  foreach ($parts as $k => $strP) {
812  if ($k % 2 == 0) {
813  // Find length of the summary part:
814  $strLen = mb_strlen($parts[$k], 'utf-8');
815  ‪$output[$k] = $parts[$k];
816  // Possibly shorten string:
817  if (!$k) {
818  // First entry at all (only cropped on the frontside)
819  if ($strLen > $postPreLgd) {
820  ‪$output[$k] = $divider . preg_replace('/^[^[:space:]]+[[:space:]]/', '', GeneralUtility::fixed_lgd_cs($parts[$k], -($postPreLgd - $postPreLgd_offset)));
821  }
822  } elseif ($summaryLgd > $summaryMax || !isset($parts[$k + 1])) {
823  // In case summary length is exceed OR if there are no more entries at all:
824  if ($strLen > $postPreLgd) {
825  ‪$output[$k] = preg_replace('/[[:space:]][^[:space:]]+$/', '', GeneralUtility::fixed_lgd_cs(
826  $parts[$k],
827  $postPreLgd - $postPreLgd_offset
828  )) . $divider;
829  }
830  } else {
831  if ($strLen > $postPreLgd * 2) {
832  ‪$output[$k] = preg_replace('/[[:space:]][^[:space:]]+$/', '', GeneralUtility::fixed_lgd_cs(
833  $parts[$k],
834  $postPreLgd - $postPreLgd_offset
835  )) . $divider . preg_replace('/^[^[:space:]]+[[:space:]]/', '', GeneralUtility::fixed_lgd_cs($parts[$k], -($postPreLgd - $postPreLgd_offset)));
836  }
837  }
838  $summaryLgd += mb_strlen(‪$output[$k], 'utf-8');
839  // Protect output:
840  ‪$output[$k] = htmlspecialchars(‪$output[$k]);
841  // If summary lgd is exceed, break the process:
842  if ($summaryLgd > $summaryMax) {
843  break;
844  }
845  } else {
846  $summaryLgd += mb_strlen($strP, 'utf-8');
847  ‪$output[$k] = '<strong class="tx-indexedsearch-redMarkup">' . htmlspecialchars($parts[$k]) . '</strong>';
848  }
849  }
850  // Return result:
851  return implode('', ‪$output);
852  }
853 
859  protected function ‪writeSearchStat(array ‪$searchWords): void
860  {
861  if (empty(‪$searchWords)) {
862  return;
863  }
864  $entries = [];
865  foreach (‪$searchWords as $val) {
866  $entries[] = [
867  mb_substr($val['sword'], 0, 50),
868  // Time stamp
869  ‪$GLOBALS['EXEC_TIME'],
870  // search page id for indexed search stats
871  ‪$GLOBALS['TSFE']->id,
872  ];
873  }
874  GeneralUtility::makeInstance(ConnectionPool::class)
875  ->getConnectionForTable('index_stat_word')
876  ->bulkInsert(
877  'index_stat_word',
878  $entries,
879  [ 'word', 'tstamp', 'pageid' ],
881  );
882  }
883 
900  protected function ‪getSearchWords($defaultOperator)
901  {
902  // Shorten search-word string to max 200 bytes - shortening the string here is only a run-away feature!
903  ‪$searchWords = mb_substr($this->‪getSword(), 0, 200);
904  // Convert to UTF-8 + conv. entities (was also converted during indexing!)
905  if (‪$GLOBALS['TSFE']->metaCharset && ‪$GLOBALS['TSFE']->metaCharset !== 'utf-8') {
906  ‪$searchWords = mb_convert_encoding(‪$searchWords, 'utf-8', ‪$GLOBALS['TSFE']->metaCharset);
907  ‪$searchWords = html_entity_decode(‪$searchWords);
908  }
909  $sWordArray = false;
910  if ($hookObj = $this->‪hookRequest('getSearchWords')) {
911  $sWordArray = $hookObj->getSearchWords_splitSWords(‪$searchWords, $defaultOperator);
912  } else {
913  // sentence
914  if ($this->searchData['searchType'] == 20) {
915  $sWordArray = [
916  [
917  'sword' => trim(‪$searchWords),
918  'oper' => 'AND',
919  ],
920  ];
921  } else {
922  // case-sensitive. Defines the words, which will be
923  // operators between words
924  $operatorTranslateTable = [
925  ['+', 'AND'],
926  ['|', 'OR'],
927  ['-', 'AND NOT'],
928  // Add operators for various languages
929  // Converts the operators to lowercase
930  [mb_strtolower(‪LocalizationUtility::translate('localizedOperandAnd', 'IndexedSearch') ?? '', 'utf-8'), 'AND'],
931  [mb_strtolower(‪LocalizationUtility::translate('localizedOperandOr', 'IndexedSearch') ?? '', 'utf-8'), 'OR'],
932  [mb_strtolower(‪LocalizationUtility::translate('localizedOperandNot', 'IndexedSearch') ?? '', 'utf-8'), 'AND NOT'],
933  ];
934  $swordArray = ‪IndexedSearchUtility::getExplodedSearchString(‪$searchWords, $defaultOperator == 1 ? 'OR' : 'AND', $operatorTranslateTable);
935  if (is_array($swordArray)) {
936  $sWordArray = $this->‪procSearchWordsByLexer($swordArray);
937  }
938  }
939  }
940  return $sWordArray;
941  }
942 
950  protected function ‪procSearchWordsByLexer(‪$searchWords)
951  {
952  $newSearchWords = [];
953  // Init lexer (used to post-processing of search words)
954  $lexerObjectClassName = (‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['indexed_search']['lexer'] ?? false) ? ‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['indexed_search']['lexer'] : Lexer::class;
955 
957  $lexer = GeneralUtility::makeInstance($lexerObjectClassName);
958  $this->lexerObj = $lexer;
959  // Traverse the search word array
960  foreach (‪$searchWords as $wordDef) {
961  // No space in word (otherwise it might be a sentence in quotes like "there is").
962  if (!str_contains($wordDef['sword'], ' ')) {
963  // Split the search word by lexer:
964  $res = $this->lexerObj->split2Words($wordDef['sword']);
965  // Traverse lexer result and add all words again:
966  foreach ($res as $word) {
967  $newSearchWords[] = [
968  'sword' => $word,
969  'oper' => $wordDef['oper'],
970  ];
971  }
972  } else {
973  $newSearchWords[] = $wordDef;
974  }
975  }
976  return $newSearchWords;
977  }
978 
985  public function ‪formAction($search = []): ResponseInterface
986  {
987  ‪$searchData = $this->‪initialize($search);
988  // Adding search field value
989  $this->view->assign('sword', $this->‪getSword());
990  // Extended search
991  if (!empty(‪$searchData['extendedSearch'])) {
992  $this->view->assignMultiple($this->‪processExtendedSearchParameters());
993  }
994  $this->view->assign('searchParams', ‪$searchData);
995 
996  return $this->‪htmlResponse();
997  }
998 
1002  public function ‪noTypoScriptAction(): ResponseInterface
1003  {
1004  return $this->‪htmlResponse();
1005  }
1006 
1007  /****************************************
1008  * building together the available options for every dropdown
1009  ***************************************/
1015  protected function ‪getAllAvailableSearchTypeOptions()
1016  {
1017  $allOptions = [];
1018  $types = [0, 1, 2, 3, 10, 20];
1019  $blindSettings = $this->settings['blind'];
1020  if (!$blindSettings['searchType']) {
1021  foreach ($types as $typeNum) {
1022  $allOptions[$typeNum] = ‪LocalizationUtility::translate('searchTypes.' . $typeNum, 'IndexedSearch');
1023  }
1024  }
1025  // Remove this option if metaphone search is disabled)
1026  if (!$this->enableMetaphoneSearch) {
1027  unset($allOptions[10]);
1028  }
1029  // disable single entries by TypoScript
1030  $allOptions = $this->‪removeOptionsFromOptionList($allOptions, $blindSettings['searchType']);
1031  return $allOptions;
1032  }
1033 
1039  protected function ‪getAllAvailableOperandsOptions()
1040  {
1041  $allOptions = [];
1042  $blindSettings = $this->settings['blind'];
1043  if (!$blindSettings['defaultOperand']) {
1044  $allOptions = [
1045  0 => ‪LocalizationUtility::translate('defaultOperands.0', 'IndexedSearch'),
1046  1 => ‪LocalizationUtility::translate('defaultOperands.1', 'IndexedSearch'),
1047  ];
1048  }
1049  // disable single entries by TypoScript
1050  $allOptions = $this->‪removeOptionsFromOptionList($allOptions, $blindSettings['defaultOperand']);
1051  return $allOptions;
1052  }
1053 
1059  protected function ‪getAllAvailableMediaTypesOptions()
1060  {
1061  $allOptions = [];
1062  $mediaTypes = [-1, 0, -2];
1063  $blindSettings = $this->settings['blind'];
1064  if (!$blindSettings['mediaType']) {
1065  foreach ($mediaTypes as $mediaType) {
1066  $allOptions[$mediaType] = ‪LocalizationUtility::translate('mediaTypes.' . $mediaType, 'IndexedSearch');
1067  }
1068  // Add media to search in:
1069  $additionalMedia = trim($this->settings['mediaList']);
1070  if ($additionalMedia !== '') {
1071  $additionalMedia = ‪GeneralUtility::trimExplode(',', $additionalMedia, true);
1072  } else {
1073  $additionalMedia = [];
1074  }
1075  foreach ($this->externalParsers as $extension => $obj) {
1076  // Skip unwanted extensions
1077  if (!empty($additionalMedia) && !in_array($extension, $additionalMedia)) {
1078  continue;
1079  }
1080  if ($name = $obj->searchTypeMediaTitle($extension)) {
1081  $translatedName = ‪LocalizationUtility::translate('mediaTypes.' . $extension, 'IndexedSearch');
1082  $allOptions[$extension] = $translatedName ?: $name;
1083  }
1084  }
1085  }
1086  // disable single entries by TypoScript
1087  $allOptions = $this->‪removeOptionsFromOptionList($allOptions, $blindSettings['mediaType']);
1088  return $allOptions;
1089  }
1090 
1096  protected function ‪getAllAvailableLanguageOptions()
1097  {
1098  $allOptions = [
1099  '-1' => ‪LocalizationUtility::translate('languageUids.-1', 'IndexedSearch'),
1100  ];
1101  $blindSettings = $this->settings['blind'];
1102  if (!$blindSettings['languageUid']) {
1103  try {
1104  $site = GeneralUtility::makeInstance(SiteFinder::class)
1105  ->getSiteByPageId(‪$GLOBALS['TSFE']->id);
1106 
1107  $languages = $site->getLanguages();
1108  foreach ($languages as $language) {
1109  $allOptions[$language->getLanguageId()] = $language->getNavigationTitle();
1110  }
1111  } catch (SiteNotFoundException $e) {
1112  // No Site found, no options
1113  $allOptions = [];
1114  }
1116  // disable single entries by TypoScript
1117  $allOptions = $this->‪removeOptionsFromOptionList($allOptions, (array)$blindSettings['languageUid']);
1118  } else {
1119  $allOptions = [];
1120  }
1121  return $allOptions;
1122  }
1123 
1133  protected function ‪getAllAvailableSectionsOptions()
1134  {
1135  $allOptions = [];
1136  $sections = [0, -1, -2, -3];
1137  $blindSettings = $this->settings['blind'];
1138  if (!$blindSettings['sections']) {
1139  foreach ($sections as $section) {
1140  $allOptions[$section] = ‪LocalizationUtility::translate('sections.' . $section, 'IndexedSearch');
1141  }
1142  }
1143  // Creating levels for section menu:
1144  // This selects the first and secondary menus for the "sections" selector - so we can search in sections and sub sections.
1145  if ($this->settings['displayLevel1Sections']) {
1146  $firstLevelMenu = $this->‪getMenuOfPages((int)$this->searchRootPageIdList);
1147  $labelLevel1 = ‪LocalizationUtility::translate('sections.rootLevel1', 'IndexedSearch');
1148  $labelLevel2 = ‪LocalizationUtility::translate('sections.rootLevel2', 'IndexedSearch');
1149  foreach ($firstLevelMenu as $firstLevelKey => $menuItem) {
1150  if (!$menuItem['nav_hide']) {
1151  $allOptions['rl1_' . $menuItem['uid']] = trim($labelLevel1 . ' ' . $menuItem['title']);
1152  if ($this->settings['displayLevel2Sections']) {
1153  $secondLevelMenu = $this->‪getMenuOfPages($menuItem['uid']);
1154  foreach ($secondLevelMenu as $secondLevelKey => $menuItemLevel2) {
1155  if (!$menuItemLevel2['nav_hide']) {
1156  $allOptions['rl2_' . $menuItemLevel2['uid']] = trim($labelLevel2 . ' ' . $menuItemLevel2['title']);
1157  } else {
1158  unset($secondLevelMenu[$secondLevelKey]);
1159  }
1160  }
1161  $allOptions['rl2_' . implode(',', array_keys($secondLevelMenu))] = ‪LocalizationUtility::translate('sections.rootLevel2All', 'IndexedSearch');
1162  }
1163  } else {
1164  unset($firstLevelMenu[$firstLevelKey]);
1165  }
1166  }
1167  $allOptions['rl1_' . implode(',', array_keys($firstLevelMenu))] = ‪LocalizationUtility::translate('sections.rootLevel1All', 'IndexedSearch');
1168  }
1169  // disable single entries by TypoScript
1170  $allOptions = $this->‪removeOptionsFromOptionList($allOptions, $blindSettings['sections']);
1171  return $allOptions;
1172  }
1173 
1180  {
1181  $allOptions = [
1182  '-1' => ‪LocalizationUtility::translate('indexingConfigurations.-1', 'IndexedSearch'),
1183  '-2' => ‪LocalizationUtility::translate('indexingConfigurations.-2', 'IndexedSearch'),
1184  '0' => ‪LocalizationUtility::translate('indexingConfigurations.0', 'IndexedSearch'),
1185  ];
1186  $blindSettings = $this->settings['blind'];
1187  if (!($blindSettings['indexingConfigurations'] ?? false)) {
1188  // add an additional index configuration
1189  if ($this->settings['defaultFreeIndexUidList']) {
1190  $uidList = ‪GeneralUtility::intExplode(',', $this->settings['defaultFreeIndexUidList']);
1191  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
1192  ->getQueryBuilderForTable('index_config');
1193  $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
1194  $result = $queryBuilder
1195  ->select('uid', 'title')
1196  ->from('index_config')
1197  ->where(
1198  $queryBuilder->expr()->in(
1199  'uid',
1200  $queryBuilder->createNamedParameter($uidList, Connection::PARAM_INT_ARRAY)
1201  )
1202  )
1203  ->executeQuery();
1204 
1205  while ($row = $result->fetchAssociative()) {
1206  $indexId = (int)$row['uid'];
1207  $title = ‪LocalizationUtility::translate('indexingConfigurations.' . $indexId, 'IndexedSearch');
1208  $allOptions[$indexId] = $title ?: $row['title'];
1209  }
1210  }
1211  // disable single entries by TypoScript
1212  $allOptions = $this->‪removeOptionsFromOptionList($allOptions, (array)($blindSettings['indexingConfigurations'] ?? []));
1213  } else {
1214  $allOptions = [];
1215  }
1216  return $allOptions;
1217  }
1218 
1228  protected function ‪getAllAvailableSortOrderOptions()
1229  {
1230  $allOptions = [];
1231  $sortOrders = ['rank_flag', 'rank_freq', 'rank_first', 'rank_count', 'mtime', 'title', 'crdate'];
1232  $blindSettings = $this->settings['blind'];
1233  if (!$blindSettings['sortOrder']) {
1234  foreach ($sortOrders as $order) {
1235  $allOptions[$order] = ‪LocalizationUtility::translate('sortOrders.' . $order, 'IndexedSearch');
1236  }
1237  }
1238  // disable single entries by TypoScript
1239  $allOptions = $this->‪removeOptionsFromOptionList($allOptions, ($blindSettings['sortOrder.'] ?? []));
1240  return $allOptions;
1241  }
1242 
1248  protected function ‪getAllAvailableGroupOptions()
1249  {
1250  $allOptions = [];
1251  $blindSettings = $this->settings['blind'];
1252  if (!($blindSettings['groupBy'] ?? false)) {
1253  $allOptions = [
1254  'sections' => ‪LocalizationUtility::translate('groupBy.sections', 'IndexedSearch'),
1255  'flat' => ‪LocalizationUtility::translate('groupBy.flat', 'IndexedSearch'),
1256  ];
1257  }
1258  // disable single entries by TypoScript
1259  $allOptions = $this->‪removeOptionsFromOptionList($allOptions, ($blindSettings['groupBy.'] ?? []));
1260  return $allOptions;
1261  }
1262 
1268  protected function ‪getAllAvailableSortDescendingOptions()
1269  {
1270  $allOptions = [];
1271  $blindSettings = $this->settings['blind'];
1272  if (!($blindSettings['descending'] ?? false)) {
1273  $allOptions = [
1274  0 => ‪LocalizationUtility::translate('sortOrders.descending', 'IndexedSearch'),
1275  1 => ‪LocalizationUtility::translate('sortOrders.ascending', 'IndexedSearch'),
1276  ];
1277  }
1278  // disable single entries by TypoScript
1279  $allOptions = $this->‪removeOptionsFromOptionList($allOptions, ($blindSettings['descending.'] ?? []));
1280  return $allOptions;
1281  }
1282 
1288  protected function ‪getAllAvailableNumberOfResultsOptions()
1289  {
1290  $allOptions = [];
1291  if (count($this->availableResultsNumbers) > 1) {
1292  $allOptions = array_combine($this->availableResultsNumbers, $this->availableResultsNumbers);
1293  }
1294  // disable single entries by TypoScript
1295  return $this->‪removeOptionsFromOptionList((array)$allOptions, $this->settings['blind']['numberOfResults']);
1296  }
1297 
1305  protected function ‪removeOptionsFromOptionList($allOptions, $blindOptions)
1306  {
1307  if (is_array($blindOptions)) {
1308  foreach ($blindOptions as $key => $val) {
1309  if ($val == 1) {
1310  unset($allOptions[$key]);
1311  }
1312  }
1313  }
1314  return $allOptions;
1315  }
1316 
1325  protected function ‪linkPage($pageUid, $row = [], $markUpSwParams = [])
1326  {
1327  $pageLanguage = GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect('language', 'contentId', 0);
1328  // Parameters for link
1329  $urlParameters = [];
1330  if ($row['static_page_arguments'] !== null) {
1331  $urlParameters = json_decode($row['static_page_arguments'], true);
1332  }
1333  // Add &type and &MP variable:
1334  if ($row['data_page_mp']) {
1335  $urlParameters['MP'] = $row['data_page_mp'];
1336  }
1337  if (($pageLanguage === 0 && $row['sys_language_uid'] > 0) || $pageLanguage > 0) {
1338  $urlParameters['L'] = (int)$row['sys_language_uid'];
1339  }
1340  // markup-GET vars:
1341  $urlParameters = array_merge($urlParameters, $markUpSwParams);
1342  // This will make sure that the path is retrieved if it hasn't been
1343  // already. Used only for the sake of the domain_record thing.
1344  $this->‪getPathFromPageId($pageUid);
1345 
1346  return $this->‪preparePageLink($pageUid, $row, $urlParameters);
1347  }
1348 
1355  protected function ‪getMenuOfPages($pageUid)
1356  {
1357  $pageRepository = GeneralUtility::makeInstance(PageRepository::class);
1358  if ($this->settings['displayLevelxAllTypes']) {
1359  return $pageRepository->getMenuForPages([$pageUid]);
1360  }
1361  return $pageRepository->getMenu($pageUid);
1362  }
1363 
1371  protected function ‪getPathFromPageId($id, $pathMP = '')
1372  {
1373  $identStr = $id . '|' . $pathMP;
1374  if (!isset($this->pathCache[$identStr])) {
1375  $this->requiredFrontendUsergroups[$id] = [];
1376  $this->domainRecords[$id] = [];
1377  try {
1378  $rl = GeneralUtility::makeInstance(RootlineUtility::class, $id, $pathMP)->get();
1379  $path = '';
1380  $pageCount = count($rl);
1381  if (!empty($rl)) {
1382  $excludeDoktypesFromPath = ‪GeneralUtility::trimExplode(
1383  ',',
1384  $this->settings['results']['pathExcludeDoktypes'] ?? '',
1385  true
1386  );
1387  $breadcrumbWrap = $this->settings['breadcrumbWrap'] ?? '/';
1388  $breadcrumbWraps = GeneralUtility::makeInstance(TypoScriptService::class)
1389  ->explodeConfigurationForOptionSplit(['wrap' => $breadcrumbWrap], $pageCount);
1390  foreach ($rl as $k => $v) {
1391  if (in_array($v['doktype'], $excludeDoktypesFromPath, false)) {
1392  continue;
1393  }
1394  // Check fe_user
1395  if ($v['fe_group'] && ($v['uid'] == $id || $v['extendToSubpages'])) {
1396  $this->requiredFrontendUsergroups[$id][] = $v['fe_group'];
1397  }
1398  // Check sys_domain
1399  if ($this->settings['detectDomainRecords']) {
1400  $domainName = $this->‪getFirstDomainForPage((int)$v['uid']);
1401  if ($domainName) {
1402  $this->domainRecords[$id][] = $domainName;
1403  // Set path accordingly
1404  $path = $domainName . $path;
1405  break;
1406  }
1407  }
1408  // Stop, if we find that the current id is the current root page.
1409  if ($v['uid'] == ‪$GLOBALS['TSFE']->config['rootLine'][0]['uid']) {
1410  array_pop($breadcrumbWraps);
1411  break;
1412  }
1413  $path = ‪$GLOBALS['TSFE']->cObj->wrap(htmlspecialchars($v['title']), array_pop($breadcrumbWraps)['wrap']) . $path;
1414  }
1415  }
1416  } catch (RootLineException $e) {
1417  $path = '';
1418  }
1419  $this->pathCache[$identStr] = $path;
1420  }
1421  return $this->pathCache[$identStr];
1422  }
1423 
1430  protected function ‪getFirstDomainForPage(int $id): string
1431  {
1432  $domain = '';
1433  try {
1434  $domain = GeneralUtility::makeInstance(SiteFinder::class)
1435  ->getSiteByRootPageId($id)
1436  ->getBase()
1437  ->getHost();
1438  } catch (‪SiteNotFoundException $e) {
1439  // site was not found, we return an empty string as default
1440  }
1441  return $domain;
1442  }
1443 
1448  protected function ‪initializeExternalParsers()
1449  {
1450  // Initialize external document parsers for icon display and other soft operations
1451  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['indexed_search']['external_parsers'] ?? [] as $extension => $className) {
1452  $this->externalParsers[$extension] = GeneralUtility::makeInstance($className);
1453  // Init parser and if it returns FALSE, unset its entry again
1454  if (!$this->externalParsers[$extension]->softInit($extension)) {
1455  unset($this->externalParsers[$extension]);
1456  }
1457  }
1458  }
1459 
1466  protected function ‪hookRequest($functionName)
1467  {
1468  // Hook: menuConfig_preProcessModMenu
1469  if (‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['indexed_search']['pi1_hooks'][$functionName] ?? false) {
1470  $hookObj = GeneralUtility::makeInstance(‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['indexed_search']['pi1_hooks'][$functionName]);
1471  if (method_exists($hookObj, $functionName)) {
1472  $hookObj->pObj = $this;
1473  return $hookObj;
1474  }
1475  }
1476  return null;
1477  }
1478 
1485  protected function ‪multiplePagesType($item_type)
1486  {
1487  return is_object($this->externalParsers[$item_type] ?? false) && $this->externalParsers[$item_type]->isMultiplePageExtension($item_type);
1488  }
1489 
1497  protected function ‪processExtendedSearchParameters()
1498  {
1499  $allSearchTypes = $this->‪getAllAvailableSearchTypeOptions();
1500  $allDefaultOperands = $this->‪getAllAvailableOperandsOptions();
1501  $allMediaTypes = $this->‪getAllAvailableMediaTypesOptions();
1502  $allLanguageUids = $this->‪getAllAvailableLanguageOptions();
1503  $allSortOrders = $this->‪getAllAvailableSortOrderOptions();
1504  $allSortDescendings = $this->‪getAllAvailableSortDescendingOptions();
1505 
1506  return [
1507  'allSearchTypes' => $allSearchTypes,
1508  'allDefaultOperands' => $allDefaultOperands,
1509  'showTypeSearch' => !empty($allSearchTypes) || !empty($allDefaultOperands),
1510  'allMediaTypes' => $allMediaTypes,
1511  'allLanguageUids' => $allLanguageUids,
1512  'showMediaAndLanguageSearch' => !empty($allMediaTypes) || !empty($allLanguageUids),
1513  'allSections' => $this->‪getAllAvailableSectionsOptions(),
1514  'allIndexConfigurations' => $this->‪getAllAvailableIndexConfigurationsOptions(),
1515  'allSortOrders' => $allSortOrders,
1516  'allSortDescendings' => $allSortDescendings,
1517  'showSortOrders' => !empty($allSortOrders) || !empty($allSortDescendings),
1518  'allNumberOfResults' => $this->‪getAllAvailableNumberOfResultsOptions(),
1519  'allGroups' => $this->‪getAllAvailableGroupOptions(),
1520  ];
1521  }
1522 
1526  protected function ‪loadSettings()
1527  {
1528  if (!is_array($this->settings['results.'] ?? false)) {
1529  $this->settings['results.'] = [];
1530  }
1531  $fullTypoScriptArray = $this->typoScriptService->convertPlainArrayToTypoScriptArray($this->settings);
1532  $this->settings['detectDomainRecords'] = $fullTypoScriptArray['detectDomainRecords'] ?? 0;
1533  $this->settings['detectDomainRecords.'] = $fullTypoScriptArray['detectDomainRecords.'] ?? [];
1534  $typoScriptArray = $fullTypoScriptArray['results.'];
1535 
1536  $this->settings['results.']['summaryCropAfter'] = ‪MathUtility::forceIntegerInRange(
1537  ‪$GLOBALS['TSFE']->cObj->stdWrapValue('summaryCropAfter', $typoScriptArray ?? []),
1538  10,
1539  5000,
1540  180
1541  );
1542  $this->settings['results.']['summaryCropSignifier'] = ‪$GLOBALS['TSFE']->cObj->stdWrapValue('summaryCropSignifier', $typoScriptArray ?? []);
1543  $this->settings['results.']['titleCropAfter'] = ‪MathUtility::forceIntegerInRange(
1544  ‪$GLOBALS['TSFE']->cObj->stdWrapValue('titleCropAfter', $typoScriptArray ?? []),
1545  10,
1546  500,
1547  50
1548  );
1549  $this->settings['results.']['titleCropSignifier'] = ‪$GLOBALS['TSFE']->cObj->stdWrapValue('titleCropSignifier', $typoScriptArray ?? []);
1550  $this->settings['results.']['markupSW_summaryMax'] = ‪MathUtility::forceIntegerInRange(
1551  ‪$GLOBALS['TSFE']->cObj->stdWrapValue('markupSW_summaryMax', $typoScriptArray ?? []),
1552  10,
1553  5000,
1554  300
1555  );
1556  $this->settings['results.']['markupSW_postPreLgd'] = ‪MathUtility::forceIntegerInRange(
1557  ‪$GLOBALS['TSFE']->cObj->stdWrapValue('markupSW_postPreLgd', $typoScriptArray ?? []),
1558  1,
1559  500,
1560  60
1561  );
1562  $this->settings['results.']['markupSW_postPreLgd_offset'] = ‪MathUtility::forceIntegerInRange(
1563  ‪$GLOBALS['TSFE']->cObj->stdWrapValue('markupSW_postPreLgd_offset', $typoScriptArray ?? []),
1564  1,
1565  50,
1566  5
1567  );
1568  $this->settings['results.']['markupSW_divider'] = ‪$GLOBALS['TSFE']->cObj->stdWrapValue('markupSW_divider', $typoScriptArray ?? []);
1569  $this->settings['results.']['hrefInSummaryCropAfter'] = ‪MathUtility::forceIntegerInRange(
1570  ‪$GLOBALS['TSFE']->cObj->stdWrapValue('hrefInSummaryCropAfter', $typoScriptArray ?? []),
1571  10,
1572  400,
1573  60
1574  );
1575  $this->settings['results.']['hrefInSummaryCropSignifier'] = ‪$GLOBALS['TSFE']->cObj->stdWrapValue('hrefInSummaryCropSignifier', $typoScriptArray ?? []);
1576  }
1577 
1584  protected function ‪getNumberOfResults($numberOfResults)
1585  {
1586  $numberOfResults = (int)$numberOfResults;
1587 
1588  return in_array($numberOfResults, $this->availableResultsNumbers) ?
1589  $numberOfResults : ‪$this->defaultResultNumber;
1590  }
1591 
1601  protected function ‪preparePageLink(int $pageUid, array $row, array $urlParameters): array
1602  {
1603  $target = '';
1604  $uri = $this->uriBuilder
1605  ->setTargetPageUid($pageUid)
1606  ->setTargetPageType($row['data_page_type'])
1607  ->setArguments($urlParameters)
1608  ->build();
1609 
1610  // If external domain, then link to that:
1611  if (!empty($this->domainRecords[$pageUid])) {
1612  $scheme = GeneralUtility::getIndpEnv('TYPO3_SSL') ? 'https://' : 'http://';
1613  $firstDomain = reset($this->domainRecords[$pageUid]);
1614  $uri = $scheme . $firstDomain . $uri;
1615  $target = $this->settings['detectDomainRecords.']['target'] ?? '';
1616  }
1617 
1618  return ['uri' => $uri, 'target' => $target];
1619  }
1620 
1628  protected function ‪linkPageATagWrap(string $linkText, array $linkData): string
1629  {
1630  $attributes = [
1631  'href' => $linkData['uri'],
1632  ];
1633  if (!empty($linkData['target'])) {
1634  $attributes['target'] = $linkData['target'];
1635  }
1636  return sprintf(
1637  '<a %s>%s</a>',
1638  GeneralUtility::implodeAttributes($attributes, true),
1639  $linkText
1640  );
1641  }
1642 
1646  protected function ‪addOperatorLabel(array $searchWord): array
1647  {
1648  if ($searchWord['oper'] ?? false) {
1649  $searchWord['operatorLabel'] = strtolower(str_replace(' ', '', (string)($searchWord['oper'])));
1650  }
1651 
1652  return $searchWord;
1653  }
1654 
1659  public function ‪setSword(‪$sword)
1660  {
1661  $this->sword = (string)‪$sword;
1662  }
1663 
1668  public function ‪getSword()
1669  {
1670  return (string)‪$this->sword;
1671  }
1672 }
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\multiplePagesType
‪bool multiplePagesType($item_type)
Definition: SearchController.php:1467
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\writeSearchStat
‪writeSearchStat(array $searchWords)
Definition: SearchController.php:841
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\markupSWpartsOfString
‪string markupSWpartsOfString($str)
Definition: SearchController.php:765
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\makeDescription
‪string makeDescription($row, $noMarkup=false, $length=180)
Definition: SearchController.php:724
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static list< string > trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
Definition: GeneralUtility.php:999
‪TYPO3\CMS\IndexedSearch\Controller\SearchController
Definition: SearchController.php:51
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\noTypoScriptAction
‪noTypoScriptAction()
Definition: SearchController.php:984
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:25
‪TYPO3\CMS\Core\Database\Connection\PARAM_INT
‪const PARAM_INT
Definition: Connection.php:49
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$searchRootPageIdList
‪int string $searchRootPageIdList
Definition: SearchController.php:74
‪TYPO3\CMS\Extbase\Annotation
Definition: IgnoreValidation.php:18
‪TYPO3\CMS\Extbase\Mvc\Controller\ActionController\htmlResponse
‪ResponseInterface htmlResponse(string $html=null)
Definition: ActionController.php:1067
‪TYPO3\CMS\Extbase\Utility\LocalizationUtility
Definition: LocalizationUtility.php:33
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$externalParsers
‪array $externalParsers
Definition: SearchController.php:99
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getAllAvailableOperandsOptions
‪array getAllAvailableOperandsOptions()
Definition: SearchController.php:1021
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$defaultResultNumber
‪int $defaultResultNumber
Definition: SearchController.php:78
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$indexerConfig
‪array $indexerConfig
Definition: SearchController.php:142
‪TYPO3\CMS\Core\Html\HtmlParser
Definition: HtmlParser.php:27
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$requiredFrontendUsergroups
‪array $requiredFrontendUsergroups
Definition: SearchController.php:118
‪TYPO3\CMS\Core\Configuration\ExtensionConfiguration
Definition: ExtensionConfiguration.php:45
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository
Definition: IndexSearchRepository.php:38
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getAllAvailableSearchTypeOptions
‪array getAllAvailableSearchTypeOptions()
Definition: SearchController.php:997
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\formAction
‪formAction($search=[])
Definition: SearchController.php:967
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$searchRepository
‪TYPO3 CMS IndexedSearch Domain Repository IndexSearchRepository $searchRepository
Definition: SearchController.php:88
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getPathFromPageId
‪string getPathFromPageId($id, $pathMP='')
Definition: SearchController.php:1353
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getAllAvailableGroupOptions
‪array getAllAvailableGroupOptions()
Definition: SearchController.php:1230
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$pathCache
‪array $pathCache
Definition: SearchController.php:130
‪TYPO3\CMS\Core\Utility\MathUtility\forceIntegerInRange
‪static int forceIntegerInRange($theInt, $min, $max=2000000000, $defaultValue=0)
Definition: MathUtility.php:32
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\compileSingleResultRow
‪array compileSingleResultRow($row, $headerOnly=0)
Definition: SearchController.php:452
‪TYPO3\CMS\Core\Utility\RootlineUtility
Definition: RootlineUtility.php:38
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getFirstDomainForPage
‪string getFirstDomainForPage(int $id)
Definition: SearchController.php:1412
‪TYPO3\CMS\Core\Exception\SiteNotFoundException
Definition: SiteNotFoundException.php:25
‪TYPO3\CMS\Core\Site\SiteFinder
Definition: SiteFinder.php:31
‪TYPO3\CMS\Core\Utility\PathUtility\getPublicResourceWebPath
‪static string getPublicResourceWebPath(string $resourcePath, bool $prefixWithSitePath=true)
Definition: PathUtility.php:98
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getMenuOfPages
‪array getMenuOfPages($pageUid)
Definition: SearchController.php:1337
‪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:136
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getSword
‪string getSword()
Definition: SearchController.php:1650
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\addOperatorLabel
‪addOperatorLabel(array $searchWord)
Definition: SearchController.php:1628
‪TYPO3\CMS\Core\Database\Connection\PARAM_STR
‪const PARAM_STR
Definition: Connection.php:54
‪TYPO3\CMS\Core\Context\Context
Definition: Context.php:53
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\searchAction
‪searchAction($search=[])
Definition: SearchController.php:249
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getAllAvailableIndexConfigurationsOptions
‪array getAllAvailableIndexConfigurationsOptions()
Definition: SearchController.php:1161
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getAllAvailableSortOrderOptions
‪array getAllAvailableSortOrderOptions()
Definition: SearchController.php:1210
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\linkPageATagWrap
‪string linkPageATagWrap(string $linkText, array $linkData)
Definition: SearchController.php:1610
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\hookRequest
‪object null hookRequest($functionName)
Definition: SearchController.php:1448
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\linkPage
‪array linkPage($pageUid, $row=[], $markUpSwParams=[])
Definition: SearchController.php:1307
‪TYPO3\CMS\IndexedSearch\Controller
Definition: AdministrationController.php:16
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\setSword
‪setSword($sword)
Definition: SearchController.php:1641
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\initialize
‪array initialize($searchData=[])
Definition: SearchController.php:168
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$lexerObj
‪TYPO3 CMS IndexedSearch Lexer $lexerObj
Definition: SearchController.php:94
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$searchWords
‪array $searchWords
Definition: SearchController.php:60
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$resultSections
‪array $resultSections
Definition: SearchController.php:124
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$firstRow
‪array $firstRow
Definition: SearchController.php:105
‪TYPO3\CMS\Extbase\Utility\LocalizationUtility\translate
‪static string null translate(string $key, ?string $extensionName=null, array $arguments=null, string $languageKey=null, array $alternativeLanguageKeys=null)
Definition: LocalizationUtility.php:67
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\makeLanguageIndication
‪string makeLanguageIndication($row)
Definition: SearchController.php:642
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\procSearchWordsByLexer
‪array procSearchWordsByLexer($searchWords)
Definition: SearchController.php:932
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\removeOptionsFromOptionList
‪array removeOptionsFromOptionList($allOptions, $blindOptions)
Definition: SearchController.php:1287
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getAllAvailableMediaTypesOptions
‪array getAllAvailableMediaTypesOptions()
Definition: SearchController.php:1041
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$typoScriptService
‪TYPO3 CMS Core TypoScript TypoScriptService $typoScriptService
Definition: SearchController.php:152
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\injectTypoScriptService
‪injectTypoScriptService(TypoScriptService $typoScriptService)
Definition: SearchController.php:157
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$domainRecords
‪array $domainRecords
Definition: SearchController.php:112
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getSpecialConfigurationForResultRow
‪array getSpecialConfigurationForResultRow($row)
Definition: SearchController.php:578
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$sword
‪string $sword
Definition: SearchController.php:56
‪TYPO3\CMS\Core\Type\File\ImageInfo
Definition: ImageInfo.php:28
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\loadSettings
‪loadSettings()
Definition: SearchController.php:1508
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getDisplayResults
‪array getDisplayResults($searchWords, $resultData, $freeIndexUid=-1)
Definition: SearchController.php:325
‪TYPO3\CMS\Core\TypoScript\TypoScriptService
Definition: TypoScriptService.php:25
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$availableResultsNumbers
‪int[] $availableResultsNumbers
Definition: SearchController.php:82
‪$output
‪$output
Definition: annotationChecker.php:121
‪TYPO3\CMS\Core\Database\Connection
Definition: Connection.php:38
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\makeRating
‪string makeRating($row)
Definition: SearchController.php:605
‪TYPO3\CMS\IndexedSearch\Lexer
Definition: Lexer.php:27
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$searchData
‪array $searchData
Definition: SearchController.php:64
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getNumberOfResults
‪int getNumberOfResults($numberOfResults)
Definition: SearchController.php:1566
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\processExtendedSearchParameters
‪array processExtendedSearchParameters()
Definition: SearchController.php:1479
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Extbase\Mvc\Controller\ActionController
Definition: ActionController.php:65
‪TYPO3\CMS\Core\Utility\GeneralUtility\intExplode
‪static int[] intExplode($delimiter, $string, $removeEmptyValues=false, $limit=0)
Definition: GeneralUtility.php:927
‪TYPO3\CMS\Extbase\Mvc\Controller\ActionController\redirect
‪never redirect($actionName, $controllerName=null, $extensionName=null, array $arguments=null, $pageUid=null, $_=null, $statusCode=303)
Definition: ActionController.php:940
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:22
‪TYPO3\CMS\Core\Exception\Page\RootLineException
Definition: RootLineException.php:24
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\preparePageLink
‪array preparePageLink(int $pageUid, array $row, array $urlParameters)
Definition: SearchController.php:1583
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$enableMetaphoneSearch
‪bool $enableMetaphoneSearch
Definition: SearchController.php:148
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getSearchWords
‪array getSearchWords($defaultOperator)
Definition: SearchController.php:882
‪TYPO3\CMS\Core\Domain\Repository\PageRepository
Definition: PageRepository.php:53
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Core\Utility\PathUtility\getAbsoluteWebPath
‪static string getAbsoluteWebPath($targetPath, bool $prefixWithSitePath=true)
Definition: PathUtility.php:51
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50
‪TYPO3\CMS\Core\Database\Query\Restriction\FrontendRestrictionContainer
Definition: FrontendRestrictionContainer.php:31
‪TYPO3\CMS\IndexedSearch\Utility\IndexedSearchUtility
Definition: IndexedSearchUtility.php:26
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getAllAvailableLanguageOptions
‪array getAllAvailableLanguageOptions()
Definition: SearchController.php:1078
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\makeItemTypeIcon
‪string makeItemTypeIcon($imageType, $alt, $specRowConf)
Definition: SearchController.php:666
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getAllAvailableSortDescendingOptions
‪array getAllAvailableSortDescendingOptions()
Definition: SearchController.php:1250
‪TYPO3\CMS\IndexedSearch\Utility\IndexedSearchUtility\getExplodedSearchString
‪static array getExplodedSearchString($sword, $defaultOperator, $operatorTranslateTable)
Definition: IndexedSearchUtility.php:62
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getAllAvailableNumberOfResultsOptions
‪array getAllAvailableNumberOfResultsOptions()
Definition: SearchController.php:1270
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getAllAvailableSectionsOptions
‪array getAllAvailableSectionsOptions()
Definition: SearchController.php:1115
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\initializeExternalParsers
‪initializeExternalParsers()
Definition: SearchController.php:1430