Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
3.32% |
22 / 662 |
|
3.57% |
1 / 28 |
CRAP | |
0.00% |
0 / 1 |
ImagePage | |
3.32% |
22 / 662 |
|
3.57% |
1 / 28 |
18620.28 | |
0.00% |
0 / 1 |
newPage | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setFile | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
loadFile | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
30 | |||
view | |
0.00% |
0 / 75 |
|
0.00% |
0 / 1 |
462 | |||
getDisplayedFile | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
showTOC | |
0.00% |
0 / 44 |
|
0.00% |
0 / 1 |
6 | |||
makeMetadataTable | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
20 | |||
getLanguageForRendering | |
84.62% |
11 / 13 |
|
0.00% |
0 / 1 |
4.06 | |||
openShowImage | |
0.00% |
0 / 222 |
|
0.00% |
0 / 1 |
1722 | |||
getThumbPrevText | |
0.00% |
0 / 21 |
|
0.00% |
0 / 1 |
30 | |||
makeSizeLink | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
12 | |||
printSharedImageText | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
90 | |||
getUploadUrl | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
2 | |||
uploadLinksBox | |
0.00% |
0 / 22 |
|
0.00% |
0 / 1 |
30 | |||
closeShowImage | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
imageHistory | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
6 | |||
queryImageLinks | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
2 | |||
imageLinks | |
0.00% |
0 / 88 |
|
0.00% |
0 / 1 |
272 | |||
imageDupes | |
0.00% |
0 / 25 |
|
0.00% |
0 / 1 |
20 | |||
showError | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
2 | |||
compare | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
doRenderLangOpt | |
0.00% |
0 / 29 |
|
0.00% |
0 / 1 |
20 | |||
createXmlOptionStringForLanguage | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
6 | |||
getThumbSizes | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
3 | |||
getFile | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
isLocal | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getDuplicates | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getForeignCategories | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | /** |
3 | * This program is free software; you can redistribute it and/or modify |
4 | * it under the terms of the GNU General Public License as published by |
5 | * the Free Software Foundation; either version 2 of the License, or |
6 | * (at your option) any later version. |
7 | * |
8 | * This program is distributed in the hope that it will be useful, |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | * GNU General Public License for more details. |
12 | * |
13 | * You should have received a copy of the GNU General Public License along |
14 | * with this program; if not, write to the Free Software Foundation, Inc., |
15 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
16 | * http://www.gnu.org/copyleft/gpl.html |
17 | * |
18 | * @file |
19 | */ |
20 | |
21 | use MediaWiki\Html\Html; |
22 | use MediaWiki\Language\LanguageCode; |
23 | use MediaWiki\Linker\Linker; |
24 | use MediaWiki\MainConfigNames; |
25 | use MediaWiki\MediaWikiServices; |
26 | use MediaWiki\Request\WebRequest; |
27 | use MediaWiki\SpecialPage\SpecialPage; |
28 | use MediaWiki\Title\Title; |
29 | use MediaWiki\Title\TitleArrayFromResult; |
30 | use MediaWiki\Xml\Xml; |
31 | use Wikimedia\Rdbms\IResultWrapper; |
32 | |
33 | /** |
34 | * Rendering of file description pages. |
35 | * |
36 | * @ingroup Media |
37 | * @method WikiFilePage getPage() |
38 | */ |
39 | class ImagePage extends Article { |
40 | use MediaFileTrait; |
41 | |
42 | /** @var File|false Only temporary false, most code can assume this is a File */ |
43 | private $displayImg; |
44 | |
45 | /** @var FileRepo */ |
46 | private $repo; |
47 | |
48 | /** @var bool */ |
49 | private $fileLoaded; |
50 | |
51 | /** @var string|false Guaranteed to be HTML, {@see File::getDescriptionText} */ |
52 | protected $mExtraDescription = false; |
53 | |
54 | /** |
55 | * @param Title $title |
56 | * @return WikiFilePage |
57 | */ |
58 | protected function newPage( Title $title ) { |
59 | // Overload mPage with a file-specific page |
60 | return new WikiFilePage( $title ); |
61 | } |
62 | |
63 | /** |
64 | * @param File $file |
65 | * @return void |
66 | */ |
67 | public function setFile( $file ) { |
68 | $this->getPage()->setFile( $file ); |
69 | $this->displayImg = $file; |
70 | $this->fileLoaded = true; |
71 | } |
72 | |
73 | protected function loadFile() { |
74 | if ( $this->fileLoaded ) { |
75 | return; |
76 | } |
77 | $this->fileLoaded = true; |
78 | |
79 | $this->displayImg = $img = false; |
80 | |
81 | $this->getHookRunner()->onImagePageFindFile( $this, $img, $this->displayImg ); |
82 | if ( !$img ) { // not set by hook? |
83 | $services = MediaWikiServices::getInstance(); |
84 | $img = $services->getRepoGroup()->findFile( $this->getTitle() ); |
85 | if ( !$img ) { |
86 | $img = $services->getRepoGroup()->getLocalRepo()->newFile( $this->getTitle() ); |
87 | } |
88 | } |
89 | // @phan-suppress-next-line PhanTypeMismatchArgumentNullable should be set |
90 | $this->getPage()->setFile( $img ); |
91 | if ( !$this->displayImg ) { // not set by hook? |
92 | // @phan-suppress-next-line PhanPossiblyNullTypeMismatchProperty should be set |
93 | $this->displayImg = $img; |
94 | } |
95 | $this->repo = $img->getRepo(); |
96 | } |
97 | |
98 | public function view() { |
99 | $context = $this->getContext(); |
100 | $showEXIF = $context->getConfig()->get( MainConfigNames::ShowEXIF ); |
101 | |
102 | // For action=render, include body text only; none of the image extras |
103 | if ( $this->viewIsRenderAction ) { |
104 | parent::view(); |
105 | return; |
106 | } |
107 | |
108 | $out = $context->getOutput(); |
109 | $request = $context->getRequest(); |
110 | $diff = $request->getVal( 'diff' ); |
111 | |
112 | if ( $this->getTitle()->getNamespace() !== NS_FILE || ( $diff !== null && $this->isDiffOnlyView() ) ) { |
113 | parent::view(); |
114 | return; |
115 | } |
116 | |
117 | $this->loadFile(); |
118 | |
119 | if ( |
120 | $this->getTitle()->getNamespace() === NS_FILE |
121 | && $this->getFile()->getRedirected() |
122 | ) { |
123 | if ( |
124 | $this->getTitle()->getDBkey() == $this->getFile()->getName() |
125 | || $diff !== null |
126 | ) { |
127 | $request->setVal( 'diffonly', 'true' ); |
128 | } |
129 | |
130 | parent::view(); |
131 | return; |
132 | } |
133 | |
134 | if ( $showEXIF && $this->displayImg->exists() ) { |
135 | // @todo FIXME: Bad interface, see note on MediaHandler::formatMetadata(). |
136 | $formattedMetadata = $this->displayImg->formatMetadata( $this->getContext() ); |
137 | } else { |
138 | $formattedMetadata = false; |
139 | } |
140 | |
141 | if ( !$diff && $this->displayImg->exists() ) { |
142 | $out->addHTML( $this->showTOC( (bool)$formattedMetadata ) ); |
143 | } |
144 | |
145 | if ( !$diff ) { |
146 | $this->openShowImage(); |
147 | } |
148 | |
149 | # No need to display noarticletext, we use our own message, output in openShowImage() |
150 | if ( $this->getPage()->getId() ) { |
151 | $out->addHTML( Html::openElement( 'div', [ 'id' => 'mw-imagepage-content' ] ) ); |
152 | // NS_FILE pages render mostly in the user language (like special pages), |
153 | // except the editable wikitext content, which is rendered in the page content |
154 | // language by the parent class. |
155 | parent::view(); |
156 | $out->addHTML( Html::closeElement( 'div' ) ); |
157 | } else { |
158 | # Just need to set the right headers |
159 | $out->setArticleFlag( true ); |
160 | $out->setPageTitle( $this->getTitle()->getPrefixedText() ); |
161 | $this->getPage()->doViewUpdates( |
162 | $context->getAuthority(), |
163 | $this->getOldID() |
164 | ); |
165 | } |
166 | |
167 | # Show shared description, if needed |
168 | if ( $this->mExtraDescription ) { |
169 | $fol = $context->msg( 'shareddescriptionfollows' ); |
170 | if ( !$fol->isDisabled() ) { |
171 | $out->addWikiTextAsInterface( $fol->plain() ); |
172 | } |
173 | $out->addHTML( |
174 | Html::rawElement( |
175 | 'div', |
176 | [ 'id' => 'shared-image-desc' ], |
177 | $this->mExtraDescription |
178 | ) . "\n" |
179 | ); |
180 | } |
181 | |
182 | $this->closeShowImage(); |
183 | $this->imageHistory(); |
184 | // TODO: Cleanup the following |
185 | |
186 | $out->addHTML( Xml::element( 'h2', |
187 | [ 'id' => 'filelinks' ], |
188 | $context->msg( 'imagelinks' )->text() ) . "\n" ); |
189 | $this->imageDupes(); |
190 | # @todo FIXME: For some freaky reason, we can't redirect to foreign images. |
191 | # Yet we return metadata about the target. Definitely an issue in the FileRepo |
192 | $this->imageLinks(); |
193 | |
194 | # Allow extensions to add something after the image links |
195 | $html = ''; |
196 | $this->getHookRunner()->onImagePageAfterImageLinks( $this, $html ); |
197 | if ( $html ) { |
198 | $out->addHTML( $html ); |
199 | } |
200 | |
201 | if ( $formattedMetadata ) { |
202 | $out->addHTML( Xml::element( |
203 | 'h2', |
204 | [ 'id' => 'metadata' ], |
205 | $context->msg( 'metadata' )->text() ) . "\n" ); |
206 | $out->wrapWikiTextAsInterface( |
207 | 'mw-imagepage-section-metadata', |
208 | $this->makeMetadataTable( $formattedMetadata ) |
209 | ); |
210 | $out->addModules( [ 'mediawiki.action.view.metadata' ] ); |
211 | } |
212 | |
213 | // Add remote Filepage.css |
214 | if ( !$this->repo->isLocal() ) { |
215 | $css = $this->repo->getDescriptionStylesheetUrl(); |
216 | if ( $css ) { |
217 | $out->addStyle( $css ); |
218 | } |
219 | } |
220 | |
221 | $out->addModuleStyles( [ |
222 | 'mediawiki.action.view.filepage', // Add MediaWiki styles for a file page |
223 | ] ); |
224 | } |
225 | |
226 | /** |
227 | * @return File |
228 | */ |
229 | public function getDisplayedFile() { |
230 | $this->loadFile(); |
231 | return $this->displayImg; |
232 | } |
233 | |
234 | /** |
235 | * Create the TOC |
236 | * |
237 | * @param bool $metadata Whether or not to show the metadata link |
238 | * @return string |
239 | */ |
240 | protected function showTOC( $metadata ) { |
241 | $r = [ |
242 | Html::rawElement( |
243 | 'li', |
244 | [], |
245 | Html::rawElement( |
246 | 'a', |
247 | [ 'href' => '#file' ], |
248 | $this->getContext()->msg( 'file-anchor-link' )->escaped() |
249 | ) |
250 | ), |
251 | Html::rawElement( |
252 | 'li', |
253 | [], |
254 | Html::rawElement( |
255 | 'a', |
256 | [ 'href' => '#filehistory' ], |
257 | $this->getContext()->msg( 'filehist' )->escaped() |
258 | ) |
259 | ), |
260 | Html::rawElement( |
261 | 'li', |
262 | [], |
263 | Html::rawElement( |
264 | 'a', |
265 | [ 'href' => '#filelinks' ], |
266 | $this->getContext()->msg( 'imagelinks' )->escaped() |
267 | ) |
268 | ), |
269 | ]; |
270 | |
271 | $this->getHookRunner()->onImagePageShowTOC( $this, $r ); |
272 | |
273 | if ( $metadata ) { |
274 | $r[] = Html::rawElement( |
275 | 'li', |
276 | [], |
277 | Html::rawElement( |
278 | 'a', |
279 | [ 'href' => '#metadata' ], |
280 | $this->getContext()->msg( 'metadata' )->escaped() |
281 | ) |
282 | ); |
283 | } |
284 | |
285 | return Html::rawElement( 'ul', [ |
286 | 'id' => 'filetoc', |
287 | 'role' => 'navigation' |
288 | ], implode( "\n", $r ) ); |
289 | } |
290 | |
291 | /** |
292 | * Make a table with metadata to be shown in the output page. |
293 | * |
294 | * @todo FIXME: Bad interface, see note on MediaHandler::formatMetadata(). |
295 | * |
296 | * @param array $metadata The array containing the Exif data |
297 | * @return string The metadata table. This is treated as Wikitext (!) |
298 | */ |
299 | protected function makeMetadataTable( $metadata ) { |
300 | $r = $this->getContext()->msg( 'metadata-help' )->plain(); |
301 | // Initial state of collapsible rows is collapsed |
302 | // see mediawiki.action.view.filepage.less and mediawiki.action.view.metadata module. |
303 | $r .= "<table id=\"mw_metadata\" class=\"mw_metadata collapsed\">\n"; |
304 | foreach ( $metadata as $type => $stuff ) { |
305 | foreach ( $stuff as $v ) { |
306 | $class = str_replace( ' ', '_', $v['id'] ); |
307 | if ( $type === 'collapsed' ) { |
308 | $class .= ' mw-metadata-collapsible'; |
309 | } |
310 | $r .= Html::rawElement( 'tr', |
311 | [ 'class' => $class ], |
312 | Html::rawElement( 'th', [], $v['name'] ) |
313 | . Html::rawElement( 'td', [], $v['value'] ) |
314 | ); |
315 | } |
316 | } |
317 | $r .= "</table>\n"; |
318 | return $r; |
319 | } |
320 | |
321 | /** |
322 | * Returns language code to be used for displaying the image, based on request context and |
323 | * languages available in the file. |
324 | * |
325 | * @param WebRequest $request |
326 | * @param File $file |
327 | * @return string|null a valid IETF language tag |
328 | */ |
329 | private function getLanguageForRendering( WebRequest $request, File $file ) { |
330 | $handler = $file->getHandler(); |
331 | if ( !$handler ) { |
332 | return null; |
333 | } |
334 | |
335 | $requestLanguage = $request->getVal( 'lang' ); |
336 | if ( $requestLanguage === null ) { |
337 | // For on File pages about a translatable SVG, decide which |
338 | // language to render the large thumbnail in (T310445) |
339 | $services = MediaWikiServices::getInstance(); |
340 | $variantLangCode = $services->getLanguageConverterFactory() |
341 | ->getLanguageConverter( $services->getContentLanguage() ) |
342 | ->getPreferredVariant(); |
343 | $requestLanguage = LanguageCode::bcp47( $variantLangCode ); |
344 | } |
345 | if ( $handler->validateParam( 'lang', $requestLanguage ) ) { |
346 | return $file->getMatchedLanguage( $requestLanguage ); |
347 | } |
348 | |
349 | return $handler->getDefaultRenderLanguage( $file ); |
350 | } |
351 | |
352 | protected function openShowImage() { |
353 | $context = $this->getContext(); |
354 | $mainConfig = $context->getConfig(); |
355 | $enableUploads = $mainConfig->get( MainConfigNames::EnableUploads ); |
356 | $send404Code = $mainConfig->get( MainConfigNames::Send404Code ); |
357 | $svgMaxSize = $mainConfig->get( MainConfigNames::SVGMaxSize ); |
358 | $enableLegacyMediaDOM = $mainConfig->get( MainConfigNames::ParserEnableLegacyMediaDOM ); |
359 | $this->loadFile(); |
360 | $out = $context->getOutput(); |
361 | $user = $context->getUser(); |
362 | $lang = $context->getLanguage(); |
363 | $sitedir = MediaWikiServices::getInstance()->getContentLanguage()->getDir(); |
364 | $request = $context->getRequest(); |
365 | |
366 | if ( $this->displayImg->exists() ) { |
367 | [ $maxWidth, $maxHeight ] = $this->getImageLimitsFromOption( $user, 'imagesize' ); |
368 | |
369 | # image |
370 | $page = $request->getIntOrNull( 'page' ); |
371 | if ( $page === null ) { |
372 | $params = []; |
373 | $page = 1; |
374 | } else { |
375 | $params = [ 'page' => $page ]; |
376 | } |
377 | |
378 | $renderLang = $this->getLanguageForRendering( $request, $this->displayImg ); |
379 | if ( $renderLang !== null ) { |
380 | $params['lang'] = $renderLang; |
381 | } |
382 | |
383 | $width_orig = $this->displayImg->getWidth( $page ); |
384 | $width = $width_orig; |
385 | $height_orig = $this->displayImg->getHeight( $page ); |
386 | $height = $height_orig; |
387 | |
388 | $filename = wfEscapeWikiText( $this->displayImg->getName() ); |
389 | $linktext = $filename; |
390 | |
391 | $this->getHookRunner()->onImageOpenShowImageInlineBefore( $this, $out ); |
392 | |
393 | if ( $this->displayImg->allowInlineDisplay() ) { |
394 | # image |
395 | # "Download high res version" link below the image |
396 | # $msgsize = $this->getContext()->msg( 'file-info-size', $width_orig, $height_orig, |
397 | # Language::formatSize( $this->displayImg->getSize() ), $mime )->escaped(); |
398 | # We'll show a thumbnail of this image |
399 | if ( $width > $maxWidth || |
400 | $height > $maxHeight || |
401 | $this->displayImg->isVectorized() |
402 | ) { |
403 | [ $width, $height ] = $this->displayImg->getDisplayWidthHeight( |
404 | $maxWidth, $maxHeight, $page |
405 | ); |
406 | $linktext = $context->msg( 'show-big-image' )->escaped(); |
407 | |
408 | $thumbSizes = $this->getThumbSizes( $width_orig, $height_orig ); |
409 | # Generate thumbnails or thumbnail links as needed... |
410 | $otherSizes = []; |
411 | foreach ( $thumbSizes as $size ) { |
412 | // We include a thumbnail size in the list, if it is |
413 | // less than or equal to the original size of the image |
414 | // asset ($width_orig/$height_orig). We also exclude |
415 | // the current thumbnail's size ($width/$height) |
416 | // since that is added to the message separately, so |
417 | // it can be denoted as the current size being shown. |
418 | // Vectorized images are limited by $wgSVGMaxSize big, |
419 | // so all thumbs less than or equal that are shown. |
420 | if ( ( ( $size[0] <= $width_orig && $size[1] <= $height_orig ) |
421 | || ( $this->displayImg->isVectorized() |
422 | && max( $size[0], $size[1] ) <= $svgMaxSize ) |
423 | ) |
424 | && $size[0] != $width && $size[1] != $height |
425 | && $size[0] != $maxWidth && $size[1] != $maxHeight |
426 | ) { |
427 | $sizeLink = $this->makeSizeLink( $params, $size[0], $size[1] ); |
428 | if ( $sizeLink ) { |
429 | $otherSizes[] = $sizeLink; |
430 | } |
431 | } |
432 | } |
433 | $otherSizes = array_unique( $otherSizes ); |
434 | |
435 | $sizeLinkBigImagePreview = $this->makeSizeLink( $params, $width, $height ); |
436 | $msgsmall = $this->getThumbPrevText( $params, $sizeLinkBigImagePreview ); |
437 | if ( count( $otherSizes ) ) { |
438 | $msgsmall .= ' ' . |
439 | Html::rawElement( |
440 | 'span', |
441 | [ 'class' => 'mw-filepage-other-resolutions' ], |
442 | $context->msg( 'show-big-image-other' ) |
443 | ->rawParams( $lang->pipeList( $otherSizes ) ) |
444 | ->params( count( $otherSizes ) ) |
445 | ->parse() |
446 | ); |
447 | } |
448 | } elseif ( $width == 0 && $height == 0 ) { |
449 | # Some sort of audio file that doesn't have dimensions |
450 | # Don't output a no hi res message for such a file |
451 | $msgsmall = ''; |
452 | } else { |
453 | # Image is small enough to show full size on image page |
454 | $msgsmall = $this->getContext()->msg( 'file-nohires' )->parse(); |
455 | } |
456 | |
457 | $params['width'] = $width; |
458 | $params['height'] = $height; |
459 | $params['isFilePageThumb'] = true; |
460 | // Allow the MediaHandler to handle query string parameters on the file page, |
461 | // e.g. start time for videos (T203994) |
462 | $params['imagePageParams'] = $request->getQueryValuesOnly(); |
463 | $thumbnail = $this->displayImg->transform( $params ); |
464 | Linker::processResponsiveImages( $this->displayImg, $thumbnail, $params ); |
465 | |
466 | $anchorclose = Html::rawElement( |
467 | 'div', |
468 | [ 'class' => 'mw-filepage-resolutioninfo' ], |
469 | $msgsmall |
470 | ); |
471 | |
472 | $isMulti = $this->displayImg->isMultipage() && $this->displayImg->pageCount() > 1; |
473 | if ( $isMulti ) { |
474 | $out->addModules( 'mediawiki.page.image.pagination' ); |
475 | /* TODO: multipageimage class is deprecated since Jan 2023 */ |
476 | $out->addHTML( '<div class="mw-filepage-multipage multipageimage">' ); |
477 | } |
478 | |
479 | if ( $thumbnail ) { |
480 | $options = [ |
481 | 'alt' => $this->displayImg->getTitle()->getPrefixedText(), |
482 | 'file-link' => true, |
483 | ]; |
484 | $out->addHTML( |
485 | Html::rawElement( |
486 | 'div', |
487 | [ 'class' => 'fullImageLink', 'id' => 'file' ], |
488 | $thumbnail->toHtml( $options ) . $anchorclose |
489 | ) . "\n" |
490 | ); |
491 | } |
492 | |
493 | if ( $isMulti ) { |
494 | $linkPrev = $linkNext = ''; |
495 | $count = $this->displayImg->pageCount(); |
496 | if ( !$enableLegacyMediaDOM ) { |
497 | $out->addModules( 'mediawiki.page.media' ); |
498 | } |
499 | |
500 | if ( $page > 1 ) { |
501 | $label = $context->msg( 'imgmultipageprev' )->text(); |
502 | // on the client side, this link is generated in ajaxifyPageNavigation() |
503 | // in the mediawiki.page.image.pagination module |
504 | $linkPrev = $this->linkRenderer->makeKnownLink( |
505 | $this->getTitle(), |
506 | $label, |
507 | [], |
508 | [ 'page' => $page - 1 ] |
509 | ); |
510 | $thumbPrevPage = Linker::makeThumbLinkObj( |
511 | $this->getTitle(), |
512 | $this->displayImg, |
513 | $linkPrev, |
514 | $label, |
515 | 'none', |
516 | [ 'page' => $page - 1, 'isFilePageThumb' => true ] |
517 | ); |
518 | } else { |
519 | $thumbPrevPage = ''; |
520 | } |
521 | |
522 | if ( $page < $count ) { |
523 | $label = $context->msg( 'imgmultipagenext' )->text(); |
524 | $linkNext = $this->linkRenderer->makeKnownLink( |
525 | $this->getTitle(), |
526 | $label, |
527 | [], |
528 | [ 'page' => $page + 1 ] |
529 | ); |
530 | $thumbNextPage = Linker::makeThumbLinkObj( |
531 | $this->getTitle(), |
532 | $this->displayImg, |
533 | $linkNext, |
534 | $label, |
535 | 'none', |
536 | [ 'page' => $page + 1, 'isFilePageThumb' => true ] |
537 | ); |
538 | } else { |
539 | $thumbNextPage = ''; |
540 | } |
541 | |
542 | $script = $mainConfig->get( MainConfigNames::Script ); |
543 | |
544 | $formParams = [ |
545 | 'name' => 'pageselector', |
546 | 'action' => $script, |
547 | ]; |
548 | $options = []; |
549 | for ( $i = 1; $i <= $count; $i++ ) { |
550 | $options[] = Xml::option( $lang->formatNum( $i ), (string)$i, $i == $page ); |
551 | } |
552 | $select = Xml::tags( 'select', |
553 | [ 'id' => 'pageselector', 'name' => 'page' ], |
554 | implode( "\n", $options ) ); |
555 | |
556 | /* TODO: multipageimagenavbox class is deprecated since Jan 2023 */ |
557 | $out->addHTML( |
558 | '<div class="mw-filepage-multipage-navigation multipageimagenavbox">' . |
559 | $linkPrev . |
560 | Html::rawElement( 'form', $formParams, |
561 | Html::hidden( 'title', $this->getTitle()->getPrefixedDBkey() ) . |
562 | $context->msg( 'imgmultigoto' )->rawParams( $select )->parse() . |
563 | $context->msg( 'word-separator' )->escaped() . |
564 | Xml::submitButton( $context->msg( 'imgmultigo' )->text() ) |
565 | ) . |
566 | "$thumbPrevPage\n$thumbNextPage\n$linkNext</div></div>" |
567 | ); |
568 | } |
569 | } elseif ( $this->displayImg->isSafeFile() ) { |
570 | # if direct link is allowed but it's not a renderable image, show an icon. |
571 | $icon = $this->displayImg->iconThumb(); |
572 | |
573 | $out->addHTML( |
574 | Html::rawElement( |
575 | 'div', |
576 | [ 'class' => 'fullImageLink', 'id' => 'file' ], |
577 | $icon->toHtml( [ 'file-link' => true ] ) |
578 | ) . "\n" |
579 | ); |
580 | } |
581 | |
582 | $longDesc = $context->msg( 'parentheses', $this->displayImg->getLongDesc() )->text(); |
583 | |
584 | $handler = $this->displayImg->getHandler(); |
585 | |
586 | // If this is a filetype with potential issues, warn the user. |
587 | if ( $handler ) { |
588 | $warningConfig = $handler->getWarningConfig( $this->displayImg ); |
589 | |
590 | if ( $warningConfig !== null ) { |
591 | // The warning will be displayed via CSS and JavaScript. |
592 | // We just need to tell the client side what message to use. |
593 | $output = $context->getOutput(); |
594 | $output->addJsConfigVars( 'wgFileWarning', $warningConfig ); |
595 | $output->addModules( $warningConfig['module'] ); |
596 | $output->addModules( 'mediawiki.filewarning' ); |
597 | } |
598 | } |
599 | |
600 | $medialink = "[[Media:$filename|$linktext]]"; |
601 | |
602 | if ( !$this->displayImg->isSafeFile() ) { |
603 | $warning = $context->msg( 'mediawarning' )->plain(); |
604 | // <bdi> is needed here to separate the file name, which |
605 | // most likely ends in Latin characters, from the description, |
606 | // which may begin with the file type. In RTL environment |
607 | // this will get messy. |
608 | $out->wrapWikiTextAsInterface( 'fullMedia', <<<EOT |
609 | <bdi dir="$sitedir"><span class="dangerousLink">$medialink</span></bdi> <span class="fileInfo">$longDesc</span> |
610 | EOT |
611 | ); |
612 | // phpcs:enable |
613 | $out->wrapWikiTextAsInterface( 'mediaWarning', $warning ); |
614 | } else { |
615 | $out->wrapWikiTextAsInterface( 'fullMedia', <<<EOT |
616 | <bdi dir="$sitedir">$medialink</bdi> <span class="fileInfo">$longDesc</span> |
617 | EOT |
618 | ); |
619 | } |
620 | |
621 | $renderLangOptions = $this->displayImg->getAvailableLanguages(); |
622 | if ( count( $renderLangOptions ) >= 1 ) { |
623 | $out->addHTML( $this->doRenderLangOpt( $renderLangOptions, $renderLang ) ); |
624 | } |
625 | |
626 | // Add cannot animate thumbnail warning |
627 | if ( !$this->displayImg->canAnimateThumbIfAppropriate() ) { |
628 | // Include the extension so wiki admins can |
629 | // customize it on a per file-type basis |
630 | // (aka say things like use format X instead). |
631 | // additionally have a specific message for |
632 | // file-no-thumb-animation-gif |
633 | $ext = $this->displayImg->getExtension(); |
634 | $noAnimMesg = wfMessageFallback( |
635 | 'file-no-thumb-animation-' . $ext, |
636 | 'file-no-thumb-animation' |
637 | )->setContext( $context )->plain(); |
638 | |
639 | $out->wrapWikiTextAsInterface( 'mw-noanimatethumb', $noAnimMesg ); |
640 | } |
641 | |
642 | if ( !$this->displayImg->isLocal() ) { |
643 | $this->printSharedImageText(); |
644 | } |
645 | } else { |
646 | # Image does not exist |
647 | if ( !$this->getPage()->getId() ) { |
648 | $dbr = $this->dbProvider->getReplicaDatabase(); |
649 | |
650 | # No article exists either |
651 | # Show deletion log to be consistent with normal articles |
652 | LogEventsList::showLogExtract( |
653 | $out, |
654 | [ 'delete', 'move', 'protect', 'merge' ], |
655 | $this->getTitle()->getPrefixedText(), |
656 | '', |
657 | [ 'lim' => 10, |
658 | 'conds' => [ $dbr->expr( 'log_action', '!=', 'revision' ) ], |
659 | 'showIfEmpty' => false, |
660 | 'msgKey' => [ 'moveddeleted-notice' ] |
661 | ] |
662 | ); |
663 | } |
664 | |
665 | if ( $enableUploads && |
666 | $context->getAuthority()->isAllowed( 'upload' ) |
667 | ) { |
668 | // Only show an upload link if the user can upload |
669 | $uploadTitle = SpecialPage::getTitleFor( 'Upload' ); |
670 | $nofile = [ |
671 | 'filepage-nofile-link', |
672 | $uploadTitle->getFullURL( [ |
673 | 'wpDestFile' => $this->getFile()->getName() |
674 | ] ) |
675 | ]; |
676 | } else { |
677 | $nofile = 'filepage-nofile'; |
678 | } |
679 | // Note, if there is an image description page, but |
680 | // no image, then this setRobotPolicy is overridden |
681 | // by Article::View(). |
682 | $out->setRobotPolicy( 'noindex,nofollow' ); |
683 | $out->wrapWikiMsg( "<div id='mw-imagepage-nofile' class='plainlinks'>\n$1\n</div>", $nofile ); |
684 | if ( !$this->getPage()->getId() && $send404Code ) { |
685 | // If there is no image, no shared image, and no description page, |
686 | // output a 404, to be consistent with Article::showMissingArticle. |
687 | $request->response()->statusHeader( 404 ); |
688 | } |
689 | } |
690 | $out->setFileVersion( $this->displayImg ); |
691 | } |
692 | |
693 | /** |
694 | * Make the text under the image to say what size preview |
695 | * |
696 | * @param array $params parameters for thumbnail |
697 | * @param string $sizeLinkBigImagePreview HTML for the current size |
698 | * @return string HTML output |
699 | */ |
700 | protected function getThumbPrevText( $params, $sizeLinkBigImagePreview ) { |
701 | if ( $sizeLinkBigImagePreview ) { |
702 | // Show a different message of preview is different format from original. |
703 | $previewTypeDiffers = false; |
704 | $origExt = $thumbExt = $this->displayImg->getExtension(); |
705 | if ( $this->displayImg->getHandler() ) { |
706 | $origMime = $this->displayImg->getMimeType(); |
707 | $typeParams = $params; |
708 | $this->displayImg->getHandler()->normaliseParams( $this->displayImg, $typeParams ); |
709 | [ $thumbExt, $thumbMime ] = $this->displayImg->getHandler()->getThumbType( |
710 | $origExt, $origMime, $typeParams ); |
711 | if ( $thumbMime !== $origMime ) { |
712 | $previewTypeDiffers = true; |
713 | } |
714 | } |
715 | if ( $previewTypeDiffers ) { |
716 | return $this->getContext()->msg( 'show-big-image-preview-differ' )-> |
717 | rawParams( $sizeLinkBigImagePreview )-> |
718 | params( strtoupper( $origExt ) )-> |
719 | params( strtoupper( $thumbExt ) )-> |
720 | parse(); |
721 | } else { |
722 | return $this->getContext()->msg( 'show-big-image-preview' )-> |
723 | rawParams( $sizeLinkBigImagePreview )-> |
724 | parse(); |
725 | } |
726 | } else { |
727 | return ''; |
728 | } |
729 | } |
730 | |
731 | /** |
732 | * Creates a thumbnail of specified size and returns an HTML link to it |
733 | * @param array $params Scaler parameters |
734 | * @param int $width |
735 | * @param int $height |
736 | * @return string |
737 | */ |
738 | protected function makeSizeLink( $params, $width, $height ) { |
739 | $params['width'] = $width; |
740 | $params['height'] = $height; |
741 | $thumbnail = $this->displayImg->transform( $params ); |
742 | if ( $thumbnail && !$thumbnail->isError() ) { |
743 | return Html::rawElement( 'a', [ |
744 | 'href' => $thumbnail->getUrl(), |
745 | 'class' => 'mw-thumbnail-link' |
746 | ], $this->getContext()->msg( 'show-big-image-size' )->numParams( |
747 | $thumbnail->getWidth(), $thumbnail->getHeight() |
748 | )->parse() ); |
749 | } else { |
750 | return ''; |
751 | } |
752 | } |
753 | |
754 | /** |
755 | * Show a notice that the file is from a shared repository |
756 | */ |
757 | protected function printSharedImageText() { |
758 | $out = $this->getContext()->getOutput(); |
759 | $this->loadFile(); |
760 | |
761 | $descUrl = $this->getFile()->getDescriptionUrl(); |
762 | $descText = $this->getFile()->getDescriptionText( $this->getContext()->getLanguage() ); |
763 | |
764 | /* Add canonical to head if there is no local page for this shared file */ |
765 | if ( $descUrl && !$this->getPage()->getId() ) { |
766 | $out->setCanonicalUrl( $descUrl ); |
767 | } |
768 | |
769 | $wrap = "<div class=\"sharedUploadNotice\">\n$1\n</div>\n"; |
770 | $repo = $this->getFile()->getRepo()->getDisplayName(); |
771 | |
772 | if ( $descUrl && |
773 | $descText && |
774 | !$this->getContext()->msg( 'sharedupload-desc-here' )->isDisabled() |
775 | ) { |
776 | $out->wrapWikiMsg( $wrap, [ 'sharedupload-desc-here', $repo, $descUrl ] ); |
777 | } elseif ( $descUrl && |
778 | !$this->getContext()->msg( 'sharedupload-desc-there' )->isDisabled() |
779 | ) { |
780 | $out->wrapWikiMsg( $wrap, [ 'sharedupload-desc-there', $repo, $descUrl ] ); |
781 | } else { |
782 | $out->wrapWikiMsg( $wrap, [ 'sharedupload', $repo ], ''/*BACKCOMPAT*/ ); |
783 | } |
784 | |
785 | if ( $descText ) { |
786 | $this->mExtraDescription = $descText; |
787 | } |
788 | } |
789 | |
790 | public function getUploadUrl() { |
791 | $this->loadFile(); |
792 | $uploadTitle = SpecialPage::getTitleFor( 'Upload' ); |
793 | return $uploadTitle->getFullURL( [ |
794 | 'wpDestFile' => $this->getFile()->getName(), |
795 | 'wpForReUpload' => 1 |
796 | ] ); |
797 | } |
798 | |
799 | /** |
800 | * Add the re-upload link (or message about not being able to re-upload) to the output. |
801 | */ |
802 | protected function uploadLinksBox() { |
803 | if ( !$this->getContext()->getConfig()->get( MainConfigNames::EnableUploads ) ) { |
804 | return; |
805 | } |
806 | |
807 | $this->loadFile(); |
808 | if ( !$this->getFile()->isLocal() ) { |
809 | return; |
810 | } |
811 | |
812 | $canUpload = $this->getContext()->getAuthority() |
813 | ->probablyCan( 'upload', $this->getTitle() ); |
814 | if ( $canUpload && UploadBase::userCanReUpload( |
815 | $this->getContext()->getAuthority(), |
816 | $this->getFile() ) |
817 | ) { |
818 | // "Upload a new version of this file" link |
819 | $ulink = $this->linkRenderer->makeExternalLink( |
820 | $this->getUploadUrl(), |
821 | $this->getContext()->msg( 'uploadnewversion-linktext' ), |
822 | $this->getTitle() |
823 | ); |
824 | $attrs = [ 'class' => 'plainlinks', 'id' => 'mw-imagepage-reupload-link' ]; |
825 | $linkPara = Html::rawElement( 'p', $attrs, $ulink ); |
826 | } else { |
827 | // "You cannot overwrite this file." message |
828 | $attrs = [ 'id' => 'mw-imagepage-upload-disallowed' ]; |
829 | $msg = $this->getContext()->msg( 'upload-disallowed-here' )->text(); |
830 | $linkPara = Html::element( 'p', $attrs, $msg ); |
831 | } |
832 | |
833 | $uploadLinks = Html::rawElement( 'div', [ 'class' => 'mw-imagepage-upload-links' ], $linkPara ); |
834 | $this->getContext()->getOutput()->addHTML( $uploadLinks ); |
835 | } |
836 | |
837 | /** |
838 | * For overloading |
839 | */ |
840 | protected function closeShowImage() { |
841 | } |
842 | |
843 | /** |
844 | * If the page we've just displayed is in the "Image" namespace, |
845 | * we follow it with an upload history of the image and its usage. |
846 | */ |
847 | protected function imageHistory() { |
848 | $this->loadFile(); |
849 | $out = $this->getContext()->getOutput(); |
850 | $pager = new ImageHistoryPseudoPager( |
851 | $this, |
852 | MediaWikiServices::getInstance()->getLinkBatchFactory() |
853 | ); |
854 | $out->addHTML( $pager->getBody() ); |
855 | $out->getMetadata()->setPreventClickjacking( $pager->getPreventClickjacking() ); |
856 | |
857 | $this->getFile()->resetHistory(); // free db resources |
858 | |
859 | # Exist check because we don't want to show this on pages where an image |
860 | # doesn't exist along with the noimage message, that would suck. -ævar |
861 | if ( $this->getFile()->exists() ) { |
862 | $this->uploadLinksBox(); |
863 | } |
864 | } |
865 | |
866 | /** |
867 | * @param string|string[] $target |
868 | * @param int $limit |
869 | * @return IResultWrapper |
870 | */ |
871 | protected function queryImageLinks( $target, $limit ) { |
872 | return $this->dbProvider->getReplicaDatabase()->newSelectQueryBuilder() |
873 | ->select( [ 'page_namespace', 'page_title', 'il_to' ] ) |
874 | ->from( 'imagelinks' ) |
875 | ->join( 'page', null, 'il_from = page_id' ) |
876 | ->where( [ 'il_to' => $target ] ) |
877 | ->orderBy( 'il_from' ) |
878 | ->limit( $limit + 1 ) |
879 | ->caller( __METHOD__ )->fetchResultSet(); |
880 | } |
881 | |
882 | protected function imageLinks() { |
883 | $limit = 100; |
884 | |
885 | $out = $this->getContext()->getOutput(); |
886 | |
887 | $rows = []; |
888 | $redirects = []; |
889 | foreach ( $this->getTitle()->getRedirectsHere( NS_FILE ) as $redir ) { |
890 | $redirects[$redir->getDBkey()] = []; |
891 | $rows[] = (object)[ |
892 | 'page_namespace' => NS_FILE, |
893 | 'page_title' => $redir->getDBkey(), |
894 | ]; |
895 | } |
896 | |
897 | $res = $this->queryImageLinks( $this->getTitle()->getDBkey(), $limit + 1 ); |
898 | foreach ( $res as $row ) { |
899 | $rows[] = $row; |
900 | } |
901 | $count = count( $rows ); |
902 | |
903 | $hasMore = $count > $limit; |
904 | if ( !$hasMore && count( $redirects ) ) { |
905 | $res = $this->queryImageLinks( array_keys( $redirects ), |
906 | $limit - count( $rows ) + 1 ); |
907 | foreach ( $res as $row ) { |
908 | $redirects[$row->il_to][] = $row; |
909 | $count++; |
910 | } |
911 | $hasMore = ( $res->numRows() + count( $rows ) ) > $limit; |
912 | } |
913 | |
914 | if ( $count == 0 ) { |
915 | $out->wrapWikiMsg( |
916 | Html::rawElement( 'div', |
917 | [ 'id' => 'mw-imagepage-nolinkstoimage' ], "\n$1\n" ), |
918 | 'nolinkstoimage' |
919 | ); |
920 | return; |
921 | } |
922 | |
923 | $out->addHTML( "<div id='mw-imagepage-section-linkstoimage'>\n" ); |
924 | if ( !$hasMore ) { |
925 | $out->addWikiMsg( 'linkstoimage', $count ); |
926 | } else { |
927 | // More links than the limit. Add a link to [[Special:Whatlinkshere]] |
928 | $out->addWikiMsg( 'linkstoimage-more', |
929 | $this->getContext()->getLanguage()->formatNum( $limit ), |
930 | $this->getTitle()->getPrefixedDBkey() |
931 | ); |
932 | } |
933 | |
934 | $out->addHTML( |
935 | Html::openElement( 'ul', |
936 | [ 'class' => 'mw-imagepage-linkstoimage' ] ) . "\n" |
937 | ); |
938 | // Sort the list by namespace:title |
939 | usort( $rows, [ $this, 'compare' ] ); |
940 | |
941 | // Create links for every element |
942 | $currentCount = 0; |
943 | foreach ( $rows as $element ) { |
944 | $currentCount++; |
945 | if ( $currentCount > $limit ) { |
946 | break; |
947 | } |
948 | |
949 | $link = $this->linkRenderer->makeKnownLink( |
950 | Title::makeTitle( $element->page_namespace, $element->page_title ), |
951 | null, |
952 | [], |
953 | // Add a redirect=no to make redirect pages reachable |
954 | [ 'redirect' => isset( $redirects[$element->page_title] ) ? 'no' : null ] |
955 | ); |
956 | if ( !isset( $redirects[$element->page_title] ) ) { |
957 | # No redirects |
958 | $liContents = $link; |
959 | } elseif ( count( $redirects[$element->page_title] ) === 0 ) { |
960 | # Redirect without usages |
961 | $liContents = $this->getContext()->msg( 'linkstoimage-redirect' ) |
962 | ->rawParams( $link, '' ) |
963 | ->parse(); |
964 | } else { |
965 | # Redirect with usages |
966 | $li = ''; |
967 | foreach ( $redirects[$element->page_title] as $row ) { |
968 | $currentCount++; |
969 | if ( $currentCount > $limit ) { |
970 | break; |
971 | } |
972 | |
973 | $link2 = $this->linkRenderer->makeKnownLink( |
974 | Title::makeTitle( $row->page_namespace, $row->page_title ) ); |
975 | $li .= Html::rawElement( |
976 | 'li', |
977 | [ 'class' => 'mw-imagepage-linkstoimage-ns' . $element->page_namespace ], |
978 | $link2 |
979 | ) . "\n"; |
980 | } |
981 | |
982 | $ul = Html::rawElement( |
983 | 'ul', |
984 | [ 'class' => 'mw-imagepage-redirectstofile' ], |
985 | $li |
986 | ) . "\n"; |
987 | $liContents = $this->getContext()->msg( 'linkstoimage-redirect' )->rawParams( |
988 | $link, $ul )->parse(); |
989 | } |
990 | $out->addHTML( Html::rawElement( |
991 | 'li', |
992 | [ 'class' => 'mw-imagepage-linkstoimage-ns' . $element->page_namespace ], |
993 | $liContents |
994 | ) . "\n" |
995 | ); |
996 | |
997 | } |
998 | $out->addHTML( Html::closeElement( 'ul' ) . "\n" ); |
999 | $res->free(); |
1000 | |
1001 | // Add a links to [[Special:Whatlinkshere]] |
1002 | if ( $currentCount > $limit ) { |
1003 | $out->addWikiMsg( 'morelinkstoimage', $this->getTitle()->getPrefixedDBkey() ); |
1004 | } |
1005 | $out->addHTML( Html::closeElement( 'div' ) . "\n" ); |
1006 | } |
1007 | |
1008 | protected function imageDupes() { |
1009 | $this->loadFile(); |
1010 | $out = $this->getContext()->getOutput(); |
1011 | |
1012 | $dupes = $this->getPage()->getDuplicates(); |
1013 | if ( count( $dupes ) == 0 ) { |
1014 | return; |
1015 | } |
1016 | |
1017 | $out->addHTML( "<div id='mw-imagepage-section-duplicates'>\n" ); |
1018 | $out->addWikiMsg( 'duplicatesoffile', |
1019 | $this->getContext()->getLanguage()->formatNum( count( $dupes ) ), $this->getTitle()->getDBkey() |
1020 | ); |
1021 | $out->addHTML( "<ul class='mw-imagepage-duplicates'>\n" ); |
1022 | |
1023 | /** |
1024 | * @var File $file |
1025 | */ |
1026 | foreach ( $dupes as $file ) { |
1027 | $fromSrc = ''; |
1028 | if ( $file->isLocal() ) { |
1029 | $link = $this->linkRenderer->makeKnownLink( $file->getTitle() ); |
1030 | } else { |
1031 | $link = $this->linkRenderer->makeExternalLink( |
1032 | $file->getDescriptionUrl(), |
1033 | $file->getTitle()->getPrefixedText(), |
1034 | $this->getTitle() |
1035 | ); |
1036 | $fromSrc = $this->getContext()->msg( |
1037 | 'shared-repo-from', |
1038 | $file->getRepo()->getDisplayName() |
1039 | )->escaped(); |
1040 | } |
1041 | $out->addHTML( "<li>{$link} {$fromSrc}</li>\n" ); |
1042 | } |
1043 | $out->addHTML( "</ul></div>\n" ); |
1044 | } |
1045 | |
1046 | /** |
1047 | * Display an error with a wikitext description |
1048 | * |
1049 | * @param string $description |
1050 | */ |
1051 | public function showError( $description ) { |
1052 | $out = $this->getContext()->getOutput(); |
1053 | $out->setPageTitleMsg( $this->getContext()->msg( 'internalerror' ) ); |
1054 | $out->setRobotPolicy( 'noindex,nofollow' ); |
1055 | $out->setArticleRelated( false ); |
1056 | $out->disableClientCache(); |
1057 | $out->addWikiTextAsInterface( $description ); |
1058 | } |
1059 | |
1060 | /** |
1061 | * Callback for usort() to do link sorts by (namespace, title) |
1062 | * Function copied from Title::compare() |
1063 | * |
1064 | * @param stdClass $a Object page to compare with |
1065 | * @param stdClass $b Object page to compare with |
1066 | * @return int Result of string comparison, or namespace comparison |
1067 | */ |
1068 | protected function compare( $a, $b ) { |
1069 | return $a->page_namespace <=> $b->page_namespace |
1070 | ?: strcmp( $a->page_title, $b->page_title ); |
1071 | } |
1072 | |
1073 | /** |
1074 | * Output a drop-down box for language options for the file |
1075 | * |
1076 | * @param array $langChoices Array of string language codes |
1077 | * @param string|null $renderLang Language code for the language we want the file to rendered in, |
1078 | * it is pre-selected in the drop down box, use null to select the default case in the option list |
1079 | * @return string HTML to insert underneath image. |
1080 | */ |
1081 | protected function doRenderLangOpt( array $langChoices, $renderLang ) { |
1082 | $context = $this->getContext(); |
1083 | $script = $context->getConfig()->get( MainConfigNames::Script ); |
1084 | $opts = ''; |
1085 | |
1086 | $matchedRenderLang = $renderLang === null ? null : $this->displayImg->getMatchedLanguage( $renderLang ); |
1087 | |
1088 | foreach ( $langChoices as $lang ) { |
1089 | $opts .= $this->createXmlOptionStringForLanguage( |
1090 | $lang, |
1091 | $matchedRenderLang === $lang |
1092 | ); |
1093 | } |
1094 | |
1095 | // Allow for the default case in an svg <switch> that is displayed if no |
1096 | // systemLanguage attribute matches |
1097 | $opts .= "\n" . |
1098 | Xml::option( |
1099 | $context->msg( 'img-lang-default' )->text(), |
1100 | 'und', |
1101 | $matchedRenderLang === null || $matchedRenderLang === 'und' |
1102 | ); |
1103 | |
1104 | $select = Html::rawElement( |
1105 | 'select', |
1106 | [ 'id' => 'mw-imglangselector', 'name' => 'lang' ], |
1107 | $opts |
1108 | ); |
1109 | $submit = Xml::submitButton( $context->msg( 'img-lang-go' )->text() ); |
1110 | |
1111 | $formContents = $context->msg( 'img-lang-info' ) |
1112 | ->rawParams( $select, $submit ) |
1113 | ->parse(); |
1114 | $formContents .= Html::hidden( 'title', $this->getTitle()->getPrefixedDBkey() ); |
1115 | |
1116 | $langSelectLine = Html::rawElement( 'div', [ 'id' => 'mw-imglangselector-line' ], |
1117 | Html::rawElement( 'form', [ 'action' => $script ], $formContents ) |
1118 | ); |
1119 | return $langSelectLine; |
1120 | } |
1121 | |
1122 | /** |
1123 | * @param string $lang |
1124 | * @param bool $selected |
1125 | * @return string |
1126 | */ |
1127 | private function createXmlOptionStringForLanguage( $lang, $selected ) { |
1128 | // TODO: There is no good way to get the language name of a BCP code, |
1129 | // as MW language codes take precedence |
1130 | $name = MediaWikiServices::getInstance() |
1131 | ->getLanguageNameUtils() |
1132 | ->getLanguageName( $lang, $this->getContext()->getLanguage()->getCode() ); |
1133 | if ( $name !== '' ) { |
1134 | $display = $this->getContext()->msg( 'img-lang-opt', $lang, $name )->text(); |
1135 | } else { |
1136 | $display = $lang; |
1137 | } |
1138 | return "\n" . |
1139 | Xml::option( |
1140 | $display, |
1141 | $lang, |
1142 | $selected |
1143 | ); |
1144 | } |
1145 | |
1146 | /** |
1147 | * Get alternative thumbnail sizes. |
1148 | * |
1149 | * @note This will only list several alternatives if thumbnails are rendered on 404 |
1150 | * @param int $origWidth Actual width of image |
1151 | * @param int $origHeight Actual height of image |
1152 | * @return int[][] An array of [width, height] pairs. |
1153 | * @phan-return array<int,array{0:int,1:int}> |
1154 | */ |
1155 | protected function getThumbSizes( $origWidth, $origHeight ) { |
1156 | $context = $this->getContext(); |
1157 | $imageLimits = $context->getConfig()->get( MainConfigNames::ImageLimits ); |
1158 | if ( $this->displayImg->getRepo()->canTransformVia404() ) { |
1159 | $thumbSizes = $imageLimits; |
1160 | // Also include the full sized resolution in the list, so |
1161 | // that users know they can get it. This will link to the |
1162 | // original file asset if mustRender() === false. In the case |
1163 | // that we mustRender, some users have indicated that they would |
1164 | // find it useful to have the full size image in the rendered |
1165 | // image format. |
1166 | $thumbSizes[] = [ $origWidth, $origHeight ]; |
1167 | } else { |
1168 | # Creating thumb links triggers thumbnail generation. |
1169 | # Just generate the thumb for the current users prefs. |
1170 | $thumbSizes = [ |
1171 | $this->getImageLimitsFromOption( $context->getUser(), 'thumbsize' ) |
1172 | ]; |
1173 | if ( !$this->displayImg->mustRender() ) { |
1174 | // We can safely include a link to the "full-size" preview, |
1175 | // without actually rendering. |
1176 | $thumbSizes[] = [ $origWidth, $origHeight ]; |
1177 | } |
1178 | } |
1179 | return $thumbSizes; |
1180 | } |
1181 | |
1182 | /** |
1183 | * @see WikiFilePage::getFile |
1184 | * @return File |
1185 | */ |
1186 | public function getFile(): File { |
1187 | return $this->getPage()->getFile(); |
1188 | } |
1189 | |
1190 | /** |
1191 | * @see WikiFilePage::isLocal |
1192 | * @return bool |
1193 | */ |
1194 | public function isLocal() { |
1195 | return $this->getPage()->isLocal(); |
1196 | } |
1197 | |
1198 | /** |
1199 | * @see WikiFilePage::getDuplicates |
1200 | * @return File[]|null |
1201 | */ |
1202 | public function getDuplicates() { |
1203 | return $this->getPage()->getDuplicates(); |
1204 | } |
1205 | |
1206 | /** |
1207 | * @see WikiFilePage::getForeignCategories |
1208 | * @return TitleArrayFromResult |
1209 | */ |
1210 | public function getForeignCategories() { |
1211 | return $this->getPage()->getForeignCategories(); |
1212 | } |
1213 | |
1214 | } |