Completed
Branch master (939199)
by
unknown
39:35
created

includes/page/ImagePage.php (5 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Special handling for file description pages.
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License along
16
 * with this program; if not, write to the Free Software Foundation, Inc.,
17
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
 * http://www.gnu.org/copyleft/gpl.html
19
 *
20
 * @file
21
 */
22
23
/**
24
 * Class for viewing MediaWiki file description pages
25
 *
26
 * @ingroup Media
27
 */
28
class ImagePage extends Article {
29
	/** @var File */
30
	private $displayImg;
31
32
	/** @var FileRepo */
33
	private $repo;
34
35
	/** @var bool */
36
	private $fileLoaded;
37
38
	/** @var bool */
39
	protected $mExtraDescription = false;
40
41
	/**
42
	 * @var WikiFilePage
43
	 */
44
	protected $mPage;
45
46
	/**
47
	 * @param Title $title
48
	 * @return WikiFilePage
49
	 */
50
	protected function newPage( Title $title ) {
51
		// Overload mPage with a file-specific page
52
		return new WikiFilePage( $title );
53
	}
54
55
	/**
56
	 * @param File $file
57
	 * @return void
58
	 */
59
	public function setFile( $file ) {
60
		$this->mPage->setFile( $file );
61
		$this->displayImg = $file;
62
		$this->fileLoaded = true;
63
	}
64
65
	protected function loadFile() {
66
		if ( $this->fileLoaded ) {
67
			return;
68
		}
69
		$this->fileLoaded = true;
70
71
		$this->displayImg = $img = false;
72
		Hooks::run( 'ImagePageFindFile', [ $this, &$img, &$this->displayImg ] );
73
		if ( !$img ) { // not set by hook?
74
			$img = wfFindFile( $this->getTitle() );
75
			if ( !$img ) {
76
				$img = wfLocalFile( $this->getTitle() );
77
			}
78
		}
79
		$this->mPage->setFile( $img );
80
		if ( !$this->displayImg ) { // not set by hook?
81
			$this->displayImg = $img;
82
		}
83
		$this->repo = $img->getRepo();
84
	}
85
86
	/**
87
	 * Handler for action=render
88
	 * Include body text only; none of the image extras
89
	 */
90
	public function render() {
91
		$this->getContext()->getOutput()->setArticleBodyOnly( true );
92
		parent::view();
93
	}
94
95
	public function view() {
96
		global $wgShowEXIF;
97
98
		$out = $this->getContext()->getOutput();
99
		$request = $this->getContext()->getRequest();
100
		$diff = $request->getVal( 'diff' );
101
		$diffOnly = $request->getBool(
102
			'diffonly',
103
			$this->getContext()->getUser()->getOption( 'diffonly' )
104
		);
105
106
		if ( $this->getTitle()->getNamespace() != NS_FILE || ( $diff !== null && $diffOnly ) ) {
107
			parent::view();
108
			return;
109
		}
110
111
		$this->loadFile();
112
113
		if ( $this->getTitle()->getNamespace() == NS_FILE && $this->mPage->getFile()->getRedirected() ) {
114
			if ( $this->getTitle()->getDBkey() == $this->mPage->getFile()->getName() || $diff !== null ) {
115
				// mTitle is the same as the redirect target so ask Article
116
				// to perform the redirect for us.
117
				$request->setVal( 'diffonly', 'true' );
118
				parent::view();
119
				return;
120
			} else {
121
				// mTitle is not the same as the redirect target so it is
122
				// probably the redirect page itself. Fake the redirect symbol
123
				$out->setPageTitle( $this->getTitle()->getPrefixedText() );
124
				$out->addHTML( $this->viewRedirect(
125
					Title::makeTitle( NS_FILE, $this->mPage->getFile()->getName() ),
126
					/* $appendSubtitle */ true,
127
					/* $forceKnown */ true )
128
				);
129
				$this->mPage->doViewUpdates( $this->getContext()->getUser(), $this->getOldID() );
130
				return;
131
			}
132
		}
133
134
		if ( $wgShowEXIF && $this->displayImg->exists() ) {
135
			// @todo FIXME: Bad interface, see note on MediaHandler::formatMetadata().
136
			$formattedMetadata = $this->displayImg->formatMetadata( $this->getContext() );
137
			$showmeta = $formattedMetadata !== false;
138
		} else {
139
			$showmeta = false;
140
		}
141
142
		if ( !$diff && $this->displayImg->exists() ) {
143
			$out->addHTML( $this->showTOC( $showmeta ) );
144
		}
145
146
		if ( !$diff ) {
147
			$this->openShowImage();
148
		}
149
150
		# No need to display noarticletext, we use our own message, output in openShowImage()
151
		if ( $this->mPage->getId() ) {
152
			# NS_FILE is in the user language, but this section (the actual wikitext)
153
			# should be in page content language
154
			$pageLang = $this->getTitle()->getPageViewLanguage();
155
			$out->addHTML( Xml::openElement( 'div', [ 'id' => 'mw-imagepage-content',
156
				'lang' => $pageLang->getHtmlCode(), 'dir' => $pageLang->getDir(),
157
				'class' => 'mw-content-' . $pageLang->getDir() ] ) );
158
159
			parent::view();
160
161
			$out->addHTML( Xml::closeElement( 'div' ) );
162
		} else {
163
			# Just need to set the right headers
164
			$out->setArticleFlag( true );
165
			$out->setPageTitle( $this->getTitle()->getPrefixedText() );
166
			$this->mPage->doViewUpdates( $this->getContext()->getUser(), $this->getOldID() );
167
		}
168
169
		# Show shared description, if needed
170
		if ( $this->mExtraDescription ) {
171
			$fol = $this->getContext()->msg( 'shareddescriptionfollows' );
172
			if ( !$fol->isDisabled() ) {
173
				$out->addWikiText( $fol->plain() );
174
			}
175
			$out->addHTML( '<div id="shared-image-desc">' . $this->mExtraDescription . "</div>\n" );
176
		}
177
178
		$this->closeShowImage();
179
		$this->imageHistory();
180
		// TODO: Cleanup the following
181
182
		$out->addHTML( Xml::element( 'h2',
183
			[ 'id' => 'filelinks' ],
184
				$this->getContext()->msg( 'imagelinks' )->text() ) . "\n" );
185
		$this->imageDupes();
186
		# @todo FIXME: For some freaky reason, we can't redirect to foreign images.
187
		# Yet we return metadata about the target. Definitely an issue in the FileRepo
188
		$this->imageLinks();
189
190
		# Allow extensions to add something after the image links
191
		$html = '';
192
		Hooks::run( 'ImagePageAfterImageLinks', [ $this, &$html ] );
193
		if ( $html ) {
194
			$out->addHTML( $html );
195
		}
196
197
		if ( $showmeta ) {
198
			$out->addHTML( Xml::element(
199
				'h2',
200
				[ 'id' => 'metadata' ],
201
					$this->getContext()->msg( 'metadata' )->text() ) . "\n" );
202
			$out->addWikiText( $this->makeMetadataTable( $formattedMetadata ) );
203
			$out->addModules( [ 'mediawiki.action.view.metadata' ] );
204
		}
205
206
		// Add remote Filepage.css
207
		if ( !$this->repo->isLocal() ) {
208
			$css = $this->repo->getDescriptionStylesheetUrl();
209
			if ( $css ) {
210
				$out->addStyle( $css );
211
			}
212
		}
213
214
		$out->addModuleStyles( [
215
			'filepage', // always show the local local Filepage.css, bug 29277
216
			'mediawiki.action.view.filepage', // Add MediaWiki styles for a file page
217
		] );
218
219
	}
220
221
	/**
222
	 * @return File
223
	 */
224
	public function getDisplayedFile() {
225
		$this->loadFile();
226
		return $this->displayImg;
227
	}
228
229
	/**
230
	 * Create the TOC
231
	 *
232
	 * @param bool $metadata Whether or not to show the metadata link
233
	 * @return string
234
	 */
235
	protected function showTOC( $metadata ) {
236
		$r = [
237
			'<li><a href="#file">' . $this->getContext()->msg( 'file-anchor-link' )->escaped() . '</a></li>',
238
			'<li><a href="#filehistory">' . $this->getContext()->msg( 'filehist' )->escaped() . '</a></li>',
239
			'<li><a href="#filelinks">' . $this->getContext()->msg( 'imagelinks' )->escaped() . '</a></li>',
240
		];
241
242
		Hooks::run( 'ImagePageShowTOC', [ $this, &$r ] );
243
244
		if ( $metadata ) {
245
			$r[] = '<li><a href="#metadata">' .
246
				$this->getContext()->msg( 'metadata' )->escaped() .
247
				'</a></li>';
248
		}
249
250
		return '<ul id="filetoc">' . implode( "\n", $r ) . '</ul>';
251
	}
252
253
	/**
254
	 * Make a table with metadata to be shown in the output page.
255
	 *
256
	 * @todo FIXME: Bad interface, see note on MediaHandler::formatMetadata().
257
	 *
258
	 * @param array $metadata The array containing the Exif data
259
	 * @return string The metadata table. This is treated as Wikitext (!)
260
	 */
261
	protected function makeMetadataTable( $metadata ) {
262
		$r = "<div class=\"mw-imagepage-section-metadata\">";
263
		$r .= $this->getContext()->msg( 'metadata-help' )->plain();
264
		$r .= "<table id=\"mw_metadata\" class=\"mw_metadata\">\n";
265
		foreach ( $metadata as $type => $stuff ) {
266
			foreach ( $stuff as $v ) {
267
				# @todo FIXME: Why is this using escapeId for a class?!
268
				$class = Sanitizer::escapeId( $v['id'] );
269
				if ( $type == 'collapsed' ) {
270
					// Handled by mediawiki.action.view.metadata module.
271
					$class .= ' collapsable';
272
				}
273
				$r .= "<tr class=\"$class\">\n";
274
				$r .= "<th>{$v['name']}</th>\n";
275
				$r .= "<td>{$v['value']}</td>\n</tr>";
276
			}
277
		}
278
		$r .= "</table>\n</div>\n";
279
		return $r;
280
	}
281
282
	/**
283
	 * Overloading Article's getContentObject method.
284
	 *
285
	 * Omit noarticletext if sharedupload; text will be fetched from the
286
	 * shared upload server if possible.
287
	 * @return string
288
	 */
289
	public function getContentObject() {
290
		$this->loadFile();
291
		if ( $this->mPage->getFile() && !$this->mPage->getFile()->isLocal() && 0 == $this->getId() ) {
292
			return null;
293
		}
294
		return parent::getContentObject();
295
	}
296
297
	protected function openShowImage() {
298
		global $wgEnableUploads, $wgSend404Code, $wgSVGMaxSize;
299
300
		$this->loadFile();
301
		$out = $this->getContext()->getOutput();
302
		$user = $this->getContext()->getUser();
303
		$lang = $this->getContext()->getLanguage();
304
		$dirmark = $lang->getDirMarkEntity();
305
		$request = $this->getContext()->getRequest();
306
307
		$max = $this->getImageLimitsFromOption( $user, 'imagesize' );
308
		$maxWidth = $max[0];
309
		$maxHeight = $max[1];
310
311
		if ( $this->displayImg->exists() ) {
312
			# image
313
			$page = $request->getIntOrNull( 'page' );
314
			if ( is_null( $page ) ) {
315
				$params = [];
316
				$page = 1;
317
			} else {
318
				$params = [ 'page' => $page ];
319
			}
320
321
			$renderLang = $request->getVal( 'lang' );
322
			if ( !is_null( $renderLang ) ) {
323
				$handler = $this->displayImg->getHandler();
324
				if ( $handler && $handler->validateParam( 'lang', $renderLang ) ) {
325
					$params['lang'] = $renderLang;
326
				} else {
327
					$renderLang = null;
328
				}
329
			}
330
331
			$width_orig = $this->displayImg->getWidth( $page );
332
			$width = $width_orig;
333
			$height_orig = $this->displayImg->getHeight( $page );
334
			$height = $height_orig;
335
336
			$filename = wfEscapeWikiText( $this->displayImg->getName() );
337
			$linktext = $filename;
338
339
			Hooks::run( 'ImageOpenShowImageInlineBefore', [ &$this, &$out ] );
340
341
			if ( $this->displayImg->allowInlineDisplay() ) {
342
				# image
343
				# "Download high res version" link below the image
344
				# $msgsize = $this->getContext()->msg( 'file-info-size', $width_orig, $height_orig,
345
				#   Linker::formatSize( $this->displayImg->getSize() ), $mime )->escaped();
346
				# We'll show a thumbnail of this image
347
				if ( $width > $maxWidth || $height > $maxHeight || $this->displayImg->isVectorized() ) {
348
					list( $width, $height ) = $this->getDisplayWidthHeight(
349
						$maxWidth, $maxHeight, $width, $height
0 ignored issues
show
It seems like $width can also be of type boolean; however, ImagePage::getDisplayWidthHeight() does only seem to accept integer, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
It seems like $height can also be of type boolean; however, ImagePage::getDisplayWidthHeight() does only seem to accept integer, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
350
					);
351
					$linktext = $this->getContext()->msg( 'show-big-image' )->escaped();
352
353
					$thumbSizes = $this->getThumbSizes( $width_orig, $height_orig );
0 ignored issues
show
It seems like $width_orig defined by $this->displayImg->getWidth($page) on line 331 can also be of type boolean; however, ImagePage::getThumbSizes() does only seem to accept integer, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
It seems like $height_orig defined by $this->displayImg->getHeight($page) on line 333 can also be of type boolean; however, ImagePage::getThumbSizes() does only seem to accept integer, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
354
					# Generate thumbnails or thumbnail links as needed...
355
					$otherSizes = [];
356
					foreach ( $thumbSizes as $size ) {
357
						// We include a thumbnail size in the list, if it is
358
						// less than or equal to the original size of the image
359
						// asset ($width_orig/$height_orig). We also exclude
360
						// the current thumbnail's size ($width/$height)
361
						// since that is added to the message separately, so
362
						// it can be denoted as the current size being shown.
363
						// Vectorized images are limited by $wgSVGMaxSize big,
364
						// so all thumbs less than or equal that are shown.
365
						if ( ( ( $size[0] <= $width_orig && $size[1] <= $height_orig )
366
								|| ( $this->displayImg->isVectorized()
367
									&& max( $size[0], $size[1] ) <= $wgSVGMaxSize )
368
							)
369
							&& $size[0] != $width && $size[1] != $height
370
						) {
371
							$sizeLink = $this->makeSizeLink( $params, $size[0], $size[1] );
372
							if ( $sizeLink ) {
373
								$otherSizes[] = $sizeLink;
374
							}
375
						}
376
					}
377
					$otherSizes = array_unique( $otherSizes );
378
379
					$sizeLinkBigImagePreview = $this->makeSizeLink( $params, $width, $height );
380
					$msgsmall = $this->getThumbPrevText( $params, $sizeLinkBigImagePreview );
381
					if ( count( $otherSizes ) ) {
382
						$msgsmall .= ' ' .
383
						Html::rawElement(
384
							'span',
385
							[ 'class' => 'mw-filepage-other-resolutions' ],
386
							$this->getContext()->msg( 'show-big-image-other' )
387
								->rawParams( $lang->pipeList( $otherSizes ) )
388
								->params( count( $otherSizes ) )
389
								->parse()
390
						);
391
					}
392
				} elseif ( $width == 0 && $height == 0 ) {
393
					# Some sort of audio file that doesn't have dimensions
394
					# Don't output a no hi res message for such a file
395
					$msgsmall = '';
396
				} else {
397
					# Image is small enough to show full size on image page
398
					$msgsmall = $this->getContext()->msg( 'file-nohires' )->parse();
399
				}
400
401
				$params['width'] = $width;
402
				$params['height'] = $height;
403
				$thumbnail = $this->displayImg->transform( $params );
404
				Linker::processResponsiveImages( $this->displayImg, $thumbnail, $params );
405
406
				$anchorclose = Html::rawElement(
407
					'div',
408
					[ 'class' => 'mw-filepage-resolutioninfo' ],
409
					$msgsmall
410
				);
411
412
				$isMulti = $this->displayImg->isMultipage() && $this->displayImg->pageCount() > 1;
413
				if ( $isMulti ) {
414
					$out->addModules( 'mediawiki.page.image.pagination' );
415
					$out->addHTML( '<table class="multipageimage"><tr><td>' );
416
				}
417
418
				if ( $thumbnail ) {
419
					$options = [
420
						'alt' => $this->displayImg->getTitle()->getPrefixedText(),
421
						'file-link' => true,
422
					];
423
					$out->addHTML( '<div class="fullImageLink" id="file">' .
424
						$thumbnail->toHtml( $options ) .
425
						$anchorclose . "</div>\n" );
426
				}
427
428
				if ( $isMulti ) {
429
					$count = $this->displayImg->pageCount();
430
431
					if ( $page > 1 ) {
432
						$label = $out->parse( $this->getContext()->msg( 'imgmultipageprev' )->text(), false );
433
						// on the client side, this link is generated in ajaxifyPageNavigation()
434
						// in the mediawiki.page.image.pagination module
435
						$link = Linker::linkKnown(
436
							$this->getTitle(),
437
							$label,
438
							[],
439
							[ 'page' => $page - 1 ]
440
						);
441
						$thumb1 = Linker::makeThumbLinkObj(
442
							$this->getTitle(),
443
							$this->displayImg,
444
							$link,
445
							$label,
446
							'none',
447
							[ 'page' => $page - 1 ]
448
						);
449
					} else {
450
						$thumb1 = '';
451
					}
452
453
					if ( $page < $count ) {
454
						$label = $this->getContext()->msg( 'imgmultipagenext' )->text();
455
						$link = Linker::linkKnown(
456
							$this->getTitle(),
457
							$label,
458
							[],
459
							[ 'page' => $page + 1 ]
460
						);
461
						$thumb2 = Linker::makeThumbLinkObj(
462
							$this->getTitle(),
463
							$this->displayImg,
464
							$link,
465
							$label,
466
							'none',
467
							[ 'page' => $page + 1 ]
468
						);
469
					} else {
470
						$thumb2 = '';
471
					}
472
473
					global $wgScript;
474
475
					$formParams = [
476
						'name' => 'pageselector',
477
						'action' => $wgScript,
478
					];
479
					$options = [];
480
					for ( $i = 1; $i <= $count; $i++ ) {
481
						$options[] = Xml::option( $lang->formatNum( $i ), $i, $i == $page );
482
					}
483
					$select = Xml::tags( 'select',
484
						[ 'id' => 'pageselector', 'name' => 'page' ],
485
						implode( "\n", $options ) );
486
487
					$out->addHTML(
488
						'</td><td><div class="multipageimagenavbox">' .
489
						Xml::openElement( 'form', $formParams ) .
490
						Html::hidden( 'title', $this->getTitle()->getPrefixedDBkey() ) .
491
						$this->getContext()->msg( 'imgmultigoto' )->rawParams( $select )->parse() .
492
						Xml::submitButton( $this->getContext()->msg( 'imgmultigo' )->text() ) .
493
						Xml::closeElement( 'form' ) .
494
						"<hr />$thumb1\n$thumb2<br style=\"clear: both\" /></div></td></tr></table>"
495
					);
496
				}
497
			} elseif ( $this->displayImg->isSafeFile() ) {
498
				# if direct link is allowed but it's not a renderable image, show an icon.
499
				$icon = $this->displayImg->iconThumb();
500
501
				$out->addHTML( '<div class="fullImageLink" id="file">' .
502
					$icon->toHtml( [ 'file-link' => true ] ) .
503
					"</div>\n" );
504
			}
505
506
			$longDesc = $this->getContext()->msg( 'parentheses', $this->displayImg->getLongDesc() )->text();
507
508
			$handler = $this->displayImg->getHandler();
509
510
			// If this is a filetype with potential issues, warn the user.
511
			if ( $handler ) {
512
				$warningConfig = $handler->getWarningConfig( $this->displayImg );
513
514
				if ( $warningConfig !== null ) {
515
					// The warning will be displayed via CSS and JavaScript.
516
					// We just need to tell the client side what message to use.
517
					$output = $this->getContext()->getOutput();
518
					$output->addJsConfigVars( 'wgFileWarning', $warningConfig );
519
					$output->addModules( $warningConfig['module'] );
520
					$output->addModules( 'mediawiki.filewarning' );
521
				}
522
			}
523
524
			$medialink = "[[Media:$filename|$linktext]]";
525
526
			if ( !$this->displayImg->isSafeFile() ) {
527
				$warning = $this->getContext()->msg( 'mediawarning' )->plain();
528
				// dirmark is needed here to separate the file name, which
529
				// most likely ends in Latin characters, from the description,
530
				// which may begin with the file type. In RTL environment
531
				// this will get messy.
532
				// The dirmark, however, must not be immediately adjacent
533
				// to the filename, because it can get copied with it.
534
				// See bug 25277.
535
				// @codingStandardsIgnoreStart Ignore long line
536
				$out->addWikiText( <<<EOT
537
<div class="fullMedia"><span class="dangerousLink">{$medialink}</span> $dirmark<span class="fileInfo">$longDesc</span></div>
538
<div class="mediaWarning">$warning</div>
539
EOT
540
				);
541
				// @codingStandardsIgnoreEnd
542
			} else {
543
				$out->addWikiText( <<<EOT
544
<div class="fullMedia">{$medialink} {$dirmark}<span class="fileInfo">$longDesc</span>
545
</div>
546
EOT
547
				);
548
			}
549
550
			$renderLangOptions = $this->displayImg->getAvailableLanguages();
551
			if ( count( $renderLangOptions ) >= 1 ) {
552
				$currentLanguage = $renderLang;
553
				$defaultLang = $this->displayImg->getDefaultRenderLanguage();
554
				if ( is_null( $currentLanguage ) ) {
555
					$currentLanguage = $defaultLang;
556
				}
557
				$out->addHTML( $this->doRenderLangOpt( $renderLangOptions, $currentLanguage, $defaultLang ) );
558
			}
559
560
			// Add cannot animate thumbnail warning
561
			if ( !$this->displayImg->canAnimateThumbIfAppropriate() ) {
562
				// Include the extension so wiki admins can
563
				// customize it on a per file-type basis
564
				// (aka say things like use format X instead).
565
				// additionally have a specific message for
566
				// file-no-thumb-animation-gif
567
				$ext = $this->displayImg->getExtension();
568
				$noAnimMesg = wfMessageFallback(
569
					'file-no-thumb-animation-' . $ext,
570
					'file-no-thumb-animation'
571
				)->plain();
572
573
				$out->addWikiText( <<<EOT
574
<div class="mw-noanimatethumb">{$noAnimMesg}</div>
575
EOT
576
				);
577
			}
578
579
			if ( !$this->displayImg->isLocal() ) {
580
				$this->printSharedImageText();
581
			}
582
		} else {
583
			# Image does not exist
584 View Code Duplication
			if ( !$this->getId() ) {
585
				# No article exists either
586
				# Show deletion log to be consistent with normal articles
587
				LogEventsList::showLogExtract(
588
					$out,
589
					[ 'delete', 'move' ],
590
					$this->getTitle()->getPrefixedText(),
591
					'',
592
					[ 'lim' => 10,
593
						'conds' => [ "log_action != 'revision'" ],
594
						'showIfEmpty' => false,
595
						'msgKey' => [ 'moveddeleted-notice' ]
596
					]
597
				);
598
			}
599
600
			if ( $wgEnableUploads && $user->isAllowed( 'upload' ) ) {
601
				// Only show an upload link if the user can upload
602
				$uploadTitle = SpecialPage::getTitleFor( 'Upload' );
603
				$nofile = [
604
					'filepage-nofile-link',
605
					$uploadTitle->getFullURL( [ 'wpDestFile' => $this->mPage->getFile()->getName() ] )
606
				];
607
			} else {
608
				$nofile = 'filepage-nofile';
609
			}
610
			// Note, if there is an image description page, but
611
			// no image, then this setRobotPolicy is overridden
612
			// by Article::View().
613
			$out->setRobotPolicy( 'noindex,nofollow' );
614
			$out->wrapWikiMsg( "<div id='mw-imagepage-nofile' class='plainlinks'>\n$1\n</div>", $nofile );
615
			if ( !$this->getId() && $wgSend404Code ) {
616
				// If there is no image, no shared image, and no description page,
617
				// output a 404, to be consistent with Article::showMissingArticle.
618
				$request->response()->statusHeader( 404 );
619
			}
620
		}
621
		$out->setFileVersion( $this->displayImg );
622
	}
623
624
	/**
625
	 * Make the text under the image to say what size preview
626
	 *
627
	 * @param $params array parameters for thumbnail
628
	 * @param $sizeLinkBigImagePreview HTML for the current size
629
	 * @return string HTML output
630
	 */
631
	private function getThumbPrevText( $params, $sizeLinkBigImagePreview ) {
632
		if ( $sizeLinkBigImagePreview ) {
633
			// Show a different message of preview is different format from original.
634
			$previewTypeDiffers = false;
635
			$origExt = $thumbExt = $this->displayImg->getExtension();
636
			if ( $this->displayImg->getHandler() ) {
637
				$origMime = $this->displayImg->getMimeType();
638
				$typeParams = $params;
639
				$this->displayImg->getHandler()->normaliseParams( $this->displayImg, $typeParams );
640
				list( $thumbExt, $thumbMime ) = $this->displayImg->getHandler()->getThumbType(
641
					$origExt, $origMime, $typeParams );
642
				if ( $thumbMime !== $origMime ) {
643
					$previewTypeDiffers = true;
644
				}
645
			}
646
			if ( $previewTypeDiffers ) {
647
				return $this->getContext()->msg( 'show-big-image-preview-differ' )->
648
					rawParams( $sizeLinkBigImagePreview )->
649
					params( strtoupper( $origExt ) )->
650
					params( strtoupper( $thumbExt ) )->
651
					parse();
652
			} else {
653
				return $this->getContext()->msg( 'show-big-image-preview' )->
654
					rawParams( $sizeLinkBigImagePreview )->
655
				parse();
656
			}
657
		} else {
658
			return '';
659
		}
660
	}
661
662
	/**
663
	 * Creates an thumbnail of specified size and returns an HTML link to it
664
	 * @param array $params Scaler parameters
665
	 * @param int $width
666
	 * @param int $height
667
	 * @return string
668
	 */
669
	private function makeSizeLink( $params, $width, $height ) {
670
		$params['width'] = $width;
671
		$params['height'] = $height;
672
		$thumbnail = $this->displayImg->transform( $params );
673
		if ( $thumbnail && !$thumbnail->isError() ) {
674
			return Html::rawElement( 'a', [
675
				'href' => $thumbnail->getUrl(),
676
				'class' => 'mw-thumbnail-link'
677
				], $this->getContext()->msg( 'show-big-image-size' )->numParams(
678
					$thumbnail->getWidth(), $thumbnail->getHeight()
679
				)->parse() );
680
		} else {
681
			return '';
682
		}
683
	}
684
685
	/**
686
	 * Show a notice that the file is from a shared repository
687
	 */
688
	protected function printSharedImageText() {
689
		$out = $this->getContext()->getOutput();
690
		$this->loadFile();
691
692
		$descUrl = $this->mPage->getFile()->getDescriptionUrl();
693
		$descText = $this->mPage->getFile()->getDescriptionText( $this->getContext()->getLanguage() );
694
695
		/* Add canonical to head if there is no local page for this shared file */
696
		if ( $descUrl && $this->mPage->getId() == 0 ) {
697
			$out->setCanonicalUrl( $descUrl );
698
		}
699
700
		$wrap = "<div class=\"sharedUploadNotice\">\n$1\n</div>\n";
701
		$repo = $this->mPage->getFile()->getRepo()->getDisplayName();
702
703
		if ( $descUrl &&
704
			$descText &&
705
			$this->getContext()->msg( 'sharedupload-desc-here' )->plain() !== '-'
706
		) {
707
			$out->wrapWikiMsg( $wrap, [ 'sharedupload-desc-here', $repo, $descUrl ] );
708
		} elseif ( $descUrl &&
709
			$this->getContext()->msg( 'sharedupload-desc-there' )->plain() !== '-'
710
		) {
711
			$out->wrapWikiMsg( $wrap, [ 'sharedupload-desc-there', $repo, $descUrl ] );
712
		} else {
713
			$out->wrapWikiMsg( $wrap, [ 'sharedupload', $repo ], ''/*BACKCOMPAT*/ );
714
		}
715
716
		if ( $descText ) {
717
			$this->mExtraDescription = $descText;
718
		}
719
	}
720
721
	public function getUploadUrl() {
722
		$this->loadFile();
723
		$uploadTitle = SpecialPage::getTitleFor( 'Upload' );
724
		return $uploadTitle->getFullURL( [
725
			'wpDestFile' => $this->mPage->getFile()->getName(),
726
			'wpForReUpload' => 1
727
		] );
728
	}
729
730
	/**
731
	 * Print out the various links at the bottom of the image page, e.g. reupload,
732
	 * external editing (and instructions link) etc.
733
	 */
734
	protected function uploadLinksBox() {
735
		global $wgEnableUploads;
736
737
		if ( !$wgEnableUploads ) {
738
			return;
739
		}
740
741
		$this->loadFile();
742
		if ( !$this->mPage->getFile()->isLocal() ) {
743
			return;
744
		}
745
746
		$out = $this->getContext()->getOutput();
747
		$out->addHTML( "<ul>\n" );
748
749
		# "Upload a new version of this file" link
750
		$canUpload = $this->getTitle()->quickUserCan( 'upload', $this->getContext()->getUser() );
751
		if ( $canUpload && UploadBase::userCanReUpload(
752
				$this->getContext()->getUser(),
753
				$this->mPage->getFile() )
754
		) {
755
			$ulink = Linker::makeExternalLink(
756
				$this->getUploadUrl(),
757
				$this->getContext()->msg( 'uploadnewversion-linktext' )->text()
758
			);
759
			$out->addHTML( "<li id=\"mw-imagepage-reupload-link\">"
760
				. "<div class=\"plainlinks\">{$ulink}</div></li>\n" );
761
		} else {
762
			$out->addHTML( "<li id=\"mw-imagepage-upload-disallowed\">"
763
				. $this->getContext()->msg( 'upload-disallowed-here' )->escaped() . "</li>\n" );
764
		}
765
766
		$out->addHTML( "</ul>\n" );
767
	}
768
769
	/**
770
	 * For overloading
771
	 */
772
	protected function closeShowImage() {
773
	}
774
775
	/**
776
	 * If the page we've just displayed is in the "Image" namespace,
777
	 * we follow it with an upload history of the image and its usage.
778
	 */
779
	protected function imageHistory() {
780
		$this->loadFile();
781
		$out = $this->getContext()->getOutput();
782
		$pager = new ImageHistoryPseudoPager( $this );
783
		$out->addHTML( $pager->getBody() );
784
		$out->preventClickjacking( $pager->getPreventClickjacking() );
785
786
		$this->mPage->getFile()->resetHistory(); // free db resources
787
788
		# Exist check because we don't want to show this on pages where an image
789
		# doesn't exist along with the noimage message, that would suck. -ævar
790
		if ( $this->mPage->getFile()->exists() ) {
791
			$this->uploadLinksBox();
792
		}
793
	}
794
795
	/**
796
	 * @param string $target
797
	 * @param int $limit
798
	 * @return ResultWrapper
799
	 */
800
	protected function queryImageLinks( $target, $limit ) {
801
		$dbr = wfGetDB( DB_REPLICA );
802
803
		return $dbr->select(
804
			[ 'imagelinks', 'page' ],
805
			[ 'page_namespace', 'page_title', 'il_to' ],
806
			[ 'il_to' => $target, 'il_from = page_id' ],
807
			__METHOD__,
808
			[ 'LIMIT' => $limit + 1, 'ORDER BY' => 'il_from', ]
809
		);
810
	}
811
812
	protected function imageLinks() {
813
		$limit = 100;
814
815
		$out = $this->getContext()->getOutput();
816
817
		$rows = [];
818
		$redirects = [];
819
		foreach ( $this->getTitle()->getRedirectsHere( NS_FILE ) as $redir ) {
820
			$redirects[$redir->getDBkey()] = [];
821
			$rows[] = (object)[
822
				'page_namespace' => NS_FILE,
823
				'page_title' => $redir->getDBkey(),
824
			];
825
		}
826
827
		$res = $this->queryImageLinks( $this->getTitle()->getDBkey(), $limit + 1 );
828
		foreach ( $res as $row ) {
829
			$rows[] = $row;
830
		}
831
		$count = count( $rows );
832
833
		$hasMore = $count > $limit;
834
		if ( !$hasMore && count( $redirects ) ) {
835
			$res = $this->queryImageLinks( array_keys( $redirects ),
836
				$limit - count( $rows ) + 1 );
837
			foreach ( $res as $row ) {
838
				$redirects[$row->il_to][] = $row;
839
				$count++;
840
			}
841
			$hasMore = ( $res->numRows() + count( $rows ) ) > $limit;
842
		}
843
844
		if ( $count == 0 ) {
845
			$out->wrapWikiMsg(
846
				Html::rawElement( 'div',
847
					[ 'id' => 'mw-imagepage-nolinkstoimage' ], "\n$1\n" ),
848
				'nolinkstoimage'
849
			);
850
			return;
851
		}
852
853
		$out->addHTML( "<div id='mw-imagepage-section-linkstoimage'>\n" );
854
		if ( !$hasMore ) {
855
			$out->addWikiMsg( 'linkstoimage', $count );
856
		} else {
857
			// More links than the limit. Add a link to [[Special:Whatlinkshere]]
858
			$out->addWikiMsg( 'linkstoimage-more',
859
				$this->getContext()->getLanguage()->formatNum( $limit ),
860
				$this->getTitle()->getPrefixedDBkey()
861
			);
862
		}
863
864
		$out->addHTML(
865
			Html::openElement( 'ul',
866
				[ 'class' => 'mw-imagepage-linkstoimage' ] ) . "\n"
867
		);
868
		$count = 0;
869
870
		// Sort the list by namespace:title
871
		usort( $rows, [ $this, 'compare' ] );
872
873
		// Create links for every element
874
		$currentCount = 0;
875
		foreach ( $rows as $element ) {
876
			$currentCount++;
877
			if ( $currentCount > $limit ) {
878
				break;
879
			}
880
881
			$query = [];
882
			# Add a redirect=no to make redirect pages reachable
883
			if ( isset( $redirects[$element->page_title] ) ) {
884
				$query['redirect'] = 'no';
885
			}
886
			$link = Linker::linkKnown(
887
				Title::makeTitle( $element->page_namespace, $element->page_title ),
888
				null, [], $query
889
			);
890
			if ( !isset( $redirects[$element->page_title] ) ) {
891
				# No redirects
892
				$liContents = $link;
893
			} elseif ( count( $redirects[$element->page_title] ) === 0 ) {
894
				# Redirect without usages
895
				$liContents = $this->getContext()->msg( 'linkstoimage-redirect' )
896
					->rawParams( $link, '' )
897
					->parse();
898
			} else {
899
				# Redirect with usages
900
				$li = '';
901
				foreach ( $redirects[$element->page_title] as $row ) {
902
					$currentCount++;
903
					if ( $currentCount > $limit ) {
904
						break;
905
					}
906
907
					$link2 = Linker::linkKnown( Title::makeTitle( $row->page_namespace, $row->page_title ) );
908
					$li .= Html::rawElement(
909
						'li',
910
						[ 'class' => 'mw-imagepage-linkstoimage-ns' . $element->page_namespace ],
911
						$link2
912
						) . "\n";
913
				}
914
915
				$ul = Html::rawElement(
916
					'ul',
917
					[ 'class' => 'mw-imagepage-redirectstofile' ],
918
					$li
919
					) . "\n";
920
				$liContents = $this->getContext()->msg( 'linkstoimage-redirect' )->rawParams(
921
					$link, $ul )->parse();
922
			}
923
			$out->addHTML( Html::rawElement(
924
					'li',
925
					[ 'class' => 'mw-imagepage-linkstoimage-ns' . $element->page_namespace ],
926
					$liContents
927
				) . "\n"
928
			);
929
930
		};
931
		$out->addHTML( Html::closeElement( 'ul' ) . "\n" );
932
		$res->free();
933
934
		// Add a links to [[Special:Whatlinkshere]]
935
		if ( $count > $limit ) {
936
			$out->addWikiMsg( 'morelinkstoimage', $this->getTitle()->getPrefixedDBkey() );
937
		}
938
		$out->addHTML( Html::closeElement( 'div' ) . "\n" );
939
	}
940
941
	protected function imageDupes() {
942
		$this->loadFile();
943
		$out = $this->getContext()->getOutput();
944
945
		$dupes = $this->mPage->getDuplicates();
946
		if ( count( $dupes ) == 0 ) {
947
			return;
948
		}
949
950
		$out->addHTML( "<div id='mw-imagepage-section-duplicates'>\n" );
951
		$out->addWikiMsg( 'duplicatesoffile',
952
			$this->getContext()->getLanguage()->formatNum( count( $dupes ) ), $this->getTitle()->getDBkey()
953
		);
954
		$out->addHTML( "<ul class='mw-imagepage-duplicates'>\n" );
955
956
		/**
957
		 * @var $file File
958
		 */
959
		foreach ( $dupes as $file ) {
960
			$fromSrc = '';
961
			if ( $file->isLocal() ) {
962
				$link = Linker::linkKnown( $file->getTitle() );
963
			} else {
964
				$link = Linker::makeExternalLink( $file->getDescriptionUrl(),
965
					$file->getTitle()->getPrefixedText() );
966
				$fromSrc = $this->getContext()->msg(
967
					'shared-repo-from',
968
					$file->getRepo()->getDisplayName()
969
				)->text();
970
			}
971
			$out->addHTML( "<li>{$link} {$fromSrc}</li>\n" );
972
		}
973
		$out->addHTML( "</ul></div>\n" );
974
	}
975
976
	/**
977
	 * Delete the file, or an earlier version of it
978
	 */
979
	public function delete() {
980
		$file = $this->mPage->getFile();
981
		if ( !$file->exists() || !$file->isLocal() || $file->getRedirected() ) {
982
			// Standard article deletion
983
			parent::delete();
984
			return;
985
		}
986
987
		$deleter = new FileDeleteForm( $file );
988
		$deleter->execute();
989
	}
990
991
	/**
992
	 * Display an error with a wikitext description
993
	 *
994
	 * @param string $description
995
	 */
996
	function showError( $description ) {
997
		$out = $this->getContext()->getOutput();
998
		$out->setPageTitle( $this->getContext()->msg( 'internalerror' ) );
999
		$out->setRobotPolicy( 'noindex,nofollow' );
1000
		$out->setArticleRelated( false );
1001
		$out->enableClientCache( false );
1002
		$out->addWikiText( $description );
1003
	}
1004
1005
	/**
1006
	 * Callback for usort() to do link sorts by (namespace, title)
1007
	 * Function copied from Title::compare()
1008
	 *
1009
	 * @param object $a Object page to compare with
1010
	 * @param object $b Object page to compare with
1011
	 * @return int Result of string comparison, or namespace comparison
1012
	 */
1013
	protected function compare( $a, $b ) {
1014
		if ( $a->page_namespace == $b->page_namespace ) {
1015
			return strcmp( $a->page_title, $b->page_title );
1016
		} else {
1017
			return $a->page_namespace - $b->page_namespace;
1018
		}
1019
	}
1020
1021
	/**
1022
	 * Returns the corresponding $wgImageLimits entry for the selected user option
1023
	 *
1024
	 * @param User $user
1025
	 * @param string $optionName Name of a option to check, typically imagesize or thumbsize
1026
	 * @return array
1027
	 * @since 1.21
1028
	 */
1029
	public function getImageLimitsFromOption( $user, $optionName ) {
1030
		global $wgImageLimits;
1031
1032
		$option = $user->getIntOption( $optionName );
1033
		if ( !isset( $wgImageLimits[$option] ) ) {
1034
			$option = User::getDefaultOption( $optionName );
1035
		}
1036
1037
		// The user offset might still be incorrect, specially if
1038
		// $wgImageLimits got changed (see bug #8858).
1039
		if ( !isset( $wgImageLimits[$option] ) ) {
1040
			// Default to the first offset in $wgImageLimits
1041
			$option = 0;
1042
		}
1043
1044
		return isset( $wgImageLimits[$option] )
1045
			? $wgImageLimits[$option]
1046
			: [ 800, 600 ]; // if nothing is set, fallback to a hardcoded default
1047
	}
1048
1049
	/**
1050
	 * Output a drop-down box for language options for the file
1051
	 *
1052
	 * @param array $langChoices Array of string language codes
1053
	 * @param string $curLang Language code file is being viewed in.
1054
	 * @param string $defaultLang Language code that image is rendered in by default
1055
	 * @return string HTML to insert underneath image.
1056
	 */
1057
	protected function doRenderLangOpt( array $langChoices, $curLang, $defaultLang ) {
1058
		global $wgScript;
1059
		sort( $langChoices );
1060
		$curLang = wfBCP47( $curLang );
1061
		$defaultLang = wfBCP47( $defaultLang );
1062
		$opts = '';
1063
		$haveCurrentLang = false;
1064
		$haveDefaultLang = false;
1065
1066
		// We make a list of all the language choices in the file.
1067
		// Additionally if the default language to render this file
1068
		// is not included as being in this file (for example, in svgs
1069
		// usually the fallback content is the english content) also
1070
		// include a choice for that. Last of all, if we're viewing
1071
		// the file in a language not on the list, add it as a choice.
1072
		foreach ( $langChoices as $lang ) {
1073
			$code = wfBCP47( $lang );
1074
			$name = Language::fetchLanguageName( $code, $this->getContext()->getLanguage()->getCode() );
1075 View Code Duplication
			if ( $name !== '' ) {
1076
				$display = $this->getContext()->msg( 'img-lang-opt', $code, $name )->text();
1077
			} else {
1078
				$display = $code;
1079
			}
1080
			$opts .= "\n" . Xml::option( $display, $code, $curLang === $code );
1081
			if ( $curLang === $code ) {
1082
				$haveCurrentLang = true;
1083
			}
1084
			if ( $defaultLang === $code ) {
1085
				$haveDefaultLang = true;
1086
			}
1087
		}
1088
		if ( !$haveDefaultLang ) {
1089
			// Its hard to know if the content is really in the default language, or
1090
			// if its just unmarked content that could be in any language.
1091
			$opts = Xml::option(
1092
					$this->getContext()->msg( 'img-lang-default' )->text(),
1093
				$defaultLang,
1094
				$defaultLang === $curLang
1095
			) . $opts;
1096
		}
1097
		if ( !$haveCurrentLang && $defaultLang !== $curLang ) {
1098
			$name = Language::fetchLanguageName( $curLang, $this->getContext()->getLanguage()->getCode() );
1099 View Code Duplication
			if ( $name !== '' ) {
1100
				$display = $this->getContext()->msg( 'img-lang-opt', $curLang, $name )->text();
1101
			} else {
1102
				$display = $curLang;
1103
			}
1104
			$opts = Xml::option( $display, $curLang, true ) . $opts;
1105
		}
1106
1107
		$select = Html::rawElement(
1108
			'select',
1109
			[ 'id' => 'mw-imglangselector', 'name' => 'lang' ],
1110
			$opts
1111
		);
1112
		$submit = Xml::submitButton( $this->getContext()->msg( 'img-lang-go' )->text() );
1113
1114
		$formContents = $this->getContext()->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' => $wgScript ], $formContents )
1121
		);
1122
		return $langSelectLine;
1123
	}
1124
1125
	/**
1126
	 * Get the width and height to display image at.
1127
	 *
1128
	 * @note This method assumes that it is only called if one
1129
	 *  of the dimensions are bigger than the max, or if the
1130
	 *  image is vectorized.
1131
	 *
1132
	 * @param int $maxWidth Max width to display at
1133
	 * @param int $maxHeight Max height to display at
1134
	 * @param int $width Actual width of the image
1135
	 * @param int $height Actual height of the image
1136
	 * @throws MWException
1137
	 * @return array Array (width, height)
1138
	 */
1139
	protected function getDisplayWidthHeight( $maxWidth, $maxHeight, $width, $height ) {
1140
		if ( !$maxWidth || !$maxHeight ) {
1141
			// should never happen
1142
			throw new MWException( 'Using a choice from $wgImageLimits that is 0x0' );
1143
		}
1144
1145
		if ( !$width || !$height ) {
1146
			return [ 0, 0 ];
1147
		}
1148
1149
		# Calculate the thumbnail size.
1150
		if ( $width <= $maxWidth && $height <= $maxHeight ) {
0 ignored issues
show
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
1151
			// Vectorized image, do nothing.
1152
		} elseif ( $width / $height >= $maxWidth / $maxHeight ) {
1153
			# The limiting factor is the width, not the height.
1154
			$height = round( $height * $maxWidth / $width );
1155
			$width = $maxWidth;
1156
			# Note that $height <= $maxHeight now.
1157
		} else {
1158
			$newwidth = floor( $width * $maxHeight / $height );
1159
			$height = round( $height * $newwidth / $width );
1160
			$width = $newwidth;
1161
			# Note that $height <= $maxHeight now, but might not be identical
1162
			# because of rounding.
1163
		}
1164
		return [ $width, $height ];
1165
	}
1166
1167
	/**
1168
	 * Get alternative thumbnail sizes.
1169
	 *
1170
	 * @note This will only list several alternatives if thumbnails are rendered on 404
1171
	 * @param int $origWidth Actual width of image
1172
	 * @param int $origHeight Actual height of image
1173
	 * @return array An array of [width, height] pairs.
1174
	 */
1175
	protected function getThumbSizes( $origWidth, $origHeight ) {
1176
		global $wgImageLimits;
1177
		if ( $this->displayImg->getRepo()->canTransformVia404() ) {
1178
			$thumbSizes = $wgImageLimits;
1179
			// Also include the full sized resolution in the list, so
1180
			// that users know they can get it. This will link to the
1181
			// original file asset if mustRender() === false. In the case
1182
			// that we mustRender, some users have indicated that they would
1183
			// find it useful to have the full size image in the rendered
1184
			// image format.
1185
			$thumbSizes[] = [ $origWidth, $origHeight ];
1186
		} else {
1187
			# Creating thumb links triggers thumbnail generation.
1188
			# Just generate the thumb for the current users prefs.
1189
			$thumbSizes = [
1190
				$this->getImageLimitsFromOption( $this->getContext()->getUser(), 'thumbsize' )
1191
			];
1192
			if ( !$this->displayImg->mustRender() ) {
1193
				// We can safely include a link to the "full-size" preview,
1194
				// without actually rendering.
1195
				$thumbSizes[] = [ $origWidth, $origHeight ];
1196
			}
1197
		}
1198
		return $thumbSizes;
1199
	}
1200
1201
	/**
1202
	 * @see WikiFilePage::getFile
1203
	 * @return bool|File
1204
	 */
1205
	public function getFile() {
1206
		return $this->mPage->getFile();
1207
	}
1208
1209
	/**
1210
	 * @see WikiFilePage::isLocal
1211
	 * @return bool
1212
	 */
1213
	public function isLocal() {
1214
		return $this->mPage->isLocal();
1215
	}
1216
1217
	/**
1218
	 * @see WikiFilePage::getDuplicates
1219
	 * @return array|null
1220
	 */
1221
	public function getDuplicates() {
1222
		return $this->mPage->getDuplicates();
1223
	}
1224
1225
	/**
1226
	 * @see WikiFilePage::getForeignCategories
1227
	 * @return TitleArray|Title[]
1228
	 */
1229
	public function getForeignCategories() {
1230
		$this->mPage->getForeignCategories();
1231
	}
1232
1233
}
1234