00001 <?php
00002 # Copyright (C) 2004 Brion Vibber <brion@pobox.com>
00003 # http://www.mediawiki.org/
00004 #
00005 # This program is free software; you can redistribute it and/or modify
00006 # it under the terms of the GNU General Public License as published by
00007 # the Free Software Foundation; either version 2 of the License, or
00008 # (at your option) any later version.
00009 #
00010 # This program is distributed in the hope that it will be useful,
00011 # but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00013 # GNU General Public License for more details.
00014 #
00015 # You should have received a copy of the GNU General Public License along
00016 # with this program; if not, write to the Free Software Foundation, Inc.,
00017 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00018 # http://www.gnu.org/copyleft/gpl.html
00019
00031 function wfSpecialSearch( $par = '' ) {
00032 global $wgRequest, $wgUser;
00033
00034
00035 $titleParam = str_replace( '_', ' ', $par );
00036
00037 $search = str_replace( "\n", " ", $wgRequest->getText( 'search', $titleParam ) );
00038 $searchPage = new SpecialSearch( $wgRequest, $wgUser );
00039 if( $wgRequest->getVal( 'fulltext' )
00040 || !is_null( $wgRequest->getVal( 'offset' ))
00041 || !is_null( $wgRequest->getVal( 'searchx' )) )
00042 {
00043 $searchPage->showResults( $search );
00044 } else {
00045 $searchPage->goResult( $search );
00046 }
00047 }
00048
00053 class SpecialSearch {
00054
00063 function __construct( &$request, &$user ) {
00064 list( $this->limit, $this->offset ) = $request->getLimitOffset( 20, 'searchlimit' );
00065 $this->mPrefix = $request->getVal('prefix', '');
00066 # Extract requested namespaces
00067 $this->namespaces = $this->powerSearch( $request );
00068 if( empty( $this->namespaces ) ) {
00069 $this->namespaces = SearchEngine::userNamespaces( $user );
00070 }
00071 $this->searchRedirects = $request->getcheck( 'redirs' ) ? true : false;
00072 $this->searchAdvanced = $request->getVal( 'advanced' );
00073 $this->active = 'advanced';
00074 $this->sk = $user->getSkin();
00075 $this->didYouMeanHtml = ''; # html of did you mean... link
00076 $this->fulltext = $request->getVal('fulltext');
00077 }
00078
00083 public function goResult( $term ) {
00084 global $wgOut;
00085 $this->setupPage( $term );
00086 # Try to go to page as entered.
00087 $t = Title::newFromText( $term );
00088 # If the string cannot be used to create a title
00089 if( is_null( $t ) ) {
00090 return $this->showResults( $term );
00091 }
00092 # If there's an exact or very near match, jump right there.
00093 $t = SearchEngine::getNearMatch( $term );
00094 if( !is_null( $t ) ) {
00095 $wgOut->redirect( $t->getFullURL() );
00096 return;
00097 }
00098 # No match, generate an edit URL
00099 $t = Title::newFromText( $term );
00100 if( !is_null( $t ) ) {
00101 global $wgGoToEdit;
00102 wfRunHooks( 'SpecialSearchNogomatch', array( &$t ) );
00103 # If the feature is enabled, go straight to the edit page
00104 if( $wgGoToEdit ) {
00105 $wgOut->redirect( $t->getFullURL( array( 'action' => 'edit' ) ) );
00106 return;
00107 }
00108 }
00109 return $this->showResults( $term );
00110 }
00111
00115 public function showResults( $term ) {
00116 global $wgOut, $wgUser, $wgDisableTextSearch, $wgContLang, $wgScript;
00117 wfProfileIn( __METHOD__ );
00118
00119 $sk = $wgUser->getSkin();
00120
00121 $this->searchEngine = SearchEngine::create();
00122 $search =& $this->searchEngine;
00123 $search->setLimitOffset( $this->limit, $this->offset );
00124 $search->setNamespaces( $this->namespaces );
00125 $search->showRedirects = $this->searchRedirects;
00126 $search->prefix = $this->mPrefix;
00127 $term = $search->transformSearchTerm($term);
00128
00129 $this->setupPage( $term );
00130
00131 if( $wgDisableTextSearch ) {
00132 global $wgSearchForwardUrl;
00133 if( $wgSearchForwardUrl ) {
00134 $url = str_replace( '$1', urlencode( $term ), $wgSearchForwardUrl );
00135 $wgOut->redirect( $url );
00136 wfProfileOut( __METHOD__ );
00137 return;
00138 }
00139 global $wgInputEncoding;
00140 $wgOut->addHTML(
00141 Xml::openElement( 'fieldset' ) .
00142 Xml::element( 'legend', null, wfMsg( 'search-external' ) ) .
00143 Xml::element( 'p', array( 'class' => 'mw-searchdisabled' ), wfMsg( 'searchdisabled' ) ) .
00144 wfMsg( 'googlesearch',
00145 htmlspecialchars( $term ),
00146 htmlspecialchars( $wgInputEncoding ),
00147 htmlspecialchars( wfMsg( 'searchbutton' ) )
00148 ) .
00149 Xml::closeElement( 'fieldset' )
00150 );
00151 wfProfileOut( __METHOD__ );
00152 return;
00153 }
00154
00155 $t = Title::newFromText( $term );
00156
00157
00158 $rewritten = $search->replacePrefixes($term);
00159
00160 $titleMatches = $search->searchTitle( $rewritten );
00161 if( !($titleMatches instanceof SearchResultTooMany))
00162 $textMatches = $search->searchText( $rewritten );
00163
00164
00165 if( $textMatches && $textMatches->hasSuggestion() ) {
00166 $st = SpecialPage::getTitleFor( 'Search' );
00167
00168 # mirror Go/Search behaviour of original request ..
00169 $didYouMeanParams = array( 'search' => $textMatches->getSuggestionQuery() );
00170
00171 if($this->fulltext != null)
00172 $didYouMeanParams['fulltext'] = $this->fulltext;
00173
00174 $stParams = array_merge(
00175 $didYouMeanParams,
00176 $this->powerSearchOptions()
00177 );
00178
00179 $suggestionSnippet = $textMatches->getSuggestionSnippet();
00180
00181 if( $suggestionSnippet == '' )
00182 $suggestionSnippet = null;
00183
00184 $suggestLink = $sk->linkKnown(
00185 $st,
00186 $suggestionSnippet,
00187 array(),
00188 $stParams
00189 );
00190
00191 $this->didYouMeanHtml = '<div class="searchdidyoumean">'.wfMsg('search-suggest',$suggestLink).'</div>';
00192 }
00193
00194 $wgOut->addHtml(
00195 Xml::openElement(
00196 'form',
00197 array(
00198 'id' => ( $this->searchAdvanced ? 'powersearch' : 'search' ),
00199 'method' => 'get',
00200 'action' => $wgScript
00201 )
00202 )
00203 );
00204 $wgOut->addHtml(
00205 Xml::openElement( 'table', array( 'id'=>'mw-search-top-table', 'border'=>0, 'cellpadding'=>0, 'cellspacing'=>0 ) ) .
00206 Xml::openElement( 'tr' ) .
00207 Xml::openElement( 'td' ) . "\n" .
00208 $this->shortDialog( $term ) .
00209 Xml::closeElement('td') .
00210 Xml::closeElement('tr') .
00211 Xml::closeElement('table')
00212 );
00213
00214
00215 if( $titleMatches instanceof SearchResultTooMany ) {
00216 $wgOut->addWikiText( '==' . wfMsg( 'toomanymatches' ) . "==\n" );
00217 wfProfileOut( __METHOD__ );
00218 return;
00219 }
00220
00221 $filePrefix = $wgContLang->getFormattedNsText(NS_FILE).':';
00222 if( trim( $term ) === '' || $filePrefix === trim( $term ) ) {
00223 $wgOut->addHTML( $this->searchFocus() );
00224 $wgOut->addHTML( $this->formHeader($term, 0, 0));
00225 if( $this->searchAdvanced ) {
00226 $wgOut->addHTML( $this->powerSearchBox( $term ) );
00227 }
00228 $wgOut->addHTML( '</form>' );
00229
00230 wfProfileOut( __METHOD__ );
00231 return;
00232 }
00233
00234
00235 $titleMatchesNum = $titleMatches ? $titleMatches->numRows() : 0;
00236 $textMatchesNum = $textMatches ? $textMatches->numRows() : 0;
00237
00238 $num = $titleMatchesNum + $textMatchesNum;
00239
00240
00241 $numTitleMatches = $titleMatches && !is_null( $titleMatches->getTotalHits() ) ?
00242 $titleMatches->getTotalHits() : $titleMatchesNum;
00243 $numTextMatches = $textMatches && !is_null( $textMatches->getTotalHits() ) ?
00244 $textMatches->getTotalHits() : $textMatchesNum;
00245
00246
00247 $totalRes = 0;
00248 if($titleMatches && !is_null( $titleMatches->getTotalHits() ) )
00249 $totalRes += $titleMatches->getTotalHits();
00250 if($textMatches && !is_null( $textMatches->getTotalHits() ))
00251 $totalRes += $textMatches->getTotalHits();
00252
00253
00254 $wgOut->addHTML( $this->formHeader($term, $num, $totalRes));
00255 if( $this->searchAdvanced ) {
00256 $wgOut->addHTML( $this->powerSearchBox( $term ) );
00257 }
00258
00259 $wgOut->addHtml( Xml::closeElement( 'form' ) );
00260 $wgOut->addHtml( "<div class='searchresults'>" );
00261
00262
00263 if( $num || $this->offset ) {
00264
00265 $this->showCreateLink( $t );
00266 $prevnext = wfViewPrevNext( $this->offset, $this->limit,
00267 SpecialPage::getTitleFor( 'Search' ),
00268 wfArrayToCGI( $this->powerSearchOptions(), array( 'search' => $term ) ),
00269 max( $titleMatchesNum, $textMatchesNum ) < $this->limit
00270 );
00271
00272 wfRunHooks( 'SpecialSearchResults', array( $term, &$titleMatches, &$textMatches ) );
00273 } else {
00274 wfRunHooks( 'SpecialSearchNoResults', array( $term ) );
00275 }
00276
00277 if( $titleMatches ) {
00278 if( $numTitleMatches > 0 ) {
00279 $wgOut->wrapWikiMsg( "==$1==\n", 'titlematches' );
00280 $wgOut->addHTML( $this->showMatches( $titleMatches ) );
00281 }
00282 $titleMatches->free();
00283 }
00284 if( $textMatches ) {
00285
00286 if( $numTextMatches > 0 && $numTitleMatches > 0 ) {
00287
00288 $wgOut->wrapWikiMsg( "==$1==\n", 'textmatches' );
00289 } elseif( $totalRes == 0 ) {
00290 # Don't show the 'no text matches' if we received title matches
00291 # $wgOut->wrapWikiMsg( "==$1==\n", 'notextmatches' );
00292 }
00293
00294 if( $textMatches->hasInterwikiResults() ) {
00295 $wgOut->addHTML( $this->showInterwiki( $textMatches->getInterwikiResults(), $term ) );
00296 }
00297
00298 if( $numTextMatches > 0 ) {
00299 $wgOut->addHTML( $this->showMatches( $textMatches ) );
00300 }
00301
00302 $textMatches->free();
00303 }
00304 if( $num === 0 ) {
00305 $wgOut->addWikiMsg( 'search-nonefound', wfEscapeWikiText( $term ) );
00306 $this->showCreateLink( $t );
00307 }
00308 $wgOut->addHtml( "</div>" );
00309 if( $num === 0 ) {
00310 $wgOut->addHTML( $this->searchFocus() );
00311 }
00312
00313 if( $num || $this->offset ) {
00314 $wgOut->addHTML( "<p class='mw-search-pager-bottom'>{$prevnext}</p>\n" );
00315 }
00316 wfProfileOut( __METHOD__ );
00317 }
00318
00319 protected function showCreateLink( $t ) {
00320 global $wgOut;
00321
00322
00323 $messageName = null;
00324 if( !is_null($t) ) {
00325 if( $t->isKnown() ) {
00326 $messageName = 'searchmenu-exists';
00327 } elseif( $t->userCan( 'create' ) ) {
00328 $messageName = 'searchmenu-new';
00329 }
00330 }
00331 if( $messageName ) {
00332 $wgOut->addWikiMsg( $messageName, wfEscapeWikiText( $t->getPrefixedText() ) );
00333 } else {
00334
00335 $wgOut->addHtml( '<p></p>' );
00336 }
00337 }
00338
00342 protected function setupPage( $term ) {
00343 global $wgOut;
00344
00345 $nsAllSet = array_keys( SearchEngine::searchableNamespaces() );
00346 if( $this->searchAdvanced )
00347 $this->active = 'advanced';
00348 else {
00349 $profiles = $this->getSearchProfiles();
00350
00351 foreach( $profiles as $key => $data ) {
00352 if ( $this->namespaces == $data['namespaces'] && $key != 'advanced')
00353 $this->active = $key;
00354 }
00355
00356 }
00357 # Should advanced UI be used?
00358 $this->searchAdvanced = ($this->active === 'advanced');
00359 if( !empty( $term ) ) {
00360 $wgOut->setPageTitle( wfMsg( 'searchresults') );
00361 $wgOut->setHTMLTitle( wfMsg( 'pagetitle', wfMsg( 'searchresults-title', $term ) ) );
00362 }
00363 $wgOut->setArticleRelated( false );
00364 $wgOut->setRobotPolicy( 'noindex,nofollow' );
00365
00366 $wgOut->addScriptFile( 'search.js' );
00367 $wgOut->allowClickjacking();
00368 }
00369
00377 protected function powerSearch( &$request ) {
00378 $arr = array();
00379 foreach( SearchEngine::searchableNamespaces() as $ns => $name ) {
00380 if( $request->getCheck( 'ns' . $ns ) ) {
00381 $arr[] = $ns;
00382 }
00383 }
00384 return $arr;
00385 }
00386
00391 protected function powerSearchOptions() {
00392 $opt = array();
00393 foreach( $this->namespaces as $n ) {
00394 $opt['ns' . $n] = 1;
00395 }
00396 $opt['redirs'] = $this->searchRedirects ? 1 : 0;
00397 if( $this->searchAdvanced ) {
00398 $opt['advanced'] = $this->searchAdvanced;
00399 }
00400 return $opt;
00401 }
00402
00408 protected function showMatches( &$matches ) {
00409 global $wgContLang;
00410 wfProfileIn( __METHOD__ );
00411
00412 $terms = $wgContLang->convertForSearchResult( $matches->termMatches() );
00413
00414 $out = "";
00415 $infoLine = $matches->getInfo();
00416 if( !is_null($infoLine) ) {
00417 $out .= "\n<!-- {$infoLine} -->\n";
00418 }
00419 $off = $this->offset + 1;
00420 $out .= "<ul class='mw-search-results'>\n";
00421 while( $result = $matches->next() ) {
00422 $out .= $this->showHit( $result, $terms );
00423 }
00424 $out .= "</ul>\n";
00425
00426
00427 $out = $wgContLang->convert( $out );
00428 wfProfileOut( __METHOD__ );
00429 return $out;
00430 }
00431
00437 protected function showHit( $result, $terms ) {
00438 global $wgContLang, $wgLang, $wgUser;
00439 wfProfileIn( __METHOD__ );
00440
00441 if( $result->isBrokenTitle() ) {
00442 wfProfileOut( __METHOD__ );
00443 return "<!-- Broken link in search result -->\n";
00444 }
00445
00446 $sk = $wgUser->getSkin();
00447 $t = $result->getTitle();
00448
00449 $titleSnippet = $result->getTitleSnippet($terms);
00450
00451 if( $titleSnippet == '' )
00452 $titleSnippet = null;
00453
00454 $link_t = clone $t;
00455
00456 wfRunHooks( 'ShowSearchHitTitle',
00457 array( &$link_t, &$titleSnippet, $result, $terms, $this ) );
00458
00459 $link = $this->sk->linkKnown(
00460 $link_t,
00461 $titleSnippet
00462 );
00463
00464
00465
00466
00467 if( !$t->userCanRead() ) {
00468 wfProfileOut( __METHOD__ );
00469 return "<li>{$link}</li>\n";
00470 }
00471
00472
00473
00474
00475 if( $result->isMissingRevision() ) {
00476 wfProfileOut( __METHOD__ );
00477 return "<!-- missing page " . htmlspecialchars( $t->getPrefixedText() ) . "-->\n";
00478 }
00479
00480
00481 $redirectTitle = $result->getRedirectTitle();
00482 $redirectText = $result->getRedirectSnippet($terms);
00483 $sectionTitle = $result->getSectionTitle();
00484 $sectionText = $result->getSectionSnippet($terms);
00485 $redirect = '';
00486
00487 if( !is_null($redirectTitle) ) {
00488 if( $redirectText == '' )
00489 $redirectText = null;
00490
00491 $redirect = "<span class='searchalttitle'>" .
00492 wfMsg(
00493 'search-redirect',
00494 $this->sk->linkKnown(
00495 $redirectTitle,
00496 $redirectText
00497 )
00498 ) .
00499 "</span>";
00500 }
00501
00502 $section = '';
00503
00504
00505 if( !is_null($sectionTitle) ) {
00506 if( $sectionText == '' )
00507 $sectionText = null;
00508
00509 $section = "<span class='searchalttitle'>" .
00510 wfMsg(
00511 'search-section', $this->sk->linkKnown(
00512 $sectionTitle,
00513 $sectionText
00514 )
00515 ) .
00516 "</span>";
00517 }
00518
00519
00520 $extract = "<div class='searchresult'>".$result->getTextSnippet($terms)."</div>";
00521
00522
00523 if( is_null( $result->getScore() ) ) {
00524
00525 $score = '';
00526 } else {
00527 $percent = sprintf( '%2.1f', $result->getScore() * 100 );
00528 $score = wfMsg( 'search-result-score', $wgLang->formatNum( $percent ) )
00529 . ' - ';
00530 }
00531
00532
00533 $byteSize = $result->getByteSize();
00534 $wordCount = $result->getWordCount();
00535 $timestamp = $result->getTimestamp();
00536 $size = wfMsgExt(
00537 'search-result-size',
00538 array( 'parsemag', 'escape' ),
00539 $this->sk->formatSize( $byteSize ),
00540 $wgLang->formatNum( $wordCount )
00541 );
00542 $date = $wgLang->timeanddate( $timestamp );
00543
00544
00545 $related = '';
00546 if( $result->hasRelated() ) {
00547 $st = SpecialPage::getTitleFor( 'Search' );
00548 $stParams = array_merge(
00549 $this->powerSearchOptions(),
00550 array(
00551 'search' => wfMsgForContent( 'searchrelated' ) . ':' . $t->getPrefixedText(),
00552 'fulltext' => wfMsg( 'search' )
00553 )
00554 );
00555
00556 $related = ' -- ' . $sk->linkKnown(
00557 $st,
00558 wfMsg('search-relatedarticle'),
00559 array(),
00560 $stParams
00561 );
00562 }
00563
00564
00565 if( $t->getNamespace() == NS_FILE ) {
00566 $img = wfFindFile( $t );
00567 if( $img ) {
00568 $thumb = $img->transform( array( 'width' => 120, 'height' => 120 ) );
00569 if( $thumb ) {
00570 $desc = $img->getShortDesc();
00571 wfProfileOut( __METHOD__ );
00572
00573
00574
00575 return "<li>" .
00576 '<table class="searchResultImage">' .
00577 '<tr>' .
00578 '<td width="120" align="center" valign="top">' .
00579 $thumb->toHtml( array( 'desc-link' => true ) ) .
00580 '</td>' .
00581 '<td valign="top">' .
00582 $link .
00583 $extract .
00584 "<div class='mw-search-result-data'>{$score}{$desc} - {$date}{$related}</div>" .
00585 '</td>' .
00586 '</tr>' .
00587 '</table>' .
00588 "</li>\n";
00589 }
00590 }
00591 }
00592
00593 wfProfileOut( __METHOD__ );
00594 return "<li>{$link} {$redirect} {$section} {$extract}\n" .
00595 "<div class='mw-search-result-data'>{$score}{$size} - {$date}{$related}</div>" .
00596 "</li>\n";
00597
00598 }
00599
00605 protected function showInterwiki( &$matches, $query ) {
00606 global $wgContLang;
00607 wfProfileIn( __METHOD__ );
00608 $terms = $wgContLang->convertForSearchResult( $matches->termMatches() );
00609
00610 $out = "<div id='mw-search-interwiki'><div id='mw-search-interwiki-caption'>".
00611 wfMsg('search-interwiki-caption')."</div>\n";
00612 $off = $this->offset + 1;
00613 $out .= "<ul class='mw-search-iwresults'>\n";
00614
00615
00616 $customCaptions = array();
00617 $customLines = explode("\n",wfMsg('search-interwiki-custom'));
00618 foreach($customLines as $line) {
00619 $parts = explode(":",$line,2);
00620 if(count($parts) == 2)
00621 $customCaptions[$parts[0]] = $parts[1];
00622 }
00623
00624 $prev = null;
00625 while( $result = $matches->next() ) {
00626 $out .= $this->showInterwikiHit( $result, $prev, $terms, $query, $customCaptions );
00627 $prev = $result->getInterwikiPrefix();
00628 }
00629
00630 $out .= "</ul></div>\n";
00631
00632
00633 $out = $wgContLang->convert( $out );
00634 wfProfileOut( __METHOD__ );
00635 return $out;
00636 }
00637
00647 protected function showInterwikiHit( $result, $lastInterwiki, $terms, $query, $customCaptions) {
00648 wfProfileIn( __METHOD__ );
00649 global $wgContLang, $wgLang;
00650
00651 if( $result->isBrokenTitle() ) {
00652 wfProfileOut( __METHOD__ );
00653 return "<!-- Broken link in search result -->\n";
00654 }
00655
00656 $t = $result->getTitle();
00657
00658 $titleSnippet = $result->getTitleSnippet($terms);
00659
00660 if( $titleSnippet == '' )
00661 $titleSnippet = null;
00662
00663 $link = $this->sk->linkKnown(
00664 $t,
00665 $titleSnippet
00666 );
00667
00668
00669 $redirectTitle = $result->getRedirectTitle();
00670 $redirectText = $result->getRedirectSnippet($terms);
00671 $redirect = '';
00672 if( !is_null($redirectTitle) ) {
00673 if( $redirectText == '' )
00674 $redirectText = null;
00675
00676 $redirect = "<span class='searchalttitle'>" .
00677 wfMsg(
00678 'search-redirect',
00679 $this->sk->linkKnown(
00680 $redirectTitle,
00681 $redirectText
00682 )
00683 ) .
00684 "</span>";
00685 }
00686
00687 $out = "";
00688
00689 if(is_null($lastInterwiki) || $lastInterwiki != $t->getInterwiki()) {
00690 if( key_exists($t->getInterwiki(),$customCaptions) )
00691
00692 $caption = $customCaptions[$t->getInterwiki()];
00693 else{
00694
00695
00696 $parsed = parse_url($t->getFullURL());
00697 $caption = wfMsg('search-interwiki-default', $parsed['host']);
00698 }
00699
00700 $searchTitle = Title::newFromText($t->getInterwiki().":Special:Search");
00701 $searchLink = $this->sk->linkKnown(
00702 $searchTitle,
00703 wfMsg('search-interwiki-more'),
00704 array(),
00705 array(
00706 'search' => $query,
00707 'fulltext' => 'Search'
00708 )
00709 );
00710 $out .= "</ul><div class='mw-search-interwiki-project'><span class='mw-search-interwiki-more'>
00711 {$searchLink}</span>{$caption}</div>\n<ul>";
00712 }
00713
00714 $out .= "<li>{$link} {$redirect}</li>\n";
00715 wfProfileOut( __METHOD__ );
00716 return $out;
00717 }
00718
00719
00725 protected function powerSearchBox( $term ) {
00726 global $wgScript, $wgContLang;
00727
00728
00729 $rows = array();
00730 foreach( SearchEngine::searchableNamespaces() as $namespace => $name ) {
00731 $subject = MWNamespace::getSubject( $namespace );
00732 if( !array_key_exists( $subject, $rows ) ) {
00733 $rows[$subject] = "";
00734 }
00735 $name = str_replace( '_', ' ', $name );
00736 if( $name == '' ) {
00737 $name = wfMsg( 'blanknamespace' );
00738 }
00739 $rows[$subject] .=
00740 Xml::openElement(
00741 'td', array( 'style' => 'white-space: nowrap' )
00742 ) .
00743 Xml::checkLabel(
00744 $name,
00745 "ns{$namespace}",
00746 "mw-search-ns{$namespace}",
00747 in_array( $namespace, $this->namespaces )
00748 ) .
00749 Xml::closeElement( 'td' );
00750 }
00751 $rows = array_values( $rows );
00752 $numRows = count( $rows );
00753
00754
00755
00756 $namespaceTables = '';
00757 for( $i = 0; $i < $numRows; $i += 4 ) {
00758 $namespaceTables .= Xml::openElement(
00759 'table',
00760 array( 'cellpadding' => 0, 'cellspacing' => 0, 'border' => 0 )
00761 );
00762 for( $j = $i; $j < $i + 4 && $j < $numRows; $j++ ) {
00763 $namespaceTables .= Xml::tags( 'tr', null, $rows[$j] );
00764 }
00765 $namespaceTables .= Xml::closeElement( 'table' );
00766 }
00767
00768 $redirects = '';
00769 if( $this->searchEngine->acceptListRedirects() ) {
00770 $redirects =
00771 Xml::check(
00772 'redirs', $this->searchRedirects, array( 'value' => '1', 'id' => 'redirs' )
00773 ) .
00774 ' ' .
00775 Xml::label( wfMsg( 'powersearch-redir' ), 'redirs' );
00776 }
00777
00778 return
00779 Xml::openElement(
00780 'fieldset',
00781 array( 'id' => 'mw-searchoptions', 'style' => 'margin:0em;' )
00782 ) .
00783 Xml::element( 'legend', null, wfMsg('powersearch-legend') ) .
00784 Xml::tags( 'h4', null, wfMsgExt( 'powersearch-ns', array( 'parseinline' ) ) ) .
00785 Xml::tags(
00786 'div',
00787 array( 'id' => 'mw-search-togglebox' ),
00788 Xml::label( wfMsg( 'powersearch-togglelabel' ), 'mw-search-togglelabel' ) .
00789 Xml::element(
00790 'input',
00791 array(
00792 'type'=>'button',
00793 'id' => 'mw-search-toggleall',
00794 'onclick' => 'mwToggleSearchCheckboxes("all");',
00795 'value' => wfMsg( 'powersearch-toggleall' )
00796 )
00797 ) .
00798 Xml::element(
00799 'input',
00800 array(
00801 'type'=>'button',
00802 'id' => 'mw-search-togglenone',
00803 'onclick' => 'mwToggleSearchCheckboxes("none");',
00804 'value' => wfMsg( 'powersearch-togglenone' )
00805 )
00806 )
00807 ) .
00808 Xml::element( 'div', array( 'class' => 'divider' ), '', false ) .
00809 $namespaceTables .
00810 Xml::element( 'div', array( 'class' => 'divider' ), '', false ) .
00811 $redirects .
00812 Xml::hidden( 'title', SpecialPage::getTitleFor( 'Search' )->getPrefixedText() ) .
00813 Xml::hidden( 'advanced', $this->searchAdvanced ) .
00814 Xml::hidden( 'fulltext', 'Advanced search' ) .
00815 Xml::closeElement( 'fieldset' );
00816 }
00817
00818 protected function searchFocus() {
00819 $id = $this->searchAdvanced ? 'powerSearchText' : 'searchText';
00820 return Html::inlineScript(
00821 "hookEvent(\"load\", function() {" .
00822 "document.getElementById('$id').focus();" .
00823 "});" );
00824 }
00825
00826 protected function getSearchProfiles() {
00827
00828 $nsAllSet = array_keys( SearchEngine::searchableNamespaces() );
00829
00830 $profiles = array(
00831 'default' => array(
00832 'message' => 'searchprofile-articles',
00833 'tooltip' => 'searchprofile-articles-tooltip',
00834 'namespaces' => SearchEngine::defaultNamespaces(),
00835 'namespace-messages' => SearchEngine::namespacesAsText(
00836 SearchEngine::defaultNamespaces()
00837 ),
00838 ),
00839 'images' => array(
00840 'message' => 'searchprofile-images',
00841 'tooltip' => 'searchprofile-images-tooltip',
00842 'namespaces' => array( NS_FILE ),
00843 ),
00844 'help' => array(
00845 'message' => 'searchprofile-project',
00846 'tooltip' => 'searchprofile-project-tooltip',
00847 'namespaces' => SearchEngine::helpNamespaces(),
00848 'namespace-messages' => SearchEngine::namespacesAsText(
00849 SearchEngine::helpNamespaces()
00850 ),
00851 ),
00852 'all' => array(
00853 'message' => 'searchprofile-everything',
00854 'tooltip' => 'searchprofile-everything-tooltip',
00855 'namespaces' => $nsAllSet,
00856 ),
00857 'advanced' => array(
00858 'message' => 'searchprofile-advanced',
00859 'tooltip' => 'searchprofile-advanced-tooltip',
00860 'namespaces' => $this->namespaces,
00861 'parameters' => array( 'advanced' => 1 ),
00862 )
00863 );
00864
00865 wfRunHooks( 'SpecialSearchProfiles', array( &$profiles ) );
00866
00867 foreach( $profiles as $key => &$data ) {
00868 sort($data['namespaces']);
00869 }
00870
00871 return $profiles;
00872 }
00873
00874 protected function formHeader( $term, $resultsShown, $totalNum ) {
00875 global $wgContLang, $wgLang;
00876
00877 $out = Xml::openElement('div', array( 'class' => 'mw-search-formheader' ) );
00878
00879 $bareterm = $term;
00880 if( $this->startsWithImage( $term ) ) {
00881
00882 $bareterm = substr( $term, strpos( $term, ':' ) + 1 );
00883 }
00884
00885
00886 $profiles = $this->getSearchProfiles();
00887
00888
00889 $out .= Xml::openElement( 'div', array( 'class' => 'search-types' ) );
00890 $out .= Xml::openElement( 'ul' );
00891 foreach ( $profiles as $id => $profile ) {
00892 $tooltipParam = isset( $profile['namespace-messages'] ) ?
00893 $wgLang->commaList( $profile['namespace-messages'] ) : null;
00894 $out .= Xml::tags(
00895 'li',
00896 array(
00897 'class' => $this->active == $id ? 'current' : 'normal'
00898 ),
00899 $this->makeSearchLink(
00900 $bareterm,
00901 $profile['namespaces'],
00902 wfMsg( $profile['message'] ),
00903 wfMsg( $profile['tooltip'], $tooltipParam ),
00904 isset( $profile['parameters'] ) ? $profile['parameters'] : array()
00905 )
00906 );
00907 }
00908 $out .= Xml::closeElement( 'ul' );
00909 $out .= Xml::closeElement('div') ;
00910
00911
00912 if ( $resultsShown > 0 ) {
00913 if ( $totalNum > 0 ){
00914 $top = wfMsgExt( 'showingresultsheader', array( 'parseinline' ),
00915 $wgLang->formatNum( $this->offset + 1 ),
00916 $wgLang->formatNum( $this->offset + $resultsShown ),
00917 $wgLang->formatNum( $totalNum ),
00918 wfEscapeWikiText( $term ),
00919 $wgLang->formatNum( $resultsShown )
00920 );
00921 } elseif ( $resultsShown >= $this->limit ) {
00922 $top = wfShowingResults( $this->offset, $this->limit );
00923 } else {
00924 $top = wfShowingResultsNum( $this->offset, $this->limit, $resultsShown );
00925 }
00926 $out .= Xml::tags( 'div', array( 'class' => 'results-info' ),
00927 Xml::tags( 'ul', null, Xml::tags( 'li', null, $top ) )
00928 );
00929 }
00930
00931 $out .= Xml::element( 'div', array( 'style' => 'clear:both' ), '', false );
00932 $out .= Xml::closeElement('div');
00933
00934
00935 if ( !$this->searchAdvanced ) {
00936 foreach( $this->namespaces as $ns ) {
00937 $out .= Xml::hidden( "ns{$ns}", '1' );
00938 }
00939 }
00940
00941 return $out;
00942 }
00943
00944 protected function shortDialog( $term ) {
00945 $searchTitle = SpecialPage::getTitleFor( 'Search' );
00946 $searchable = SearchEngine::searchableNamespaces();
00947 $out = Html::hidden( 'title', $searchTitle->getPrefixedText() ) . "\n";
00948
00949 $out .= Html::hidden( "redirs", (int)$this->searchRedirects ) . "\n";
00950
00951 $out .= Html::input( 'search', $term, 'search', array(
00952 'id' => $this->searchAdvanced ? 'powerSearchText' : 'searchText',
00953 'size' => '50',
00954 'autofocus'
00955 ) ) . "\n";
00956 $out .= Html::hidden( 'fulltext', 'Search' ) . "\n";
00957 $out .= Xml::submitButton( wfMsg( 'searchbutton' ) ) . "\n";
00958 return $out . $this->didYouMeanHtml;
00959 }
00960
00962 protected function makeSearchLink( $term, $namespaces, $label, $tooltip, $params=array() ) {
00963 $opt = $params;
00964 foreach( $namespaces as $n ) {
00965 $opt['ns' . $n] = 1;
00966 }
00967 $opt['redirs'] = $this->searchRedirects ? 1 : 0;
00968
00969 $st = SpecialPage::getTitleFor( 'Search' );
00970 $stParams = array_merge(
00971 array(
00972 'search' => $term,
00973 'fulltext' => wfMsg( 'search' )
00974 ),
00975 $opt
00976 );
00977
00978 return Xml::element(
00979 'a',
00980 array(
00981 'href' => $st->getLocalURL( $stParams ),
00982 'title' => $tooltip,
00983 'onmousedown' => 'mwSearchHeaderClick(this);',
00984 'onkeydown' => 'mwSearchHeaderClick(this);'),
00985 $label
00986 );
00987 }
00988
00990 protected function startsWithImage( $term ) {
00991 global $wgContLang;
00992
00993 $p = explode( ':', $term );
00994 if( count( $p ) > 1 ) {
00995 return $wgContLang->getNsIndex( $p[0] ) == NS_FILE;
00996 }
00997 return false;
00998 }
00999
01001 protected function startsWithAll( $term ) {
01002
01003 $allkeyword = wfMsgForContent('searchall');
01004
01005 $p = explode( ':', $term );
01006 if( count( $p ) > 1 ) {
01007 return $p[0] == $allkeyword;
01008 }
01009 return false;
01010 }
01011 }
01012