EBernhardson has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/322012

Change subject: [WIP] Pull rendering of single result out of SpecialSearch
......................................................................

[WIP] Pull rendering of single result out of SpecialSearch

Bug: T150390
Change-Id: If78cb0c29ae394f16e465c15a8e8246c1b56dcea
---
M autoload.php
M includes/specials/SpecialSearch.php
A includes/widget/BasicSearchResultWidget.php
A includes/widget/InterwikiSearchResultWidget.php
4 files changed, 297 insertions(+), 230 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core 
refs/changes/12/322012/1

diff --git a/autoload.php b/autoload.php
index 30ef985..3b7d587 100644
--- a/autoload.php
+++ b/autoload.php
@@ -919,10 +919,13 @@
        'MediaWiki\\Tidy\\RaggettInternalPHP' => __DIR__ . 
'/includes/tidy/RaggettInternalPHP.php',
        '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\\NamespaceInputWidget' => __DIR__ . 
'/includes/widget/NamespaceInputWidget.php',
        'MediaWiki\\Widget\\SearchInputWidget' => __DIR__ . 
'/includes/widget/SearchInputWidget.php',
        'MediaWiki\\Widget\\TitleInputWidget' => __DIR__ . 
'/includes/widget/TitleInputWidget.php',
diff --git a/includes/specials/SpecialSearch.php 
b/includes/specials/SpecialSearch.php
index 6b3517f..9994839 100644
--- a/includes/specials/SpecialSearch.php
+++ b/includes/specials/SpecialSearch.php
@@ -741,155 +741,8 @@
         * @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 ] );
