# http://www.mediawiki.org/ # # 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 # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # 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 ); $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, 'search' ); } else { $searchPage->goResult( $search ); } } /** * implements Special:Search - Run text & title search and display the output * @ingroup SpecialPage */ class SpecialSearch { /** * Set up basic search parameters from the request and user settings. * Typically you'll pass $wgRequest and $wgUser. * * @param WebRequest $request * @param User $user * @public */ function SpecialSearch( &$request, &$user ) { list( $this->limit, $this->offset ) = $request->getLimitOffset( 20, 'searchlimit' ); $this->namespaces = $this->powerSearch( $request ); if( empty( $this->namespaces ) ) { $this->namespaces = SearchEngine::userNamespaces( $user ); } $this->searchRedirects = $request->getcheck( 'redirs' ) ? true : false; $this->searchAdvanced = $request->getVal('advanced'); } /** * If an exact title match can be found, jump straight ahead to it. * @param string $term * @public */ function goResult( $term ) { global $wgOut, $wgGoToEdit; $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 ); } # If there's an exact or very near match, jump right there. $t = SearchEngine::getNearMatch( $term ); if( !is_null( $t ) ) { $wgOut->redirect( $t->getFullURL() ); return; } # No match, generate an edit URL $t = Title::newFromText( $term ); if( ! is_null( $t ) ) { wfRunHooks( 'SpecialSearchNogomatch', array( &$t ) ); # If the feature is enabled, go straight to the edit page if( $wgGoToEdit ) { $wgOut->redirect( $t->getFullURL( 'action=edit' ) ); return; } } return $this->showResults( $term ); } /** * @param string $term * @public */ function showResults( $term ) { $fname = 'SpecialSearch::showResults'; wfProfileIn( $fname ); global $wgOut, $wgUser; $sk = $wgUser->getSkin(); $this->setupPage( $term ); $this->searchEngine = SearchEngine::create(); $t = Title::newFromText( $term ); $wgOut->addHtml( Xml::openElement( 'table', array( 'border'=>0, 'cellpadding'=>0, 'cellspacing'=>0 ) ) . Xml::openElement( 'tr' ) . Xml::openElement( 'td' ) . "\n" ); if( $this->searchAdvanced ) { $wgOut->addHTML( $this->powerSearchBox( $term ) ); $showMenu = false; } else { $wgOut->addHTML( $this->shortDialog( $term ) ); $showMenu = true; } $wgOut->addHtml( Xml::closeElement('td') . Xml::closeElement('tr') . Xml::closeElement('table') ); if( '' === trim( $term ) ) { // Empty query -- straight view of search form wfProfileOut( $fname ); return; } global $wgDisableTextSearch; if( $wgDisableTextSearch ) { global $wgSearchForwardUrl; if( $wgSearchForwardUrl ) { $url = str_replace( '$1', urlencode( $term ), $wgSearchForwardUrl ); $wgOut->redirect( $url ); return; } 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( $fname ); return; } $search =& $this->searchEngine; $search->setLimitOffset( $this->limit, $this->offset ); $search->setNamespaces( $this->namespaces ); $search->showRedirects = $this->searchRedirects; $rewritten = $search->replacePrefixes($term); $titleMatches = $search->searchTitle( $rewritten ); // Sometimes the search engine knows there are too many hits if( $titleMatches instanceof SearchResultTooMany ) { $wgOut->addWikiText( '==' . wfMsg( 'toomanymatches' ) . "==\n" ); wfProfileOut( $fname ); return; } $textMatches = $search->searchText( $rewritten ); // did you mean... suggestions if( $textMatches && $textMatches->hasSuggestion() ) { $st = SpecialPage::getTitleFor( 'Search' ); $stParams = wfArrayToCGI( array( 'search' => $textMatches->getSuggestionQuery(), 'fulltext' => wfMsg('search') ), $this->powerSearchOptions() ); $suggestLink = ''. $textMatches->getSuggestionSnippet().''; $wgOut->addHTML('
{$top}
\n" ); } // prev/next links if( $num || $this->offset ) { $prevnext = wfViewPrevNext( $this->offset, $this->limit, SpecialPage::getTitleFor( 'Search' ), wfArrayToCGI( $this->powerSearchOptions(), array( 'search' => $term ) ), ($highestNum < $this->limit) ); $wgOut->addHTML( "{$prevnext}
\n" ); wfRunHooks( 'SpecialSearchResults', array( $term, &$titleMatches, &$textMatches ) ); } else { wfRunHooks( 'SpecialSearchNoResults', array( $term ) ); } $wgOut->addHtml( "{$prevnext}
\n" ); } wfProfileOut( $fname ); } /** * */ protected function setupPage( $term ) { global $wgOut; if( !empty( $term ) ) { $wgOut->setPageTitle( wfMsg( 'searchresults') ); $wgOut->setHTMLTitle( wfMsg( 'pagetitle', wfMsg( 'searchresults-title', $term) ) ); } $wgOut->setArticleRelated( false ); $wgOut->setRobotPolicy( 'noindex,nofollow' ); } /** * Extract "power search" namespace settings from the request object, * returning a list of index numbers to search. * * @param WebRequest $request * @return array */ protected function powerSearch( &$request ) { $arr = array(); foreach( SearchEngine::searchableNamespaces() as $ns => $name ) { if( $request->getCheck( 'ns' . $ns ) ) { $arr[] = $ns; } } return $arr; } /** * Reconstruct the 'power search' options for links * @return array */ 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; return $opt; } /** * Show whole set of results * * @param SearchResultSet $matches */ protected function showMatches( &$matches ) { global $wgContLang; $fname = 'SpecialSearch::showMatches'; wfProfileIn( $fname ); $terms = $wgContLang->convertForSearchResult( $matches->termMatches() ); $out = ""; $infoLine = $matches->getInfo(); if( !is_null($infoLine) ) $out .= "\n\n"; $off = $this->offset + 1; $out .= "' . $thumb->toHtml( array( 'desc-link' => true ) ) . ' | ' . '' .
$link .
$extract .
" {$score}{$desc} - {$date}{$related} " .
' | ' .
'
" .
wfMsgExt( 'powersearch-ns', array( 'parseinline' ) ) .
"
" .
$namespaces .
"
" . $redirect . " " . $redirectLabel . "
" . wfMsgExt( 'powersearch-field', array( 'parseinline' ) ) . " " . $searchField . " " . $searchButton . ' (' . wfMsgExt('searchmenu-help',array('parseinline') ) . ')' . ""; $out .= wfMsgExt( 'searchmenu-prefix', array('parseinline'), $term ); return Xml::openElement( 'fieldset', array('id' => 'mw-searchoptions','style' => 'margin:0em;') ) . Xml::element( 'legend', null, wfMsg('searchresults') ) . $this->formHeader($term) . $out . Xml::closeElement( 'fieldset' ); } protected function powerSearchFocus() { global $wgJsMimeType; return ""; } /** Make a search link with some target namespaces */ 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; $st = SpecialPage::getTitleFor( 'Search' ); $stParams = wfArrayToCGI( array( 'search' => $term, 'fulltext' => wfMsg( 'search' ) ), $opt ); return Xml::element( 'a', array( 'href'=> $st->getLocalURL( $stParams ), 'title' => $tooltip ), $label ); } /** Check if query starts with image: prefix */ protected function startsWithImage( $term ) { global $wgContLang; $p = explode( ':', $term ); if( count( $p ) > 1 ) { return $wgContLang->getNsIndex( $p[0] ) == NS_IMAGE; } return false; } protected function formHeader( $term ) { global $wgContLang, $wgCanonicalNamespaceNames; $sep = ' '; $out = Xml::openElement('div', array( 'style' => 'padding-bottom:0.5em;' ) ); $bareterm = $term; if( $this->startsWithImage( $term ) ) $bareterm = substr( $term, strpos( $term, ':' ) + 1 ); // delete all/image prefix $nsAllSet = array_keys( SearchEngine::searchableNamespaces() ); // figure out the active search profile header if( $this->searchAdvanced ) $active = 'advanced'; else if( $this->namespaces === NS_IMAGE || $this->startsWithImage( $term ) ) $active = 'images'; elseif( $this->namespaces === $nsAllSet ) $active = 'all'; elseif( $this->namespaces === SearchEngine::defaultNamespaces() ) $active = 'default'; elseif( $this->namespaces === SearchEngine::defaultAndProjectNamespaces() ) $active = 'withproject'; elseif( $this->namespaces === SearchEngine::projectNamespaces() ) $active = 'project'; else $active = 'advanced'; // search profiles headers $m = wfMsg( 'searchprofile-articles' ); $tt = wfMsg( 'searchprofile-articles-tooltip', implode( ', ', SearchEngine::namespacesAsText( SearchEngine::defaultNamespaces() ) ) ); if( $active == 'default' ) { $out .= Xml::element( 'strong', array( 'title'=>$tt ), $m ); } else { $out .= $this->makeSearchLink( $bareterm, SearchEngine::defaultNamespaces(), $m, $tt ); } $out .= $sep; $m = wfMsg( 'searchprofile-images' ); $tt = wfMsg( 'searchprofile-images-tooltip' ); if( $active == 'images' ) { $out .= Xml::element( 'strong', array( 'title'=>$tt ), $m ); } else { $out .= $this->makeSearchLink( $wgContLang->getFormattedNsText(NS_IMAGE).':'.$bareterm, array() , $m, $tt ); } $out .= $sep; $m = wfMsg( 'searchprofile-articles-and-proj' ); $tt = wfMsg( 'searchprofile-project-tooltip', implode( ', ', SearchEngine::namespacesAsText( SearchEngine::defaultAndProjectNamespaces() ) ) ); if( $active == 'withproject' ) { $out .= Xml::element( 'strong', array( 'title'=>$tt ), $m ); } else { $out .= $this->makeSearchLink( $bareterm, SearchEngine::defaultAndProjectNamespaces(), $m, $tt ); } $out .= $sep; $m = wfMsg( 'searchprofile-project' ); $tt = wfMsg( 'searchprofile-project-tooltip', implode( ', ', SearchEngine::namespacesAsText( SearchEngine::projectNamespaces() ) ) ); if( $active == 'project' ) { $out .= Xml::element( 'strong', array( 'title'=>$tt ), $m ); } else { $out .= $this->makeSearchLink( $bareterm, SearchEngine::projectNamespaces(), $m, $tt ); } $out .= $sep; $m = wfMsg( 'searchprofile-everything' ); $tt = wfMsg( 'searchprofile-everything-tooltip' ); if( $active == 'all' ) { $out .= Xml::element( 'strong', array( 'title'=>$tt ), $m ); } else { $out .= $this->makeSearchLink( $bareterm, $nsAllSet, $m, $tt ); } $out .= $sep; $m = wfMsg( 'searchprofile-advanced' ); $tt = wfMsg( 'searchprofile-advanced-tooltip' ); if( $active == 'advanced' ) { $out .= Xml::element( 'strong', array( 'title'=>$tt ), $m ); } else { $out .= $this->makeSearchLink( $bareterm, $this->namespaces, $m, $tt, array( 'advanced' => '1' ) ); } $out .= Xml::closeElement('div') ; return $out; } protected function shortDialog( $term ) { global $wgScript; $out = Xml::openElement( 'form', array( 'id' => 'search', 'method' => 'get', 'action' => $wgScript ) ); $searchTitle = SpecialPage::getTitleFor( 'Search' ); $out .= Xml::hidden( 'title', $searchTitle->getPrefixedText() ) . "\n"; $out .= Xml::input( 'search', 50, $term, array( 'type' => 'text', 'id' => 'searchText' ) ) . "\n"; foreach( SearchEngine::searchableNamespaces() as $ns => $name ) { if( in_array( $ns, $this->namespaces ) ) { $out .= Xml::hidden( "ns{$ns}", '1' ); } } $out .= Xml::submitButton( wfMsg( 'searchbutton' ), array( 'name' => 'fulltext' ) ); $out .= ' (' . wfMsgExt('searchmenu-help',array('parseinline') ) . ')'; $out .= Xml::closeElement( 'form' ); $out .= wfMsgExt( 'searchmenu-prefix', array('parseinline'), $term ); return Xml::openElement( 'fieldset', array('id' => 'mw-searchoptions','style' => 'margin:0em;') ) . Xml::element( 'legend', null, wfMsg('searchresults') ) . $this->formHeader($term) . $out . Xml::closeElement( 'fieldset' ); } }