EBernhardson has uploaded a new change for review. https://gerrit.wikimedia.org/r/322015
Change subject: [WIP] Extract main search result rendering from SpecialSearch ...................................................................... [WIP] Extract main search result rendering from SpecialSearch Change-Id: Id1d6b357f45a2cf615d9412cc95dd597c724e8b6 --- M autoload.php M includes/specials/SpecialSearch.php A includes/widget/BasicSearchResultSetWidget.php R includes/widget/FullSearchResultWidget.php A includes/widget/InterwikiSearchResultSetWidget.php A includes/widget/SearchResultWidget.php R includes/widget/SimpleSearchResultWidget.php 7 files changed, 339 insertions(+), 227 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core refs/changes/15/322015/1 diff --git a/autoload.php b/autoload.php index 99d7303..1f67fa3 100644 --- a/autoload.php +++ b/autoload.php @@ -920,15 +920,17 @@ 'MediaWiki\\Tidy\\RaggettWrapper' => __DIR__ . '/includes/tidy/RaggettWrapper.php', 'MediaWiki\\Tidy\\TidyDriverBase' => __DIR__ . '/includes/tidy/TidyDriverBase.php', 'MediaWiki\\Widget\\BasicSearchResultSetWidget' => __DIR__ . '/includes/widget/BasicSearchResultSetWidget.php', - 'MediaWiki\\Widget\\BasicSearchResultWidget' => __DIR__ . '/includes/widget/BasicSearchResultWidget.php', 'MediaWiki\\Widget\\ComplexNamespaceInputWidget' => __DIR__ . '/includes/widget/ComplexNamespaceInputWidget.php', 'MediaWiki\\Widget\\ComplexTitleInputWidget' => __DIR__ . '/includes/widget/ComplexTitleInputWidget.php', 'MediaWiki\\Widget\\DateInputWidget' => __DIR__ . '/includes/widget/DateInputWidget.php', 'MediaWiki\\Widget\\DateTimeInputWidget' => __DIR__ . '/includes/widget/DateTimeInputWidget.php', - 'MediaWiki\\Widget\\InterwikiSearchResultWidget' => __DIR__ . '/includes/widget/InterwikiSearchResultWidget.php', + 'MediaWiki\\Widget\\FullSearchResultWidget' => __DIR__ . '/includes/widget/FullSearchResultWidget.php', + 'MediaWiki\\Widget\\InterwikiSearchResultSetWidget' => __DIR__ . '/includes/widget/InterwikiSearchResultSetWidget.php', 'MediaWiki\\Widget\\NamespaceInputWidget' => __DIR__ . '/includes/widget/NamespaceInputWidget.php', 'MediaWiki\\Widget\\SearchFormWidget' => __DIR__ . '/includes/widget/SearchFormWidget.php', 'MediaWiki\\Widget\\SearchInputWidget' => __DIR__ . '/includes/widget/SearchInputWidget.php', + 'MediaWiki\\Widget\\SearchResultWidget' => __DIR__ . '/includes/widget/SearchResultWidget.php', + 'MediaWiki\\Widget\\SimpleSearchResultWidget' => __DIR__ . '/includes/widget/SimpleSearchResultWidget.php', 'MediaWiki\\Widget\\TitleInputWidget' => __DIR__ . '/includes/widget/TitleInputWidget.php', 'MediaWiki\\Widget\\UserInputWidget' => __DIR__ . '/includes/widget/UserInputWidget.php', 'MemCachedClientforWiki' => __DIR__ . '/includes/compat/MemcachedClientCompat.php', diff --git a/includes/specials/SpecialSearch.php b/includes/specials/SpecialSearch.php index 2ab5e58..943bb57 100644 --- a/includes/specials/SpecialSearch.php +++ b/includes/specials/SpecialSearch.php @@ -269,19 +269,45 @@ public function showResults( $term ) { global $wgContLang; + $out = $this->getOutput(); + $filePrefix = $wgContLang->getFormattedNsText( NS_FILE ) . ':'; + $isEmptySearch = trim( $term ) === '' || $filePrefix === trim( $term ); + + if ( $this->searchEngineType !== null ) { + $this->setExtraParam( 'srbackend', $this->searchEngineType ); + } + + $formWidget = new MediaWiki\Widget\SearchFormWidget( + $this, + $this->searchConfig, + $this->getSearchProfiles() + ); + if ( trim( $term ) === '' || $filePrefix === trim( $term ) ) { + // empty search request. Just form and quit. + if ( !Hooks::run( 'SpecialSearchResultsPrepend', [ $this, $out, $term ] ) ) { + # Hook requested termination + return; + } + $out->enableOOUI(); + $out->addHtml( $formWidget->render( $profile, $term, 0, 0, $this->offset, $this->isPowerSearch(), $this->profile ) ); + return; + } + $search = $this->getSearchEngine(); $search->setFeatureData( 'rewrite', $this->runSuggestion ); $search->setLimitOffset( $this->limit, $this->offset ); $search->setNamespaces( $this->namespaces ); $search->prefix = $this->mPrefix; $term = $search->transformSearchTerm( $term ); - $out = $this->getOutput(); - Hooks::run( 'SpecialSearchSetupEngine', [ $this, $this->profile, $search ] ); + if ( !Hooks::run( 'SpecialSearchResultsPrepend', [ $this, $out, $term ] ) ) { # Hook requested termination return; } + + // For BC purposes this should come after SpecialSearchResultsPrepend + $out->enableOOUI(); $title = Title::newFromText( $term ); $showSuggestion = $title === null || !$title->isKnown(); @@ -289,7 +315,6 @@ // fetch search results $rewritten = $search->replacePrefixes( $term ); - $titleMatches = $search->searchTitle( $rewritten ); $textMatches = $search->searchText( $rewritten ); @@ -301,7 +326,7 @@ // did you mean... suggestions $didYouMeanHtml = ''; - if ( $showSuggestion && $textMatches ) { + if ( $textMatches ) { if ( $textMatches->hasRewrittenQuery() ) { $didYouMeanHtml = $this->getDidYouMeanRewrittenHtml( $term, $textMatches ); } elseif ( $textMatches->hasSuggestion() ) { @@ -322,18 +347,12 @@ $num = $titleMatchesNum + $textMatchesNum; $totalRes = $numTitleMatches + $numTextMatches; - // start rendering the page - $out->enableOOUI(); - $formWidget = new MediaWiki\Widget\SearchFormWidget( - $this, - $this->searchConfig, - $this->getSearchProfiles() - ); + // Render the top form for search, profile selectors, etc. + // @TODO: See if we can do this before attempting to call search engine. $out->addHtml( $formWidget->render( $profile, $term, $num, $totalRes, $this->offset, $this->isPowerSearch(), $this->profile ) ); - $filePrefix = $wgContLang->getFormattedNsText( NS_FILE ) . ':'; - if ( trim( $term ) === '' || $filePrefix === trim( $term ) ) { - // Empty query -- straight view of search form + // Empty query -- straight view of search form + if ( $isEmptySearch ) { return; } @@ -347,87 +366,59 @@ ) ); } + // Show the create link ahead + if ( !$textStatus || $textStatus->isOK() ) { + $this->showCreateLink( $title, $num, $titleMatches, $textMatches ); + } + // prev/next links $prevnext = null; - if ( $num || $this->offset ) { - // Show the create link ahead - $this->showCreateLink( $title, $num, $titleMatches, $textMatches ); - if ( $totalRes > $this->limit || $this->offset ) { - if ( $this->searchEngineType !== null ) { - $this->setExtraParam( 'srbackend', $this->searchEngineType ); - } - $prevnext = $this->getLanguage()->viewPrevNext( - $this->getPageTitle(), - $this->offset, - $this->limit, - $this->powerSearchOptions() + [ 'search' => $term ], - $this->limit + $this->offset >= $totalRes - ); - } + if ( $totalRes > $this->limit || $this->offset ) { + $prevnext = $this->getLanguage()->viewPrevNext( + $this->getPageTitle(), + $this->offset, + $this->limit, + $this->powerSearchOptions() + [ 'search' => $term ], + $this->limit + $this->offset >= $totalRes + ); } Hooks::run( 'SpecialSearchResults', [ $term, &$titleMatches, &$textMatches ] ); $out->parserOptions()->setEditSection( false ); - if ( $titleMatches ) { - if ( $numTitleMatches > 0 ) { - $out->wrapWikiMsg( "==$1==\n", 'titlematches' ); - $out->addHTML( $this->showMatches( $titleMatches ) ); - } - $titleMatches->free(); - } - - if ( $textMatches ) { - // output appropriate heading - if ( $numTextMatches > 0 && $numTitleMatches > 0 ) { - $out->addHTML( '<div class="mw-search-visualclear"></div>' ); - // if no title matches the heading is redundant - $out->wrapWikiMsg( "==$1==\n", 'textmatches' ); - } - - // show results - if ( $numTextMatches > 0 ) { - $search->augmentSearchResults( $textMatches ); - $out->addHTML( $this->showMatches( $textMatches ) ); - } - - // show secondary interwiki results if any - if ( $textMatches->hasInterwikiResults( SearchResultSet::SECONDARY_RESULTS ) ) { - $out->addHTML( $this->showInterwiki( $textMatches->getInterwikiResults( - SearchResultSet::SECONDARY_RESULTS ), $term ) ); - } - } - - $hasOtherResults = $textMatches && - $textMatches->hasInterwikiResults( SearchResultSet::INLINE_RESULTS ); if ( $num === 0 ) { if ( $textStatus && !$textStatus->isOK() ) { $out->addHTML( '<div class="error">' . $textStatus->getMessage( 'search-error' ) . '</div>' ); } else { - if ( !$this->offset ) { - // If we have an offset the create link was rendered earlier in this function. - // This class needs a good de-spaghettification, but for now this will - // do the job. - $this->showCreateLink( $title, $num, $titleMatches, $textMatches ); - } - $out->wrapWikiMsg( "<p class=\"mw-search-nonefound\">\n$1</p>", - [ $hasOtherResults ? 'search-nonefound-thiswiki' : 'search-nonefound', - wfEscapeWikiText( $term ) - ] ); + $hasOtherResults = $textMatches && + $textMatches->hasInterwikiResults( SearchResultSet::INLINE_RESULTS ); + + $out->addHTML( + "<p class='mw-search-nonefound'>" . + $this->msg( $hasOtherResults ? 'search-nonefound-thiswiki' : 'search-nonefound', $term )->escaped() . + "</p>" + ); } + } else { + if ( $numTextMatches > 0 ) { + $search->augmentSearchResults( $textMatches ); + } + $widget = new MediaWiki\Widget\BasicSearchResultSetWidget( + $this, + // Single search results in the main view + new \MediaWiki\Widget\FullSearchResultWidget( $this ), + // Sidebar of interwiki search results + new \MediaWiki\Widget\InterwikiSearchResultSetWidget( + $this, + new \MediaWiki\Widget\SimpleSearchResultWidget( $this ) + ) + ); + $out->addHtml( $widget->render( $term, $this->offset, $titleMatches, $textMatches ) ); } - if ( $hasOtherResults ) { - foreach ( $textMatches->getInterwikiResults( SearchResultSet::INLINE_RESULTS ) - as $interwiki => $interwikiResult ) { - if ( $interwikiResult instanceof Status || $interwikiResult->numRows() == 0 ) { - // ignore bad interwikis for now - continue; - } - // TODO: wiki header - $out->addHTML( $this->showMatches( $interwikiResult, $interwiki ) ); - } + if ( $titleMatches ) { + $titleMatches->free(); } if ( $textMatches ) { @@ -443,18 +434,6 @@ $out->addHTML( "</div>" ); Hooks::run( 'SpecialSearchResultsAppend', [ $this, $out, $term ] ); - } - - /** - * Produce wiki header for interwiki results - * @param string $interwiki Interwiki name - * @param SearchResultSet $interwikiResult The result set - * @return string - */ - protected function interwikiHeader( $interwiki, $interwikiResult ) { - // TODO: we need to figure out how to name wikis correctly - $wikiMsg = $this->msg( 'search-interwiki-results-' . $interwiki )->parse(); - return "<p class=\"mw-search-interwiki-header mw-search-visualclear\">\n$wikiMsg</p>"; } /** @@ -629,12 +608,12 @@ */ protected function powerSearchOptions() { $opt = []; - if ( !$this->isPowerSearch() ) { - $opt['profile'] = $this->profile; - } else { + if ( $this->isPowerSearch() ) { foreach ( $this->namespaces as $n ) { $opt['ns' . $n] = 1; } + } else { + $opt['profile'] = $this->profile; } return $opt + $this->extraParams; @@ -694,7 +673,9 @@ $pos = $this->offset; if ( $result && $interwiki ) { - $out .= $this->interwikiHeader( $interwiki, $matches ); + // TODO: we need to figure out how to name wikis correctly + $wikiMsg = $this->msg( 'search-interwiki-results-' . $interwiki )->parse(); + $out .= "<p class=\"mw-search-interwiki-header mw-search-visualclear\">\n$wikiMsg</p>"; } $out .= "<ul class='mw-search-results'>\n"; @@ -708,115 +689,6 @@ $out = $wgContLang->convert( $out ); return $out; - } - - /** - * Format a single hit result - * - * @param SearchResult $result - * @param array $terms Terms to highlight - * @param int $position Position within the search results, including offset. - * - * @return string - */ - protected function showHit( SearchResult $result, $terms, $position ) { - $widget = new \MediaWiki\Widget\BasicSearchResultWidget( $this ); - return $widget->render( $result, $terms, $position ); - } - - /** - * Extract custom captions from search-interwiki-custom message - */ - protected function getCustomCaptions() { - if ( is_null( $this->customCaptions ) ) { - $this->customCaptions = []; - // format per line <iwprefix>:<caption> - $customLines = explode( "\n", $this->msg( 'search-interwiki-custom' )->text() ); - foreach ( $customLines as $line ) { - $parts = explode( ":", $line, 2 ); - if ( count( $parts ) == 2 ) { // validate line - $this->customCaptions[$parts[0]] = $parts[1]; - } - } - } - } - - /** - * Show results from other wikis - * - * @param SearchResultSet|array $matches - * @param string $query - * - * @return string - */ - protected function showInterwiki( $matches, $query ) { - global $wgContLang; - - - // work out custom project captions - $this->getCustomCaptions(); - - if ( !is_array( $matches ) ) { - $matches = [ $matches ]; - } - - $iwResults = []; - foreach ( $matches as $set ) { - $result = $set->next(); - while ( $result ) { - if ( !$result->isBrokenTitle() ) { - $iwResults[$result->getTitle()->getInterwiki()][] = $result; - } - $result = $set->next(); - } - } - - $out = ''; - $widget = new MediaWiki\Widget\InterwikiSearchResultWidget( $this ); - foreach ( $iwResults as $iwPrefix => $results ) { - $out .= $this->iwHeaderHtml( $iwPrefix ); - $out .= "<ul class='mw-search-iwresults'>"; - foreach ( $results as $result ) { - $out .= $widget->render( $result ); - } - $ot .= "</ul>"; - } - - $out = - "<div id='mw-search-interwiki'>" . - "<div id='mw-search-interwiki-caption'>" . - $this->msg( 'search-interwiki-caption' )->text() . - "</div>" . - $out . - "</div>"; - - // convert the whole thing to desired language variant - return $wgContLang->convert( $out ); - } - - protected function iwHeaderHtml( $iwPrefix ) { - if ( isset( $this->customCaptions[$iwPrefix] ) ) { - $caption = $this->customCaptions[$iwPrefix]; - } else { - $iwLookup = MediaWiki\MediaWikiServices::getInstance()->getInterwikiLookup(); - $interwiki = $iwLookup->fetch( $iwPrefix ); - $parsed = wfParseUrl( wfExpandUrl( $interwiki ? $interwiki->getURL() : '/' ) ); - $caption = $this->msg( 'search-interwiki-default', $parsed['host'] )->text(); - } - $searchLink = Linker::linkKnown( - Title::newFromText( "$iwPrefix:Special:Search" ), - $this->msg( 'search-interwiki-more' )->text(), - [], - [ - 'search' => $query, - 'fulltext' => 1, - ] - ); - return - "<div class='mw-search-interwiki-project'>" . - "<span class='mw-search-interwiki-more'>{$searchLink}</span>" . - $caption . - "</div>"; } /** diff --git a/includes/widget/BasicSearchResultSetWidget.php b/includes/widget/BasicSearchResultSetWidget.php new file mode 100644 index 0000000..182437b --- /dev/null +++ b/includes/widget/BasicSearchResultSetWidget.php @@ -0,0 +1,92 @@ +<?php + +namespace MediaWiki\Widget; + +use SearchResultSet; +use SpecialSearch; + +class BasicSearchResultSetWidget { + /** @var SpecialSearch */ + protected $specialPage; + /** @var SearchResultWidget */ + protected $resultWidget; + /** @var InterwikiSearchResultSetWidget */ + protected $sidebarWidget; + + public function __construct( SpecialSearch $specialPage, SearchResultWidget $resultWidget, InterwikiSearchResultSetWidget $sidbarWidget ) { + $this->specialPage = $specialPage; + $this->resultWidget = $resultWidget; + $this->sidbarWidget = $sidbarWidget; + } + + public function render( $term, $offset, SearchResultSet $titleResultSet = null, SearchResultSet $testResultSet = null ) { + global $wgContLang; + + $hasTitle = $titleResultSet ? $titleResultSet->numRows() > 0 : false; + $hasText = $testResultSet ? $testResultSet->numRows() > 0 : false; + $hasSecondary = $testResultSet + ? $testResultSet->hasInterwikiResults( SearchResultSet::SECONDARY_RESULTS ) + : false; + $hasSecondaryInline = $testResultSet + ? $testResultSet->hasInterwikiResults( SearchResultSet::INLINE_RESULTS ) + : false; + + + if ( !$hasTitle && !$hasText && !$hasSecondary && !$hasSecondaryInline ) { + return ''; + } + + $out = ''; + if ( $hasTitle ) { + $out .= $this->header( $this->specialPage->msg( 'titlematches' )->text() ) + . $this->renderOne( $titleResultSet ); + } + + if ( $hasText ) { + if ( $hasTitle ) { + $out .= "<div class='mw-search-visualclear'></div>" . + $this->header( $this->specialPage->msg( 'textmatches' ) ); + } + $out .= $this->renderOne( $testResultSet, $offset ); + } + + if ( $hasSecondaryInline ) { + foreach ( $testResultSet->getInterwikiResults( SearchResultSet::INLINE_RESULTS ) as $interwiki => $results ) { + if ( $results instanceof Status || $results->numRows() === 0 ) { + // ignore bad interwikis for now + continue; + } + $out .= + "<p class=>'mw-search-interwiki-header mw-search-visualclear'>" . + $this->specialPage->msg( "search-interwiki-results-{$interwiki}" )->parse() . + "</p>"; + $out .= $this->renderOne( $results, $offset ); + } + } + + if ( $hasSecondary ) { + $out .= $this->sidbarWidget->render( + $term, + $testResultSet->getInterwikiResults( SearchResultSet::SECONDARY_RESULTS ) + ); + } + + // Convert the whole thing to desired language variant + return $wgContLang->convert( $out ); + } + + protected function renderOne( SearchResultSet $resultSet, $offset ) { + global $wgContLang; + + $terms = $wgContLang->convertForSearchResult( $resultSet->termMatches() ); + + $hits = []; + $result = $resultSet->next(); + while ( $result ) { + $hits[] .= $this->resultWidget->render( $result, $terms, $offset++ ); + $result = $resultSet->next(); + } + + return "<ul class='mw-search-results'>" . implode( '', $hits ) . "</ul>"; + } +} diff --git a/includes/widget/BasicSearchResultWidget.php b/includes/widget/FullSearchResultWidget.php similarity index 85% rename from includes/widget/BasicSearchResultWidget.php rename to includes/widget/FullSearchResultWidget.php index 9627cbe..8e674a9 100644 --- a/includes/widget/BasicSearchResultWidget.php +++ b/includes/widget/FullSearchResultWidget.php @@ -8,7 +8,14 @@ use SpecialSearch; use Title; -class BasicSearchResultWidget { +/** + * Renders a 'full' multi-line search result with metadata. + * + * The Title + * some *highlighted* *text* about the search result + * 5KB (651 words) - 12:40, 6 Aug 2016 + */ +class FullSearchResultWidget implements SearchResultWidget { /** @var SpecialSearch */ protected $specialPage; @@ -31,7 +38,7 @@ $link = $this->generateMainLinkHtml( $result ); // If page content is not readable, just return ths title. - // This is not quite safe, but better than showing excerpts from + // This is not quite safe, but better than showing excerpts from // non-readable pages. Note that hiding the entry entirely would // screw up paging (really?). if ( !$result->getTitle()->userCan( 'read', $this->specialPage->getUser() ) ) { @@ -46,9 +53,14 @@ $this->specialPage->getUser() ); list( $file, $desc, $thumb ) = $this->generateFileHtml( $result ); - $extract = "<div class='searchresult'>" . - $result->getTextSnippet( $terms ) . - "</div>"; + $snippet = $result->getTextSnippet( $terms ); + if ( $snippet ) { + $extract = "<div class='searchresult'>" . + $result->getTextSnippet( $terms ) . + "</div>"; + } else { + $extract = ''; + } if ( $thumb === null ) { // If no thumb, then the description is about size @@ -63,7 +75,7 @@ if ( !Hooks::run( 'ShowSearchHit', [ $this->specialPage, $result, $terms, &$link, &$redirect, &$section, &$extract, - &$score, &$size, &$date, &$related, &$html + &$score, &$size, &$date, &$related, &$html ] ) ) { return $html; } @@ -71,14 +83,14 @@ // All the pieces have been collected. Now generate the final HTML $joined = "{$link} {$redirect} {$category} {$section} {$file}"; - $meta = "<div class='mw-search-result-data'>{$desc} - {$date}</div>"; + $meta = $this->buildMeta( $desc, $date ); if ( $thumb === null ) { - $html = + $html = "<div class='mw-search-result-heading'>{$joined}</div>" . "{$extract} {$meta}"; } else { - $html = + $html = "<table class='searchResultImage'>" . "<tr>" . "<td style='width: 120px; text-align: center; vertical-align: top'>" . @@ -119,9 +131,9 @@ protected function generateAltTitleHtml( $msgKey, Title $title = null, $text ) { $inner = $title === null - ? $text + ? $text : Linker::linkKnown( $title, $text ?: null ); - + return "<span class='searchalttitle'>" . $this->specialPage->msg( $msgKey )->rawParams( $inner )->text() . "</span>"; @@ -155,7 +167,8 @@ return $this->specialPage->msg( 'search-result-category-size' ) ->numParams( $cat->getPageCount(), $cat->getSubcatCount(), $cat->getFileCount() ) ->escaped(); - } else { + // TODO: This is a bit odd...but requires changing the i18n message to fix + } elseif ( $result->getByteSize() !== null || $result->getWordCount() > 0 ) { $lang = $this->specialPage->getLanguage(); $bytes = $lang->formatSize( $result->getByteSize() ); $words = $result->getWordCount(); @@ -164,6 +177,8 @@ ->numParams( $words ) ->escaped(); } + + return ''; } /** @@ -200,4 +215,18 @@ return [ $html, $descHtml, $thumbHtml ]; } + + protected function buildMeta( $desc, $date ) { + if ( $desc && $date ) { + $meta = "{$desc} - {$date}"; + } elseif ( $desc ) { + $meta = $desc; + } elseif ( $date ) { + $meta = $date; + } else { + return ''; + } + + return "<div class='mw-search-result-data'>{$meta}</div>"; + } } diff --git a/includes/widget/InterwikiSearchResultSetWidget.php b/includes/widget/InterwikiSearchResultSetWidget.php new file mode 100644 index 0000000..2c50c11 --- /dev/null +++ b/includes/widget/InterwikiSearchResultSetWidget.php @@ -0,0 +1,104 @@ +<?php + +namespace MediaWiki\Widget; + +use Linker; +use SpecialSearch; +use Title; + +class InterwikiSearchResultSetWidget { + /** @var SpecialSearch */ + protected $specialSearch; + /** @var SearchResultWidget */ + protected $resultWidget; + /** @var string[]|null */ + protected $customCaptions; + + public function __construct( SpecialSearch $specialSearch, SearchResultWidget $resultWidget ) { + $this->specialSearch = $specialSearch; + $this->resultWidget = $resultWidget; + } + + /** + * @var string $term + * @var SearchResultSet|SearchResultSet[] $resultSets + */ + public function render( $term, $resultSets ) { + global $wgContLang; + + if ( !is_array( $resultSets ) ) { + $resultSets = [$resultSets]; + } + + $this->loadCustomCaptions(); + + $iwResults = []; + foreach ( $resultSets as $resultSet ) { + $result = $resultSet->next(); + while ( $result ) { + if ( !$result->isBrokenTitle() ) { + $iwResults[$result->getTitle()->getInterwiki()][] = $result; + } + $result = $resultSet->next(); + } + } + + $out = ''; + foreach ( $iwResults as $iwPrefix => $results ) { + $out .= $this->headerHtml( $iwPrefix ); + $out .= "<ul class='mw-search-iwresults'>"; + foreach ( $results as $result ) { + $out .= $this->resultWidget->render( $result ); + } + $out .= "</ul>"; + } + + return + "<div id='mw-search-interwiki'>" . + "<div id='mw-search-interwiki-caption'>" . + $this->specialSearch->msg( 'search-interwiki-caption' )->text() . + '</div>' . + $out . + "</div>"; + } + + protected function headerHtml( $iwPrefix ) { + if ( isset( $this->customCaptions[$iwPrefix] ) ) { + $caption = $this->customCaptions[$iwPrefix]; + } else { + $iwLookup = \MediaWiki\MediaWikiServices::getInstance()->getInterwikiLookup(); + $interwiki = $iwLookup->fetch( $iwPrefix ); + $parsed = wfParseUrl( wfExpandUrl( $interwiki ? $interwiki->getURL() : '/' ) ); + $caption = $this->specialSearch->msg( 'search-interwiki-default', $parsed['host'] )->text(); + } + $searchLink = Linker::linkKnown( + Title::newFromText( "$iwPrefix:Special:Search" ), + $this->specialSearch->msg( 'search-interwiki-more' )->text(), + [], + [ + 'search' => $query, + 'fulltext' => 1, + ] + ); + return + "<div class='mw-search-interwiki-project'>" . + "<span class='mw-search-interwiki-more'>{$searchLink}</span>" . + $caption . + "</div>"; + } + + protected function loadCustomCaptions() { + if ( $this->customCaptions !== null ) { + return; + } + + $this->customCaptions = []; + $customLines = explode( "\n", $this->specialSearch->msg( 'search-interwiki-custom' )->text() ); + foreach ( $customLines as $line ) { + $parts = explode( ':', $line, 2 ); + if ( count( $parts ) === 2 ) { + $this->customCaptions[$parts[0]] = $parts[1]; + } + } + } +} diff --git a/includes/widget/SearchResultWidget.php b/includes/widget/SearchResultWidget.php new file mode 100644 index 0000000..30916ff --- /dev/null +++ b/includes/widget/SearchResultWidget.php @@ -0,0 +1,14 @@ +<?php + +namespace MediaWiki\Widget; + +use SearchResult; + +interface SearchResultWidget { + /** + * @param SearchResult $result The result to render + * @param string $terms Terms to be highlighted (@see SearchResult::getTextSnippet) + * @param int $position The result position, including offset + */ + public function render( SearchResult $result, $terms, $position ); +} diff --git a/includes/widget/InterwikiSearchResultWidget.php b/includes/widget/SimpleSearchResultWidget.php similarity index 74% rename from includes/widget/InterwikiSearchResultWidget.php rename to includes/widget/SimpleSearchResultWidget.php index b34df7c..4593344 100644 --- a/includes/widget/InterwikiSearchResultWidget.php +++ b/includes/widget/SimpleSearchResultWidget.php @@ -7,7 +7,10 @@ use SpecialSearch; use Title; -class InterwikiSearchResultWidget { +/** + * Renders a simple one-line result + */ +class SimpleSearchResultWidget implements SearchResultWidget { /** @var SpecialSearch */ protected $specialSearch; @@ -15,7 +18,7 @@ $this->specialSearch = $specialSearch; } - public function render( SearchResult $result, $lastInterwiki ) { + public function render( SearchResult $result, $terms, $position ) { $title = $result->getTitle(); $titleSnippet = $result->getTitleSnippet(); if ( $titleSnippet === '' ) { @@ -25,13 +28,9 @@ $link = Linker::linkKnown( $title, $titleSnippet ); $redirectTitle = $result->getRedirectTitle(); - $redirectText = $result->getRedirectSnippet(); $redirect = ''; - if ( $redirectTitle === null ) { - if ( $redirectText === '' ) { - $redirectText = null; - } - + if ( $redirectTitle !== null ) { + $redirectText = $result->getRedirectSnippet() ?: null; $redirect = "<span class='searchalttitle'>" . $this->specialSearch->msg( 'search-redirect' )->rawParams( -- To view, visit https://gerrit.wikimedia.org/r/322015 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: Id1d6b357f45a2cf615d9412cc95dd597c724e8b6 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/core Gerrit-Branch: master Gerrit-Owner: EBernhardson <ebernhard...@wikimedia.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits