MediaWiki REL1_39
FullSearchResultWidget.php
Go to the documentation of this file.
1<?php
2
4
5use Category;
6use Html;
7use HtmlArmor;
12use SearchResult;
14use Title;
15
25 protected $specialPage;
27 protected $linkRenderer;
29 private $hookRunner;
30
32 HookContainer $hookContainer
33 ) {
34 $this->specialPage = $specialPage;
35 $this->linkRenderer = $linkRenderer;
36 $this->hookRunner = new HookRunner( $hookContainer );
37 }
38
44 public function render( SearchResult $result, $position ) {
45 // If the page doesn't *exist*... our search index is out of date.
46 // The least confusing at this point is to drop the result.
47 // You may get less results, but... on well. :P
48 if ( $result->isBrokenTitle() || $result->isMissingRevision() ) {
49 return '';
50 }
51
52 $link = $this->generateMainLinkHtml( $result, $position );
53 // If page content is not readable, just return ths title.
54 // This is not quite safe, but better than showing excerpts from
55 // non-readable pages. Note that hiding the entry entirely would
56 // screw up paging (really?).
57 if ( !$this->specialPage->getAuthority()->definitelyCan( 'read', $result->getTitle() ) ) {
58 return Html::rawElement( 'li', [], $link );
59 }
60
61 $redirect = $this->generateRedirectHtml( $result );
62 $section = $this->generateSectionHtml( $result );
63 $category = $this->generateCategoryHtml( $result );
64 $date = htmlspecialchars(
65 $this->specialPage->getLanguage()->userTimeAndDate(
66 $result->getTimestamp(),
67 $this->specialPage->getUser()
68 )
69 );
70 list( $file, $desc, $thumb ) = $this->generateFileHtml( $result );
71 $snippet = $result->getTextSnippet();
72 if ( $snippet ) {
73 $extract = Html::rawElement( 'div', [ 'class' => 'searchresult' ], $snippet );
74 } else {
75 $extract = '';
76 }
77
78 if ( $thumb === null ) {
79 // If no thumb, then the description is about size
80 $desc = $this->generateSizeHtml( $result );
81
82 // Let hooks do their own final construction if desired.
83 // FIXME: Not sure why this is only for results without thumbnails,
84 // but keeping it as-is for now to prevent breaking hook consumers.
85 $html = null;
86 $score = '';
87 $related = '';
88 // TODO: remove this instanceof and always pass [], let implementors do the cast if
89 // they want to be SearchDatabase specific
90 $terms = $result instanceof \SqlSearchResult ? $result->getTermMatches() : [];
91 if ( !$this->hookRunner->onShowSearchHit( $this->specialPage, $result,
92 $terms, $link, $redirect, $section, $extract, $score,
93 // @phan-suppress-next-line PhanTypeMismatchArgument Type mismatch on pass-by-ref args
94 $desc, $date, $related, $html )
95 ) {
96 return $html;
97 }
98 }
99
100 // All the pieces have been collected. Now generate the final HTML
101 $joined = "{$link} {$redirect} {$category} {$section} {$file}";
102 $meta = $this->buildMeta( $desc, $date );
103
104 if ( $thumb === null ) {
105 $html = Html::rawElement(
106 'div',
107 [ 'class' => 'mw-search-result-heading' ],
108 $joined
109 );
110 $html .= $extract . ' ' . $meta;
111 } else {
112 $tableCells = Html::rawElement(
113 'td',
114 [ 'style' => 'width: 120px; text-align: center; vertical-align: top' ],
115 $thumb
116 ) . Html::rawElement(
117 'td',
118 [ 'style' => 'vertical-align: top' ],
119 "$joined $extract $meta"
120 );
121 $html = Html::rawElement(
122 'table',
123 [ 'class' => 'searchResultImage' ],
124 Html::rawElement(
125 'tr',
126 [],
127 $tableCells
128 )
129 );
130 }
131
132 return Html::rawElement( 'li', [ 'class' => 'mw-search-result' ], $html );
133 }
134
145 protected function generateMainLinkHtml( SearchResult $result, $position ) {
146 $snippet = $result->getTitleSnippet();
147 if ( $snippet === '' ) {
148 $snippet = null;
149 } else {
150 $snippet = new HtmlArmor( $snippet );
151 }
152
153 // clone to prevent hook from changing the title stored inside $result
154 $title = clone $result->getTitle();
155 $query = [];
156
157 $attributes = [ 'data-serp-pos' => $position ];
158 $this->hookRunner->onShowSearchHitTitle( $title, $snippet, $result,
159 $result instanceof \SqlSearchResult ? $result->getTermMatches() : [],
160 // @phan-suppress-next-line PhanTypeMismatchArgument Type mismatch on pass-by-ref args
161 $this->specialPage, $query, $attributes );
162
163 $link = $this->linkRenderer->makeLink(
164 $title,
165 $snippet,
166 $attributes,
167 $query
168 );
169
170 return $link;
171 }
172
183 protected function generateAltTitleHtml( $msgKey, ?Title $title, $text ) {
184 $inner = $title === null
185 ? $text
186 : $this->linkRenderer->makeLink( $title, $text ? new HtmlArmor( $text ) : null );
187
188 return "<span class='searchalttitle'>" .
189 $this->specialPage->msg( $msgKey )->rawParams( $inner )->parse()
190 . "</span>";
191 }
192
197 protected function generateRedirectHtml( SearchResult $result ) {
198 $title = $result->getRedirectTitle();
199 return $title === null
200 ? ''
201 : $this->generateAltTitleHtml( 'search-redirect', $title, $result->getRedirectSnippet() );
202 }
203
208 protected function generateSectionHtml( SearchResult $result ) {
209 $title = $result->getSectionTitle();
210 return $title === null
211 ? ''
212 : $this->generateAltTitleHtml( 'search-section', $title, $result->getSectionSnippet() );
213 }
214
219 protected function generateCategoryHtml( SearchResult $result ) {
220 $snippet = $result->getCategorySnippet();
221 return $snippet
222 ? $this->generateAltTitleHtml( 'search-category', null, $snippet )
223 : '';
224 }
225
230 protected function generateSizeHtml( SearchResult $result ) {
231 $title = $result->getTitle();
232 if ( $title->getNamespace() === NS_CATEGORY ) {
233 $cat = Category::newFromTitle( $title );
234 return $this->specialPage->msg( 'search-result-category-size' )
235 ->numParams( $cat->getMemberCount(), $cat->getSubcatCount(), $cat->getFileCount() )
236 ->escaped();
237 // TODO: This is a bit odd...but requires changing the i18n message to fix
238 } elseif ( $result->getByteSize() !== null || $result->getWordCount() > 0 ) {
239 return $this->specialPage->msg( 'search-result-size' )
240 ->sizeParams( $result->getByteSize() )
241 ->numParams( $result->getWordCount() )
242 ->escaped();
243 }
244
245 return '';
246 }
247
254 protected function generateFileHtml( SearchResult $result ) {
255 $title = $result->getTitle();
256 if ( $title->getNamespace() !== NS_FILE ) {
257 return [ '', null, null ];
258 }
259
260 if ( $result->isFileMatch() ) {
261 $html = Html::rawElement(
262 'span',
263 [ 'class' => 'searchalttitle' ],
264 $this->specialPage->msg( 'search-file-match' )->escaped()
265 );
266 } else {
267 $html = '';
268 }
269
270 $descHtml = null;
271 $thumbHtml = null;
272
273 $img = $result->getFile() ?: MediaWikiServices::getInstance()->getRepoGroup()
274 ->findFile( $title );
275 if ( $img ) {
276 $thumb = $img->transform( [ 'width' => 120, 'height' => 120 ] );
277 if ( $thumb ) {
278 $descHtml = $this->specialPage->msg( 'parentheses' )
279 ->rawParams( $img->getShortDesc() )
280 ->escaped();
281 $thumbHtml = $thumb->toHtml( [ 'desc-link' => true ] );
282 }
283 }
284
285 return [ $html, $descHtml, $thumbHtml ];
286 }
287
295 protected function buildMeta( $desc, $date ) {
296 if ( $desc && $date ) {
297 $meta = "{$desc} - {$date}";
298 } elseif ( $desc ) {
299 $meta = $desc;
300 } elseif ( $date ) {
301 $meta = $date;
302 } else {
303 return '';
304 }
305
306 return "<div class='mw-search-result-data'>{$meta}</div>";
307 }
308}
const NS_FILE
Definition Defines.php:70
const NS_CATEGORY
Definition Defines.php:78
Category objects are immutable, strictly speaking.
Definition Category.php:33
Marks HTML that shouldn't be escaped.
Definition HtmlArmor.php:30
This class is a collection of static functions that serve two purposes:
Definition Html.php:51
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
Class that generates HTML anchor link elements for pages.
Service locator for MediaWiki core services.
static getInstance()
Returns the global default instance of the top level service locator.
Renders a 'full' multi-line search result with metadata.
generateAltTitleHtml( $msgKey, ?Title $title, $text)
Generates an alternate title link, such as (redirect from Foo).
generateMainLinkHtml(SearchResult $result, $position)
Generates HTML for the primary call to action.
__construct(SpecialSearch $specialPage, LinkRenderer $linkRenderer, HookContainer $hookContainer)
NOTE: this class is being refactored into an abstract base class.
implements Special:Search - Run text & title search and display the output
Represents a title within MediaWiki.
Definition Title.php:49
Renders a single search result to HTML.
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
Definition router.php:42