From c1f9b1f7b1b77776192048005dcc66dcf3df2bfb Mon Sep 17 00:00:00 2001 From: Pierre Schmitz Date: Sat, 27 Dec 2014 15:41:37 +0100 Subject: Update to MediaWiki 1.24.1 --- includes/specials/SpecialSearch.php | 661 ++++++++++++++++++------------------ 1 file changed, 328 insertions(+), 333 deletions(-) (limited to 'includes/specials/SpecialSearch.php') diff --git a/includes/specials/SpecialSearch.php b/includes/specials/SpecialSearch.php index 8609c740..88ab7d82 100644 --- a/includes/specials/SpecialSearch.php +++ b/includes/specials/SpecialSearch.php @@ -30,25 +30,24 @@ class SpecialSearch extends SpecialPage { /** * Current search profile. Search profile is just a name that identifies - * the active search tab on the search page (content, help, discussions...) + * the active search tab on the search page (content, discussions...) * For users tt replaces the set of enabled namespaces from the query * string when applicable. Extensions can add new profiles with hooks * with custom search options just for that profile. - * null|string + * @var null|string */ protected $profile; - function getProfile() { return $this->profile; } - /// Search engine + /** @var SearchEngine Search engine */ protected $searchEngine; - /// Search engine type, if not default + /** @var string Search engine type, if not default */ protected $searchEngineType; - /// For links + /** @var array For links */ protected $extraParams = array(); - /// No idea, apparently used by some other classes + /** @var string No idea, apparently used by some other classes */ protected $mPrefix; /** @@ -60,12 +59,6 @@ class SpecialSearch extends SpecialPage { * @var array */ protected $namespaces; - function getNamespaces() { return $this->namespaces; } - - /** - * @var bool - */ - protected $searchRedirects; /** * @var string @@ -81,14 +74,17 @@ class SpecialSearch extends SpecialPage { /** * Entry point * - * @param string $par or null + * @param string $par */ public function execute( $par ) { $this->setHeaders(); $this->outputHeader(); $out = $this->getOutput(); $out->allowClickjacking(); - $out->addModuleStyles( 'mediawiki.special' ); + $out->addModuleStyles( array( + 'mediawiki.special', 'mediawiki.special.search', 'mediawiki.ui', 'mediawiki.ui.button', + 'mediawiki.ui.input', + ) ); // Strip underscores from title parameter; most of the time we'll want // text form here. But don't strip underscores from actual text params! @@ -100,13 +96,22 @@ class SpecialSearch extends SpecialPage { $search = str_replace( "\n", " ", $request->getText( 'search', $titleParam ) ); $this->load(); + if ( !is_null( $request->getVal( 'nsRemember' ) ) ) { + $this->saveNamespaces(); + // Remove the token from the URL to prevent the user from inadvertently + // exposing it (e.g. by pasting it into a public wiki page) or undoing + // later settings changes (e.g. by reloading the page). + $query = $request->getValues(); + unset( $query['title'], $query['nsRemember'] ); + $out->redirect( $this->getPageTitle()->getFullURL( $query ) ); + return; + } $this->searchEngineType = $request->getVal( 'srbackend' ); if ( $request->getVal( 'fulltext' ) || !is_null( $request->getVal( 'offset' ) ) - || !is_null( $request->getVal( 'searchx' ) ) ) - { + ) { $this->showResults( $search ); } else { $this->goResult( $search ); @@ -120,7 +125,7 @@ class SpecialSearch extends SpecialPage { */ public function load() { $request = $this->getRequest(); - list( $this->limit, $this->offset ) = $request->getLimitOffset( 20, 'searchlimit' ); + list( $this->limit, $this->offset ) = $request->getLimitOffset( 20, '' ); $this->mPrefix = $request->getVal( 'prefix', '' ); $user = $this->getUser(); @@ -160,9 +165,6 @@ class SpecialSearch extends SpecialPage { } } - // Redirects defaults to true, but we don't know whether it was ticked of or just missing - $default = $request->getBool( 'profile' ) ? 0 : 1; - $this->searchRedirects = $request->getBool( 'redirs', $default ) ? 1 : 0; $this->didYouMeanHtml = ''; # html of did you mean... link $this->fulltext = $request->getVal( 'fulltext' ); $this->profile = $profile; @@ -171,57 +173,44 @@ class SpecialSearch extends SpecialPage { /** * If an exact title match can be found, jump straight ahead to it. * - * @param $term String + * @param string $term */ public function goResult( $term ) { $this->setupPage( $term ); # Try to go to page as entered. - $t = Title::newFromText( $term ); + $title = Title::newFromText( $term ); # If the string cannot be used to create a title - if ( is_null( $t ) ) { + if ( is_null( $title ) ) { $this->showResults( $term ); + return; } # If there's an exact or very near match, jump right there. - $t = SearchEngine::getNearMatch( $term ); + $title = SearchEngine::getNearMatch( $term ); - if ( !wfRunHooks( 'SpecialSearchGo', array( &$t, &$term ) ) ) { - # Hook requested termination - return; - } + if ( !is_null( $title ) ) { + $this->getOutput()->redirect( $title->getFullURL() ); - if ( !is_null( $t ) ) { - $this->getOutput()->redirect( $t->getFullURL() ); return; } # No match, generate an edit URL - $t = Title::newFromText( $term ); - if ( !is_null( $t ) ) { - global $wgGoToEdit; - wfRunHooks( 'SpecialSearchNogomatch', array( &$t ) ); - wfDebugLog( 'nogomatch', $t->getText(), false ); - - # If the feature is enabled, go straight to the edit page - if ( $wgGoToEdit ) { - $this->getOutput()->redirect( $t->getFullURL( array( 'action' => 'edit' ) ) ); - return; - } + $title = Title::newFromText( $term ); + if ( !is_null( $title ) ) { + wfRunHooks( 'SpecialSearchNogomatch', array( &$title ) ); } $this->showResults( $term ); } /** - * @param $term String + * @param string $term */ public function showResults( $term ) { - global $wgDisableTextSearch, $wgSearchForwardUrl, $wgContLang, $wgScript; - wfProfileIn( __METHOD__ ); + global $wgContLang; + $profile = new ProfileSection( __METHOD__ ); $search = $this->getSearchEngine(); $search->setLimitOffset( $this->limit, $this->offset ); $search->setNamespaces( $this->namespaces ); - $search->showRedirects = $this->searchRedirects; // BC - $search->setFeatureData( 'list-redirects', $this->searchRedirects ); $search->prefix = $this->mPrefix; $term = $search->transformSearchTerm( $term ); @@ -231,15 +220,20 @@ class SpecialSearch extends SpecialPage { $out = $this->getOutput(); - if ( $wgDisableTextSearch ) { - if ( $wgSearchForwardUrl ) { - $url = str_replace( '$1', urlencode( $term ), $wgSearchForwardUrl ); + if ( $this->getConfig()->get( 'DisableTextSearch' ) ) { + $searchFowardUrl = $this->getConfig()->get( 'SearchForwardUrl' ); + if ( $searchFowardUrl ) { + $url = str_replace( '$1', urlencode( $term ), $searchFowardUrl ); $out->redirect( $url ); } else { $out->addHTML( Xml::openElement( 'fieldset' ) . Xml::element( 'legend', null, $this->msg( 'search-external' )->text() ) . - Xml::element( 'p', array( 'class' => 'mw-searchdisabled' ), $this->msg( 'searchdisabled' )->text() ) . + Xml::element( + 'p', + array( 'class' => 'mw-searchdisabled' ), + $this->msg( 'searchdisabled' )->text() + ) . $this->msg( 'googlesearch' )->rawParams( htmlspecialchars( $term ), 'UTF-8', @@ -248,19 +242,19 @@ class SpecialSearch extends SpecialPage { Xml::closeElement( 'fieldset' ) ); } - wfProfileOut( __METHOD__ ); + return; } - $t = Title::newFromText( $term ); + $title = Title::newFromText( $term ); + $showSuggestion = $title === null || !$title->isKnown(); + $search->setShowSuggestion( $showSuggestion ); // fetch search results $rewritten = $search->replacePrefixes( $term ); $titleMatches = $search->searchTitle( $rewritten ); - if ( !( $titleMatches instanceof SearchResultTooMany ) ) { - $textMatches = $search->searchText( $rewritten ); - } + $textMatches = $search->searchText( $rewritten ); $textStatus = null; if ( $textMatches instanceof Status ) { @@ -269,9 +263,7 @@ class SpecialSearch extends SpecialPage { } // did you mean... suggestions - if ( $textMatches && !$textStatus && $textMatches->hasSuggestion() ) { - $st = SpecialPage::getTitleFor( 'Search' ); - + if ( $showSuggestion && $textMatches && !$textStatus && $textMatches->hasSuggestion() ) { # mirror Go/Search behavior of original request .. $didYouMeanParams = array( 'search' => $textMatches->getSuggestionQuery() ); @@ -291,18 +283,18 @@ class SpecialSearch extends SpecialPage { } $suggestLink = Linker::linkKnown( - $st, + $this->getPageTitle(), $suggestionSnippet, array(), $stParams ); - $this->didYouMeanHtml = '
' . $this->msg( 'search-suggest' )->rawParams( $suggestLink )->text() . '
'; + $this->didYouMeanHtml = '
' + . $this->msg( 'search-suggest' )->rawParams( $suggestLink )->text() . '
'; } if ( !wfRunHooks( 'SpecialSearchResultsPrepend', array( $this, $out, $term ) ) ) { # Hook requested termination - wfProfileOut( __METHOD__ ); return; } @@ -313,78 +305,62 @@ class SpecialSearch extends SpecialPage { array( 'id' => ( $this->profile === 'advanced' ? 'powersearch' : 'search' ), 'method' => 'get', - 'action' => $wgScript + 'action' => wfScript(), ) ) ); - $out->addHtml( - Xml::openElement( 'table', array( 'id' => 'mw-search-top-table', 'cellpadding' => 0, 'cellspacing' => 0 ) ) . - Xml::openElement( 'tr' ) . - Xml::openElement( 'td' ) . "\n" . - $this->shortDialog( $term ) . - Xml::closeElement( 'td' ) . - Xml::closeElement( 'tr' ) . - Xml::closeElement( 'table' ) - ); - // Sometimes the search engine knows there are too many hits - if ( $titleMatches instanceof SearchResultTooMany ) { - $out->wrapWikiMsg( "==$1==\n", 'toomanymatches' ); - wfProfileOut( __METHOD__ ); - return; + // Get number of results + $titleMatchesNum = $textMatchesNum = $numTitleMatches = $numTextMatches = 0; + if ( $titleMatches ) { + $titleMatchesNum = $titleMatches->numRows(); + $numTitleMatches = $titleMatches->getTotalHits(); + } + if ( $textMatches ) { + $textMatchesNum = $textMatches->numRows(); + $numTextMatches = $textMatches->getTotalHits(); } + $num = $titleMatchesNum + $textMatchesNum; + $totalRes = $numTitleMatches + $numTextMatches; + + $out->addHtml( + # This is an awful awful ID name. It's not a table, but we + # named it poorly from when this was a table so now we're + # stuck with it + Xml::openElement( 'div', array( 'id' => 'mw-search-top-table' ) ) . + $this->shortDialog( $term, $num, $totalRes ) . + Xml::closeElement( 'div' ) . + $this->formHeader( $term ) . + Xml::closeElement( 'form' ) + ); $filePrefix = $wgContLang->getFormattedNsText( NS_FILE ) . ':'; if ( trim( $term ) === '' || $filePrefix === trim( $term ) ) { - $out->addHTML( $this->formHeader( $term, 0, 0 ) ); - $out->addHtml( $this->getProfileForm( $this->profile, $term ) ); - $out->addHTML( '' ); // Empty query -- straight view of search form - wfProfileOut( __METHOD__ ); return; } - // Get number of results - $titleMatchesNum = $titleMatches ? $titleMatches->numRows() : 0; - $textMatchesNum = $textMatches ? $textMatches->numRows() : 0; - // Total initial query matches (possible false positives) - $num = $titleMatchesNum + $textMatchesNum; - - // Get total actual results (after second filtering, if any) - $numTitleMatches = $titleMatches && !is_null( $titleMatches->getTotalHits() ) ? - $titleMatches->getTotalHits() : $titleMatchesNum; - $numTextMatches = $textMatches && !is_null( $textMatches->getTotalHits() ) ? - $textMatches->getTotalHits() : $textMatchesNum; - - // get total number of results if backend can calculate it - $totalRes = 0; - if ( $titleMatches && !is_null( $titleMatches->getTotalHits() ) ) { - $totalRes += $titleMatches->getTotalHits(); - } - if ( $textMatches && !is_null( $textMatches->getTotalHits() ) ) { - $totalRes += $textMatches->getTotalHits(); - } - - // show number of results and current offset - $out->addHTML( $this->formHeader( $term, $num, $totalRes ) ); - $out->addHtml( $this->getProfileForm( $this->profile, $term ) ); - - $out->addHtml( Xml::closeElement( 'form' ) ); $out->addHtml( "
" ); // prev/next links + $prevnext = null; if ( $num || $this->offset ) { // Show the create link ahead - $this->showCreateLink( $t ); - $prevnext = $this->getLanguage()->viewPrevNext( $this->getTitle(), $this->offset, $this->limit, - $this->powerSearchOptions() + array( 'search' => $term ), - max( $titleMatchesNum, $textMatchesNum ) < $this->limit - ); - //$out->addHTML( "

{$prevnext}

\n" ); - wfRunHooks( 'SpecialSearchResults', array( $term, &$titleMatches, &$textMatches ) ); - } else { - wfRunHooks( 'SpecialSearchNoResults', array( $term ) ); + $this->showCreateLink( $title, $num, $titleMatches, $textMatches ); + if ( $totalRes > $this->limit || $this->offset ) { + if ( $this->searchEngineType !== null ) { + $this->setExtraParam( 'srbackend', $this->searchEngineType ); + } + $prevnext = $this->getLanguage()->viewPrevNext( + $this->getPageTitle(), + $this->offset, + $this->limit, + $this->powerSearchOptions() + array( 'search' => $term ), + $this->limit + $this->offset >= $totalRes + ); + } } + wfRunHooks( 'SpecialSearchResults', array( $term, &$titleMatches, &$textMatches ) ); $out->parserOptions()->setEditSection( false ); if ( $titleMatches ) { @@ -399,10 +375,8 @@ class SpecialSearch extends SpecialPage { if ( $numTextMatches > 0 && $numTitleMatches > 0 ) { // if no title matches the heading is redundant $out->wrapWikiMsg( "==$1==\n", 'textmatches' ); - } elseif ( $totalRes == 0 ) { - # Don't show the 'no text matches' if we received title matches - # $out->wrapWikiMsg( "==$1==\n", 'notextmatches' ); } + // show interwiki results if any if ( $textMatches->hasInterwikiResults() ) { $out->addHTML( $this->showInterwiki( $textMatches->getInterwikiResults(), $term ) ); @@ -417,49 +391,60 @@ class SpecialSearch extends SpecialPage { if ( $num === 0 ) { if ( $textStatus ) { $out->addHTML( '
' . - htmlspecialchars( $textStatus->getWikiText( 'search-error' ) ) . '
' ); + $textStatus->getMessage( 'search-error' ) . '
' ); } else { $out->wrapWikiMsg( "

\n$1

", array( 'search-nonefound', wfEscapeWikiText( $term ) ) ); - $this->showCreateLink( $t ); + $this->showCreateLink( $title, $num, $titleMatches, $textMatches ); } } $out->addHtml( "" ); - if ( $num || $this->offset ) { + if ( $prevnext ) { $out->addHTML( "

{$prevnext}

\n" ); } - wfRunHooks( 'SpecialSearchResultsAppend', array( $this, $out, $term ) ); - wfProfileOut( __METHOD__ ); } /** - * @param $t Title + * @param Title $title + * @param int $num The number of search results found + * @param null|SearchResultSet $titleMatches Results from title search + * @param null|SearchResultSet $textMatches Results from text search */ - protected function showCreateLink( $t ) { + protected function showCreateLink( $title, $num, $titleMatches, $textMatches ) { // show direct page/create link if applicable // Check DBkey !== '' in case of fragment link only. - if ( is_null( $t ) || $t->getDBkey() === '' ) { + if ( is_null( $title ) || $title->getDBkey() === '' + || ( $titleMatches !== null && $titleMatches->searchContainedSyntax() ) + || ( $textMatches !== null && $textMatches->searchContainedSyntax() ) + ) { // invalid title // preserve the paragraph for margins etc... $this->getOutput()->addHtml( '

' ); + return; } - if ( $t->isKnown() ) { + $linkClass = 'mw-search-createlink'; + if ( $title->isKnown() ) { $messageName = 'searchmenu-exists'; - } elseif ( $t->userCan( 'create', $this->getUser() ) ) { + $linkClass = 'mw-search-exists'; + } elseif ( $title->quickUserCan( 'create', $this->getUser() ) ) { $messageName = 'searchmenu-new'; } else { $messageName = 'searchmenu-new-nocreate'; } - $params = array( $messageName, wfEscapeWikiText( $t->getPrefixedText() ) ); - wfRunHooks( 'SpecialSearchCreateLink', array( $t, &$params ) ); + $params = array( + $messageName, + wfEscapeWikiText( $title->getPrefixedText() ), + Message::numParam( $num ) + ); + wfRunHooks( 'SpecialSearchCreateLink', array( $title, &$params ) ); // Extensions using the hook might still return an empty $messageName if ( $messageName ) { - $this->getOutput()->wrapWikiMsg( "

\n$1

", $params ); + $this->getOutput()->wrapWikiMsg( "

\n$1

", $params ); } else { // preserve the paragraph for margins etc... $this->getOutput()->addHtml( '

' ); @@ -467,7 +452,7 @@ class SpecialSearch extends SpecialPage { } /** - * @param $term string + * @param string $term */ protected function setupPage( $term ) { # Should advanced UI be used? @@ -475,9 +460,10 @@ class SpecialSearch extends SpecialPage { $out = $this->getOutput(); if ( strval( $term ) !== '' ) { $out->setPageTitle( $this->msg( 'searchresults' ) ); - $out->setHTMLTitle( $this->msg( 'pagetitle' )->rawParams( - $this->msg( 'searchresults-title' )->rawParams( $term )->text() - ) ); + $out->setHTMLTitle( $this->msg( 'pagetitle' ) + ->rawParams( $this->msg( 'searchresults-title' )->rawParams( $term )->text() ) + ->inContentLanguage()->text() + ); } // add javascript specific to special:search $out->addModules( 'mediawiki.special.search' ); @@ -487,8 +473,8 @@ class SpecialSearch extends SpecialPage { * Extract "power search" namespace settings from the request object, * returning a list of index numbers to search. * - * @param $request WebRequest - * @return Array + * @param WebRequest $request + * @return array */ protected function powerSearch( &$request ) { $arr = array(); @@ -504,11 +490,10 @@ class SpecialSearch extends SpecialPage { /** * Reconstruct the 'power search' options for links * - * @return Array + * @return array */ protected function powerSearchOptions() { $opt = array(); - $opt['redirs'] = $this->searchRedirects ? 1 : 0; if ( $this->profile !== 'advanced' ) { $opt['profile'] = $this->profile; } else { @@ -516,28 +501,58 @@ class SpecialSearch extends SpecialPage { $opt['ns' . $n] = 1; } } + return $opt + $this->extraParams; } + /** + * Save namespace preferences when we're supposed to + * + * @return bool Whether we wrote something + */ + protected function saveNamespaces() { + $user = $this->getUser(); + $request = $this->getRequest(); + + if ( $user->isLoggedIn() && + $user->matchEditToken( + $request->getVal( 'nsRemember' ), + 'searchnamespace', + $request + ) + ) { + // Reset namespace preferences: namespaces are not searched + // when they're not mentioned in the URL parameters. + foreach ( MWNamespace::getValidNamespaces() as $n ) { + $user->setOption( 'searchNs' . $n, false ); + } + // The request parameters include all the namespaces to be searched. + // Even if they're the same as an existing profile, they're not eaten. + foreach ( $this->namespaces as $n ) { + $user->setOption( 'searchNs' . $n, true ); + } + + $user->saveSettings(); + return true; + } + + return false; + } + /** * Show whole set of results * - * @param $matches SearchResultSet + * @param SearchResultSet $matches * * @return string */ protected function showMatches( &$matches ) { global $wgContLang; - wfProfileIn( __METHOD__ ); + $profile = new ProfileSection( __METHOD__ ); $terms = $wgContLang->convertForSearchResult( $matches->termMatches() ); - $out = ""; - $infoLine = $matches->getInfo(); - if ( !is_null( $infoLine ) ) { - $out .= "\n\n"; - } - $out .= "