‪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;
37 use TYPO3\CMS\Frontend\Typolink\LinkResult;
49 
58 {
59  protected string ‪$sword = '';
60  protected array ‪$searchWords = [];
61 
70  protected string|int ‪$searchRootPageIdList = 0;
71 
72  protected int ‪$defaultResultNumber = 10;
73  protected array ‪$availableResultsNumbers = [];
74  protected array ‪$externalParsers = [];
75 
79  protected array ‪$firstRow = [];
80 
84  protected array ‪$requiredFrontendUsergroups = [];
85 
89  protected array ‪$resultSections = [];
90 
91  protected array ‪$pathCache = [];
92 
93  public function ‪__construct(
94  private readonly ‪Context $context,
95  private readonly ‪IndexSearchRepository $searchRepository,
96  private readonly ‪TypoScriptService $typoScriptService,
97  private readonly ‪Lexer $lexer,
98  private readonly ‪LinkFactory $linkFactory,
99  ) {}
100 
104  protected function ‪initialize(array $searchData = []): array
105  {
106  if (!is_array($searchData)) {
107  $searchData = [];
108  }
109 
110  // Sets availableResultsNumbers - has to be called before request settings are read to avoid DoS attack
111  $this->availableResultsNumbers = array_filter(‪GeneralUtility::intExplode(',', (string)($this->settings['blind']['numberOfResults'] ?? '')));
112 
113  // Sets default result number if at least one availableResultsNumbers exists
114  if (isset($this->availableResultsNumbers[0])) {
115  $this->defaultResultNumber = $this->availableResultsNumbers[0];
116  }
117 
118  $this->‪loadSettings();
119 
120  // setting default values
121  if (is_array($this->settings['defaultOptions'])) {
122  $searchData = array_merge($this->settings['defaultOptions'], $searchData);
123  }
124  // Hand in the current site language as languageUid
125  $searchData['languageUid'] = $this->context->getPropertyFromAspect('language', 'id', 0);
126 
128  // If "_sections" is set, this value overrides any existing value.
129  if ($searchData['_sections'] ?? false) {
130  $searchData['sections'] = $searchData['_sections'];
131  }
132  // If "_sections" is set, this value overrides any existing value.
133  if (($searchData['_freeIndexUid'] ?? '') !== '' && ($searchData['_freeIndexUid'] ?? '') !== '_') {
134  $searchData['freeIndexUid'] = $searchData['_freeIndexUid'];
135  }
136  $searchData['numberOfResults'] = $this->‪getNumberOfResults((int)($searchData['numberOfResults'] ?? 0));
137  // This gets the search-words into the $searchWordArray
138  $this->sword = $searchData['sword'] ?? '';
139  // Add previous search words to current
140  if (($searchData['sword_prev_include'] ?? false) && ($searchData['sword_prev'] ?? false)) {
141  $this->sword = trim($searchData['sword_prev']) . ' ' . ‪$this->sword;
142  }
143  // This is the id of the site root.
144  // This value may be a commalist of integer (prepared for this)
145  $localRootLine = $this->request->getAttribute('frontend.page.information')->getLocalRootLine();
146  $this->searchRootPageIdList = (int)$localRootLine[0]['uid'];
147  // Setting the list of root PIDs for the search. Notice, these page IDs MUST
148  // have a TypoScript with root flag on them! Basically this list is used
149  // to select on the "rl0" field and page ids are registered as "rl0" only if
150  // a TypoScript record with root flag is there.
151  // This happens AFTER the use of $this->searchRootPageIdList above because
152  // the above will then fetch the menu for the CURRENT site - regardless
153  // of this kind of searching here. Thus a general search will lookup in
154  // the WHOLE database while a specific section search will take the current sections.
155  $rootPidListFromSettings = (string)($this->settings['rootPidList'] ?? '');
156  if ($rootPidListFromSettings) {
157  $this->searchRootPageIdList = implode(',', ‪GeneralUtility::intExplode(',', $rootPidListFromSettings));
158  }
159  $this->searchRepository->initialize($this->settings, $searchData, $this->externalParsers, $this->searchRootPageIdList);
160  // $this->searchData is used in $this->getSearchWords
161  $this->searchWords = $this->‪getSearchWords($searchData, (bool)$searchData['defaultOperand']);
162 
163  return $searchData;
164  }
165 
171  public function ‪searchAction(array $search = []): ResponseInterface
172  {
173  // check if TypoScript is loaded
174  if (!isset($this->settings['results'])) {
175  return $this->‪redirect('noTypoScript');
176  }
177 
178  $searchData = $this->‪initialize($search);
179  // Find free index uid:
180  // @todo: what exactly is this? Apparently, it can either be an int or a string (╯°□°)╯︵ ┻━┻
181  $freeIndexUid = $searchData['freeIndexUid'];
182  if ($freeIndexUid == -2) {
183  $freeIndexUid = $this->settings['defaultFreeIndexUidList'];
184  } elseif (!isset($searchData['freeIndexUid'])) {
185  // index configuration is disabled
186  $freeIndexUid = -1;
187  }
188 
189  if (!empty($searchData['extendedSearch'])) {
190  $this->view->assignMultiple($this->‪processExtendedSearchParameters());
191  }
192 
193  $indexCfgs = ‪GeneralUtility::intExplode(',', (string)$freeIndexUid);
194  $resultsets = [];
195  foreach ($indexCfgs as $freeIndexUid) {
196  // Get result rows
197  $resultData = $this->searchRepository->doSearch($this->searchWords, $freeIndexUid);
198 
199  // Display search results
200  $resultsets[$freeIndexUid] = $this->‪getDisplayResults($searchData, $this->searchWords, $resultData, $freeIndexUid);
201 
202  // Create header if we are searching more than one indexing configuration
203  if (count($indexCfgs) > 1) {
204  if ($freeIndexUid > 0) {
205  $indexCfgRec = $this->searchRepository->getIndexConfigurationById($freeIndexUid);
206  if (is_array($indexCfgRec)) {
207  $categoryTitle = ‪LocalizationUtility::translate('indexingConfigurationHeader.' . $freeIndexUid, 'IndexedSearch');
208  $categoryTitle = $categoryTitle ?: $indexCfgRec['title'];
209  $resultsets[$freeIndexUid]['categoryTitle'] = $categoryTitle;
210  }
211  } else {
212  $categoryTitle = ‪LocalizationUtility::translate('indexingConfigurationHeader.' . $freeIndexUid, 'IndexedSearch');
213  $resultsets[$freeIndexUid]['categoryTitle'] = $categoryTitle;
214  }
215  }
216  // Write search statistics
217  $pageId = $this->request->getAttribute('frontend.page.information')->getId();
218  $this->searchRepository->writeSearchStat($pageId, $this->searchWords ?: []);
219  }
220  $this->view->assign('resultsets', $resultsets);
221  $this->view->assign('searchParams', $searchData);
222  $this->view->assign('firstRow', $this->firstRow);
223  $this->view->assign('searchWords', array_map([$this, 'addOperatorLabel'], $this->searchWords));
224 
225  return $this->‪htmlResponse();
226  }
227 
228  /****************************************
229  * functions to make the result rows and result sets
230  * ready for the output
231  ***************************************/
239  protected function ‪getDisplayResults(array $searchData, array ‪$searchWords, array|bool $resultData, int $freeIndexUid = -1): array
240  {
241  $result = [
242  'count' => $resultData['count'] ?? 0,
243  'searchWords' => ‪$searchWords,
244  'pagination' => null,
245  ];
246  // Perform display of result rows array
247  if ($resultData) {
248  // Set first selected row (for calculation of ranking later)
249  $this->firstRow = $resultData['firstRow'];
250  // Result display here
251  $result['rows'] = $this->‪compileResultRows($searchData, $resultData['resultRows'], $freeIndexUid);
252  $result['affectedSections'] = ‪$this->resultSections;
253  // Browsing box
254  if ($resultData['count']) {
255  // could we get this in the view?
256  if (($searchData['group'] ?? '') === GroupOption::SECTIONS->value && $freeIndexUid <= 0) {
257  $resultSectionsCount = count($this->resultSections);
258  $result['sectionText'] = sprintf(‪LocalizationUtility::translate('result.' . ($resultSectionsCount > 1 ? 'inNsections' : 'inNsection'), 'IndexedSearch') ?? '', $resultSectionsCount);
259  }
260  }
261 
262  $paginator = new ‪SlicePaginator(
263  $result['rows'],
264  ((int)$searchData['pointer']) + 1,
265  $resultData['count'],
266  $searchData['numberOfResults'],
267  );
268  $result['pagination'] = new ‪SimplePagination($paginator);
269  }
270  // Print a message telling which words in which sections we searched for
271  if (str_starts_with($searchData['sections'], 'rl')) {
272  $result['searchedInSectionInfo'] = (‪LocalizationUtility::translate('result.inSection', 'IndexedSearch') ?? '') . ' "' . $this->‪getPathFromPageId((int)substr($searchData['sections'], 4)) . '"';
273  }
274 
275  return $result;
276  }
277 
286  protected function ‪compileResultRows(array $searchData, array $resultRows, int $freeIndexUid = -1): array
287  {
288  $finalResultRows = [];
289  // Transfer result rows to new variable,
290  // performing some mapping of sub-results etc.
291  $newResultRows = [];
292  foreach ($resultRows as $row) {
293  $id = md5((string)$row['phash_grouping']);
294  if (is_array($newResultRows[$id] ?? null)) {
295  // swapping:
296  if (!$newResultRows[$id]['show_resume'] && $row['show_resume']) {
297  // Remove old
298  $subrows = $newResultRows[$id]['_sub'];
299  unset($newResultRows[$id]['_sub']);
300  $subrows[] = $newResultRows[$id];
301  // Insert new:
302  $newResultRows[$id] = $row;
303  $newResultRows[$id]['_sub'] = $subrows;
304  } else {
305  $newResultRows[$id]['_sub'][] = $row;
306  }
307  } else {
308  $newResultRows[$id] = $row;
309  }
310  }
311  $resultRows = $newResultRows;
312  $this->resultSections = [];
313  if ($freeIndexUid <= 0 && ($searchData['group'] ?? '') === GroupOption::SECTIONS->value) {
314  $rl2flag = str_starts_with($searchData['sections'], 'rl');
315  $sections = [];
316  foreach ($resultRows as $row) {
317  $id = $row['rl0'] . '-' . $row['rl1'] . ($rl2flag ? '-' . $row['rl2'] : '');
318  $sections[$id][] = $row;
319  }
320  $this->resultSections = [];
321  foreach ($sections as $id => $resultRows) {
322  $rlParts = explode('-', $id);
323  if ($rlParts[2] ?? null) {
324  $theId = $rlParts[2];
325  $theRLid = 'rl2_' . $rlParts[2];
326  } elseif ($rlParts[1] ?? null) {
327  $theId = $rlParts[1];
328  $theRLid = 'rl1_' . $rlParts[1];
329  } else {
330  $theId = $rlParts[0] ?? '0';
331  $theRLid = '0';
332  }
333  $sectionName = $this->‪getPathFromPageId((int)$theId);
334  $sectionName = ltrim($sectionName, '/');
335  if (!trim($sectionName)) {
336  $sectionTitleLinked = ‪LocalizationUtility::translate('result.unnamedSection', 'IndexedSearch') . ':';
337  } else {
338  $onclick = 'document.forms[\'tx_indexedsearch\'][\'tx_indexedsearch_pi2[search][_sections]\'].value=' . GeneralUtility::quoteJSvalue($theRLid) . ';document.forms[\'tx_indexedsearch\'].submit();return false;';
339  $sectionTitleLinked = '<a href="#" onclick="' . htmlspecialchars($onclick) . '">' . $sectionName . ':</a>';
340  }
341  $resultRowsCount = count($resultRows);
342  $this->resultSections[$id] = [$sectionName, $resultRowsCount];
343  // Add section header
344  $finalResultRows[] = [
345  'isSectionHeader' => true,
346  'numResultRows' => $resultRowsCount,
347  'sectionId' => $id,
348  'sectionTitle' => $sectionTitleLinked,
349  ];
350  // Render result rows
351  foreach ($resultRows as $row) {
352  $finalResultRows[] = $this->‪compileSingleResultRow($searchData, $row);
353  }
354  }
355  } else {
356  // flat mode or no sections at all
357  foreach ($resultRows as $row) {
358  $finalResultRows[] = $this->‪compileSingleResultRow($searchData, $row);
359  }
360  }
361  return $finalResultRows;
362  }
363 
371  protected function ‪compileSingleResultRow(array $searchData, array $row, int $headerOnly = 0): array
372  {
373  $typoScriptConfigArray = $this->request->getAttribute('frontend.typoscript')->getConfigArray();
374  $resultData = $row;
375  $resultData['headerOnly'] = $headerOnly;
376  if (isset($row['static_page_arguments']) && $this->‪multiplePagesType($row['item_type'])) {
377  try {
378  $dat = json_decode($row['static_page_arguments'], true, 512, JSON_THROW_ON_ERROR);
379  if (is_string($dat['key'] ?? null) && $dat['key'] !== '') {
380  $pp = explode('-', $dat['key']);
381  if ($pp[0] !== $pp[1]) {
382  $resultData['titleaddition'] = ', ' . ‪LocalizationUtility::translate('result.pages', 'IndexedSearch') . ' ' . $dat['key'];
383  } else {
384  $resultData['titleaddition'] = ', ' . ‪LocalizationUtility::translate('result.page', 'IndexedSearch') . ' ' . $pp[0];
385  }
386  }
387  } catch (\JsonException) {
388  }
389  }
390  $title = $resultData['item_title'] . ($resultData['titleaddition'] ?? '');
391  $title = ‪GeneralUtility::fixed_lgd_cs($title, (int)$this->settings['results.']['titleCropAfter'], $this->settings['results.']['titleCropSignifier']);
392  // If external media, link to the media-file instead.
393  if ($row['item_type']) {
394  if ($row['show_resume']) {
395  $targetAttribute = '';
396  if ($typoScriptConfigArray['fileTarget'] ?? false) {
397  $targetAttribute = ' target="' . htmlspecialchars($typoScriptConfigArray['fileTarget']) . '"';
398  }
399  $title = '<a href="' . htmlspecialchars($row['data_filename']) . '"' . $targetAttribute . '>' . htmlspecialchars($title) . '</a>';
400  } else {
401  // Suspicious, so linking to page instead...
402  $copiedRow = $row;
403  unset($copiedRow['static_page_arguments']);
404  $title = LinkResult::adapt($this->‪linkPage((int)$row['page_id'], $copiedRow, $title))->getHtml();
405  }
406  } else {
407  // Else the page
408  $title = LinkResult::adapt($this->‪linkPage((int)$row['data_page_id'], $row, $title))->getHtml();
409  }
410  $resultData['title'] = $title;
411  $resultData['description'] = $this->‪makeDescription(
412  $row,
413  !($searchData['extResume'] && !$headerOnly),
414  $this->settings['results.']['summaryCropAfter']
415  );
416  $resultData['size'] = GeneralUtility::formatSize($row['item_size']);
417  $resultData['created'] = $row['item_crdate'];
418  $resultData['modified'] = $row['item_mtime'];
419  $pI = parse_url($row['data_filename']);
420  if ($pI['scheme'] ?? false) {
421  $targetAttribute = '';
422  if ($typoScriptConfigArray['fileTarget'] ?? false) {
423  $targetAttribute = ' target="' . htmlspecialchars($typoScriptConfigArray['fileTarget']) . '"';
424  }
425  $resultData['pathTitle'] = $row['data_filename'];
426  $resultData['pathUri'] = $row['data_filename'];
427  $resultData['path'] = '<a href="' . htmlspecialchars($row['data_filename']) . '"' . $targetAttribute . '>' . htmlspecialchars($row['data_filename']) . '</a>';
428  } else {
429  $pathId = $row['data_page_id'] ?: $row['page_id'];
430  $pathMP = $row['data_page_id'] ? $row['data_page_mp'] : '';
431  $pathStr = $this->‪getPathFromPageId($pathId, $pathMP);
432  $pathLinkResult = $this->‪linkPage((int)$pathId, $row, $pathStr);
433 
434  $resultData['pathTitle'] = $pathStr;
435  $resultData['pathUri'] = $pathLinkResult->getUrl();
436  $resultData['path'] = LinkResult::adapt($pathLinkResult)->getHtml();
437 
438  // check if the access is restricted
439  if (is_array($this->requiredFrontendUsergroups[$pathId]) && !empty($this->requiredFrontendUsergroups[$pathId])) {
440  $lockedIcon = ‪PathUtility::getPublicResourceWebPath('EXT:indexed_search/Resources/Public/Icons/FileTypes/locked.gif');
441  $resultData['access'] = '<img src="' . htmlspecialchars($lockedIcon) . '"'
442  . ' width="12" height="15" vspace="5" title="'
443  . sprintf(‪LocalizationUtility::translate('result.memberGroups', 'IndexedSearch') ?? '', implode(',', array_unique($this->requiredFrontendUsergroups[$pathId])))
444  . '" alt="" />';
445  }
446  }
447  // If there are subrows (e.g. subpages in a PDF-file or if a duplicate page
448  // is selected due to user-login (phash_grouping))
449  if (is_array($row['_sub'] ?? false)) {
450  $resultData['subresults'] = [];
451  if ($this->‪multiplePagesType($row['item_type'])) {
452  $resultData['subresults']['header'] = ‪LocalizationUtility::translate('result.otherMatching', 'IndexedSearch');
453  foreach ($row['_sub'] as $subRow) {
454  $resultData['subresults']['items'][] = $this->‪compileSingleResultRow($searchData, $subRow, 1);
455  }
456  } else {
457  $resultData['subresults']['header'] = ‪LocalizationUtility::translate('result.otherMatching', 'IndexedSearch');
458  $resultData['subresults']['info'] = ‪LocalizationUtility::translate('result.otherPageAsWell', 'IndexedSearch');
459  }
460  }
461  return $resultData;
462  }
463 
470  protected function ‪makeDescription(array $row, bool $noMarkup = false, int $length = 180): string
471  {
472  $markedSW = '';
473  $outputStr = '';
474  if ($row['show_resume']) {
475  if (!$noMarkup) {
476  $ftdrow = $this->searchRepository->getFullTextRowByPhash($row['phash']);
477  if (is_array($ftdrow)) {
478  // Cut HTTP references after some length
479  $content = preg_replace('/(http:\\/\\/[^ ]{' . $this->settings['results.']['hrefInSummaryCropAfter'] . '})([^ ]+)/i', '$1...', $ftdrow['fulltextdata']);
480  $markedSW = $this->‪markupSWpartsOfString($content);
481  }
482  }
483  if (!trim($markedSW)) {
484  $outputStr = ‪GeneralUtility::fixed_lgd_cs($row['item_description'], (int)$length, $this->settings['results.']['summaryCropSignifier']);
485  $outputStr = htmlspecialchars($outputStr);
486  }
487  ‪$output = $outputStr ?: $markedSW;
488  } else {
489  ‪$output = '<span class="noResume">' . ‪LocalizationUtility::translate('result.noResume', 'IndexedSearch') . '</span>';
490  }
491  return ‪$output;
492  }
493 
499  protected function ‪markupSWpartsOfString(string $str): string
500  {
501  $htmlParser = GeneralUtility::makeInstance(HtmlParser::class);
502  // Init:
503  $str = str_replace('&nbsp;', ' ', $htmlParser->bidir_htmlspecialchars($str, -1));
504  $str = preg_replace('/\\s\\s+/', ' ', $str);
505  $swForReg = [];
506  // Prepare search words for regex:
507  foreach ($this->searchWords as $d) {
508  $swForReg[] = preg_quote($d['sword'], '/');
509  }
510  $regExString = '(' . implode('|', $swForReg) . ')';
511  // Split and combine:
512  $parts = preg_split('/' . $regExString . '/iu', ' ' . $str . ' ', 20000, PREG_SPLIT_DELIM_CAPTURE);
513  $parts = $parts ?: [];
514  // Constants:
515  $summaryMax = $this->settings['results.']['markupSW_summaryMax'];
516  $postPreLgd = (int)$this->settings['results.']['markupSW_postPreLgd'];
517  $postPreLgd_offset = (int)$this->settings['results.']['markupSW_postPreLgd_offset'];
518  $divider = $this->settings['results.']['markupSW_divider'];
519  $occurrences = (count($parts) - 1) / 2;
520  if ($occurrences) {
521  $postPreLgd = ‪MathUtility::forceIntegerInRange($summaryMax / $occurrences, $postPreLgd, $summaryMax / 2);
522  }
523  // Variable:
524  $summaryLgd = 0;
525  ‪$output = [];
526  // Shorten in-between strings:
527  foreach ($parts as $k => $strP) {
528  if ($k % 2 === 0) {
529  // Find length of the summary part:
530  $strLen = mb_strlen($parts[$k], 'utf-8');
531  ‪$output[$k] = $parts[$k];
532  // Possibly shorten string:
533  if (!$k) {
534  // First entry at all (only cropped on the frontside)
535  if ($strLen > $postPreLgd) {
536  ‪$output[$k] = $divider . preg_replace('/^[^[:space:]]+[[:space:]]/', '', ‪GeneralUtility::fixed_lgd_cs($parts[$k], -($postPreLgd - $postPreLgd_offset)));
537  }
538  } elseif ($summaryLgd > $summaryMax || !isset($parts[$k + 1])) {
539  // In case summary length is exceed OR if there are no more entries at all:
540  if ($strLen > $postPreLgd) {
541  ‪$output[$k] = preg_replace('/[[:space:]][^[:space:]]+$/', '', ‪GeneralUtility::fixed_lgd_cs(
542  $parts[$k],
543  $postPreLgd - $postPreLgd_offset
544  )) . $divider;
545  }
546  } else {
547  if ($strLen > $postPreLgd * 2) {
548  ‪$output[$k] = preg_replace('/[[:space:]][^[:space:]]+$/', '', ‪GeneralUtility::fixed_lgd_cs(
549  $parts[$k],
550  $postPreLgd - $postPreLgd_offset
551  )) . $divider . preg_replace('/^[^[:space:]]+[[:space:]]/', '', ‪GeneralUtility::fixed_lgd_cs($parts[$k], -($postPreLgd - $postPreLgd_offset)));
552  }
553  }
554  $summaryLgd += mb_strlen(‪$output[$k], 'utf-8');
555  // Protect output:
556  ‪$output[$k] = htmlspecialchars(‪$output[$k]);
557  // If summary lgd is exceed, break the process:
558  if ($summaryLgd > $summaryMax) {
559  break;
560  }
561  } else {
562  $summaryLgd += mb_strlen($strP, 'utf-8');
563  ‪$output[$k] = '<strong class="tx-indexedsearch-redMarkup">' . htmlspecialchars($parts[$k]) . '</strong>';
564  }
565  }
566  // Return result:
567  return implode('', ‪$output);
568  }
569 
585  protected function ‪getSearchWords(array $searchData, bool $useDefaultOperator): array
586  {
587  // Shorten search-word string to max 200 bytes - shortening the string here is only a run-away feature!
588  ‪$searchWords = mb_substr($this->sword, 0, 200);
589  if ((int)$searchData['searchType'] === ‪SearchType::SENTENCE->value) {
590  $sWordArray = [
591  [
592  'sword' => trim(‪$searchWords),
593  'oper' => 'AND',
594  ],
595  ];
596  } else {
597  // case-sensitive. Defines the words, which will be
598  // operators between words
599  $operatorTranslateTable = [
600  ['+', 'AND'],
601  ['|', 'OR'],
602  ['-', 'AND NOT'],
603  // Add operators for various languages
604  // Converts the operators to lowercase
605  [mb_strtolower(‪LocalizationUtility::translate('localizedOperandAnd', 'IndexedSearch') ?? '', 'utf-8'), 'AND'],
606  [mb_strtolower(‪LocalizationUtility::translate('localizedOperandOr', 'IndexedSearch') ?? '', 'utf-8'), 'OR'],
607  [mb_strtolower(‪LocalizationUtility::translate('localizedOperandNot', 'IndexedSearch') ?? '', 'utf-8'), 'AND NOT'],
608  ];
609  $defaultOperator = $useDefaultOperator ? 'OR' : 'AND';
610  $swordArray = ‪IndexedSearchUtility::getExplodedSearchString(‪$searchWords, $defaultOperator, $operatorTranslateTable);
611  $sWordArray = $this->‪procSearchWordsByLexer($swordArray);
612  }
613  return $sWordArray;
614  }
615 
623  protected function ‪procSearchWordsByLexer(array ‪$searchWords): array
624  {
625  // Traverse the search word array
626  $newSearchWords = [];
627  foreach (‪$searchWords as $wordDef) {
628  // No space in word (otherwise it might be a sentence in quotes like "there is").
629  if (!str_contains($wordDef['sword'], ' ')) {
630  // Split the search word by lexer:
631  $res = $this->lexer->split2Words($wordDef['sword']);
632  // Traverse lexer result and add all words again:
633  foreach ($res as $word) {
634  $newSearchWords[] = [
635  'sword' => $word,
636  'oper' => $wordDef['oper'],
637  ];
638  }
639  } else {
640  $newSearchWords[] = $wordDef;
641  }
642  }
643  return $newSearchWords;
644  }
645 
651  public function ‪formAction(array $search = []): ResponseInterface
652  {
653  // check if TypoScript is loaded
654  if (!isset($this->settings['results'])) {
655  return $this->‪redirect('noTypoScript');
656  }
657 
658  $searchData = $this->‪initialize($search);
659  // Adding search field value
660  $this->view->assign('sword', $this->sword);
661  // Extended search
662  if (!empty($searchData['extendedSearch'])) {
663  $this->view->assignMultiple($this->‪processExtendedSearchParameters());
664  }
665  $this->view->assign('searchParams', $searchData);
666 
667  return $this->‪htmlResponse();
668  }
669 
673  public function ‪noTypoScriptAction(): ResponseInterface
674  {
675  return $this->‪htmlResponse();
676  }
677 
678  /****************************************
679  * building together the available options for every dropdown
680  ***************************************/
686  protected function ‪getAllAvailableSearchTypeOptions(): array
687  {
688  $allOptions = [];
689  $blindSettings = $this->settings['blind'];
690  if (!$blindSettings['searchType']) {
691  foreach (SearchType::cases() as $searchType) {
692  $typeNum = $searchType->value;
693  $allOptions[$typeNum] = ‪LocalizationUtility::translate('searchTypes.' . $typeNum, 'IndexedSearch');
694  }
695  }
696  // disable single entries by TypoScript
697  return $this->‪removeOptionsFromOptionList($allOptions, $blindSettings['searchType']);
698  }
699 
705  protected function ‪getAllAvailableOperandsOptions(): array
706  {
707  $allOptions = [];
708  $blindSettings = $this->settings['blind'];
709  if (!$blindSettings['defaultOperand']) {
710  foreach (DefaultOperand::cases() as $defaultOperand) {
711  $operand = $defaultOperand->value;
712  $allOptions[$operand] = ‪LocalizationUtility::translate('defaultOperands.' . $operand, 'IndexedSearch');
713  }
714  }
715  // disable single entries by TypoScript
716  return $this->‪removeOptionsFromOptionList($allOptions, $blindSettings['defaultOperand']);
717  }
718 
724  protected function ‪getAllAvailableMediaTypesOptions(): array
725  {
726  $allOptions = [];
727  $blindSettings = $this->settings['blind'];
728  if (!$blindSettings['mediaType']) {
729  foreach ([MediaType::ALL_MEDIA, MediaType::INTERNAL_PAGES, ‪MediaType::ALL_EXTERNAL] as $mediaTypeCase) {
730  $mediaType = $mediaTypeCase->value;
731  $allOptions[$mediaType] = ‪LocalizationUtility::translate('mediaTypes.' . $mediaType, 'IndexedSearch');
732  }
733  // Add media to search in:
734  $additionalMedia = trim($this->settings['mediaList']);
735  if ($additionalMedia !== '') {
736  $additionalMedia = ‪GeneralUtility::trimExplode(',', $additionalMedia, true);
737  } else {
738  $additionalMedia = [];
739  }
740  foreach ($this->externalParsers as $extension => $obj) {
741  // Skip unwanted extensions
742  if (!empty($additionalMedia) && !in_array($extension, $additionalMedia, true)) {
743  continue;
744  }
745  if ($name = $obj->searchTypeMediaTitle($extension)) {
746  $translatedName = ‪LocalizationUtility::translate('mediaTypes.' . $extension, 'IndexedSearch');
747  $allOptions[$extension] = $translatedName ?: $name;
748  }
749  }
750  }
751  // disable single entries by TypoScript
752  return $this->‪removeOptionsFromOptionList($allOptions, $blindSettings['mediaType']);
753  }
754 
764  protected function ‪getAllAvailableSectionsOptions(): array
765  {
766  $allOptions = [];
767  $blindSettings = $this->settings['blind'];
768  if (!$blindSettings['sections']) {
769  foreach (SectionType::cases() as $sectionType) {
770  $section = $sectionType->value;
771  $allOptions[$section] = ‪LocalizationUtility::translate('sections.' . $section, 'IndexedSearch');
772  }
773  }
774  // Creating levels for section menu:
775  // This selects the first and secondary menus for the "sections" selector - so we can search in sections and sub-sections.
776  if ($this->settings['displayLevel1Sections']) {
777  $firstLevelMenu = $this->‪getMenuOfPages((int)$this->searchRootPageIdList);
778  $labelLevel1 = ‪LocalizationUtility::translate('sections.rootLevel1', 'IndexedSearch');
779  $labelLevel2 = ‪LocalizationUtility::translate('sections.rootLevel2', 'IndexedSearch');
780  foreach ($firstLevelMenu as $firstLevelKey => $menuItem) {
781  if (!$menuItem['nav_hide']) {
782  $allOptions['rl1_' . $menuItem['uid']] = trim($labelLevel1 . ' ' . $menuItem['title']);
783  if ($this->settings['displayLevel2Sections']) {
784  $secondLevelMenu = $this->‪getMenuOfPages($menuItem['uid']);
785  foreach ($secondLevelMenu as $secondLevelKey => $menuItemLevel2) {
786  if (!$menuItemLevel2['nav_hide']) {
787  $allOptions['rl2_' . $menuItemLevel2['uid']] = trim($labelLevel2 . ' ' . $menuItemLevel2['title']);
788  } else {
789  unset($secondLevelMenu[$secondLevelKey]);
790  }
791  }
792  $allOptions['rl2_' . implode(',', array_keys($secondLevelMenu))] = ‪LocalizationUtility::translate('sections.rootLevel2All', 'IndexedSearch');
793  }
794  } else {
795  unset($firstLevelMenu[$firstLevelKey]);
796  }
797  }
798  $allOptions['rl1_' . implode(',', array_keys($firstLevelMenu))] = ‪LocalizationUtility::translate('sections.rootLevel1All', 'IndexedSearch');
799  }
800  // disable single entries by TypoScript
801  return $this->‪removeOptionsFromOptionList($allOptions, $blindSettings['sections']);
802  }
803 
810  {
811  foreach ([IndexingConfiguration::ALL_MIXED, ‪IndexingConfiguration::ALL_CATEGORIZED, IndexingConfiguration::PAGES] as $indexingConfiguration) {
812  $value = $indexingConfiguration->value;
813  $allOptions[$value] = ‪LocalizationUtility::translate('indexingConfigurations.' . $value, 'IndexedSearch');
814  }
815  $blindSettings = $this->settings['blind'];
816  if (!($blindSettings['indexingConfigurations'] ?? false)) {
817  // add an additional index configuration
818  $defaultFreeIndexUidList = (string)($this->settings['defaultFreeIndexUidList'] ?? '');
819  if ($defaultFreeIndexUidList) {
820  $uidList = ‪GeneralUtility::intExplode(',', $defaultFreeIndexUidList);
821  foreach ($uidList as ‪$uid) {
822  $row = $this->searchRepository->getIndexConfigurationById(‪$uid);
823  if (is_array($row)) {
824  $indexId = (int)$row['uid'];
825  $title = ‪LocalizationUtility::translate('indexingConfigurations.' . $indexId, 'IndexedSearch');
826  $allOptions[$indexId] = $title ?: $row['title'];
827  }
828  }
829  }
830  // disable single entries by TypoScript
831  $allOptions = $this->‪removeOptionsFromOptionList($allOptions, (array)($blindSettings['indexingConfigurations'] ?? []));
832  } else {
833  $allOptions = [];
834  }
835  return $allOptions;
836  }
837 
847  protected function ‪getAllAvailableSortOrderOptions(): array
848  {
849  $allOptions = [];
850  $sortOrders = ['rank_flag', 'rank_freq', 'rank_first', 'rank_count', 'mtime', 'title', 'crdate'];
851  $blindSettings = $this->settings['blind'];
852  if (!$blindSettings['sortOrder']) {
853  foreach ($sortOrders as $order) {
854  $allOptions[$order] = ‪LocalizationUtility::translate('sortOrders.' . $order, 'IndexedSearch');
855  }
856  }
857  // disable single entries by TypoScript
858  return $this->‪removeOptionsFromOptionList($allOptions, $blindSettings['sortOrder.'] ?? []);
859  }
860 
866  protected function ‪getAllAvailableGroupOptions(): array
867  {
868  $allOptions = [];
869  $blindSettings = $this->settings['blind'];
870  if (!($blindSettings['groupBy'] ?? false)) {
871  foreach (GroupOption::cases() as $groupOption) {
872  $value = $groupOption->value;
873  $allOptions[$value] = ‪LocalizationUtility::translate('groupBy.' . $value, 'IndexedSearch');
874  }
875  }
876  // disable single entries by TypoScript
877  return $this->‪removeOptionsFromOptionList($allOptions, ($blindSettings['groupBy.'] ?? []));
878  }
879 
885  protected function ‪getAllAvailableSortDescendingOptions(): array
886  {
887  $allOptions = [];
888  $blindSettings = $this->settings['blind'];
889  if (!($blindSettings['descending'] ?? false)) {
890  $allOptions = [
891  0 => ‪LocalizationUtility::translate('sortOrders.descending', 'IndexedSearch'),
892  1 => ‪LocalizationUtility::translate('sortOrders.ascending', 'IndexedSearch'),
893  ];
894  }
895  // disable single entries by TypoScript
896  return $this->‪removeOptionsFromOptionList($allOptions, $blindSettings['descending.'] ?? []);
897  }
898 
904  protected function ‪getAllAvailableNumberOfResultsOptions(): array
905  {
906  $allOptions = [];
907  if (count($this->availableResultsNumbers) > 1) {
908  $allOptions = array_combine($this->availableResultsNumbers, $this->availableResultsNumbers);
909  }
910  // disable single entries by TypoScript
911  return $this->‪removeOptionsFromOptionList($allOptions, $this->settings['blind']['numberOfResults'] ?? []);
912  }
913 
921  protected function ‪removeOptionsFromOptionList(array $allOptions, mixed $blindOptions): array
922  {
923  if (is_array($blindOptions)) {
924  foreach ($blindOptions as $key => $val) {
925  if ((int)$val === 1) {
926  unset($allOptions[$key]);
927  }
928  }
929  }
930  return $allOptions;
931  }
932 
936  protected function ‪linkPage(int $pageUid, array $row, string $linkText): ‪LinkResultInterface
937  {
938  $pageLanguage = $this->context->getPropertyFromAspect('language', 'contentId', 0);
939 
940  $linkConfiguration = [
941  'parameter' => $pageUid . ',' . $row['data_page_type'],
942  ];
943 
944  // Parameters for link
945  $urlParameters = [];
946  if (isset($row['static_page_arguments'])) {
947  $urlParameters = json_decode($row['static_page_arguments'], true);
948  }
949  if (!empty($row['data_page_mp'] ?? false)) {
950  $urlParameters['MP'] = $row['data_page_mp'];
951  }
952  if (($pageLanguage === 0 && $row['sys_language_uid'] > 0) || $pageLanguage > 0) {
953  $linkConfiguration['_language'] = (int)$row['sys_language_uid'];
954  }
955 
956  if ($urlParameters !== []) {
957  $linkConfiguration['additionalParams'] = ‪GeneralUtility::implodeArrayForUrl('', $urlParameters);
958  }
959 
960  $cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
961  $cObj->start($row, 'pages');
962  return $this->linkFactory->create($linkText, $linkConfiguration, $cObj);
963  }
964 
971  protected function ‪getMenuOfPages(int $pageUid): array
972  {
973  $pageRepository = GeneralUtility::makeInstance(PageRepository::class);
974  if ($this->settings['displayLevelxAllTypes']) {
975  return $pageRepository->getMenuForPages([$pageUid]);
976  }
977  return $pageRepository->getMenu($pageUid);
978  }
979 
987  protected function ‪getPathFromPageId(int $id, string $pathMP = ''): string
988  {
989  $identStr = $id . '|' . $pathMP;
990  if (!isset($this->pathCache[$identStr])) {
991  $this->requiredFrontendUsergroups[$id] = [];
992  try {
993  $rl = GeneralUtility::makeInstance(RootlineUtility::class, $id, $pathMP)->get();
994  $path = '';
995  $pageCount = count($rl);
996  if (!empty($rl)) {
997  $excludeDoktypesFromPath = ‪GeneralUtility::trimExplode(
998  ',',
999  $this->settings['results']['pathExcludeDoktypes'] ?? '',
1000  true
1001  );
1002  $breadcrumbWrap = $this->settings['breadcrumbWrap'] ?? '/';
1003  $breadcrumbWraps = $this->typoScriptService->explodeConfigurationForOptionSplit(['wrap' => $breadcrumbWrap], $pageCount);
1004  foreach ($rl as $v) {
1005  ‪$uid = (int)$v['uid'];
1006 
1007  if (in_array($v['doktype'], $excludeDoktypesFromPath, false)) {
1008  continue;
1009  }
1010  // Check fe_user
1011  if ($v['fe_group'] && (‪$uid === $id || $v['extendToSubpages'])) {
1012  $this->requiredFrontendUsergroups[$id][] = $v['fe_group'];
1013  }
1014  // Stop, if we find that the current id is the current root page.
1015  $localRootLine = $this->request->getAttribute('frontend.page.information')->getLocalRootLine();
1016  if (‪$uid === (int)$localRootLine[0]['uid']) {
1017  array_pop($breadcrumbWraps);
1018  break;
1019  }
1020  $path = $this->‪getTypoScriptFrontendController()->cObj->wrap(htmlspecialchars($v['title']), array_pop($breadcrumbWraps)['wrap']) . $path;
1021  }
1022  }
1023  } catch (‪RootLineException $e) {
1024  $path = '';
1025  }
1026  $this->pathCache[$identStr] = $path;
1027  }
1028  return $this->pathCache[$identStr];
1029  }
1030 
1035  protected function ‪initializeExternalParsers(): void
1036  {
1037  // Initialize external document parsers for icon display and other soft operations
1038  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['indexed_search']['external_parsers'] ?? [] as $extension => $className) {
1039  $this->externalParsers[$extension] = GeneralUtility::makeInstance($className);
1040  // Init parser and if it returns FALSE, unset its entry again
1041  if (!$this->externalParsers[$extension]->softInit($extension)) {
1042  unset($this->externalParsers[$extension]);
1043  }
1044  }
1045  }
1046 
1053  protected function ‪multiplePagesType(string $item_type): bool
1054  {
1055  return is_object($this->externalParsers[$item_type] ?? false) && $this->externalParsers[$item_type]->isMultiplePageExtension($item_type);
1056  }
1057 
1065  protected function ‪processExtendedSearchParameters(): array
1066  {
1067  $allSearchTypes = $this->‪getAllAvailableSearchTypeOptions();
1068  $allDefaultOperands = $this->‪getAllAvailableOperandsOptions();
1069  $allMediaTypes = $this->‪getAllAvailableMediaTypesOptions();
1070  $allSortOrders = $this->‪getAllAvailableSortOrderOptions();
1071  $allSortDescendings = $this->‪getAllAvailableSortDescendingOptions();
1072  return [
1073  'allSearchTypes' => $allSearchTypes,
1074  'allDefaultOperands' => $allDefaultOperands,
1075  'showTypeSearch' => !empty($allSearchTypes) || !empty($allDefaultOperands),
1076  'allMediaTypes' => $allMediaTypes,
1077  'allSections' => $this->‪getAllAvailableSectionsOptions(),
1078  'allIndexConfigurations' => $this->‪getAllAvailableIndexConfigurationsOptions(),
1079  'allSortOrders' => $allSortOrders,
1080  'allSortDescendings' => $allSortDescendings,
1081  'showSortOrders' => !empty($allSortOrders) || !empty($allSortDescendings),
1082  'allNumberOfResults' => $this->‪getAllAvailableNumberOfResultsOptions(),
1083  'allGroups' => $this->‪getAllAvailableGroupOptions(),
1084  ];
1085  }
1086 
1090  protected function ‪loadSettings(): void
1091  {
1092  if (!is_array($this->settings['results.'] ?? false)) {
1093  $this->settings['results.'] = [];
1094  }
1095  $fullTypoScriptArray = $this->typoScriptService->convertPlainArrayToTypoScriptArray($this->settings);
1096  $typoScriptArray = $fullTypoScriptArray['results.'];
1097 
1098  $typoScriptFrontendController = $this->‪getTypoScriptFrontendController();
1099  $this->settings['results.']['summaryCropAfter'] = ‪MathUtility::forceIntegerInRange(
1100  $typoScriptFrontendController->cObj->stdWrapValue('summaryCropAfter', $typoScriptArray ?? []),
1101  10,
1102  5000,
1103  180
1104  );
1105  $this->settings['results.']['summaryCropSignifier'] = $typoScriptFrontendController->cObj->stdWrapValue('summaryCropSignifier', $typoScriptArray ?? []);
1106  $this->settings['results.']['titleCropAfter'] = ‪MathUtility::forceIntegerInRange(
1107  $typoScriptFrontendController->cObj->stdWrapValue('titleCropAfter', $typoScriptArray ?? []),
1108  10,
1109  500,
1110  50
1111  );
1112  $this->settings['results.']['titleCropSignifier'] = $typoScriptFrontendController->cObj->stdWrapValue('titleCropSignifier', $typoScriptArray ?? []);
1113  $this->settings['results.']['markupSW_summaryMax'] = ‪MathUtility::forceIntegerInRange(
1114  $typoScriptFrontendController->cObj->stdWrapValue('markupSW_summaryMax', $typoScriptArray ?? []),
1115  10,
1116  5000,
1117  300
1118  );
1119  $this->settings['results.']['markupSW_postPreLgd'] = ‪MathUtility::forceIntegerInRange(
1120  $typoScriptFrontendController->cObj->stdWrapValue('markupSW_postPreLgd', $typoScriptArray ?? []),
1121  1,
1122  500,
1123  60
1124  );
1125  $this->settings['results.']['markupSW_postPreLgd_offset'] = ‪MathUtility::forceIntegerInRange(
1126  $typoScriptFrontendController->cObj->stdWrapValue('markupSW_postPreLgd_offset', $typoScriptArray ?? []),
1127  1,
1128  50,
1129  5
1130  );
1131  $this->settings['results.']['markupSW_divider'] = $typoScriptFrontendController->cObj->stdWrapValue('markupSW_divider', $typoScriptArray ?? []);
1132  $this->settings['results.']['hrefInSummaryCropAfter'] = ‪MathUtility::forceIntegerInRange(
1133  $typoScriptFrontendController->cObj->stdWrapValue('hrefInSummaryCropAfter', $typoScriptArray ?? []),
1134  10,
1135  400,
1136  60
1137  );
1138  $this->settings['results.']['hrefInSummaryCropSignifier'] = $typoScriptFrontendController->cObj->stdWrapValue('hrefInSummaryCropSignifier', $typoScriptArray ?? []);
1139  }
1140 
1144  protected function ‪getNumberOfResults(int $numberOfResults): int
1145  {
1146  return in_array($numberOfResults, $this->availableResultsNumbers, true) ?
1147  $numberOfResults : ‪$this->defaultResultNumber;
1148  }
1149 
1153  protected function ‪addOperatorLabel(array $searchWord): array
1154  {
1155  if ($searchWord['oper'] ?? false) {
1156  $searchWord['operatorLabel'] = strtolower(str_replace(' ', '', (string)($searchWord['oper'])));
1157  }
1158 
1159  return $searchWord;
1160  }
1161 
1163  {
1164  return $this->request->getAttribute('frontend.controller');
1165  }
1166 }
‪TYPO3\CMS\IndexedSearch\Controller\SearchController
Definition: SearchController.php:58
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\noTypoScriptAction
‪noTypoScriptAction()
Definition: SearchController.php:673
‪TYPO3\CMS\IndexedSearch\Type\SENTENCE
‪@ SENTENCE
Definition: SearchType.php:29
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:27
‪TYPO3\CMS\Core\Utility\GeneralUtility\fixed_lgd_cs
‪static string fixed_lgd_cs(string $string, int $chars, string $appendString='...')
Definition: GeneralUtility.php:92
‪TYPO3\CMS\Extbase\Annotation
Definition: IgnoreValidation.php:18
‪TYPO3\CMS\IndexedSearch\Type\MediaType
‪MediaType
Definition: MediaType.php:24
‪TYPO3\CMS\Extbase\Utility\LocalizationUtility
Definition: LocalizationUtility.php:35
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$externalParsers
‪array $externalParsers
Definition: SearchController.php:74
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getAllAvailableOperandsOptions
‪array getAllAvailableOperandsOptions()
Definition: SearchController.php:705
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$defaultResultNumber
‪int $defaultResultNumber
Definition: SearchController.php:72
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\formAction
‪formAction(array $search=[])
Definition: SearchController.php:651
‪TYPO3\CMS\Core\Html\HtmlParser
Definition: HtmlParser.php:26
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$requiredFrontendUsergroups
‪array $requiredFrontendUsergroups
Definition: SearchController.php:84
‪TYPO3\CMS\IndexedSearch\Type\SearchType
‪SearchType
Definition: SearchType.php:24
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$searchRootPageIdList
‪string int $searchRootPageIdList
Definition: SearchController.php:70
‪TYPO3\CMS\Core\Utility\GeneralUtility\implodeArrayForUrl
‪static string implodeArrayForUrl(string $name, array $theArray, string $str='', bool $skipBlank=false, bool $rawurlencodeParamName=false)
Definition: GeneralUtility.php:860
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository
Definition: IndexSearchRepository.php:43
‪TYPO3\CMS\IndexedSearch\Type\SectionType
‪SectionType
Definition: SectionType.php:24
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getAllAvailableSearchTypeOptions
‪array getAllAvailableSearchTypeOptions()
Definition: SearchController.php:686
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\linkPage
‪linkPage(int $pageUid, array $row, string $linkText)
Definition: SearchController.php:936
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getAllAvailableGroupOptions
‪array getAllAvailableGroupOptions()
Definition: SearchController.php:866
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\markupSWpartsOfString
‪markupSWpartsOfString(string $str)
Definition: SearchController.php:499
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getSearchWords
‪getSearchWords(array $searchData, bool $useDefaultOperator)
Definition: SearchController.php:585
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$pathCache
‪array $pathCache
Definition: SearchController.php:91
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\multiplePagesType
‪bool multiplePagesType(string $item_type)
Definition: SearchController.php:1053
‪TYPO3\CMS\Core\Utility\RootlineUtility
Definition: RootlineUtility.php:40
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getDisplayResults
‪getDisplayResults(array $searchData, array $searchWords, array|bool $resultData, int $freeIndexUid=-1)
Definition: SearchController.php:239
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getPathFromPageId
‪string getPathFromPageId(int $id, string $pathMP='')
Definition: SearchController.php:987
‪TYPO3\CMS\IndexedSearch\Type\DefaultOperand
‪DefaultOperand
Definition: DefaultOperand.php:24
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\makeDescription
‪makeDescription(array $row, bool $noMarkup=false, int $length=180)
Definition: SearchController.php:470
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\__construct
‪__construct(private readonly Context $context, private readonly IndexSearchRepository $searchRepository, private readonly TypoScriptService $typoScriptService, private readonly Lexer $lexer, private readonly LinkFactory $linkFactory,)
Definition: SearchController.php:93
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\addOperatorLabel
‪addOperatorLabel(array $searchWord)
Definition: SearchController.php:1153
‪TYPO3\CMS\Core\Context\Context
Definition: Context.php:54
‪TYPO3\CMS\IndexedSearch\Type\GroupOption
‪GroupOption
Definition: GroupOption.php:24
‪TYPO3\CMS\Core\Pagination\SimplePagination
Definition: SimplePagination.php:21
‪TYPO3\CMS\IndexedSearch\Type\IndexingConfiguration
‪IndexingConfiguration
Definition: IndexingConfiguration.php:24
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getAllAvailableIndexConfigurationsOptions
‪array getAllAvailableIndexConfigurationsOptions()
Definition: SearchController.php:809
‪TYPO3\CMS\IndexedSearch\Type\ALL_EXTERNAL
‪@ ALL_EXTERNAL
Definition: MediaType.php:27
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$availableResultsNumbers
‪array $availableResultsNumbers
Definition: SearchController.php:73
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getAllAvailableSortOrderOptions
‪array getAllAvailableSortOrderOptions()
Definition: SearchController.php:847
‪TYPO3\CMS\Extbase\Mvc\Controller\ActionController\htmlResponse
‪htmlResponse(string $html=null)
Definition: ActionController.php:802
‪TYPO3\CMS\IndexedSearch\Controller
Definition: AdministrationController.php:18
‪TYPO3\CMS\Core\Utility\PathUtility\getPublicResourceWebPath
‪static getPublicResourceWebPath(string $resourcePath, bool $prefixWithSitePath=true)
Definition: PathUtility.php:97
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\removeOptionsFromOptionList
‪array removeOptionsFromOptionList(array $allOptions, mixed $blindOptions)
Definition: SearchController.php:921
‪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:60
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$resultSections
‪array $resultSections
Definition: SearchController.php:89
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$firstRow
‪array $firstRow
Definition: SearchController.php:79
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\procSearchWordsByLexer
‪array procSearchWordsByLexer(array $searchWords)
Definition: SearchController.php:623
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\compileResultRows
‪array compileResultRows(array $searchData, array $resultRows, int $freeIndexUid=-1)
Definition: SearchController.php:286
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getAllAvailableMediaTypesOptions
‪array getAllAvailableMediaTypesOptions()
Definition: SearchController.php:724
‪TYPO3\CMS\IndexedSearch\Type\ALL_CATEGORIZED
‪@ ALL_CATEGORIZED
Definition: IndexingConfiguration.php:27
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getNumberOfResults
‪getNumberOfResults(int $numberOfResults)
Definition: SearchController.php:1144
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\$sword
‪string $sword
Definition: SearchController.php:59
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\compileSingleResultRow
‪array compileSingleResultRow(array $searchData, array $row, int $headerOnly=0)
Definition: SearchController.php:371
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\loadSettings
‪loadSettings()
Definition: SearchController.php:1090
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getTypoScriptFrontendController
‪getTypoScriptFrontendController()
Definition: SearchController.php:1162
‪TYPO3\CMS\Core\TypoScript\TypoScriptService
Definition: TypoScriptService.php:27
‪$output
‪$output
Definition: annotationChecker.php:114
‪TYPO3\CMS\Extbase\Mvc\Controller\ActionController\redirect
‪redirect(?string $actionName, ?string $controllerName=null, ?string $extensionName=null, ?array $arguments=null, ?int $pageUid=null, $_=null, int $statusCode=303)
Definition: ActionController.php:684
‪TYPO3\CMS\IndexedSearch\Lexer
Definition: Lexer.php:24
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController
Definition: TypoScriptFrontendController.php:58
‪TYPO3\CMS\Webhooks\Message\$uid
‪identifier readonly int $uid
Definition: PageModificationMessage.php:35
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\processExtendedSearchParameters
‪array processExtendedSearchParameters()
Definition: SearchController.php:1065
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Extbase\Mvc\Controller\ActionController
Definition: ActionController.php:63
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:24
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
Definition: ContentObjectRenderer.php:102
‪TYPO3\CMS\Core\Exception\Page\RootLineException
Definition: RootLineException.php:24
‪TYPO3\CMS\Core\Domain\Repository\PageRepository
Definition: PageRepository.php:69
‪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\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\initialize
‪initialize(array $searchData=[])
Definition: SearchController.php:104
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\searchAction
‪searchAction(array $search=[])
Definition: SearchController.php:171
‪TYPO3\CMS\IndexedSearch\Utility\IndexedSearchUtility\getExplodedSearchString
‪static getExplodedSearchString(string $sword, string $defaultOperator, array $operatorTranslateTable)
Definition: IndexedSearchUtility.php:36
‪TYPO3\CMS\Core\Utility\GeneralUtility\intExplode
‪static list< int > intExplode(string $delimiter, string $string, bool $removeEmptyValues=false)
Definition: GeneralUtility.php:756
‪TYPO3\CMS\IndexedSearch\Utility\IndexedSearchUtility
Definition: IndexedSearchUtility.php:29
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getAllAvailableSortDescendingOptions
‪array getAllAvailableSortDescendingOptions()
Definition: SearchController.php:885
‪TYPO3\CMS\IndexedSearch\Pagination\SlicePaginator
Definition: SlicePaginator.php:28
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static list< string > trimExplode(string $delim, string $string, bool $removeEmptyValues=false, int $limit=0)
Definition: GeneralUtility.php:822
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getAllAvailableNumberOfResultsOptions
‪array getAllAvailableNumberOfResultsOptions()
Definition: SearchController.php:904
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getAllAvailableSectionsOptions
‪array getAllAvailableSectionsOptions()
Definition: SearchController.php:764
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\initializeExternalParsers
‪initializeExternalParsers()
Definition: SearchController.php:1035
‪TYPO3\CMS\IndexedSearch\Controller\SearchController\getMenuOfPages
‪array getMenuOfPages(int $pageUid)
Definition: SearchController.php:971