'MediaWiki\\Widget\\DateTimeInputWidget' => __DIR__ . '/includes/widget/DateTimeInputWidget.php',
'MediaWiki\\Widget\\NamespaceInputWidget' => __DIR__ . '/includes/widget/NamespaceInputWidget.php',
'MediaWiki\\Widget\\SearchInputWidget' => __DIR__ . '/includes/widget/SearchInputWidget.php',
+ 'MediaWiki\\Widget\\Search\\FullSearchResultWidget' => __DIR__ . '/includes/widget/search/FullSearchResultWidget.php',
+ 'MediaWiki\\Widget\\Search\\SearchResultWidget' => __DIR__ . '/includes/widget/search/SearchResultWidget.php',
+ 'MediaWiki\\Widget\\Search\\SimpleSearchResultWidget' => __DIR__ . '/includes/widget/search/SimpleSearchResultWidget.php',
'MediaWiki\\Widget\\TitleInputWidget' => __DIR__ . '/includes/widget/TitleInputWidget.php',
'MediaWiki\\Widget\\UserInputWidget' => __DIR__ . '/includes/widget/UserInputWidget.php',
'MemCachedClientforWiki' => __DIR__ . '/includes/compat/MemcachedClientCompat.php',
function returned false.
'ShowSearchHitTitle': Customise display of search hit title/link.
-&$title: Title to link to
+$title: Title to link to
&$text: Text to use for the link
$result: The search result
$terms: The search terms entered
}
$out .= "<ul class='mw-search-results'>\n";
+ $widget = new \MediaWiki\Widget\Search\FullSearchResultWidget(
+ $this,
+ $this->getLinkRenderer()
+ );
while ( $result ) {
- $out .= $this->showHit( $result, $terms, $pos++ );
+ $out .= $widget->render( $result, $terms, $pos++ );
$result = $matches->next();
}
$out .= "</ul>\n";
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 ) {
- if ( $result->isBrokenTitle() ) {
- return '';
- }
-
- $title = $result->getTitle();
-
- $titleSnippet = $result->getTitleSnippet();
-
- if ( $titleSnippet == '' ) {
- $titleSnippet = null;
- }
-
- $link_t = clone $title;
- $query = [];
-
- Hooks::run( 'ShowSearchHitTitle',
- [ &$link_t, &$titleSnippet, $result, $terms, $this, &$query ] );
-
- $linkRenderer = $this->getLinkRenderer();
-
- if ( $titleSnippet !== null ) {
- $titleSnippet = new HtmlArmor( $titleSnippet );
- }
-
- $link = $linkRenderer->makeKnownLink(
- $link_t,
- $titleSnippet,
- [ 'data-serp-pos' => $position ], // HTML attributes
- $query
- );
-
- // 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 ( !$title->userCan( 'read', $this->getUser() ) ) {
- return "<li>{$link}</li>\n";
- }
-
- // 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() ) {
- return '';
- }
-
- // format redirects / relevant sections
- $redirectTitle = $result->getRedirectTitle();
- $redirectText = $result->getRedirectSnippet();
- $sectionTitle = $result->getSectionTitle();
- $sectionText = $result->getSectionSnippet();
- $categorySnippet = $result->getCategorySnippet();
-
- $redirect = '';
- if ( !is_null( $redirectTitle ) ) {
- if ( $redirectText == '' ) {
- $redirectText = null;
- }
-
- if ( $redirectText !== null ) {
- $redirectText = new HtmlArmor( $redirectText );
- }
-
- $redirect = "<span class='searchalttitle'>" .
- $this->msg( 'search-redirect' )->rawParams(
- $linkRenderer->makeKnownLink( $redirectTitle, $redirectText ) )->text() .
- "</span>";
- }
-
- $section = '';
- if ( !is_null( $sectionTitle ) ) {
- if ( $sectionText == '' ) {
- $sectionText = null;
- }
-
- if ( $sectionText !== null ) {
- $sectionText = new HtmlArmor( $sectionText );
- }
-
- $section = "<span class='searchalttitle'>" .
- $this->msg( 'search-section' )->rawParams(
- $linkRenderer->makeKnownLink( $sectionTitle, $sectionText ) )->text() .
- "</span>";
- }
-
- $category = '';
- if ( $categorySnippet ) {
- $category = "<span class='searchalttitle'>" .
- $this->msg( 'search-category' )->rawParams( $categorySnippet )->text() .
- "</span>";
- }
-
- // format text extract
- $extract = "<div class='searchresult'>" . $result->getTextSnippet( $terms ) . "</div>";
-
- $lang = $this->getLanguage();
-
- // format description
- $byteSize = $result->getByteSize();
- $wordCount = $result->getWordCount();
- $timestamp = $result->getTimestamp();
- $size = $this->msg( 'search-result-size', $lang->formatSize( $byteSize ) )
- ->numParams( $wordCount )->escaped();
-
- if ( $title->getNamespace() == NS_CATEGORY ) {
- $cat = Category::newFromTitle( $title );
- $size = $this->msg( 'search-result-category-size' )
- ->numParams( $cat->getPageCount(), $cat->getSubcatCount(), $cat->getFileCount() )
- ->escaped();
- }
-
- $date = $lang->userTimeAndDate( $timestamp, $this->getUser() );
-
- $fileMatch = '';
- // Include a thumbnail for media files...
- if ( $title->getNamespace() == NS_FILE ) {
- $img = $result->getFile();
- $img = $img ?: wfFindFile( $title );
- if ( $result->isFileMatch() ) {
- $fileMatch = "<span class='searchalttitle'>" .
- $this->msg( 'search-file-match' )->escaped() . "</span>";
- }
- if ( $img ) {
- $thumb = $img->transform( [ 'width' => 120, 'height' => 120 ] );
- if ( $thumb ) {
- $desc = $this->msg( 'parentheses' )->rawParams( $img->getShortDesc() )->escaped();
- // Float doesn't seem to interact well with the bullets.
- // Table messes up vertical alignment of the bullets.
- // Bullets are therefore disabled (didn't look great anyway).
- return "<li>" .
- '<table class="searchResultImage">' .
- '<tr>' .
- '<td style="width: 120px; text-align: center; vertical-align: top;">' .
- $thumb->toHtml( [ 'desc-link' => true ] ) .
- '</td>' .
- '<td style="vertical-align: top;">' .
- "{$link} {$redirect} {$category} {$section} {$fileMatch}" .
- $extract .
- "<div class='mw-search-result-data'>{$desc} - {$date}</div>" .
- '</td>' .
- '</tr>' .
- '</table>' .
- "</li>\n";
- }
- }
- }
-
- $html = null;
-
- $score = '';
- $related = '';
- if ( Hooks::run( 'ShowSearchHit', [
- $this, $result, $terms,
- &$link, &$redirect, &$section, &$extract,
- &$score, &$size, &$date, &$related,
- &$html
- ] ) ) {
- $html = "<li><div class='mw-search-result-heading'>" .
- "{$link} {$redirect} {$category} {$section} {$fileMatch}</div> {$extract}\n" .
- "<div class='mw-search-result-data'>{$size} - {$date}</div>" .
- "</li>\n";
- }
-
- return $html;
- }
-
/**
* Extract custom captions from search-interwiki-custom message
*/
* Show results from other wikis
*
* @param SearchResultSet|array $matches
- * @param string $query
+ * @param string $terms
*
* @return string
*/
- protected function showInterwiki( $matches, $query ) {
+ protected function showInterwiki( $matches, $terms ) {
global $wgContLang;
- $out = "<div id='mw-search-interwiki'><div id='mw-search-interwiki-caption'>" .
- $this->msg( 'search-interwiki-caption' )->text() . "</div>\n";
- $out .= "<ul class='mw-search-iwresults'>\n";
-
// work out custom project captions
$this->getCustomCaptions();
$matches = [ $matches ];
}
+ $iwResults = [];
foreach ( $matches as $set ) {
- $prev = null;
$result = $set->next();
while ( $result ) {
- $out .= $this->showInterwikiHit( $result, $prev, $query );
- $prev = $result->getInterwikiPrefix();
+ if ( !$result->isBrokenTitle() ) {
+ $iwResults[$result->getTitle()->getInterwiki()][] = $result;
+ }
$result = $set->next();
}
}
- // @todo Should support paging in a non-confusing way (not sure how though, maybe via ajax)..
- $out .= "</ul></div>\n";
+ $out = '';
+ $widget = new MediaWiki\Widget\Search\SimpleSearchResultWidget(
+ $this,
+ $this->getLinkRenderer()
+ );
+ foreach ( $iwResults as $iwPrefix => $results ) {
+ $out .= $this->iwHeaderHtml( $iwPrefix, $terms );
+ $out .= "<ul class='mw-search-iwresults'>";
+ foreach ( $results as $result ) {
+ // This makes the bold asumption interwiki results are never paginated.
+ // That's currently true, but could change at some point?
+ $out .= $widget->render( $result, $terms, 0 );
+ }
+ $out .= "</ul>";
+ }
- // convert the whole thing to desired language variant
- $out = $wgContLang->convert( $out );
+ $out =
+ "<div id='mw-search-interwiki'>" .
+ "<div id='mw-search-interwiki-caption'>" .
+ $this->msg( 'search-interwiki-caption' )->escaped() .
+ "</div>" .
+ $out .
+ "</div>";
- return $out;
+ // convert the whole thing to desired language variant
+ return $wgContLang->convert( $out );
}
/**
- * Show single interwiki link
- *
- * @param SearchResult $result
- * @param string $lastInterwiki
- * @param string $query
- *
- * @return string
+ * @param string $iwPrefix The interwiki prefix to render a header for
+ * @param string $terms The user-provided search terms
*/
- protected function showInterwikiHit( $result, $lastInterwiki, $query ) {
- if ( $result->isBrokenTitle() ) {
- return '';
- }
-
- $linkRenderer = $this->getLinkRenderer();
-
- $title = $result->getTitle();
-
- $titleSnippet = $result->getTitleSnippet();
-
- if ( $titleSnippet == '' ) {
- $titleSnippet = null;
- }
-
- if ( $titleSnippet !== null ) {
- $titleSnippet = new HtmlArmor( $titleSnippet );
- }
-
- $link = $linkRenderer->makeKnownLink(
- $title,
- $titleSnippet
+ protected function iwHeaderHtml( $iwPrefix, $terms ) {
+ 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' => $terms,
+ 'fulltext' => 1,
+ ]
);
-
- // format redirect if any
- $redirectTitle = $result->getRedirectTitle();
- $redirectText = $result->getRedirectSnippet();
- $redirect = '';
- if ( !is_null( $redirectTitle ) ) {
- if ( $redirectText == '' ) {
- $redirectText = null;
- }
-
- if ( $redirectText !== null ) {
- $redirectText = new HtmlArmor( $redirectText );
- }
-
- $redirect = "<span class='searchalttitle'>" .
- $this->msg( 'search-redirect' )->rawParams(
- $linkRenderer->makeKnownLink( $redirectTitle, $redirectText ) )->text() .
- "</span>";
- }
-
- $out = "";
- // display project name
- if ( is_null( $lastInterwiki ) || $lastInterwiki != $title->getInterwiki() ) {
- if ( array_key_exists( $title->getInterwiki(), $this->customCaptions ) ) {
- // captions from 'search-interwiki-custom'
- $caption = $this->customCaptions[$title->getInterwiki()];
- } else {
- // default is to show the hostname of the other wiki which might suck
- // if there are many wikis on one hostname
- $parsed = wfParseUrl( $title->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( $title->getInterwiki() . ":Special:Search" );
- $searchLink = $linkRenderer->makeKnownLink(
- $searchTitle,
- $this->msg( 'search-interwiki-more' )->text(),
- [],
- [
- 'search' => $query,
- 'fulltext' => 'Search'
- ]
- );
- $out .= "</ul><div class='mw-search-interwiki-project'><span class='mw-search-interwiki-more'>
- {$searchLink}</span>{$caption}</div>\n<ul>";
- }
-
- $out .= "<li>{$link} {$redirect}</li>\n";
-
- return $out;
+ return
+ "<div class='mw-search-interwiki-project'>" .
+ "<span class='mw-search-interwiki-more'>{$searchLink}</span>" .
+ $caption .
+ "</div>";
}
/**
--- /dev/null
+<?php
+
+namespace MediaWiki\Widget\Search;
+
+use Category;
+use Hooks;
+use HtmlArmor;
+use MediaWiki\Linker\LinkRenderer;
+use SearchResult;
+use SpecialSearch;
+use Title;
+
+/**
+ * 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;
+ /** @var LinkRenderer */
+ protected $linkRenderer;
+
+ public function __construct( SpecialSearch $specialPage, LinkRenderer $linkRenderer ) {
+ $this->specialPage = $specialPage;
+ $this->linkRenderer = $linkRenderer;
+ }
+
+ /**
+ * @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
+ * @return string HTML
+ */
+ public function render( SearchResult $result, $terms, $position ) {
+ // 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... on well. :P
+ if ( $result->isBrokenTitle() || $result->isMissingRevision() ) {
+ return '';
+ }
+
+ $link = $this->generateMainLinkHtml( $result, $terms, $position );
+ // If page content is not readable, just return ths 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 (really?).
+ if ( !$result->getTitle()->userCan( 'read', $this->specialPage->getUser() ) ) {
+ return "<li>{$link}</li>";
+ }
+
+ $redirect = $this->generateRedirectHtml( $result );
+ $section = $this->generateSectionHtml( $result );
+ $category = $this->generateCategoryHtml( $result );
+ $date = $this->specialPage->getLanguage()->userTimeAndDate(
+ $result->getTimestamp(),
+ $this->specialPage->getUser()
+ );
+ list( $file, $desc, $thumb ) = $this->generateFileHtml( $result );
+ $snippet = $result->getTextSnippet( $terms );
+ if ( $snippet ) {
+ $extract = "<div class='searchresult'>$snippet</div>";
+ } else {
+ $extract = '';
+ }
+
+ if ( $thumb === null ) {
+ // If no thumb, then the description is about size
+ $desc = $this->generateSizeHtml( $result );
+
+ // Let hooks do their own final construction if desired.
+ // FIXME: Not sure why this is only for results without thumbnails,
+ // but keeping it as-is for now to prevent breaking hook consumers.
+ $html = null;
+ $score = '';
+ $related = '';
+ if ( !Hooks::run( 'ShowSearchHit', [
+ $this->specialPage, $result, $terms,
+ &$link, &$redirect, &$section, &$extract,
+ &$score, &$size, &$date, &$related, &$html
+ ] ) ) {
+ return $html;
+ }
+ }
+
+ // All the pieces have been collected. Now generate the final HTML
+ $joined = "{$link} {$redirect} {$category} {$section} {$file}";
+ $meta = $this->buildMeta( $desc, $date );
+
+ if ( $thumb === null ) {
+ $html =
+ "<div class='mw-search-result-heading'>{$joined}</div>" .
+ "{$extract} {$meta}";
+ } else {
+ $html =
+ "<table class='searchResultImage'>" .
+ "<tr>" .
+ "<td style='width: 120px; text-align: center; vertical-align: top'>" .
+ $thumb .
+ "</td>" .
+ "<td style='vertical-align: top'>" .
+ "{$joined} {$extract} {$meta}" .
+ "</td>" .
+ "</tr>" .
+ "</table>";
+ }
+
+ return "<li>{$html}</li>";
+ }
+
+ /**
+ * Generates HTML for the primary call to action. It is
+ * typically the article title, but the search engine can
+ * return an exact snippet to use (typically the article
+ * title with highlighted words).
+ *
+ * @param SearchResult $result
+ * @param string $terms
+ * @param int $position
+ * @return string HTML
+ */
+ protected function generateMainLinkHtml( SearchResult $result, $terms, $position ) {
+ $snippet = $result->getTitleSnippet();
+ if ( $snippet === '' ) {
+ $snippet = null;
+ } else {
+ $snippet = new HtmlArmor( $snippet );
+ }
+
+ // clone to prevent hook from changing the title stored inside $result
+ $title = clone $result->getTitle();
+ $queryString = [];
+
+ Hooks::run( 'ShowSearchHitTitle',
+ [ $title, &$snippet, $result, $terms, $this->specialPage, &$queryString ] );
+
+ $link = $this->linkRenderer->makeLink(
+ $title,
+ $snippet,
+ [ 'data-serp-pos' => $position ],
+ $queryString
+ );
+
+ return $link;
+ }
+
+ /**
+ * Generates an alternate title link, such as (redirect from <a>Foo</a>).
+ *
+ * @param string $msgKey i18n message used to wrap title
+ * @param Title|null $title The title to link to, or null to generate
+ * the message without a link. In that case $text must be non-null.
+ * @param string|null $text The text snippet to display, or null
+ * to use the title
+ * @return string HTML
+ */
+ protected function generateAltTitleHtml( $msgKey, Title $title = null, $text ) {
+ $inner = $title === null
+ ? $text
+ : $this->linkRenderer->makeLink( $title, $text ? new HtmlArmor( $text ) : null );
+
+ return "<span class='searchalttitle'>" .
+ $this->specialPage->msg( $msgKey )->rawParams( $inner )->text()
+ . "</span>";
+ }
+
+ /**
+ * @param SearchResult $result
+ * @return string HTML
+ */
+ protected function generateRedirectHtml( SearchResult $result ) {
+ $title = $result->getRedirectTitle();
+ return $title === null
+ ? ''
+ : $this->generateAltTitleHtml( 'search-redirect', $title, $result->getRedirectSnippet() );
+ }
+
+ /**
+ * @param SearchResult $result
+ * @return string HTML
+ */
+ protected function generateSectionHtml( SearchResult $result ) {
+ $title = $result->getSectionTitle();
+ return $title === null
+ ? ''
+ : $this->generateAltTitleHtml( 'search-section', $title, $result->getSectionSnippet() );
+ }
+
+ /**
+ * @param SearchResult $result
+ * @return string HTML
+ */
+ protected function generateCategoryHtml( SearchResult $result ) {
+ $snippet = $result->getCategorySnippet();
+ return $snippet
+ ? $this->generateAltTitleHtml( 'search-category', null, $snippet )
+ : '';
+ }
+
+ /**
+ * @param SearchResult $result
+ * @return string HTML
+ */
+ protected function generateSizeHtml( SearchResult $result ) {
+ $title = $result->getTitle();
+ if ( $title->getNamespace() === NS_CATEGORY ) {
+ $cat = Category::newFromTitle( $title );
+ return $this->specialPage->msg( 'search-result-category-size' )
+ ->numParams( $cat->getPageCount(), $cat->getSubcatCount(), $cat->getFileCount() )
+ ->escaped();
+ // 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();
+
+ return $this->specialPage->msg( 'search-result-size', $bytes )
+ ->numParams( $words )
+ ->escaped();
+ }
+
+ return '';
+ }
+
+ /**
+ * @param SearchResult $result
+ * @return array Three element array containing the main file html,
+ * a text description of the file, and finally the thumbnail html.
+ * If no thumbnail is available the second and third will be null.
+ */
+ protected function generateFileHtml( SearchResult $result ) {
+ $title = $result->getTitle();
+ if ( $title->getNamespace() !== NS_FILE ) {
+ return [ '', null, null ];
+ }
+
+ if ( $result->isFileMatch() ) {
+ $html = "<span class='searchalttitle'>" .
+ $this->specialPage->msg( 'search-file-match' )->escaped() .
+ "</span>";
+ } else {
+ $html = '';
+ }
+
+ $descHtml = null;
+ $thumbHtml = null;
+
+ $img = $result->getFile() ?: wfFindFile( $title );
+ if ( $img ) {
+ $thumb = $img->transform( [ 'width' => 120, 'height' => 120 ] );
+ if ( $thumb ) {
+ $descHtml = $this->specialPage->msg( 'parentheses' )
+ ->rawParams( $img->getShortDesc() )
+ ->escaped();
+ $thumbHtml = $thumb->toHtml( [ 'desc-link' => true ] );
+ }
+ }
+
+ return [ $html, $descHtml, $thumbHtml ];
+ }
+
+ /**
+ * @param string $desc HTML description of result, ex: size in bytes, or empty string
+ * @param string $date HTML representation of last edit date, or empty string
+ * @return string HTML A div combining $desc and $date with a separator in a <div>.
+ * If either is missing only one will be represented. If both are missing an empty
+ * string will be returned.
+ */
+ 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>";
+ }
+}
--- /dev/null
+<?php
+
+namespace MediaWiki\Widget\Search;
+
+use SearchResult;
+
+/**
+ * Renders a single search result to HTML
+ */
+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
+ * @return string HTML
+ */
+ public function render( SearchResult $result, $terms, $position );
+}
--- /dev/null
+<?php
+
+namespace MediaWiki\Widget\Search;
+
+use HtmlArmor;
+use MediaWiki\Linker\LinkRenderer;
+use SearchResult;
+use SpecialSearch;
+use Title;
+
+/**
+ * Renders a simple one-line result
+ */
+class SimpleSearchResultWidget implements SearchResultWidget {
+ /** @var SpecialSearch */
+ protected $specialSearch;
+ /** @var LinkRenderer */
+ protected $linkRenderer;
+
+ public function __construct( SpecialSearch $specialSearch, LinkRenderer $linkRenderer ) {
+ $this->specialSearch = $specialSearch;
+ $this->linkRenderer = $linkRenderer;
+ }
+
+ /**
+ * @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
+ * @return string HTML
+ */
+ public function render( SearchResult $result, $terms, $position ) {
+ $title = $result->getTitle();
+ $titleSnippet = $result->getTitleSnippet();
+ if ( $titleSnippet ) {
+ $titleSnippet = new HtmlArmor( $titleSnippet );
+ } else {
+ $titleSnippet = null;
+ }
+
+ $link = $this->linkRenderer->makeLink( $title, $titleSnippet );
+
+ $redirectTitle = $result->getRedirectTitle();
+ $redirect = '';
+ if ( $redirectTitle !== null ) {
+ $redirectText = $result->getRedirectSnippet();
+ if ( $redirectText ) {
+ $redirectText = new HtmlArmor( $redirectText );
+ } else {
+ $redirectText = null;
+ }
+ $redirect =
+ "<span class='searchalttitle'>" .
+ $this->specialSearch->msg( 'search-redirect' )->rawParams(
+ $this->linkRenderer->makeLink( $redirectTitle, $redirectText )
+ )->text() .
+ "</span>";
+ }
+
+ return "<li>{$link} {$redirect}</li>";
+ }
+}