-
-               $link = Linker::linkKnown(
-                       $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;
-                       }
-
-                       $redirect = "<span class='searchalttitle'>" .
-                               $this->msg( 'search-redirect' )->rawParams(
-                                       Linker::linkKnown( $redirectTitle, 
$redirectText ) )->text() .
-                               "</span>";
-               }
-
-               $section = '';
-               if ( !is_null( $sectionTitle ) ) {
-                       if ( $sectionText == '' ) {
-                               $sectionText = null;
-                       }
-
-                       $section = "<span class='searchalttitle'>" .
-                               $this->msg( 'search-section' )->rawParams(
-                                       Linker::linkKnown( $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;
+               $widget = new \MediaWiki\Widget\BasicSearchResultWidget( $this 
);
+               return $widget->render( $result, $terms, $position );
        }
 
        /**
@@ -920,9 +773,6 @@
        protected function showInterwiki( $matches, $query ) {
                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();
@@ -931,97 +781,63 @@
                        $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\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
-               $out = $wgContLang->convert( $out );
-
-               return $out;
+               return $wgContLang->convert( $out );
        }
 
-       /**
-        * Show single interwiki link
-        *
-        * @param SearchResult $result
-        * @param string $lastInterwiki
-        * @param string $query
-        *
-        * @return string
-        */
-       protected function showInterwikiHit( $result, $lastInterwiki, $query ) {
-               if ( $result->isBrokenTitle() ) {
-                       return '';
+       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();
                }
-
-               $title = $result->getTitle();
-
-               $titleSnippet = $result->getTitleSnippet();
-
-               if ( $titleSnippet == '' ) {
-                       $titleSnippet = null;
-               }
-
-               $link = Linker::linkKnown(
-                       $title,
-                       $titleSnippet
+               $searchLink = Linker::linkKnown(
+                       Title::newFromText( "$iwPrefix:Special:Search" ),
+                       $this->msg( 'search-interwiki-more' )->text(),
+                       [],
+                       [
+                               'search' => $query,
+                               'fulltext' => 1,
+                       ]
                );
-
-               // format redirect if any
-               $redirectTitle = $result->getRedirectTitle();
-               $redirectText = $result->getRedirectSnippet();
-               $redirect = '';
-               if ( !is_null( $redirectTitle ) ) {
-                       if ( $redirectText == '' ) {
-                               $redirectText = null;
-                       }
-
-                       $redirect = "<span class='searchalttitle'>" .
-                               $this->msg( 'search-redirect' )->rawParams(
-                                       Linker::linkKnown( $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 = Linker::linkKnown(
-                               $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>";
        }
 
        /**
diff --git a/includes/widget/BasicSearchResultWidget.php 
b/includes/widget/BasicSearchResultWidget.php
new file mode 100644
index 0000000..9627cbe
--- /dev/null
+++ b/includes/widget/BasicSearchResultWidget.php
@@ -0,0 +1,203 @@
+<?php
+
+namespace MediaWiki\Widget;
+
+use Hooks;
+use Linker;
+use SearchResult;
+use SpecialSearch;
+use Title;
+
+class BasicSearchResultWidget {
+       /** @var SpecialSearch */
+       protected $specialPage;
+
+       public function __construct( SpecialSearch $specialPage ) {
+               $this->specialPage = $specialPage;
+       }
+       /**
+        * @param SearchResult $result The result to be rendered
+        * @param array $terms The terms to highlight (TODO: Move highlighting 
out of here?)
+        * @param int $position Position within the search results, including 
offset.
+        * @return string
+        */
+       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 );
+               // 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 
);
+               $extract = "<div class='searchresult'>" .
+                               $result->getTextSnippet( $terms ) .
+                       "</div>";
+
+               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. 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 = "<div class='mw-search-result-data'>{$desc} - 
{$date}</div>";
+
+               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>";
+       }
+
+       protected function generateMainLinkHtml( SearchResult $result ) {
+               $snippet = $result->getTitleSnippet();
+               if ( $snippet === '' ) {
+                       $snippet = null;
+               }
+
+               // 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 = Linker::linkKnown(
+                       $title,
+                       $snippet,
+                       [ 'data-serp-pos' => $position ],
+                       $queryString
+               );
+
+               return $link;
+       }
+
+       protected function generateAltTitleHtml( $msgKey, Title $title = null, 
$text ) {
+               $inner = $title === null
+                       ? $text 
+                       : Linker::linkKnown( $title, $text ?: null );
+               
+               return "<span class='searchalttitle'>" .
+                               $this->specialPage->msg( $msgKey )->rawParams( 
$inner )->text()
+                       . "</span>";
+       }
+
+       protected function generateRedirectHtml( SearchResult $result ) {
+               $title = $result->getRedirectTitle();
+               return $title === null
+                       ? ''
+                       : $this->generateAltTitleHtml( 'search-redirect', 
$title, $result->getRedirectSnippet() );
+       }
+
+       protected function generateSectionHtml( SearchResult $result ) {
+               $title = $result->getSectionTitle();
+               return $title === null
+                       ? ''
+                       : $this->generateAltTitleHtml( 'search-section', 
$title, $result->getSectionText() );
+       }
+
+       protected function generateCategoryHtml( SearchResult $result ) {
+               $snippet = $result->getCategorySnippet();
+               return $snippet
+                       ? $this->generateAltTitleHtml( 'search-category', null, 
$snippet )
+                       : '';
+       }
+
+       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();
+               } else {
+                       $lang = $this->specialPage->getLanguage();
+                       $bytes = $lang->formatSize( $result->getByteSize() );
+                       $words = $result->getWordCount();
+
+                       return $this->specialPage->msg( 'search-result-size', 
$bytes )
+                               ->numParams( $words )
+                               ->escaped();
+               }
+       }
+
+       /**
+        * @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>";
+               }
+
+               $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 ];
+       }
+}
diff --git a/includes/widget/InterwikiSearchResultWidget.php 
b/includes/widget/InterwikiSearchResultWidget.php
new file mode 100644
index 0000000..b34df7c
--- /dev/null
+++ b/includes/widget/InterwikiSearchResultWidget.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace MediaWiki\Widget;
+
+use Linker;
+use SearchResult;
+use SpecialSearch;
+use Title;
+
+class InterwikiSearchResultWidget {
+       /** @var SpecialSearch */
+       protected $specialSearch;
+
+       public function __construct( SpecialSearch $specialSearch ) {
+               $this->specialSearch = $specialSearch;
+       }
+
+       public function render( SearchResult $result, $lastInterwiki ) {
+               $title = $result->getTitle();
+               $titleSnippet = $result->getTitleSnippet();
+               if ( $titleSnippet === '' ) {
+                       $titleSnippet = null;
+               }
+
+               $link = Linker::linkKnown( $title, $titleSnippet );
+
+               $redirectTitle = $result->getRedirectTitle();
+               $redirectText = $result->getRedirectSnippet();
+               $redirect = '';
+               if ( $redirectTitle === null ) {
+                       if ( $redirectText === '' ) {
+                               $redirectText = null;
+                       }
+
+                       $redirect =
+                               "<span class='searchalttitle'>" .
+                                       $this->specialSearch->msg( 
'search-redirect' )->rawParams(
+                                               Linker::linkKnown( 
$redirectTitle, $redirectText )
+                                       )->text() .
+                               "</span>";
+               }
+
+               return "<li>{$link} {$redirect}</li>";
+       }
+}

-- 
To view, visit https://gerrit.wikimedia.org/r/322012
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: If78cb0c29ae394f16e465c15a8e8246c1b56dcea
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

Reply via email to