104 parent::__construct(
'Search' );
105 $this->searchConfig = MediaWikiServices::getInstance()->getSearchEngineConfig();
118 $term = str_replace(
"\n",
" ", $request->getText(
'search' ) );
124 if ( $par !==
null && $par !==
'' && $term ===
'' ) {
125 $query = $request->getValues();
126 unset( $query[
'title'] );
129 $query[
'search'] = str_replace(
'_',
' ', $par );
130 $out->redirect( $this->
getPageTitle()->getFullURL( $query ), 301 );
138 if ( $request->getCheck(
'nsRemember' ) ) {
143 $query = $request->getValues();
144 unset( $query[
'title'], $query[
'nsRemember'] );
145 $out->redirect( $this->
getPageTitle()->getFullURL( $query ) );
149 $this->searchEngineType = $request->getVal(
'srbackend' );
150 if ( !$request->getVal(
'fulltext' ) && !$request->getCheck(
'offset' ) ) {
152 if ( $url !==
null ) {
154 $out->redirect( $url );
160 if ( !is_null(
$title ) ) {
167 if ( $this->
getConfig()->
get(
'DisableTextSearch' ) ) {
168 $searchForwardUrl = $this->
getConfig()->get(
'SearchForwardUrl' );
169 if ( $searchForwardUrl ) {
170 $url = str_replace(
'$1', urlencode( $term ), $searchForwardUrl );
171 $out->redirect( $url );
193 $this->
msg(
'search-external' )->escaped() .
195 "<p class='mw-searchdisabled'>" .
196 $this->
msg(
'searchdisabled' )->escaped() .
198 $this->
msg(
'googlesearch' )->rawParams(
199 htmlspecialchars( $term ),
201 $this->
msg(
'searchbutton' )->escaped()
213 $this->loadStatus =
new Status();
216 list( $this->limit, $this->offset ) = $request->getLimitOffset( 20,
'' );
217 $this->mPrefix = $request->getVal(
'prefix',
'' );
218 if ( $this->mPrefix !==
'' ) {
224 if ( !in_array(
$sort, $validSorts ) ) {
225 $this->loadStatus->warning(
'search-invalid-sort-order',
$sort,
226 implode(
', ', $validSorts ) );
227 } elseif (
$sort !== $this->sort ) {
234 # Extract manually requested namespaces
236 if ( $nslist === [] ) {
237 # Fallback to user preference
238 $nslist = $this->searchConfig->userNamespaces( $user );
242 if ( $nslist === [] ) {
251 foreach ( $profiles as $key => $data ) {
252 if ( $nslist === $data[
'namespaces'] && $key !==
'advanced' ) {
256 $this->namespaces = $nslist;
257 } elseif (
$profile ===
'advanced' ) {
258 $this->namespaces = $nslist;
259 } elseif ( isset( $profiles[
$profile][
'namespaces'] ) ) {
260 $this->namespaces = $profiles[
$profile][
'namespaces'];
263 $this->loadStatus->warning(
'search-unknown-profile',
$profile );
265 $this->namespaces = $profiles[
'default'][
'namespaces'];
268 $this->fulltext = $request->getVal(
'fulltext' );
269 $this->runSuggestion = (bool)$request->getVal(
'runsuggestion',
true );
280 # If the string cannot be used to create a title
284 # If there's an exact or very near match, jump right there.
286 ->getNearMatcher( $this->
getConfig() )->getNearMatch( $term );
287 if ( is_null(
$title ) ) {
295 return $url ??
$title->getFullUrlForRedirect();
302 if ( $this->searchEngineType !==
null ) {
303 $this->
setExtraParam(
'srbackend', $this->searchEngineType );
307 $widgetOptions = $this->
getConfig()->get(
'SpecialSearchFormOptions' );
313 $filePrefix = MediaWikiServices::getInstance()->getContentLanguage()->
314 getFormattedNsText(
NS_FILE ) .
':';
315 if ( trim( $term ) ===
'' || $filePrefix === trim( $term ) ) {
317 if ( !
Hooks::run(
'SpecialSearchResultsPrepend', [ $this, $out, $term ] ) ) {
318 # Hook requested termination
325 $out->addHTML( $formWidget->render(
326 $this->profile, $term, 0, 0, $this->offset, $this->isPowerSearch(), $widgetOptions
332 $engine->setFeatureData(
'rewrite', $this->runSuggestion );
333 $engine->setLimitOffset( $this->limit, $this->offset );
334 $engine->setNamespaces( $this->namespaces );
335 $engine->setSort( $this->sort );
338 Hooks::run(
'SpecialSearchSetupEngine', [ $this, $this->profile, $engine ] );
339 if ( !
Hooks::run(
'SpecialSearchResultsPrepend', [ $this, $out, $term ] ) ) {
340 # Hook requested termination
345 $showSuggestion =
$title ===
null || !
$title->isKnown();
346 $engine->setShowSuggestion( $showSuggestion );
348 $rewritten = $engine->replacePrefixes( $term );
349 if ( $rewritten !== $term ) {
350 wfDeprecated(
'SearchEngine::replacePrefixes() (overridden by ' .
351 get_class( $engine ) .
')',
'1.32' );
355 $titleMatches = $engine->searchTitle( $rewritten );
356 $textMatches = $engine->searchText( $rewritten );
359 if ( $textMatches instanceof
Status ) {
360 $textStatus = $textMatches;
361 $textMatches = $textStatus->getValue();
365 $titleMatchesNum = $textMatchesNum = $numTitleMatches = $numTextMatches = 0;
366 if ( $titleMatches ) {
367 $titleMatchesNum = $titleMatches->numRows();
368 $numTitleMatches = $titleMatches->getTotalHits();
370 if ( $textMatches ) {
371 $textMatchesNum = $textMatches->numRows();
372 $numTextMatches = $textMatches->getTotalHits();
373 if ( $textMatchesNum > 0 ) {
374 $engine->augmentSearchResults( $textMatches );
377 $num = $titleMatchesNum + $textMatchesNum;
378 $totalRes = $numTitleMatches + $numTextMatches;
382 $out->addHTML( $formWidget->render(
383 $this->profile, $term, $num, $totalRes, $this->offset, $this->isPowerSearch(), $widgetOptions
387 if ( $textMatches ) {
389 $out->addHTML( $dymWidget->render( $term, $textMatches ) );
392 $hasSearchErrors = $textStatus && $textStatus->getErrors() !== [];
393 $hasOtherResults = $textMatches &&
397 $out->addHTML(
'<div class="searchresults mw-searchresults-has-iw">' );
399 $out->addHTML(
'<div class="searchresults">' );
402 if ( $hasSearchErrors || $this->loadStatus->getErrors() ) {
403 if ( $textStatus ===
null ) {
406 $textStatus->
merge( $this->loadStatus );
408 list( $error, $warning ) = $textStatus->splitByErrorType();
409 if ( $error->getErrors() ) {
410 $out->addHTML( Html::errorBox(
411 $error->getHTML(
'search-error' )
414 if ( $warning->getErrors() ) {
415 $out->addHTML( Html::warningBox(
416 $warning->getHTML(
'search-warning' )
424 Hooks::run(
'SpecialSearchResults', [ $term, &$titleMatches, &$textMatches ] );
427 if ( $num === 0 && !$hasSearchErrors ) {
428 $out->wrapWikiMsg(
"<p class=\"mw-search-nonefound\">\n$1</p>", [
429 $hasOtherResults ?
'search-nonefound-thiswiki' :
'search-nonefound',
440 if ( $engine->getFeatureData(
'enable-new-crossproject-page' ) !== false ) {
444 $sidebarResultWidget,
446 MediaWikiServices::getInstance()->getInterwikiLookup(),
447 $engine->getFeatureData(
'show-multimedia-search-results' )
453 $sidebarResultWidget,
455 MediaWikiServices::getInstance()->getInterwikiLookup()
461 $out->addHTML( $widget->render(
462 $term, $this->offset, $titleMatches, $textMatches
465 $out->addHTML(
'<div class="mw-search-visualclear"></div>' );
468 if ( $totalRes > $this->limit || $this->offset ) {
471 if ( $textMatches && $textMatches->getOffset() !== null ) {
472 $offset = $textMatches->getOffset();
481 $this->limit + $this->offset >= $totalRes
483 $out->addHTML(
"<p class='mw-search-pager-bottom'>{$prevNext}</p>\n" );
487 $out->addHTML(
"</div>" );
489 Hooks::run(
'SpecialSearchResultsAppend', [ $this, $out, $term ] );
503 || ( $titleMatches !==
null && $titleMatches->searchContainedSyntax() )
504 || ( $textMatches !==
null && $textMatches->searchContainedSyntax() )
508 $this->
getOutput()->addHTML(
'<p></p>' );
513 $messageName =
'searchmenu-new-nocreate';
514 $linkClass =
'mw-search-createlink';
516 if ( !
$title->isExternal() ) {
517 if (
$title->isKnown() ) {
518 $messageName =
'searchmenu-exists';
519 $linkClass =
'mw-search-exists';
524 $messageName =
'searchmenu-new';
531 Message::numParam( $num )
536 if ( $messageName ) {
537 $this->
getOutput()->wrapWikiMsg(
"<p class=\"$linkClass\">\n$1</p>", $params );
540 $this->
getOutput()->addHTML(
'<p></p>' );
557 $out->allowClickjacking();
560 if ( strval( $term ) !==
'' ) {
561 $out->setPageTitle( $this->
msg(
'searchresults' ) );
562 $out->setHTMLTitle( $this->
msg(
'pagetitle' )
563 ->plaintextParams( $this->
msg(
'searchresults-title' )->plaintextParams( $term )->text() )
564 ->inContentLanguage()->text()
568 if ( $this->mPrefix !==
'' ) {
569 $subtitle = $this->
msg(
'search-filter-title-prefix' )->plaintextParams( $this->mPrefix );
571 unset( $params[
'prefix'] );
581 'href' => $this->
getPageTitle()->getLocalURL( $params ),
582 'title' => $this->
msg(
'search-filter-title-prefix-reset' )->text(),
584 $this->
msg(
'search-filter-title-prefix-reset' )->text()
587 $out->setSubtitle( $subtitle );
590 $out->addJsConfigVars( [
'searchTerm' => $term ] );
591 $out->addModules(
'mediawiki.special.search' );
592 $out->addModuleStyles( [
593 'mediawiki.special',
'mediawiki.special.search.styles',
'mediawiki.ui',
'mediawiki.ui.button',
594 'mediawiki.ui.input',
'mediawiki.widgets.SearchInputWidget.styles',
604 return $this->profile ===
'advanced';
616 foreach ( $this->searchConfig->searchableNamespaces() as $ns => $name ) {
617 if ( $request->getCheck(
'ns' . $ns ) ) {
635 foreach ( $this->namespaces as $n ) {
654 if ( $user->isLoggedIn() &&
655 $user->matchEditToken(
656 $request->getVal(
'nsRemember' ),
663 foreach ( MediaWikiServices::getInstance()->getNamespaceInfo()->getValidNamespaces()
666 $user->setOption(
'searchNs' . $n,
false );
670 foreach ( $this->namespaces as $n ) {
671 $user->setOption(
'searchNs' . $n,
true );
675 $user->saveSettings();
689 $nsAllSet = array_keys( $this->searchConfig->searchableNamespaces() );
690 $defaultNs = $this->searchConfig->defaultNamespaces();
693 'message' =>
'searchprofile-articles',
694 'tooltip' =>
'searchprofile-articles-tooltip',
695 'namespaces' => $defaultNs,
696 'namespace-messages' => $this->searchConfig->namespacesAsText(
701 'message' =>
'searchprofile-images',
702 'tooltip' =>
'searchprofile-images-tooltip',
706 'message' =>
'searchprofile-everything',
707 'tooltip' =>
'searchprofile-everything-tooltip',
708 'namespaces' => $nsAllSet,
711 'message' =>
'searchprofile-advanced',
712 'tooltip' =>
'searchprofile-advanced-tooltip',
717 Hooks::run(
'SpecialSearchProfiles', [ &$profiles ] );
719 foreach ( $profiles as &$data ) {
720 if ( !is_array( $data[
'namespaces'] ) ) {
723 sort( $data[
'namespaces'] );
735 if ( $this->searchEngine ===
null ) {
736 $services = MediaWikiServices::getInstance();
737 $this->searchEngine = $this->searchEngineType ?
738 $services->getSearchEngineFactory()->create( $this->searchEngineType ) :
739 $services->newSearchEngine();
771 $this->extraParams[$key] = $value;