X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2Fspecials%2FSpecialSearch.php;h=079130f9825504027c4d9b1f0e16527ab07d148b;hb=127befb1786c6a4279de2f27066a84b3560e9bbd;hp=12a70f9238a60da2dbd4e9fc57366c1f934db2cf;hpb=710b86f0a8e209c0349a71223ec329953c16fbb0;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/specials/SpecialSearch.php b/includes/specials/SpecialSearch.php index 12a70f9238..079130f982 100644 --- a/includes/specials/SpecialSearch.php +++ b/includes/specials/SpecialSearch.php @@ -1,6 +1,8 @@ + * Implements Special:Search + * + * Copyright © 2004 Brion Vibber * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,64 +18,149 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html - */ - -/** - * Run text & title search and display the output + * * @file * @ingroup SpecialPage */ -/** - * Entry point - * - * @param $par String: (default '') - */ -function wfSpecialSearch( $par = '' ) { - global $wgRequest, $wgUser; - // Strip underscores from title parameter; most of the time we'll want - // text form here. But don't strip underscores from actual text params! - $titleParam = str_replace( '_', ' ', $par ); - // Fetch the search term - $search = str_replace( "\n", " ", $wgRequest->getText( 'search', $titleParam ) ); - $searchPage = new SpecialSearch( $wgRequest, $wgUser ); - if( $wgRequest->getVal( 'fulltext' ) - || !is_null( $wgRequest->getVal( 'offset' )) - || !is_null( $wgRequest->getVal( 'searchx' )) ) - { - $searchPage->showResults( $search ); - } else { - $searchPage->goResult( $search ); - } -} - /** * implements Special:Search - Run text & title search and display the output * @ingroup SpecialPage */ -class SpecialSearch { +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...) + * 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 + */ + protected $profile; + function getProfile() { return $this->profile; } + + /// Search engine + protected $searchEngine; + + /// For links + protected $extraParams = array(); + + /// No idea, apparently used by some other classes + protected $mPrefix; + + /** + * @var int + */ + protected $limit, $offset; + + /** + * @var array + */ + protected $namespaces; + function getNamespaces() { return $this->namespaces; } + + /** + * @var bool + */ + protected $searchRedirects; + + /** + * @var string + */ + protected $didYouMeanHtml, $fulltext; + + const NAMESPACES_CURRENT = 'sense'; + + public function __construct() { + parent::__construct( 'Search' ); + } + + /** + * Entry point + * + * @param $par String or null + */ + public function execute( $par ) { + $this->setHeaders(); + $this->outputHeader(); + $out = $this->getOutput(); + $out->allowClickjacking(); + $out->addModuleStyles( 'mediawiki.special' ); + + // Strip underscores from title parameter; most of the time we'll want + // text form here. But don't strip underscores from actual text params! + $titleParam = str_replace( '_', ' ', $par ); + + $request = $this->getRequest(); + + // Fetch the search term + $search = str_replace( "\n", " ", $request->getText( 'search', $titleParam ) ); + + $this->load(); + + if ( $request->getVal( 'fulltext' ) + || !is_null( $request->getVal( 'offset' ) ) + || !is_null( $request->getVal( 'searchx' ) ) ) + { + $this->showResults( $search ); + } else { + $this->goResult( $search ); + } + } /** * Set up basic search parameters from the request and user settings. - * Typically you'll pass $wgRequest and $wgUser. * - * @param $request WebRequest - * @param $user User + * @see tests/phpunit/includes/specials/SpecialSearchTest.php */ - public function __construct( &$request, &$user ) { + public function load() { + $request = $this->getRequest(); list( $this->limit, $this->offset ) = $request->getLimitOffset( 20, 'searchlimit' ); - $this->mPrefix = $request->getVal('prefix', ''); - # Extract requested namespaces - $this->namespaces = $this->powerSearch( $request ); - if( empty( $this->namespaces ) ) { - $this->namespaces = SearchEngine::userNamespaces( $user ); + $this->mPrefix = $request->getVal( 'prefix', '' ); + + $user = $this->getUser(); + + # Extract manually requested namespaces + $nslist = $this->powerSearch( $request ); + if ( !count( $nslist ) ) { + # Fallback to user preference + $nslist = SearchEngine::userNamespaces( $user ); + } + + $profile = null; + if ( !count( $nslist ) ) { + $profile = 'default'; + } + + $profile = $request->getVal( 'profile', $profile ); + $profiles = $this->getSearchProfiles(); + if ( $profile === null ) { + // BC with old request format + $profile = 'advanced'; + foreach( $profiles as $key => $data ) { + if ( $nslist === $data['namespaces'] && $key !== 'advanced') { + $profile = $key; + } + } + $this->namespaces = $nslist; + } elseif ( $profile === 'advanced' ) { + $this->namespaces = $nslist; + } else { + if ( isset( $profiles[$profile]['namespaces'] ) ) { + $this->namespaces = $profiles[$profile]['namespaces']; + } else { + // Unknown profile requested + $profile = 'default'; + $this->namespaces = $profiles['default']['namespaces']; + } } - $this->searchRedirects = $request->getcheck( 'redirs' ) ? true : false; - $this->searchAdvanced = $request->getVal( 'advanced' ); - $this->active = 'advanced'; - $this->sk = $user->getSkin(); + + // 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; } /** @@ -82,19 +169,24 @@ class SpecialSearch { * @param $term String */ public function goResult( $term ) { - global $wgOut; $this->setupPage( $term ); # Try to go to page as entered. $t = Title::newFromText( $term ); # If the string cannot be used to create a title if( is_null( $t ) ) { - return $this->showResults( $term ); + $this->showResults( $term ); + return; } # If there's an exact or very near match, jump right there. $t = SearchEngine::getNearMatch( $term ); + + if ( !wfRunHooks( 'SpecialSearchGo', array( &$t, &$term ) ) ) { + # Hook requested termination + return; + } + if( !is_null( $t ) ) { - wfRunHooks( 'SpecialSearchGomatch', array( &$t ) ); - $wgOut->redirect( $t->getFullURL() ); + $this->getOutput()->redirect( $t->getFullURL() ); return; } # No match, generate an edit URL @@ -102,54 +194,55 @@ class SpecialSearch { 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 ) { - $wgOut->redirect( $t->getFullURL( array( 'action' => 'edit' ) ) ); + $this->getOutput()->redirect( $t->getFullURL( array( 'action' => 'edit' ) ) ); return; } } - return $this->showResults( $term ); + $this->showResults( $term ); } /** * @param $term String */ public function showResults( $term ) { - global $wgOut, $wgUser, $wgDisableTextSearch, $wgContLang, $wgScript; + global $wgDisableTextSearch, $wgSearchForwardUrl, $wgContLang, $wgScript; wfProfileIn( __METHOD__ ); - $sk = $wgUser->getSkin(); - - $this->searchEngine = SearchEngine::create(); - $search =& $this->searchEngine; + $search = $this->getSearchEngine(); $search->setLimitOffset( $this->limit, $this->offset ); $search->setNamespaces( $this->namespaces ); - $search->showRedirects = $this->searchRedirects; + $search->showRedirects = $this->searchRedirects; // BC + $search->setFeatureData( 'list-redirects', $this->searchRedirects ); $search->prefix = $this->mPrefix; $term = $search->transformSearchTerm($term); + wfRunHooks( 'SpecialSearchSetupEngine', array( $this, $this->profile, $search ) ); + $this->setupPage( $term ); - if( $wgDisableTextSearch ) { - global $wgSearchForwardUrl; - if( $wgSearchForwardUrl ) { + $out = $this->getOutput(); + + if ( $wgDisableTextSearch ) { + if ( $wgSearchForwardUrl ) { $url = str_replace( '$1', urlencode( $term ), $wgSearchForwardUrl ); - $wgOut->redirect( $url ); - wfProfileOut( __METHOD__ ); - return; + $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() ) . + $this->msg( 'googlesearch' )->rawParams( + htmlspecialchars( $term ), + 'UTF-8', + $this->msg( 'searchbutton' )->escaped() + )->text() . + Xml::closeElement( 'fieldset' ) + ); } - global $wgInputEncoding; - $wgOut->addHTML( - Xml::openElement( 'fieldset' ) . - Xml::element( 'legend', null, wfMsg( 'search-external' ) ) . - Xml::element( 'p', array( 'class' => 'mw-searchdisabled' ), wfMsg( 'searchdisabled' ) ) . - wfMsg( 'googlesearch', - htmlspecialchars( $term ), - htmlspecialchars( $wgInputEncoding ), - htmlspecialchars( wfMsg( 'searchbutton' ) ) - ) . - Xml::closeElement( 'fieldset' ) - ); wfProfileOut( __METHOD__ ); return; } @@ -160,8 +253,9 @@ class SpecialSearch { $rewritten = $search->replacePrefixes($term); $titleMatches = $search->searchTitle( $rewritten ); - if( !($titleMatches instanceof SearchResultTooMany)) + if( !( $titleMatches instanceof SearchResultTooMany ) ) { $textMatches = $search->searchText( $rewritten ); + } // did you mean... suggestions if( $textMatches && $textMatches->hasSuggestion() ) { @@ -170,8 +264,9 @@ class SpecialSearch { # mirror Go/Search behaviour of original request .. $didYouMeanParams = array( 'search' => $textMatches->getSuggestionQuery() ); - if($this->fulltext != null) + if( $this->fulltext != null ) { $didYouMeanParams['fulltext'] = $this->fulltext; + } $stParams = array_merge( $didYouMeanParams, @@ -180,33 +275,34 @@ class SpecialSearch { $suggestionSnippet = $textMatches->getSuggestionSnippet(); - if( $suggestionSnippet == '' ) + if( $suggestionSnippet == '' ) { $suggestionSnippet = null; + } - $suggestLink = $sk->linkKnown( + $suggestLink = Linker::linkKnown( $st, $suggestionSnippet, array(), $stParams ); - $this->didYouMeanHtml = '
'.wfMsg('search-suggest',$suggestLink).'
'; + $this->didYouMeanHtml = '
' . $this->msg( 'search-suggest' )->rawParams( $suggestLink )->text() . '
'; } // start rendering the page - $wgOut->addHtml( + $out->addHtml( Xml::openElement( 'form', array( - 'id' => ( $this->searchAdvanced ? 'powersearch' : 'search' ), + 'id' => ( $this->profile === 'advanced' ? 'powersearch' : 'search' ), 'method' => 'get', 'action' => $wgScript ) ) ); - $wgOut->addHtml( + $out->addHtml( Xml::openElement( 'table', array( 'id'=>'mw-search-top-table', 'border'=>0, 'cellpadding'=>0, 'cellspacing'=>0 ) ) . Xml::openElement( 'tr' ) . - Xml::openElement( 'td' ) . "\n" . + Xml::openElement( 'td' ) . "\n" . $this->shortDialog( $term ) . Xml::closeElement('td') . Xml::closeElement('tr') . @@ -215,19 +311,16 @@ class SpecialSearch { // Sometimes the search engine knows there are too many hits if( $titleMatches instanceof SearchResultTooMany ) { - $wgOut->addWikiText( '==' . wfMsg( 'toomanymatches' ) . "==\n" ); + $out->wrapWikiMsg( "==$1==\n", 'toomanymatches' ); wfProfileOut( __METHOD__ ); return; } $filePrefix = $wgContLang->getFormattedNsText(NS_FILE).':'; if( trim( $term ) === '' || $filePrefix === trim( $term ) ) { - $wgOut->addHTML( $this->searchFocus() ); - $wgOut->addHTML( $this->formHeader($term, 0, 0)); - if( $this->searchAdvanced ) { - $wgOut->addHTML( $this->powerSearchBox( $term ) ); - } - $wgOut->addHTML( '' ); + $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; @@ -238,7 +331,7 @@ class SpecialSearch { $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; @@ -251,35 +344,34 @@ class SpecialSearch { $totalRes += $titleMatches->getTotalHits(); if($textMatches && !is_null( $textMatches->getTotalHits() )) $totalRes += $textMatches->getTotalHits(); - + // show number of results and current offset - $wgOut->addHTML( $this->formHeader($term, $num, $totalRes)); - if( $this->searchAdvanced ) { - $wgOut->addHTML( $this->powerSearchBox( $term ) ); - } - - $wgOut->addHtml( Xml::closeElement( 'form' ) ); - $wgOut->addHtml( "
" ); + $out->addHTML( $this->formHeader( $term, $num, $totalRes ) ); + $out->addHtml( $this->getProfileForm( $this->profile, $term ) ); + + + $out->addHtml( Xml::closeElement( 'form' ) ); + $out->addHtml( "
" ); // prev/next links if( $num || $this->offset ) { // Show the create link ahead $this->showCreateLink( $t ); - $prevnext = wfViewPrevNext( $this->offset, $this->limit, - SpecialPage::getTitleFor( 'Search' ), - wfArrayToCGI( $this->powerSearchOptions(), array( 'search' => $term ) ), + $prevnext = $this->getLanguage()->viewPrevNext( $this->getTitle(), $this->offset, $this->limit, + $this->powerSearchOptions() + array( 'search' => $term ), max( $titleMatchesNum, $textMatchesNum ) < $this->limit ); - //$wgOut->addHTML( "

{$prevnext}

\n" ); + //$out->addHTML( "

{$prevnext}

\n" ); wfRunHooks( 'SpecialSearchResults', array( $term, &$titleMatches, &$textMatches ) ); } else { wfRunHooks( 'SpecialSearchNoResults', array( $term ) ); - } + } + $out->parserOptions()->setEditSection( false ); if( $titleMatches ) { if( $numTitleMatches > 0 ) { - $wgOut->wrapWikiMsg( "==$1==\n", 'titlematches' ); - $wgOut->addHTML( $this->showMatches( $titleMatches ) ); + $out->wrapWikiMsg( "==$1==\n", 'titlematches' ); + $out->addHTML( $this->showMatches( $titleMatches ) ); } $titleMatches->free(); } @@ -287,90 +379,80 @@ class SpecialSearch { // output appropriate heading if( $numTextMatches > 0 && $numTitleMatches > 0 ) { // if no title matches the heading is redundant - $wgOut->wrapWikiMsg( "==$1==\n", 'textmatches' ); + $out->wrapWikiMsg( "==$1==\n", 'textmatches' ); } elseif( $totalRes == 0 ) { # Don't show the 'no text matches' if we received title matches - # $wgOut->wrapWikiMsg( "==$1==\n", 'notextmatches' ); + # $out->wrapWikiMsg( "==$1==\n", 'notextmatches' ); } // show interwiki results if any if( $textMatches->hasInterwikiResults() ) { - $wgOut->addHTML( $this->showInterwiki( $textMatches->getInterwikiResults(), $term ) ); + $out->addHTML( $this->showInterwiki( $textMatches->getInterwikiResults(), $term ) ); } // show results if( $numTextMatches > 0 ) { - $wgOut->addHTML( $this->showMatches( $textMatches ) ); + $out->addHTML( $this->showMatches( $textMatches ) ); } $textMatches->free(); } if( $num === 0 ) { - $wgOut->addWikiMsg( 'search-nonefound', wfEscapeWikiText( $term ) ); + $out->wrapWikiMsg( "

\n$1

", array( 'search-nonefound', wfEscapeWikiText( $term ) ) ); $this->showCreateLink( $t ); } - $wgOut->addHtml( "
" ); - if( $num === 0 ) { - $wgOut->addHTML( $this->searchFocus() ); - } + $out->addHtml( "
" ); if( $num || $this->offset ) { - $wgOut->addHTML( "

{$prevnext}

\n" ); + $out->addHTML( "

{$prevnext}

\n" ); } wfProfileOut( __METHOD__ ); } - + + /** + * @param $t Title + */ protected function showCreateLink( $t ) { - global $wgOut; - // show direct page/create link if applicable - $messageName = null; - if( !is_null($t) ) { - if( $t->isKnown() ) { - $messageName = 'searchmenu-exists'; - } elseif( $t->userCan( 'create' ) ) { - $messageName = 'searchmenu-new'; - } else { - $messageName = 'searchmenu-new-nocreate'; - } - } + + // Check DBkey !== '' in case of fragment link only. + if( is_null( $t ) || $t->getDBkey() === '' ) { + // invalid title + // preserve the paragraph for margins etc... + $this->getOutput()->addHtml( '

' ); + return; + } + + if( $t->isKnown() ) { + $messageName = 'searchmenu-exists'; + } elseif( $t->userCan( 'create' ) ) { + $messageName = 'searchmenu-new'; + } else { + $messageName = 'searchmenu-new-nocreate'; + } + $params = array( $messageName, wfEscapeWikiText( $t->getPrefixedText() ) ); + wfRunHooks( 'SpecialSearchCreateLink', array( $t, &$params ) ); + + // Extensions using the hook might still return an empty $messageName if( $messageName ) { - $wgOut->addWikiMsg( $messageName, wfEscapeWikiText( $t->getPrefixedText() ) ); + $this->getOutput()->wrapWikiMsg( "

\n$1

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

' ); + $this->getOutput()->addHtml( '

' ); } } /** - * + * @param $term string */ protected function setupPage( $term ) { - global $wgOut; - // Figure out the active search profile header - $nsAllSet = array_keys( SearchEngine::searchableNamespaces() ); - if( $this->searchAdvanced ) - $this->active = 'advanced'; - else { - $profiles = $this->getSearchProfiles(); - - foreach( $profiles as $key => $data ) { - if ( $this->namespaces == $data['namespaces'] && $key != 'advanced') - $this->active = $key; - } - - } # Should advanced UI be used? - $this->searchAdvanced = ($this->active === 'advanced'); - if( !empty( $term ) ) { - $wgOut->setPageTitle( wfMsg( 'searchresults') ); - $wgOut->setHTMLTitle( wfMsg( 'pagetitle', wfMsg( 'searchresults-title', $term ) ) ); + $this->searchAdvanced = ($this->profile === 'advanced'); + $out = $this->getOutput(); + if( strval( $term ) !== '' ) { + $out->setPageTitle( $this->msg( 'searchresults' ) ); + $out->setHTMLTitle( $this->msg( 'pagetitle', $this->msg( 'searchresults-title', $term )->plain() ) ); } - $wgOut->setArticleRelated( false ); - $wgOut->setRobotPolicy( 'noindex,nofollow' ); // add javascript specific to special:search - $wgOut->addScriptFile( 'search.js' ); - - // Bug #16886: Sister projects box moves down the first extract on IE7 - $wgOut->addStyle( 'common/IE70Fixes.css', 'screen', 'IE 7' ); + $out->addModules( 'mediawiki.special.search' ); } /** @@ -387,6 +469,7 @@ class SpecialSearch { $arr[] = $ns; } } + return $arr; } @@ -397,20 +480,23 @@ class SpecialSearch { */ protected function powerSearchOptions() { $opt = array(); - foreach( $this->namespaces as $n ) { - $opt['ns' . $n] = 1; - } $opt['redirs'] = $this->searchRedirects ? 1 : 0; - if( $this->searchAdvanced ) { - $opt['advanced'] = $this->searchAdvanced; + if( $this->profile !== 'advanced' ) { + $opt['profile'] = $this->profile; + } else { + foreach( $this->namespaces as $n ) { + $opt['ns' . $n] = 1; + } } - return $opt; + return $opt + $this->extraParams; } /** * Show whole set of results * * @param $matches SearchResultSet + * + * @return string */ protected function showMatches( &$matches ) { global $wgContLang; @@ -423,10 +509,11 @@ class SpecialSearch { if( !is_null($infoLine) ) { $out .= "\n\n"; } - $off = $this->offset + 1; $out .= "\n"; @@ -441,9 +528,10 @@ class SpecialSearch { * * @param $result SearchResult * @param $terms Array: terms to highlight + * + * @return string */ protected function showHit( $result, $terms ) { - global $wgLang, $wgUser; wfProfileIn( __METHOD__ ); if( $result->isBrokenTitle() ) { @@ -451,20 +539,19 @@ class SpecialSearch { return "\n"; } - $sk = $wgUser->getSkin(); $t = $result->getTitle(); $titleSnippet = $result->getTitleSnippet($terms); if( $titleSnippet == '' ) $titleSnippet = null; - + $link_t = clone $t; - + wfRunHooks( 'ShowSearchHitTitle', array( &$link_t, &$titleSnippet, $result, $terms, $this ) ); - $link = $this->sk->linkKnown( + $link = Linker::linkKnown( $link_t, $titleSnippet ); @@ -472,7 +559,7 @@ class SpecialSearch { //If page content is not readable, just return the title. //This is not quite safe, but better than showing excerpts from non-readable pages //Note that hiding the entry entirely would screw up paging. - if( !$t->userCanRead() ) { + if( !$t->userCan( 'read' ) ) { wfProfileOut( __METHOD__ ); return "
  • {$link}
  • \n"; } @@ -497,43 +584,35 @@ class SpecialSearch { $redirectText = null; $redirect = "" . - wfMsg( - 'search-redirect', - $this->sk->linkKnown( - $redirectTitle, - $redirectText - ) - ) . + $this->msg( 'search-redirect' )->rawParams( + Linker::linkKnown( $redirectTitle, $redirectText ) )->text() . ""; } $section = ''; - if( !is_null($sectionTitle) ) { if( $sectionText == '' ) $sectionText = null; $section = "" . - wfMsg( - 'search-section', $this->sk->linkKnown( - $sectionTitle, - $sectionText - ) - ) . + $this->msg( 'search-section' )->rawParams( + Linker::linkKnown( $sectionTitle, $sectionText ) )->text() . ""; } // format text extract $extract = "
    ".$result->getTextSnippet($terms)."
    "; + $lang = $this->getLanguage(); + // format score if( is_null( $result->getScore() ) ) { // Search engine doesn't report scoring info $score = ''; } else { $percent = sprintf( '%2.1f', $result->getScore() * 100 ); - $score = wfMsg( 'search-result-score', $wgLang->formatNum( $percent ) ) + $score = $this->msg( 'search-result-score' )->numParams( $percent )->text() . ' - '; } @@ -541,25 +620,17 @@ class SpecialSearch { $byteSize = $result->getByteSize(); $wordCount = $result->getWordCount(); $timestamp = $result->getTimestamp(); - $size = wfMsgExt( - 'search-result-size', - array( 'parsemag', 'escape' ), - $this->sk->formatSize( $byteSize ), - $wgLang->formatNum( $wordCount ) - ); + $size = $this->msg( 'search-result-size', $lang->formatSize( $byteSize ) ) + ->numParams( $wordCount )->escaped(); if( $t->getNamespace() == NS_CATEGORY ) { $cat = Category::newFromTitle( $t ); - $size = wfMsgExt( - 'search-result-category-size', - array( 'parsemag', 'escape' ), - $wgLang->formatNum( $cat->getPageCount() ), - $wgLang->formatNum( $cat->getSubcatCount() ), - $wgLang->formatNum( $cat->getFileCount() ) - ); + $size = $this->msg( 'search-result-category-size' ) + ->numParams( $cat->getPageCount(), $cat->getSubcatCount(), $cat->getFileCount() ) + ->escaped(); } - $date = $wgLang->timeanddate( $timestamp ); + $date = $lang->userTimeAndDate( $timestamp, $this->getUser() ); // link to related articles if supported $related = ''; @@ -568,14 +639,15 @@ class SpecialSearch { $stParams = array_merge( $this->powerSearchOptions(), array( - 'search' => wfMsgForContent( 'searchrelated' ) . ':' . $t->getPrefixedText(), - 'fulltext' => wfMsg( 'search' ) + 'search' => $this->msg( 'searchrelated' )->inContentLanguage()->text() . + ':' . $t->getPrefixedText(), + 'fulltext' => $this->msg( 'search' )->text() ) ); - $related = ' -- ' . $sk->linkKnown( + $related = ' -- ' . Linker::linkKnown( $st, - wfMsg('search-relatedarticle'), + $this->msg( 'search-relatedarticle' )->text(), array(), $stParams ); @@ -587,7 +659,7 @@ class SpecialSearch { if( $img ) { $thumb = $img->transform( array( 'width' => 120, 'height' => 120 ) ); if( $thumb ) { - $desc = $img->getShortDesc(); + $desc = $this->msg( 'parentheses' )->rawParams( $img->getShortDesc() )->escaped(); wfProfileOut( __METHOD__ ); // Float doesn't seem to interact well with the bullets. // Table messes up vertical alignment of the bullets. @@ -622,6 +694,8 @@ class SpecialSearch { * * @param $matches SearchResultSet * @param $query String + * + * @return string */ protected function showInterwiki( &$matches, $query ) { global $wgContLang; @@ -629,13 +703,12 @@ class SpecialSearch { $terms = $wgContLang->convertForSearchResult( $matches->termMatches() ); $out = "
    ". - wfMsg('search-interwiki-caption')."
    \n"; - $off = $this->offset + 1; + $this->msg( 'search-interwiki-caption' )->text() . "
    \n"; $out .= "\n"; @@ -664,6 +739,8 @@ class SpecialSearch { * @param $terms Array * @param $query String * @param $customCaptions Array: iw prefix -> caption + * + * @return string */ protected function showInterwikiHit( $result, $lastInterwiki, $terms, $query, $customCaptions) { wfProfileIn( __METHOD__ ); @@ -680,7 +757,7 @@ class SpecialSearch { if( $titleSnippet == '' ) $titleSnippet = null; - $link = $this->sk->linkKnown( + $link = Linker::linkKnown( $t, $titleSnippet ); @@ -694,33 +771,28 @@ class SpecialSearch { $redirectText = null; $redirect = "" . - wfMsg( - 'search-redirect', - $this->sk->linkKnown( - $redirectTitle, - $redirectText - ) - ) . + $this->msg( 'search-redirect' )->rawParams( + Linker::linkKnown( $redirectTitle, $redirectText ) )->text() . ""; } $out = ""; // display project name if(is_null($lastInterwiki) || $lastInterwiki != $t->getInterwiki()) { - if( key_exists($t->getInterwiki(),$customCaptions) ) + if( array_key_exists($t->getInterwiki(),$customCaptions) ) { // captions from 'search-interwiki-custom' $caption = $customCaptions[$t->getInterwiki()]; - else{ + } else { // default is to show the hostname of the other wiki which might suck // if there are many wikis on one hostname - $parsed = parse_url($t->getFullURL()); - $caption = wfMsg('search-interwiki-default', $parsed['host']); + $parsed = wfParseUrl( $t->getFullURL() ); + $caption = $this->msg( 'search-interwiki-default', $parsed['host'] )->text(); } // "more results" link (special page stuff could be localized, but we might not know target lang) $searchTitle = Title::newFromText($t->getInterwiki().":Special:Search"); - $searchLink = $this->sk->linkKnown( + $searchLink = Linker::linkKnown( $searchTitle, - wfMsg('search-interwiki-more'), + $this->msg( 'search-interwiki-more' )->text(), array(), array( 'search' => $query, @@ -736,14 +808,34 @@ class SpecialSearch { return $out; } + /** + * @param $profile + * @param $term + * @return String + */ + protected function getProfileForm( $profile, $term ) { + // Hidden stuff + $opts = array(); + $opts['redirs'] = $this->searchRedirects; + $opts['profile'] = $this->profile; + + if ( $profile === 'advanced' ) { + return $this->powerSearchBox( $term, $opts ); + } else { + $form = ''; + wfRunHooks( 'SpecialSearchProfileForm', array( $this, &$form, $profile, $term, $opts ) ); + return $form; + } + } /** - * Generates the power search box at bottom of [[Special:Search]] + * Generates the power search box at [[Special:Search]] * * @param $term String: search term + * @param $opts array * @return String: HTML form */ - protected function powerSearchBox( $term ) { + protected function powerSearchBox( $term, $opts ) { // Groups namespaces into rows according to subject $rows = array(); foreach( SearchEngine::searchableNamespaces() as $namespace => $name ) { @@ -753,7 +845,7 @@ class SpecialSearch { } $name = str_replace( '_', ' ', $name ); if( $name == '' ) { - $name = wfMsg( 'blanknamespace' ); + $name = $this->msg( 'blanknamespace' )->text(); } $rows[$subject] .= Xml::openElement( @@ -769,7 +861,7 @@ class SpecialSearch { } $rows = array_values( $rows ); $numRows = count( $rows ); - + // Lays out namespaces in multiple floating two-column tables so they'll // be arranged nicely while still accommodating different screen widths $namespaceTables = ''; @@ -783,15 +875,21 @@ class SpecialSearch { } $namespaceTables .= Xml::closeElement( 'table' ); } + + $showSections = array( 'namespaceTables' => $namespaceTables ); + // Show redirects check only if backend supports it - $redirects = ''; - if( $this->searchEngine->acceptListRedirects() ) { - $redirects = - Xml::check( - 'redirs', $this->searchRedirects, array( 'value' => '1', 'id' => 'redirs' ) - ) . - ' ' . - Xml::label( wfMsg( 'powersearch-redir' ), 'redirs' ); + if( $this->getSearchEngine()->supports( 'list-redirects' ) ) { + $showSections['redirects'] = + Xml::checkLabel( $this->msg( 'powersearch-redir' )->text(), 'redirs', 'redirs', $this->searchRedirects ); + } + + wfRunHooks( 'SpecialSearchPowerBox', array( &$showSections, $term, $opts ) ); + + $hidden = ''; + unset( $opts['redirs'] ); + foreach( $opts as $key => $value ) { + $hidden .= Html::hidden( $key, $value ); } // Return final output return @@ -799,19 +897,18 @@ class SpecialSearch { 'fieldset', array( 'id' => 'mw-searchoptions', 'style' => 'margin:0em;' ) ) . - Xml::element( 'legend', null, wfMsg('powersearch-legend') ) . - Xml::tags( 'h4', null, wfMsgExt( 'powersearch-ns', array( 'parseinline' ) ) ) . + Xml::element( 'legend', null, $this->msg('powersearch-legend' )->text() ) . + Xml::tags( 'h4', null, $this->msg( 'powersearch-ns' )->parse() ) . Xml::tags( 'div', array( 'id' => 'mw-search-togglebox' ), - Xml::label( wfMsg( 'powersearch-togglelabel' ), 'mw-search-togglelabel' ) . + Xml::label( $this->msg( 'powersearch-togglelabel' )->text(), 'mw-search-togglelabel' ) . Xml::element( 'input', array( 'type'=>'button', 'id' => 'mw-search-toggleall', - 'onclick' => 'mwToggleSearchCheckboxes("all");', - 'value' => wfMsg( 'powersearch-toggleall' ) + 'value' => $this->msg( 'powersearch-toggleall' )->text() ) ) . Xml::element( @@ -819,33 +916,23 @@ class SpecialSearch { array( 'type'=>'button', 'id' => 'mw-search-togglenone', - 'onclick' => 'mwToggleSearchCheckboxes("none");', - 'value' => wfMsg( 'powersearch-togglenone' ) + 'value' => $this->msg( 'powersearch-togglenone' )->text() ) ) ) . Xml::element( 'div', array( 'class' => 'divider' ), '', false ) . - $namespaceTables . - Xml::element( 'div', array( 'class' => 'divider' ), '', false ) . - $redirects . - Xml::hidden( 'title', SpecialPage::getTitleFor( 'Search' )->getPrefixedText() ) . - Xml::hidden( 'advanced', $this->searchAdvanced ) . - Xml::hidden( 'fulltext', 'Advanced search' ) . + implode( Xml::element( 'div', array( 'class' => 'divider' ), '', false ), $showSections ) . + $hidden . Xml::closeElement( 'fieldset' ); } - protected function searchFocus() { - $id = $this->searchAdvanced ? 'powerSearchText' : 'searchText'; - return Html::inlineScript( - "hookEvent(\"load\", function() {" . - "document.getElementById('$id').focus();" . - "});" ); - } - + /** + * @return array + */ protected function getSearchProfiles() { // Builds list of Search Types (profiles) $nsAllSet = array_keys( SearchEngine::searchableNamespaces() ); - + $profiles = array( 'default' => array( 'message' => 'searchprofile-articles', @@ -876,25 +963,29 @@ class SpecialSearch { 'advanced' => array( 'message' => 'searchprofile-advanced', 'tooltip' => 'searchprofile-advanced-tooltip', - 'namespaces' => $this->namespaces, - 'parameters' => array( 'advanced' => 1 ), + 'namespaces' => self::NAMESPACES_CURRENT, ) ); - + wfRunHooks( 'SpecialSearchProfiles', array( &$profiles ) ); - foreach( $profiles as $key => &$data ) { - sort($data['namespaces']); + foreach( $profiles as &$data ) { + if ( !is_array( $data['namespaces'] ) ) continue; + sort( $data['namespaces'] ); } - + return $profiles; } + /** + * @param $term + * @param $resultsShown + * @param $totalNum + * @return string + */ protected function formHeader( $term, $resultsShown, $totalNum ) { - global $wgLang; - $out = Xml::openElement('div', array( 'class' => 'mw-search-formheader' ) ); - + $bareterm = $term; if( $this->startsWithImage( $term ) ) { // Deletes prefixes @@ -902,24 +993,30 @@ class SpecialSearch { } $profiles = $this->getSearchProfiles(); - + $lang = $this->getLanguage(); + // Outputs XML for Search Types $out .= Xml::openElement( 'div', array( 'class' => 'search-types' ) ); $out .= Xml::openElement( 'ul' ); foreach ( $profiles as $id => $profile ) { + if ( !isset( $profile['parameters'] ) ) { + $profile['parameters'] = array(); + } + $profile['parameters']['profile'] = $id; + $tooltipParam = isset( $profile['namespace-messages'] ) ? - $wgLang->commaList( $profile['namespace-messages'] ) : null; + $lang->commaList( $profile['namespace-messages'] ) : null; $out .= Xml::tags( 'li', array( - 'class' => $this->active == $id ? 'current' : 'normal' + 'class' => $this->profile === $id ? 'current' : 'normal' ), $this->makeSearchLink( $bareterm, - $profile['namespaces'], - wfMsg( $profile['message'] ), - wfMsg( $profile['tooltip'], $tooltipParam ), - isset( $profile['parameters'] ) ? $profile['parameters'] : array() + array(), + $this->msg( $profile['message'] )->text(), + $this->msg( $profile['tooltip'], $tooltipParam )->text(), + $profile['parameters'] ) ); } @@ -929,74 +1026,70 @@ class SpecialSearch { // Results-info if ( $resultsShown > 0 ) { if ( $totalNum > 0 ){ - $top = wfMsgExt( 'showingresultsheader', array( 'parseinline' ), - $wgLang->formatNum( $this->offset + 1 ), - $wgLang->formatNum( $this->offset + $resultsShown ), - $wgLang->formatNum( $totalNum ), - wfEscapeWikiText( $term ), - $wgLang->formatNum( $resultsShown ) - ); + $top = $this->msg( 'showingresultsheader' ) + ->numParams( $this->offset + 1, $this->offset + $resultsShown, $totalNum ) + ->params( wfEscapeWikiText( $term ) ) + ->numParams( $resultsShown ) + ->parse(); } elseif ( $resultsShown >= $this->limit ) { - $top = wfShowingResults( $this->offset, $this->limit ); + $top = $this->msg( 'showingresults' ) + ->numParams( $this->limit, $this->offset + 1 ) + ->parse(); } else { - $top = wfShowingResultsNum( $this->offset, $this->limit, $resultsShown ); + $top = $this->msg( 'showingresultsnum' ) + ->numParams( $this->limit, $this->offset + 1, $resultsShown ) + ->parse(); } $out .= Xml::tags( 'div', array( 'class' => 'results-info' ), Xml::tags( 'ul', null, Xml::tags( 'li', null, $top ) ) ); } - + $out .= Xml::element( 'div', array( 'style' => 'clear:both' ), '', false ); $out .= Xml::closeElement('div'); - - // Adds hidden namespace fields - if ( !$this->searchAdvanced ) { - foreach( $this->namespaces as $ns ) { - $out .= Xml::hidden( "ns{$ns}", '1' ); - } - } - + return $out; } + /** + * @param $term string + * @return string + */ protected function shortDialog( $term ) { - $searchTitle = SpecialPage::getTitleFor( 'Search' ); - $out = Html::hidden( 'title', $searchTitle->getPrefixedText() ) . "\n"; - // Keep redirect setting - $out .= Html::hidden( "redirs", (int)$this->searchRedirects ) . "\n"; + $out = Html::hidden( 'title', $this->getTitle()->getPrefixedText() ); + $out .= Html::hidden( 'profile', $this->profile ) . "\n"; // Term box $out .= Html::input( 'search', $term, 'search', array( - 'id' => $this->searchAdvanced ? 'powerSearchText' : 'searchText', + 'id' => $this->profile === 'advanced' ? 'powerSearchText' : 'searchText', 'size' => '50', 'autofocus' ) ) . "\n"; $out .= Html::hidden( 'fulltext', 'Search' ) . "\n"; - $out .= Xml::submitButton( wfMsg( 'searchbutton' ) ) . "\n"; - return $out . $this->didYouMeanHtml; + $out .= Xml::submitButton( $this->msg( 'searchbutton' )->text() ) . "\n"; + return $out . $this->didYouMeanHtml; } /** * Make a search link with some target namespaces * * @param $term String - * @param $namespaces Array + * @param $namespaces Array ignored * @param $label String: link's text * @param $tooltip String: link's tooltip * @param $params Array: query string parameters * @return String: HTML fragment */ - protected function makeSearchLink( $term, $namespaces, $label, $tooltip, $params=array() ) { + protected function makeSearchLink( $term, $namespaces, $label, $tooltip, $params = array() ) { $opt = $params; foreach( $namespaces as $n ) { $opt['ns' . $n] = 1; } - $opt['redirs'] = $this->searchRedirects ? 1 : 0; + $opt['redirs'] = $this->searchRedirects; - $st = SpecialPage::getTitleFor( 'Search' ); $stParams = array_merge( array( 'search' => $term, - 'fulltext' => wfMsg( 'search' ) + 'fulltext' => $this->msg( 'search' )->text() ), $opt ); @@ -1004,10 +1097,8 @@ class SpecialSearch { return Xml::element( 'a', array( - 'href' => $st->getLocalURL( $stParams ), - 'title' => $tooltip, - 'onmousedown' => 'mwSearchHeaderClick(this);', - 'onkeydown' => 'mwSearchHeaderClick(this);'), + 'href' => $this->getTitle()->getLocalURL( $stParams ), + 'title' => $tooltip), $label ); } @@ -1027,7 +1118,7 @@ class SpecialSearch { } return false; } - + /** * Check if query starts with all: prefix * @@ -1036,13 +1127,38 @@ class SpecialSearch { */ protected function startsWithAll( $term ) { - $allkeyword = wfMsgForContent('searchall'); - + $allkeyword = $this->msg( 'searchall' )->inContentLanguage()->text(); + $p = explode( ':', $term ); if( count( $p ) > 1 ) { return $p[0] == $allkeyword; } return false; } -} + /** + * @since 1.18 + * + * @return SearchEngine + */ + public function getSearchEngine() { + if ( $this->searchEngine === null ) { + $this->searchEngine = SearchEngine::create(); + } + return $this->searchEngine; + } + + /** + * Users of hook SpecialSearchSetupEngine can use this to + * add more params to links to not lose selection when + * user navigates search results. + * @since 1.18 + * + * @param $key + * @param $value + */ + public function setExtraParam( $key, $value ) { + $this->extraParams[$key] = $value; + } + +}