X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2FSpecialSearch.php;h=0a483af52f8a7287d03d46fc2647c72da07d4ab2;hb=328e3473d61a11358d23017597672e7c1b2b44c8;hp=8c2f8aad4d7a95d53bd1fd21f4c0506840791f90;hpb=a3b490d2c4dfc25d7e0594eae8ee27ba69f6afea;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/SpecialSearch.php b/includes/SpecialSearch.php index 8c2f8aad4d..0a483af52f 100644 --- a/includes/SpecialSearch.php +++ b/includes/SpecialSearch.php @@ -19,8 +19,8 @@ /** * Run text & title search and display the output - * @package MediaWiki - * @subpackage SpecialPage + * @file + * @ingroup SpecialPage */ /** @@ -31,21 +31,20 @@ function wfSpecialSearch( $par = '' ) { global $wgRequest, $wgUser; - $search = $wgRequest->getText( 'search', $par ); + $search = str_replace( "\n", " ", $wgRequest->getText( 'search', $par ) ); $searchPage = new SpecialSearch( $wgRequest, $wgUser ); - if( $wgRequest->getVal( 'fulltext' ) || - !is_null( $wgRequest->getVal( 'offset' ) ) || - !is_null ($wgRequest->getVal( 'searchx' ) ) ) { - $searchPage->showResults( $search ); + if( $wgRequest->getVal( 'fulltext' ) + || !is_null( $wgRequest->getVal( 'offset' )) + || !is_null( $wgRequest->getVal( 'searchx' ))) { + $searchPage->showResults( $search, 'search' ); } else { $searchPage->goResult( $search ); } } /** - * @todo document - * @package MediaWiki - * @subpackage SpecialPage + * implements Special:Search - Run text & title search and display the output + * @ingroup SpecialPage */ class SpecialSearch { @@ -60,29 +59,26 @@ class SpecialSearch { function SpecialSearch( &$request, &$user ) { list( $this->limit, $this->offset ) = $request->getLimitOffset( 20, 'searchlimit' ); - if( $request->getCheck( 'searchx' ) ) { - $this->namespaces = $this->powerSearch( $request ); - } else { - $this->namespaces = $this->userNamespaces( $user ); + $this->namespaces = $this->powerSearch( $request ); + if( empty( $this->namespaces ) ) { + $this->namespaces = SearchEngine::userNamespaces( $user ); } $this->searchRedirects = $request->getcheck( 'redirs' ) ? true : false; } /** - * If an exact title match can be found, jump straight ahead to + * If an exact title match can be found, jump straight ahead to it. * @param string $term * @public */ function goResult( $term ) { global $wgOut; global $wgGoToEdit; - global $wgContLang; $this->setupPage( $term ); # Try to go to page as entered. - # $t = Title::newFromText( $term ); # If the string cannot be used to create a title @@ -99,19 +95,21 @@ class SpecialSearch { # No match, generate an edit URL $t = Title::newFromText( $term ); - if( is_null( $t ) ) { - $editurl = ''; # hrm... - } else { + 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; - } else { - $editurl = $t->escapeLocalURL( 'action=edit' ); } } - $wgOut->addWikiText( wfMsg( 'noexactmatch', wfEscapeWikiText( $term ) ) ); + + $wgOut->wrapWikiMsg( "==$1==\n", 'notitlematches' ); + if( $t->quickUserCan( 'create' ) && $t->quickUserCan( 'edit' ) ) { + $wgOut->addWikiMsg( 'noexactmatch', wfEscapeWikiText( $term ) ); + } else { + $wgOut->addWikiMsg( 'noexactmatch-nocreate', wfEscapeWikiText( $term ) ); + } return $this->showResults( $term ); } @@ -123,17 +121,18 @@ class SpecialSearch { function showResults( $term ) { $fname = 'SpecialSearch::showResults'; wfProfileIn( $fname ); + global $wgOut, $wgUser; + $sk = $wgUser->getSkin(); $this->setupPage( $term ); - global $wgUser, $wgOut; - $sk = $wgUser->getSkin(); - $wgOut->addWikiText( wfMsg( 'searchresulttext' ) ); + $wgOut->addWikiMsg( 'searchresulttext' ); - #if ( !$this->parseQuery() ) { if( '' === trim( $term ) ) { + // Empty query -- straight view of search form $wgOut->setSubtitle( '' ); $wgOut->addHTML( $this->powerSearchBox( $term ) ); + $wgOut->addHTML( $this->powerSearchFocus() ); wfProfileOut( $fname ); return; } @@ -147,67 +146,124 @@ class SpecialSearch { return; } global $wgInputEncoding; - $wgOut->addHTML( wfMsg( 'searchdisabled' ) ); $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; } + $wgOut->addHTML( $this->shortDialog( $term ) ); + $search = SearchEngine::create(); $search->setLimitOffset( $this->limit, $this->offset ); $search->setNamespaces( $this->namespaces ); $search->showRedirects = $this->searchRedirects; - $titleMatches = $search->searchTitle( $term ); - $textMatches = $search->searchText( $term ); + $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" ); + $wgOut->addHTML( $this->powerSearchBox( $term ) ); + $wgOut->addHTML( $this->powerSearchFocus() ); + 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('
'.wfMsg('search-suggest',$suggestLink).'
'); + } + // show number of results $num = ( $titleMatches ? $titleMatches->numRows() : 0 ) + ( $textMatches ? $textMatches->numRows() : 0); - if ( $num >= $this->limit ) { - $top = wfShowingResults( $this->offset, $this->limit ); - } else { - $top = wfShowingResultsNum( $this->offset, $this->limit, $num ); + $totalNum = 0; + if($titleMatches && !is_null($titleMatches->getTotalHits())) + $totalNum += $titleMatches->getTotalHits(); + if($textMatches && !is_null($textMatches->getTotalHits())) + $totalNum += $textMatches->getTotalHits(); + if ( $num > 0 ) { + if ( $totalNum > 0 ){ + $top = wfMsgExt('showingresultstotal', array( 'parseinline' ), + $this->offset+1, $this->offset+$num, $totalNum ); + } elseif ( $num >= $this->limit ) { + $top = wfShowingResults( $this->offset, $this->limit ); + } else { + $top = wfShowingResultsNum( $this->offset, $this->limit, $num ); + } + $wgOut->addHTML( "

{$top}

\n" ); } - $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 ) ) ); - $wgOut->addHTML( "
{$prevnext}\n" ); + array( 'search' => $term ) ), + ($num < $this->limit) ); + $wgOut->addHTML( "

{$prevnext}

\n" ); + wfRunHooks( 'SpecialSearchResults', array( $term, &$titleMatches, &$textMatches ) ); + } else { + wfRunHooks( 'SpecialSearchNoResults', array( $term ) ); } if( $titleMatches ) { if( $titleMatches->numRows() ) { - $wgOut->addWikiText( '==' . wfMsg( 'titlematches' ) . "==\n" ); + $wgOut->wrapWikiMsg( "==$1==\n", 'titlematches' ); $wgOut->addHTML( $this->showMatches( $titleMatches ) ); - } else { - $wgOut->addWikiText( '==' . wfMsg( 'notitlematches' ) . "==\n" ); } + $titleMatches->free(); } if( $textMatches ) { + // output appropriate heading if( $textMatches->numRows() ) { - $wgOut->addWikiText( '==' . wfMsg( 'textmatches' ) . "==\n" ); - $wgOut->addHTML( $this->showMatches( $textMatches ) ); + if($titleMatches) + $wgOut->wrapWikiMsg( "==$1==\n", 'textmatches' ); + else // if no title matches the heading is redundant + $wgOut->addHTML("
"); } elseif( $num == 0 ) { # Don't show the 'no text matches' if we received title matches - $wgOut->addWikiText( '==' . wfMsg( 'notextmatches' ) . "==\n" ); + $wgOut->wrapWikiMsg( "==$1==\n", 'notextmatches' ); } + // show interwiki results if any + if( $textMatches->hasInterwikiResults() ) + $wgOut->addHtml( $this->showInterwiki( $textMatches->getInterwikiResults(), $term )); + // show results + if( $textMatches->numRows() ) + $wgOut->addHTML( $this->showMatches( $textMatches ) ); + + $textMatches->free(); } if ( $num == 0 ) { - $wgOut->addWikiText( wfMsg( 'nonefound' ) ); + $wgOut->addWikiMsg( 'nonefound' ); } if( $num || $this->offset ) { - $wgOut->addHTML( "

{$prevnext}

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

{$prevnext}

\n" ); } $wgOut->addHTML( $this->powerSearchBox( $term ) ); wfProfileOut( $fname ); @@ -215,37 +271,20 @@ class SpecialSearch { #------------------------------------------------------------------ # Private methods below this line - + /** * */ function setupPage( $term ) { global $wgOut; - $wgOut->setPageTitle( wfMsg( 'searchresults' ) ); - $subtitlemsg = ( Title::newFromText($term) ? 'searchsubtitle' : 'searchsubtitleinvalid' ); + if( !empty( $term ) ) + $wgOut->setPageTitle( wfMsg( 'searchresults' ) ); + $subtitlemsg = ( Title::newFromText( $term ) ? 'searchsubtitle' : 'searchsubtitleinvalid' ); $wgOut->setSubtitle( $wgOut->parse( wfMsg( $subtitlemsg, wfEscapeWikiText($term) ) ) ); $wgOut->setArticleRelated( false ); $wgOut->setRobotpolicy( 'noindex,nofollow' ); } - /** - * Extract default namespaces to search from the given user's - * settings, returning a list of index numbers. - * - * @param User $user - * @return array - * @private - */ - function userNamespaces( &$user ) { - $arr = array(); - foreach( SearchEngine::searchableNamespaces() as $ns => $name ) { - if( $user->getOption( 'searchNs' . $ns ) ) { - $arr[] = $ns; - } - } - return $arr; - } - /** * Extract "power search" namespace settings from the request object, * returning a list of index numbers to search. @@ -275,29 +314,35 @@ class SpecialSearch { $opt['ns' . $n] = 1; } $opt['redirs'] = $this->searchRedirects ? 1 : 0; - $opt['searchx'] = 1; return $opt; } /** + * Show whole set of results + * * @param SearchResultSet $matches - * @param string $terms partial regexp for highlighting terms */ function showMatches( &$matches ) { $fname = 'SpecialSearch::showMatches'; wfProfileIn( $fname ); global $wgContLang; - $tm = $wgContLang->convertForSearchResult( $matches->termMatches() ); - $terms = implode( '|', $tm ); - + $terms = $wgContLang->convertForSearchResult( $matches->termMatches() ); + + $out = ""; + + $infoLine = $matches->getInfo(); + if( !is_null($infoLine) ) + $out .= "\n\n"; + + $off = $this->offset + 1; - $out = "
    \n"; + $out .= "
\n"; + $out .= "\n"; // convert the whole thing to desired language variant global $wgContLang; @@ -309,106 +354,298 @@ class SpecialSearch { /** * Format a single hit result * @param SearchResult $result - * @param string $terms partial regexp for highlighting terms + * @param array $terms terms to highlight */ function showHit( $result, $terms ) { $fname = 'SpecialSearch::showHit'; wfProfileIn( $fname ); global $wgUser, $wgContLang, $wgLang; - - $t = $result->getTitle(); - if( is_null( $t ) ) { + + if( $result->isBrokenTitle() ) { wfProfileOut( $fname ); return "\n"; } - $sk =& $wgUser->getSkin(); - - $contextlines = $wgUser->getOption( 'contextlines' ); - if ( '' == $contextlines ) { $contextlines = 5; } - $contextchars = $wgUser->getOption( 'contextchars' ); - if ( '' == $contextchars ) { $contextchars = 50; } + + $t = $result->getTitle(); + $sk = $wgUser->getSkin(); - $link = $sk->makeKnownLinkObj( $t ); - $revision = Revision::newFromTitle( $t ); - $text = $revision->getText(); - $size = wfMsgExt( 'nbytes', array( 'parsemag', 'escape'), - $wgLang->formatNum( strlen( $text ) ) ); + $link = $sk->makeKnownLinkObj( $t, $result->getTitleSnippet($terms)); - $lines = explode( "\n", $text ); + //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()) { + wfProfileOut( $fname ); + return "
  • {$link}
  • \n"; + } - $max = intval( $contextchars ) + 1; - $pat1 = "/(.*)($terms)(.{0,$max})/i"; + // If the page doesn't *exist*... our search index is out of date. + // The least confusing at this point is to drop the result. + // You may get less results, but... oh well. :P + if( $result->isMissingRevision() ) { + wfProfileOut( $fname ); + return "\n"; + } - $lineno = 0; + // format redirects / relevant sections + $redirectTitle = $result->getRedirectTitle(); + $redirectText = $result->getRedirectSnippet($terms); + $sectionTitle = $result->getSectionTitle(); + $sectionText = $result->getSectionSnippet($terms); + $redirect = ''; + if( !is_null($redirectTitle) ) + $redirect = "" + .wfMsg('search-redirect',$sk->makeKnownLinkObj( $redirectTitle, $redirectText)) + .""; + $section = ''; + if( !is_null($sectionTitle) ) + $section = "" + .wfMsg('search-section', $sk->makeKnownLinkObj( $sectionTitle, $sectionText)) + .""; + + // format text extract + $extract = "
    ".$result->getTextSnippet($terms)."
    "; + + // 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 ) ) + . ' - '; + } - $extract = ''; - wfProfileIn( "$fname-extract" ); - foreach ( $lines as $line ) { - if ( 0 == $contextlines ) { - break; - } - ++$lineno; - if ( ! preg_match( $pat1, $line, $m ) ) { - continue; + // format description + $byteSize = $result->getByteSize(); + $wordCount = $result->getWordCount(); + $timestamp = $result->getTimestamp(); + $size = wfMsgExt( 'search-result-size', array( 'parsemag', 'escape' ), + $sk->formatSize( $byteSize ), + $wordCount ); + $date = $wgLang->timeanddate( $timestamp ); + + // link to related articles if supported + $related = ''; + if( $result->hasRelated() ){ + $st = SpecialPage::getTitleFor( 'Search' ); + $stParams = wfArrayToCGI( $this->powerSearchOptions(), + array('search' => wfMsgForContent('searchrelated').':'.$t->getPrefixedText(), + 'fulltext' => wfMsg('search') )); + + $related = ' -- '. + wfMsg('search-relatedarticle').''; + } + + // Include a thumbnail for media files... + if( $t->getNamespace() == NS_IMAGE ) { + $img = wfFindFile( $t ); + if( $img ) { + $thumb = $img->getThumbnail( 120, 120 ); + if( $thumb ) { + $desc = $img->getShortDesc(); + wfProfileOut( $fname ); + // Ugly table. :D + // Float doesn't seem to interact well with the bullets. + // Table messes up vertical alignment of the bullet, but I'm + // not sure what more I can do about that. :( + return "
  • " . + '' . + '' . + '' . + '' . + '' . + '
    ' . + $thumb->toHtml( array( 'desc-link' => true ) ) . + '' . + $link . + $extract . + "
    {$score}{$desc} - {$date}{$related}
    " . + '
    ' . + "
  • \n"; + } } - --$contextlines; - $pre = $wgContLang->truncate( $m[1], -$contextchars, '...' ); + } - if ( count( $m ) < 3 ) { - $post = ''; - } else { - $post = $wgContLang->truncate( $m[3], $contextchars, '...' ); - } + wfProfileOut( $fname ); + return "
  • {$link} {$redirect} {$section} {$extract}\n" . + "
    {$score}{$size} - {$date}{$related}
    " . + "
  • \n"; - $found = $m[2]; + } - $line = htmlspecialchars( $pre . $found . $post ); - $pat2 = '/(' . $terms . ")/i"; - $line = preg_replace( $pat2, - "\\1", $line ); + /** + * Show results from other wikis + * + * @param SearchResultSet $matches + */ + function showInterwiki( &$matches, $query ) { + $fname = 'SpecialSearch::showInterwiki'; + wfProfileIn( $fname ); - $extract .= "
    {$lineno}: {$line}\n"; + global $wgContLang; + $terms = $wgContLang->convertForSearchResult( $matches->termMatches() ); + + $out = "
    ".wfMsg('search-interwiki-caption')."
    \n"; + $off = $this->offset + 1; + $out .= "
    \n"; + + // convert the whole thing to desired language variant + global $wgContLang; + $out = $wgContLang->convert( $out ); + wfProfileOut( $fname ); + return $out; + } + + /** + * Show single interwiki link + * + * @param SearchResult $result + * @param string $lastInterwiki + * @param array $terms + * @param string $query + * @param array $customCaptions iw prefix -> caption + */ + function showInterwikiHit( $result, $lastInterwiki, $terms, $query, $customCaptions){ + $fname = 'SpecialSearch::showInterwikiHit'; + wfProfileIn( $fname ); + global $wgUser, $wgContLang, $wgLang; + + if( $result->isBrokenTitle() ) { + wfProfileOut( $fname ); + return "\n"; + } + + $t = $result->getTitle(); + $sk = $wgUser->getSkin(); + + $link = $sk->makeKnownLinkObj( $t, $result->getTitleSnippet($terms)); + + // format redirect if any + $redirectTitle = $result->getRedirectTitle(); + $redirectText = $result->getRedirectSnippet($terms); + $redirect = ''; + if( !is_null($redirectTitle) ) + $redirect = "" + .wfMsg('search-redirect',$sk->makeKnownLinkObj( $redirectTitle, $redirectText)) + .""; + + $out = ""; + // display project name + if(is_null($lastInterwiki) || $lastInterwiki != $t->getInterwiki()){ + if( key_exists($t->getInterwiki(),$customCaptions) ) + // captions from 'search-interwiki-custom' + $caption = $customCaptions[$t->getInterwiki()]; + 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']); + } + // "more results" link (special page stuff could be localized, but we might not know target lang) + $searchTitle = Title::newFromText($t->getInterwiki().":Special:Search"); + $searchLink = $sk->makeKnownLinkObj( $searchTitle, wfMsg('search-interwiki-more'), + wfArrayToCGI(array('search' => $query, 'fulltext' => 'Search'))); + $out .= "
    {$searchLink}{$caption}
    \n