MediaWiki REL1_37
SpecialSearch.php
Go to the documentation of this file.
1<?php
36
50 protected $profile;
51
53 protected $searchEngine;
54
56 protected $searchEngineType = null;
57
59 protected $extraParams = [];
60
65 protected $mPrefix;
66
70 protected $limit, $offset;
71
75 protected $namespaces;
76
80 protected $fulltext;
81
85 protected $sort = SearchEngine::DEFAULT_SORT;
86
90 protected $runSuggestion = true;
91
96 protected $searchConfig;
97
100
102 private $nsInfo;
103
106
109
112
115
118
123 private $loadStatus;
124
125 private const NAMESPACES_CURRENT = 'sense';
126
137 public function __construct(
138 SearchEngineConfig $searchConfig,
140 NamespaceInfo $nsInfo,
141 IContentHandlerFactory $contentHandlerFactory,
142 InterwikiLookup $interwikiLookup,
143 ReadOnlyMode $readOnlyMode,
144 UserOptionsManager $userOptionsManager,
145 LanguageConverterFactory $languageConverterFactory
146 ) {
147 parent::__construct( 'Search' );
148 $this->searchConfig = $searchConfig;
149 $this->searchEngineFactory = $searchEngineFactory;
150 $this->nsInfo = $nsInfo;
151 $this->contentHandlerFactory = $contentHandlerFactory;
152 $this->interwikiLookup = $interwikiLookup;
153 $this->readOnlyMode = $readOnlyMode;
154 $this->userOptionsManager = $userOptionsManager;
155 $this->languageConverterFactory = $languageConverterFactory;
156 }
157
163 public function execute( $par ) {
164 $request = $this->getRequest();
165 $out = $this->getOutput();
166
167 // Fetch the search term
168 $term = str_replace( "\n", " ", $request->getText( 'search' ) );
169
170 // Historically search terms have been accepted not only in the search query
171 // parameter, but also as part of the primary url. This can have PII implications
172 // in releasing page view data. As such issue a 301 redirect to the correct
173 // URL.
174 if ( $par !== null && $par !== '' && $term === '' ) {
175 $query = $request->getValues();
176 unset( $query['title'] );
177 // Strip underscores from title parameter; most of the time we'll want
178 // text form here. But don't strip underscores from actual text params!
179 $query['search'] = str_replace( '_', ' ', $par );
180 $out->redirect( $this->getPageTitle()->getFullURL( $query ), 301 );
181 return;
182 }
183
184 // Need to load selected namespaces before handling nsRemember
185 $this->load();
186 // TODO: This performs database actions on GET request, which is going to
187 // be a problem for our multi-datacenter work.
188 if ( $request->getCheck( 'nsRemember' ) ) {
189 $this->saveNamespaces();
190 // Remove the token from the URL to prevent the user from inadvertently
191 // exposing it (e.g. by pasting it into a public wiki page) or undoing
192 // later settings changes (e.g. by reloading the page).
193 $query = $request->getValues();
194 unset( $query['title'], $query['nsRemember'] );
195 $out->redirect( $this->getPageTitle()->getFullURL( $query ) );
196 return;
197 }
198
199 if ( !$request->getVal( 'fulltext' ) && !$request->getCheck( 'offset' ) ) {
200 $url = $this->goResult( $term );
201 if ( $url !== null ) {
202 // successful 'go'
203 $out->redirect( $url );
204 return;
205 }
206 // No match. If it could plausibly be a title
207 // run the No go match hook.
208 $title = Title::newFromText( $term );
209 if ( $title !== null ) {
210 $this->getHookRunner()->onSpecialSearchNogomatch( $title );
211 }
212 }
213
214 $this->setupPage( $term );
215
216 if ( $this->getConfig()->get( 'DisableTextSearch' ) ) {
217 $searchForwardUrl = $this->getConfig()->get( 'SearchForwardUrl' );
218 if ( $searchForwardUrl ) {
219 $url = str_replace( '$1', urlencode( $term ), $searchForwardUrl );
220 $out->redirect( $url );
221 } else {
222 $out->addHTML( $this->showGoogleSearch( $term ) );
223 }
224
225 return;
226 }
227
228 $this->showResults( $term );
229 }
230
239 private function showGoogleSearch( $term ) {
240 return "<fieldset>" .
241 "<legend>" .
242 $this->msg( 'search-external' )->escaped() .
243 "</legend>" .
244 "<p class='mw-searchdisabled'>" .
245 $this->msg( 'searchdisabled' )->escaped() .
246 "</p>" .
247 // googlesearch is part of $wgRawHtmlMessages and safe to use as is here
248 $this->msg( 'googlesearch' )->rawParams(
249 htmlspecialchars( $term ),
250 'UTF-8',
251 $this->msg( 'searchbutton' )->escaped()
252 )->text() .
253 "</fieldset>";
254 }
255
261 public function load() {
262 $this->loadStatus = new Status();
263
264 $request = $this->getRequest();
265 $this->searchEngineType = $request->getVal( 'srbackend' );
266
267 list( $this->limit, $this->offset ) = $request->getLimitOffsetForUser(
268 $this->getUser(),
269 20,
270 ''
271 );
272 $this->mPrefix = $request->getVal( 'prefix', '' );
273 if ( $this->mPrefix !== '' ) {
274 $this->setExtraParam( 'prefix', $this->mPrefix );
275 }
276
277 $sort = $request->getVal( 'sort', SearchEngine::DEFAULT_SORT );
278 $validSorts = $this->getSearchEngine()->getValidSorts();
279 if ( !in_array( $sort, $validSorts ) ) {
280 $this->loadStatus->warning( 'search-invalid-sort-order', $sort,
281 implode( ', ', $validSorts ) );
282 } elseif ( $sort !== $this->sort ) {
283 $this->sort = $sort;
284 $this->setExtraParam( 'sort', $this->sort );
285 }
286
287 $user = $this->getUser();
288
289 # Extract manually requested namespaces
290 $nslist = $this->powerSearch( $request );
291 if ( $nslist === [] ) {
292 # Fallback to user preference
293 $nslist = $this->searchConfig->userNamespaces( $user );
294 }
295
296 $profile = null;
297 if ( $nslist === [] ) {
298 $profile = 'default';
299 }
300
301 $profile = $request->getVal( 'profile', $profile );
302 $profiles = $this->getSearchProfiles();
303 if ( $profile === null ) {
304 // BC with old request format
305 $profile = 'advanced';
306 foreach ( $profiles as $key => $data ) {
307 if ( $nslist === $data['namespaces'] && $key !== 'advanced' ) {
308 $profile = $key;
309 }
310 }
311 $this->namespaces = $nslist;
312 } elseif ( $profile === 'advanced' ) {
313 $this->namespaces = $nslist;
314 } elseif ( isset( $profiles[$profile]['namespaces'] ) ) {
315 $this->namespaces = $profiles[$profile]['namespaces'];
316 } else {
317 // Unknown profile requested
318 $this->loadStatus->warning( 'search-unknown-profile', $profile );
319 $profile = 'default';
320 $this->namespaces = $profiles['default']['namespaces'];
321 }
322
323 $this->fulltext = $request->getVal( 'fulltext' );
324 $this->runSuggestion = (bool)$request->getVal( 'runsuggestion', true );
325 $this->profile = $profile;
326 }
327
334 public function goResult( $term ) {
335 # If the string cannot be used to create a title
336 if ( Title::newFromText( $term ) === null ) {
337 return null;
338 }
339 # If there's an exact or very near match, jump right there.
340 $title = $this->getSearchEngine()
341 ->getNearMatcher( $this->getConfig() )->getNearMatch( $term );
342 if ( $title === null ) {
343 return null;
344 }
345 $url = null;
346 if ( !$this->getHookRunner()->onSpecialSearchGoResult( $term, $title, $url ) ) {
347 return null;
348 }
349
350 if (
351 // If there is a preference set to NOT redirect on exact page match
352 // then return null (which prevents direction)
353 !$this->redirectOnExactMatch()
354 // BUT ...
355 // ... ignore no-redirect preference if the exact page match is an interwiki link
356 && !$title->isExternal()
357 // ... ignore no-redirect preference if the exact page match is NOT in the main
358 // namespace AND there's a namespace in the search string
359 && !( $title->getNamespace() !== NS_MAIN && strpos( $term, ':' ) > 0 )
360 ) {
361 return null;
362 }
363
364 return $url ?? $title->getFullUrlForRedirect();
365 }
366
367 private function redirectOnExactMatch() {
370 // If the preference for whether to redirect is disabled, use the default setting
371 $defaultOptions = $this->userOptionsManager->getDefaultOptions();
372 return $defaultOptions['search-match-redirect'];
373 } else {
374 // Otherwise use the user's preference
375 return $this->userOptionsManager->getOption( $this->getUser(), 'search-match-redirect' );
376 }
377 }
378
382 public function showResults( $term ) {
383 if ( $this->searchEngineType !== null ) {
384 $this->setExtraParam( 'srbackend', $this->searchEngineType );
385 }
386
387 $out = $this->getOutput();
388 $widgetOptions = $this->getConfig()->get( 'SpecialSearchFormOptions' );
390 $this,
391 $this->searchConfig,
392 $this->getHookContainer(),
393 $this->languageConverterFactory->getLanguageConverter( $this->getLanguage() ),
394 $this->nsInfo,
395 $this->getSearchProfiles()
396 );
397 $filePrefix = $this->getContentLanguage()->getFormattedNsText( NS_FILE ) . ':';
398 if ( trim( $term ) === '' || $filePrefix === trim( $term ) ) {
399 // Empty query -- straight view of search form
400 if ( !$this->getHookRunner()->onSpecialSearchResultsPrepend( $this, $out, $term ) ) {
401 # Hook requested termination
402 return;
403 }
404 $out->enableOOUI();
405 // The form also contains the 'Showing results 0 - 20 of 1234' so we can
406 // only do the form render here for the empty $term case. Rendering
407 // the form when a search is provided is repeated below.
408 $out->addHTML( $formWidget->render(
409 $this->profile, $term, 0, 0, $this->offset, $this->isPowerSearch(), $widgetOptions
410 ) );
411 return;
412 }
413
414 $engine = $this->getSearchEngine();
415 $engine->setFeatureData( 'rewrite', $this->runSuggestion );
416 $engine->setLimitOffset( $this->limit, $this->offset );
417 $engine->setNamespaces( $this->namespaces );
418 $engine->setSort( $this->sort );
419 $engine->prefix = $this->mPrefix;
420
421 $this->getHookRunner()->onSpecialSearchSetupEngine( $this, $this->profile, $engine );
422 if ( !$this->getHookRunner()->onSpecialSearchResultsPrepend( $this, $out, $term ) ) {
423 # Hook requested termination
424 return;
425 }
426
427 $title = Title::newFromText( $term );
428 $languageConverter = $this->languageConverterFactory->getLanguageConverter( $this->getContentLanguage() );
429 if ( $languageConverter->hasVariants() ) {
430 // findVariantLink will replace the link arg as well but we want to keep our original
431 // search string, use a copy in the $variantTerm var so that $term remains intact.
432 $variantTerm = $term;
433 $languageConverter->findVariantLink( $variantTerm, $title );
434 }
435
436 $showSuggestion = $title === null || !$title->isKnown();
437 $engine->setShowSuggestion( $showSuggestion );
438
439 $rewritten = $engine->replacePrefixes( $term );
440 if ( $rewritten !== $term ) {
441 wfDeprecatedMsg( 'SearchEngine::replacePrefixes() was overridden by ' .
442 get_class( $engine ) . ', this is deprecated since MediaWiki 1.32',
443 '1.32', false, false );
444 }
445
446 // fetch search results
447 $titleMatches = $engine->searchTitle( $rewritten );
448 $textMatches = $engine->searchText( $rewritten );
449
450 $textStatus = null;
451 if ( $textMatches instanceof Status ) {
452 $textStatus = $textMatches;
453 $textMatches = $textStatus->getValue();
454 }
455
456 // Get number of results
457 $titleMatchesNum = $textMatchesNum = $numTitleMatches = $numTextMatches = 0;
458 if ( $titleMatches ) {
459 $titleMatchesNum = $titleMatches->numRows();
460 $numTitleMatches = $titleMatches->getTotalHits();
461 }
462 if ( $textMatches ) {
463 $textMatchesNum = $textMatches->numRows();
464 $numTextMatches = $textMatches->getTotalHits();
465 if ( $textMatchesNum > 0 ) {
466 $engine->augmentSearchResults( $textMatches );
467 }
468 }
469 $num = $titleMatchesNum + $textMatchesNum;
470 $totalRes = $numTitleMatches + $numTextMatches;
471
472 // start rendering the page
473 $out->enableOOUI();
474 $out->addHTML( $formWidget->render(
475 $this->profile, $term, $num, $totalRes, $this->offset, $this->isPowerSearch(), $widgetOptions
476 ) );
477
478 // did you mean... suggestions
479 if ( $textMatches ) {
480 $dymWidget = new MediaWiki\Search\SearchWidgets\DidYouMeanWidget( $this );
481 $out->addHTML( $dymWidget->render( $term, $textMatches ) );
482 }
483
484 $hasSearchErrors = $textStatus && $textStatus->getErrors() !== [];
485 $hasOtherResults = $textMatches &&
486 $textMatches->hasInterwikiResults( ISearchResultSet::INLINE_RESULTS );
487
488 if ( $textMatches && $textMatches->hasInterwikiResults( ISearchResultSet::SECONDARY_RESULTS ) ) {
489 $out->addHTML( '<div class="searchresults mw-searchresults-has-iw">' );
490 } else {
491 $out->addHTML( '<div class="searchresults">' );
492 }
493
494 if ( $hasSearchErrors || $this->loadStatus->getErrors() ) {
495 if ( $textStatus === null ) {
496 $textStatus = $this->loadStatus;
497 } else {
498 $textStatus->merge( $this->loadStatus );
499 }
500 list( $error, $warning ) = $textStatus->splitByErrorType();
501 if ( $error->getErrors() ) {
502 $out->addHTML( Html::errorBox(
503 $error->getHTML( 'search-error' )
504 ) );
505 }
506 if ( $warning->getErrors() ) {
507 $out->addHTML( Html::warningBox(
508 $warning->getHTML( 'search-warning' )
509 ) );
510 }
511 }
512
513 // Show the create link ahead
514 $this->showCreateLink( $title, $num, $titleMatches, $textMatches );
515
516 $this->getHookRunner()->onSpecialSearchResults( $term, $titleMatches, $textMatches );
517
518 // If we have no results and have not already displayed an error message
519 if ( $num === 0 && !$hasSearchErrors ) {
520 $out->wrapWikiMsg( "<p class=\"mw-search-nonefound\">\n$1</p>", [
521 $hasOtherResults ? 'search-nonefound-thiswiki' : 'search-nonefound',
522 wfEscapeWikiText( $term ),
523 $term,
524 ] );
525 }
526
527 // Although $num might be 0 there can still be secondary or inline
528 // results to display.
530 $mainResultWidget = new FullSearchResultWidget(
531 $this, $linkRenderer, $this->getHookContainer() );
532
533 // Default (null) on. Can be explicitly disabled.
534 if ( $engine->getFeatureData( 'enable-new-crossproject-page' ) !== false ) {
535 $sidebarResultWidget = new InterwikiSearchResultWidget( $this, $linkRenderer );
536 $sidebarResultsWidget = new InterwikiSearchResultSetWidget(
537 $this,
538 $sidebarResultWidget,
540 $this->interwikiLookup,
541 $engine->getFeatureData( 'show-multimedia-search-results' )
542 );
543 } else {
544 $sidebarResultWidget = new SimpleSearchResultWidget( $this, $linkRenderer );
545 $sidebarResultsWidget = new SimpleSearchResultSetWidget(
546 $this,
547 $sidebarResultWidget,
549 $this->interwikiLookup
550 );
551 }
552
553 $widget = new BasicSearchResultSetWidget( $this, $mainResultWidget, $sidebarResultsWidget );
554
555 $out->addHTML( $widget->render(
556 $term, $this->offset, $titleMatches, $textMatches
557 ) );
558
559 $out->addHTML( '<div class="mw-search-visualclear"></div>' );
560 $this->prevNextLinks( $totalRes, $textMatches, $term, $out );
561
562 // Close <div class='searchresults'>
563 $out->addHTML( "</div>" );
564
565 $this->getHookRunner()->onSpecialSearchResultsAppend( $this, $out, $term );
566 }
567
574 protected function showCreateLink( $title, $num, $titleMatches, $textMatches ) {
575 // show direct page/create link if applicable
576
577 // Check DBkey !== '' in case of fragment link only.
578 if ( $title === null || $title->getDBkey() === ''
579 || ( $titleMatches !== null && $titleMatches->searchContainedSyntax() )
580 || ( $textMatches !== null && $textMatches->searchContainedSyntax() )
581 ) {
582 // invalid title
583 // preserve the paragraph for margins etc...
584 $this->getOutput()->addHTML( '<p></p>' );
585
586 return;
587 }
588
589 $messageName = 'searchmenu-new-nocreate';
590 $linkClass = 'mw-search-createlink';
591
592 if ( !$title->isExternal() ) {
593 if ( $title->isKnown() ) {
594 $messageName = 'searchmenu-exists';
595 $linkClass = 'mw-search-exists';
596 } elseif (
597 $this->contentHandlerFactory->getContentHandler( $title->getContentModel() )
598 ->supportsDirectEditing()
599 && $this->getAuthority()->probablyCan( 'edit', $title )
600 ) {
601 $messageName = 'searchmenu-new';
602 }
603 }
604
605 $params = [
606 $messageName,
607 wfEscapeWikiText( $title->getPrefixedText() ),
608 Message::numParam( $num )
609 ];
610 $this->getHookRunner()->onSpecialSearchCreateLink( $title, $params );
611
612 // Extensions using the hook might still return an empty $messageName
613 // @phan-suppress-next-line PhanRedundantCondition Set by hook
614 if ( $messageName ) {
615 $this->getOutput()->wrapWikiMsg( "<p class=\"$linkClass\">\n$1</p>", $params );
616 } else {
617 // preserve the paragraph for margins etc...
618 $this->getOutput()->addHTML( '<p></p>' );
619 }
620 }
621
628 protected function setupPage( $term ) {
629 $out = $this->getOutput();
630
631 $this->setHeaders();
632 $this->outputHeader();
633 // TODO: Is this true? The namespace remember uses a user token
634 // on save.
635 $out->allowClickjacking();
636 $this->addHelpLink( 'Help:Searching' );
637
638 if ( strval( $term ) !== '' ) {
639 $out->setPageTitle( $this->msg( 'searchresults' ) );
640 $out->setHTMLTitle( $this->msg( 'pagetitle' )
641 ->plaintextParams( $this->msg( 'searchresults-title' )->plaintextParams( $term )->text() )
642 ->inContentLanguage()->text()
643 );
644 }
645
646 if ( $this->mPrefix !== '' ) {
647 $subtitle = $this->msg( 'search-filter-title-prefix' )->plaintextParams( $this->mPrefix );
648 $params = $this->powerSearchOptions();
649 unset( $params['prefix'] );
650 $params += [
651 'search' => $term,
652 'fulltext' => 1,
653 ];
654
655 $subtitle .= ' (';
656 $subtitle .= Xml::element(
657 'a',
658 [
659 'href' => $this->getPageTitle()->getLocalURL( $params ),
660 'title' => $this->msg( 'search-filter-title-prefix-reset' )->text(),
661 ],
662 $this->msg( 'search-filter-title-prefix-reset' )->text()
663 );
664 $subtitle .= ')';
665 $out->setSubtitle( $subtitle );
666 }
667
668 $out->addJsConfigVars( [ 'searchTerm' => $term ] );
669 $out->addModules( 'mediawiki.special.search' );
670 $out->addModuleStyles( [
671 'mediawiki.special', 'mediawiki.special.search.styles', 'mediawiki.ui', 'mediawiki.ui.button',
672 'mediawiki.ui.input', 'mediawiki.widgets.SearchInputWidget.styles',
673 ] );
674 }
675
681 protected function isPowerSearch() {
682 return $this->profile === 'advanced';
683 }
684
692 protected function powerSearch( &$request ) {
693 $arr = [];
694 foreach ( $this->searchConfig->searchableNamespaces() as $ns => $name ) {
695 if ( $request->getCheck( 'ns' . $ns ) ) {
696 $arr[] = $ns;
697 }
698 }
699
700 return $arr;
701 }
702
710 public function powerSearchOptions() {
711 $opt = [];
712 if ( $this->isPowerSearch() ) {
713 foreach ( $this->namespaces as $n ) {
714 $opt['ns' . $n] = 1;
715 }
716 } else {
717 $opt['profile'] = $this->profile;
718 }
719
720 return $opt + $this->extraParams;
721 }
722
728 protected function saveNamespaces() {
729 $user = $this->getUser();
730 $request = $this->getRequest();
731
732 if ( $user->isRegistered() &&
733 $user->matchEditToken(
734 $request->getVal( 'nsRemember' ),
735 'searchnamespace',
736 $request
737 ) && !$this->readOnlyMode->isReadOnly()
738 ) {
739 // Reset namespace preferences: namespaces are not searched
740 // when they're not mentioned in the URL parameters.
741 foreach ( $this->nsInfo->getValidNamespaces() as $n ) {
742 $this->userOptionsManager->setOption( $user, 'searchNs' . $n, false );
743 }
744 // The request parameters include all the namespaces to be searched.
745 // Even if they're the same as an existing profile, they're not eaten.
746 foreach ( $this->namespaces as $n ) {
747 $this->userOptionsManager->setOption( $user, 'searchNs' . $n, true );
748 }
749
750 DeferredUpdates::addCallableUpdate( static function () use ( $user ) {
751 $user->saveSettings();
752 } );
753
754 return true;
755 }
756
757 return false;
758 }
759
764 protected function getSearchProfiles() {
765 // Builds list of Search Types (profiles)
766 $nsAllSet = array_keys( $this->searchConfig->searchableNamespaces() );
767 $defaultNs = $this->searchConfig->defaultNamespaces();
768 $profiles = [
769 'default' => [
770 'message' => 'searchprofile-articles',
771 'tooltip' => 'searchprofile-articles-tooltip',
772 'namespaces' => $defaultNs,
773 'namespace-messages' => $this->searchConfig->namespacesAsText(
774 $defaultNs
775 ),
776 ],
777 'images' => [
778 'message' => 'searchprofile-images',
779 'tooltip' => 'searchprofile-images-tooltip',
780 'namespaces' => [ NS_FILE ],
781 ],
782 'all' => [
783 'message' => 'searchprofile-everything',
784 'tooltip' => 'searchprofile-everything-tooltip',
785 'namespaces' => $nsAllSet,
786 ],
787 'advanced' => [
788 'message' => 'searchprofile-advanced',
789 'tooltip' => 'searchprofile-advanced-tooltip',
790 'namespaces' => self::NAMESPACES_CURRENT,
791 ]
792 ];
793
794 $this->getHookRunner()->onSpecialSearchProfiles( $profiles );
795
796 foreach ( $profiles as &$data ) {
797 if ( !is_array( $data['namespaces'] ) ) {
798 continue;
799 }
800 sort( $data['namespaces'] );
801 }
802
803 return $profiles;
804 }
805
811 public function getSearchEngine() {
812 if ( $this->searchEngine === null ) {
813 $this->searchEngine = $this->searchEngineFactory->create( $this->searchEngineType );
814 }
815
816 return $this->searchEngine;
817 }
818
823 public function getProfile() {
824 return $this->profile;
825 }
826
831 public function getNamespaces() {
832 return $this->namespaces;
833 }
834
844 public function setExtraParam( $key, $value ) {
845 $this->extraParams[$key] = $value;
846 }
847
856 public function getPrefix() {
857 return $this->mPrefix;
858 }
859
866 private function prevNextLinks( ?int $totalRes, ?ISearchResultSet $textMatches, string $term, OutputPage $out ) {
867 if ( $totalRes > $this->limit || $this->offset ) {
868 // Allow matches to define the correct offset, as interleaved
869 // AB testing may require a different next page offset.
870 if ( $textMatches && $textMatches->getOffset() !== null ) {
871 $offset = $textMatches->getOffset();
872 } else {
873 $offset = $this->offset;
874 }
875
876 // use the rewritten search term for subsequent page searches
877 $newSearchTerm = $term;
878 if ( $textMatches && $textMatches->hasRewrittenQuery() ) {
879 $newSearchTerm = $textMatches->getQueryAfterRewrite();
880 }
881
882 $prevNext =
883 $this->buildPrevNextNavigation( $offset, $this->limit,
884 $this->powerSearchOptions() + [ 'search' => $newSearchTerm ],
885 $this->limit + $this->offset >= $totalRes );
886 $out->addHTML( "<p class='mw-search-pager-bottom'>{$prevNext}</p>\n" );
887 }
888 }
889
890 protected function getGroupName() {
891 return 'pages';
892 }
893}
bool $wgSearchMatchRedirectPreference
Set true to allow logged-in users to set a preference whether or not matches in search results should...
const NS_FILE
Definition Defines.php:70
const NS_MAIN
Definition Defines.php:64
wfDeprecatedMsg( $msg, $version=false, $component=false, $callerOffset=2)
Log a deprecation warning with arbitrary message text.
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
SearchEngineFactory null $searchEngineFactory
Definition SearchApi.php:33
An interface for creating language converters.
Renders a suggested search for the user, or tells the user a suggested search was run instead of the ...
Renders a 'full' multi-line search result with metadata.
Renders one or more ISearchResultSets into a sidebar grouped by interwiki prefix.
Renders one or more ISearchResultSets into a sidebar grouped by interwiki prefix.
A service class to control user options.
static numParam( $num)
Definition Message.php:1101
This is a utility class for dealing with namespaces that encodes all the "magic" behaviors of them ba...
This is one of the Core classes and should be read at least once by any new developers.
addHTML( $text)
Append $text to the body HTML.
A service class for fetching the wiki's current read-only mode.
Configuration handling class for SearchEngine.
Factory class for SearchEngine.
Contain a class for special pages.
Parent class for all special pages.
outputHeader( $summaryMessageKey='')
Outputs a summary message on top of special pages Per default the message key is the canonical name o...
setHeaders()
Sets headers - this should be called from the execute() method of all derived classes!
getOutput()
Get the OutputPage being used for this instance.
getUser()
Shortcut to get the User executing this instance.
buildPrevNextNavigation( $offset, $limit, array $query=[], $atend=false, $subpage=false)
Generate (prev x| next x) (20|50|100...) type links for paging.
LinkRenderer null $linkRenderer
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
getConfig()
Shortcut to get main config object.
getRequest()
Get the WebRequest being used for this instance.
getPageTitle( $subpage=false)
Get a self-referential title object.
addHelpLink( $to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
getContentLanguage()
Shortcut to get content language.
implements Special:Search - Run text & title search and display the output
IContentHandlerFactory $contentHandlerFactory
string null $searchEngineType
Search engine type, if not default.
goResult( $term)
If an exact title match can be found, jump straight ahead to it.
setExtraParam( $key, $value)
Users of hook SpecialSearchSetupEngine can use this to add more params to links to not lose selection...
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
string $mPrefix
The prefix url parameter.
null string $profile
Current search profile.
load()
Set up basic search parameters from the request and user settings.
SearchEngineConfig $searchConfig
Search engine configurations.
InterwikiLookup $interwikiLookup
getPrefix()
The prefix value send to Special:Search using the 'prefix' URI param It means that the user is willin...
__construct(SearchEngineConfig $searchConfig, SearchEngineFactory $searchEngineFactory, NamespaceInfo $nsInfo, IContentHandlerFactory $contentHandlerFactory, InterwikiLookup $interwikiLookup, ReadOnlyMode $readOnlyMode, UserOptionsManager $userOptionsManager, LanguageConverterFactory $languageConverterFactory)
saveNamespaces()
Save namespace preferences when we're supposed to.
UserOptionsManager $userOptionsManager
getProfile()
Current search profile.
SearchEngine $searchEngine
Search engine.
NamespaceInfo $nsInfo
isPowerSearch()
Return true if current search is a power (advanced) search.
Status $loadStatus
Holds any parameter validation errors that should be displayed back to the user.
getNamespaces()
Current namespaces.
powerSearchOptions()
Reconstruct the 'power search' options for links TODO: Instead of exposing this publicly,...
powerSearch(&$request)
Extract "power search" namespace settings from the request object, returning a list of index numbers ...
showGoogleSearch( $term)
Output a google search form if search is disabled.
prevNextLinks(?int $totalRes, ?ISearchResultSet $textMatches, string $term, OutputPage $out)
array $extraParams
For links.
SearchEngineFactory $searchEngineFactory
ReadOnlyMode $readOnlyMode
execute( $par)
Entry point.
setupPage( $term)
Sets up everything for the HTML output page including styles, javascript, page title,...
showCreateLink( $title, $num, $titleMatches, $textMatches)
LanguageConverterFactory $languageConverterFactory
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition Status.php:44
A set of SearchEngine results.
hasRewrittenQuery()
Some search modes will run an alternative query that it thinks gives a better result than the provide...
Service interface for looking up Interwiki records.