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