Completed
Branch master (5cbada)
by
unknown
28:59
created

ImagePage::uploadLinksBox()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 34
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 22
nc 4
nop 0
dl 0
loc 34
rs 8.439
c 0
b 0
f 0
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
	 * Constructor from a page id
57
	 * @param int $id Article ID to load
58
	 * @return ImagePage|null
59
	 */
60
	public static function newFromID( $id ) {
61
		$t = Title::newFromID( $id );
62
		# @todo FIXME: Doesn't inherit right
63
		return $t == null ? null : new self( $t );
64
		# return $t == null ? null : new static( $t ); // PHP 5.3
65
	}
66
67
	/**
68
	 * @param File $file
69
	 * @return void
70
	 */
71
	public function setFile( $file ) {
72
		$this->mPage->setFile( $file );
73
		$this->displayImg = $file;
74
		$this->fileLoaded = true;
75
	}
76
77
	protected function loadFile() {
78
		if ( $this->fileLoaded ) {
79
			return;
80
		}
81
		$this->fileLoaded = true;
82
83
		$this->displayImg = $img = false;
0 ignored issues
show
Documentation Bug introduced by
It seems like $img = false of type false is incompatible with the declared type object<File> of property $displayImg.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
84
		Hooks::run( 'ImagePageFindFile', [ $this, &$img, &$this->displayImg ] );
85
		if ( !$img ) { // not set by hook?
86
			$img = wfFindFile( $this->getTitle() );
87
			if ( !$img ) {
88
				$img = wfLocalFile( $this->getTitle() );
89
			}
90
		}
91
		$this->mPage->setFile( $img );
0 ignored issues
show
Bug introduced by
It seems like $img can also be of type boolean or null; however, WikiFilePage::setFile() does only seem to accept object<File>, 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...
92
		if ( !$this->displayImg ) { // not set by hook?
93
			$this->displayImg = $img;
0 ignored issues
show
Documentation Bug introduced by
It seems like $img can also be of type boolean. However, the property $displayImg is declared as type object<File>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
94
		}
95
		$this->repo = $img->getRepo();
96
	}
97
98
	/**
99
	 * Handler for action=render
100
	 * Include body text only; none of the image extras
101
	 */
102
	public function render() {
103
		$this->getContext()->getOutput()->setArticleBodyOnly( true );
104
		parent::view();
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (view() instead of render()). Are you sure this is correct? If so, you might want to change this to $this->view().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
105
	}
106
107
	public function view() {
108
		global $wgShowEXIF;
109
110
		$out = $this->getContext()->getOutput();
111
		$request = $this->getContext()->getRequest();
112
		$diff = $request->getVal( 'diff' );
113
		$diffOnly = $request->getBool(
114
			'diffonly',
115
			$this->getContext()->getUser()->getOption( 'diffonly' )
116
		);
117
118
		if ( $this->getTitle()->getNamespace() != NS_FILE || ( $diff !== null && $diffOnly ) ) {
119
			parent::view();
120
			return;
121
		}
122
123
		$this->loadFile();
124
125
		if ( $this->getTitle()->getNamespace() == NS_FILE && $this->mPage->getFile()->getRedirected() ) {
126
			if ( $this->getTitle()->getDBkey() == $this->mPage->getFile()->getName() || $diff !== null ) {
127
				// mTitle is the same as the redirect target so ask Article
128
				// to perform the redirect for us.
129
				$request->setVal( 'diffonly', 'true' );
130
				parent::view();
131
				return;
132
			} else {
133
				// mTitle is not the same as the redirect target so it is
134
				// probably the redirect page itself. Fake the redirect symbol
135
				$out->setPageTitle( $this->getTitle()->getPrefixedText() );
136
				$out->addHTML( $this->viewRedirect(
137
					Title::makeTitle( NS_FILE, $this->mPage->getFile()->getName() ),
138
					/* $appendSubtitle */ true,
139
					/* $forceKnown */ true )
140
				);
141
				$this->mPage->doViewUpdates( $this->getContext()->getUser(), $this->getOldID() );
142
				return;
143
			}
144
		}
145
146
		if ( $wgShowEXIF && $this->displayImg->exists() ) {
147
			// @todo FIXME: Bad interface, see note on MediaHandler::formatMetadata().
148
			$formattedMetadata = $this->displayImg->formatMetadata( $this->getContext() );
149
			$showmeta = $formattedMetadata !== false;
150
		} else {
151
			$showmeta = false;
152
		}
153
154
		if ( !$diff && $this->displayImg->exists() ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $diff of type null|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
155
			$out->addHTML( $this->showTOC( $showmeta ) );
156
		}
157
158
		if ( !$diff ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $diff of type null|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
159
			$this->openShowImage();
160
		}
161
162
		# No need to display noarticletext, we use our own message, output in openShowImage()
163
		if ( $this->mPage->getId() ) {
164
			# NS_FILE is in the user language, but this section (the actual wikitext)
165
			# should be in page content language
166
			$pageLang = $this->getTitle()->getPageViewLanguage();
167
			$out->addHTML( Xml::openElement( 'div', [ 'id' => 'mw-imagepage-content',
168
				'lang' => $pageLang->getHtmlCode(), 'dir' => $pageLang->getDir(),
169
				'class' => 'mw-content-' . $pageLang->getDir() ] ) );
170
171
			parent::view();
172
173
			$out->addHTML( Xml::closeElement( 'div' ) );
174
		} else {
175
			# Just need to set the right headers
176
			$out->setArticleFlag( true );
177
			$out->setPageTitle( $this->getTitle()->getPrefixedText() );
178
			$this->mPage->doViewUpdates( $this->getContext()->getUser(), $this->getOldID() );
179
		}
180
181
		# Show shared description, if needed
182
		if ( $this->mExtraDescription ) {
183
			$fol = $this->getContext()->msg( 'shareddescriptionfollows' );
184
			if ( !$fol->isDisabled() ) {
185
				$out->addWikiText( $fol->plain() );
186
			}
187
			$out->addHTML( '<div id="shared-image-desc">' . $this->mExtraDescription . "</div>\n" );
188
		}
189
190
		$this->closeShowImage();
191
		$this->imageHistory();
192
		// TODO: Cleanup the following
193
194
		$out->addHTML( Xml::element( 'h2',
195
			[ 'id' => 'filelinks' ],
196
				$this->getContext()->msg( 'imagelinks' )->text() ) . "\n" );
197
		$this->imageDupes();
198
		# @todo FIXME: For some freaky reason, we can't redirect to foreign images.
199
		# Yet we return metadata about the target. Definitely an issue in the FileRepo
200
		$this->imageLinks();
201
202
		# Allow extensions to add something after the image links
203
		$html = '';
204
		Hooks::run( 'ImagePageAfterImageLinks', [ $this, &$html ] );
205
		if ( $html ) {
206
			$out->addHTML( $html );
207
		}
208
209
		if ( $showmeta ) {
210
			$out->addHTML( Xml::element(
211
				'h2',
212
				[ 'id' => 'metadata' ],
213
					$this->getContext()->msg( 'metadata' )->text() ) . "\n" );
214
			$out->addWikiText( $this->makeMetadataTable( $formattedMetadata ) );
0 ignored issues
show
Bug introduced by
The variable $formattedMetadata does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
215
			$out->addModules( [ 'mediawiki.action.view.metadata' ] );
216
		}
217
218
		// Add remote Filepage.css
219
		if ( !$this->repo->isLocal() ) {
220
			$css = $this->repo->getDescriptionStylesheetUrl();
221
			if ( $css ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $css of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
222
				$out->addStyle( $css );
223
			}
224
		}
225
		// always show the local local Filepage.css, bug 29277
226
		$out->addModuleStyles( 'filepage' );
227
228
		// Add MediaWiki styles for a file page
229
		$out->addModuleStyles( 'mediawiki.action.view.filepage' );
230
	}
231
232
	/**
233
	 * @return File
234
	 */
235
	public function getDisplayedFile() {
236
		$this->loadFile();
237
		return $this->displayImg;
238
	}
239
240
	/**
241
	 * Create the TOC
242
	 *
243
	 * @param bool $metadata Whether or not to show the metadata link
244
	 * @return string
245
	 */
246
	protected function showTOC( $metadata ) {
247
		$r = [
248
			'<li><a href="#file">' . $this->getContext()->msg( 'file-anchor-link' )->escaped() . '</a></li>',
249
			'<li><a href="#filehistory">' . $this->getContext()->msg( 'filehist' )->escaped() . '</a></li>',
250
			'<li><a href="#filelinks">' . $this->getContext()->msg( 'imagelinks' )->escaped() . '</a></li>',
251
		];
252
253
		Hooks::run( 'ImagePageShowTOC', [ $this, &$r ] );
254
255
		if ( $metadata ) {
256
			$r[] = '<li><a href="#metadata">' .
257
				$this->getContext()->msg( 'metadata' )->escaped() .
258
				'</a></li>';
259
		}
260
261
		return '<ul id="filetoc">' . implode( "\n", $r ) . '</ul>';
262
	}
263
264
	/**
265
	 * Make a table with metadata to be shown in the output page.
266
	 *
267
	 * @todo FIXME: Bad interface, see note on MediaHandler::formatMetadata().
268
	 *
269
	 * @param array $metadata The array containing the Exif data
270
	 * @return string The metadata table. This is treated as Wikitext (!)
271
	 */
272
	protected function makeMetadataTable( $metadata ) {
273
		$r = "<div class=\"mw-imagepage-section-metadata\">";
274
		$r .= $this->getContext()->msg( 'metadata-help' )->plain();
275
		$r .= "<table id=\"mw_metadata\" class=\"mw_metadata\">\n";
276
		foreach ( $metadata as $type => $stuff ) {
277
			foreach ( $stuff as $v ) {
278
				# @todo FIXME: Why is this using escapeId for a class?!
279
				$class = Sanitizer::escapeId( $v['id'] );
280
				if ( $type == 'collapsed' ) {
281
					// Handled by mediawiki.action.view.metadata module.
282
					$class .= ' collapsable';
283
				}
284
				$r .= "<tr class=\"$class\">\n";
285
				$r .= "<th>{$v['name']}</th>\n";
286
				$r .= "<td>{$v['value']}</td>\n</tr>";
287
			}
288
		}
289
		$r .= "</table>\n</div>\n";
290
		return $r;
291
	}
292
293
	/**
294
	 * Overloading Article's getContentObject method.
295
	 *
296
	 * Omit noarticletext if sharedupload; text will be fetched from the
297
	 * shared upload server if possible.
298
	 * @return string
299
	 */
300
	public function getContentObject() {
301
		$this->loadFile();
302
		if ( $this->mPage->getFile() && !$this->mPage->getFile()->isLocal() && 0 == $this->getId() ) {
303
			return null;
304
		}
305
		return parent::getContentObject();
306
	}
307
308
	protected function openShowImage() {
309
		global $wgEnableUploads, $wgSend404Code, $wgSVGMaxSize;
310
311
		$this->loadFile();
312
		$out = $this->getContext()->getOutput();
313
		$user = $this->getContext()->getUser();
314
		$lang = $this->getContext()->getLanguage();
315
		$dirmark = $lang->getDirMarkEntity();
316
		$request = $this->getContext()->getRequest();
317
318
		$max = $this->getImageLimitsFromOption( $user, 'imagesize' );
319
		$maxWidth = $max[0];
320
		$maxHeight = $max[1];
321
322
		if ( $this->displayImg->exists() ) {
323
			# image
324
			$page = $request->getIntOrNull( 'page' );
325
			if ( is_null( $page ) ) {
326
				$params = [];
327
				$page = 1;
328
			} else {
329
				$params = [ 'page' => $page ];
330
			}
331
332
			$renderLang = $request->getVal( 'lang' );
333
			if ( !is_null( $renderLang ) ) {
334
				$handler = $this->displayImg->getHandler();
335
				if ( $handler && $handler->validateParam( 'lang', $renderLang ) ) {
336
					$params['lang'] = $renderLang;
337
				} else {
338
					$renderLang = null;
339
				}
340
			}
341
342
			$width_orig = $this->displayImg->getWidth( $page );
343
			$width = $width_orig;
344
			$height_orig = $this->displayImg->getHeight( $page );
345
			$height = $height_orig;
346
347
			$filename = wfEscapeWikiText( $this->displayImg->getName() );
348
			$linktext = $filename;
349
350
			Hooks::run( 'ImageOpenShowImageInlineBefore', [ &$this, &$out ] );
351
352
			if ( $this->displayImg->allowInlineDisplay() ) {
353
				# image
354
				# "Download high res version" link below the image
355
				# $msgsize = $this->getContext()->msg( 'file-info-size', $width_orig, $height_orig,
356
				#   Linker::formatSize( $this->displayImg->getSize() ), $mime )->escaped();
357
				# We'll show a thumbnail of this image
358
				if ( $width > $maxWidth || $height > $maxHeight || $this->displayImg->isVectorized() ) {
359
					list( $width, $height ) = $this->getDisplayWidthHeight(
360
						$maxWidth, $maxHeight, $width, $height
361
					);
362
					$linktext = $this->getContext()->msg( 'show-big-image' )->escaped();
363
364
					$thumbSizes = $this->getThumbSizes( $width_orig, $height_orig );
365
					# Generate thumbnails or thumbnail links as needed...
366
					$otherSizes = [];
367
					foreach ( $thumbSizes as $size ) {
368
						// We include a thumbnail size in the list, if it is
369
						// less than or equal to the original size of the image
370
						// asset ($width_orig/$height_orig). We also exclude
371
						// the current thumbnail's size ($width/$height)
372
						// since that is added to the message separately, so
373
						// it can be denoted as the current size being shown.
374
						// Vectorized images are limited by $wgSVGMaxSize big,
375
						// so all thumbs less than or equal that are shown.
376
						if ( ( ( $size[0] <= $width_orig && $size[1] <= $height_orig )
377
								|| ( $this->displayImg->isVectorized()
378
									&& max( $size[0], $size[1] ) <= $wgSVGMaxSize )
379
							)
380
							&& $size[0] != $width && $size[1] != $height
381
						) {
382
							$sizeLink = $this->makeSizeLink( $params, $size[0], $size[1] );
383
							if ( $sizeLink ) {
384
								$otherSizes[] = $sizeLink;
385
							}
386
						}
387
					}
388
					$otherSizes = array_unique( $otherSizes );
389
390
					$sizeLinkBigImagePreview = $this->makeSizeLink( $params, $width, $height );
391
					$msgsmall = $this->getThumbPrevText( $params, $sizeLinkBigImagePreview );
392
					if ( count( $otherSizes ) ) {
393
						$msgsmall .= ' ' .
394
						Html::rawElement(
395
							'span',
396
							[ 'class' => 'mw-filepage-other-resolutions' ],
397
							$this->getContext()->msg( 'show-big-image-other' )
398
								->rawParams( $lang->pipeList( $otherSizes ) )
399
								->params( count( $otherSizes ) )
400
								->parse()
401
						);
402
					}
403
				} elseif ( $width == 0 && $height == 0 ) {
404
					# Some sort of audio file that doesn't have dimensions
405
					# Don't output a no hi res message for such a file
406
					$msgsmall = '';
407
				} else {
408
					# Image is small enough to show full size on image page
409
					$msgsmall = $this->getContext()->msg( 'file-nohires' )->parse();
410
				}
411
412
				$params['width'] = $width;
413
				$params['height'] = $height;
414
				$thumbnail = $this->displayImg->transform( $params );
415
				Linker::processResponsiveImages( $this->displayImg, $thumbnail, $params );
0 ignored issues
show
Security Bug introduced by
It seems like $thumbnail defined by $this->displayImg->transform($params) on line 414 can also be of type false; however, Linker::processResponsiveImages() does only seem to accept object<MediaTransformOutput>, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
416
417
				$anchorclose = Html::rawElement(
418
					'div',
419
					[ 'class' => 'mw-filepage-resolutioninfo' ],
420
					$msgsmall
421
				);
422
423
				$isMulti = $this->displayImg->isMultipage() && $this->displayImg->pageCount() > 1;
424
				if ( $isMulti ) {
425
					$out->addModules( 'mediawiki.page.image.pagination' );
426
					$out->addHTML( '<table class="multipageimage"><tr><td>' );
427
				}
428
429
				if ( $thumbnail ) {
430
					$options = [
431
						'alt' => $this->displayImg->getTitle()->getPrefixedText(),
432
						'file-link' => true,
433
					];
434
					$out->addHTML( '<div class="fullImageLink" id="file">' .
435
						$thumbnail->toHtml( $options ) .
436
						$anchorclose . "</div>\n" );
437
				}
438
439
				if ( $isMulti ) {
440
					$count = $this->displayImg->pageCount();
441
442
					if ( $page > 1 ) {
443
						$label = $out->parse( $this->getContext()->msg( 'imgmultipageprev' )->text(), false );
444
						// on the client side, this link is generated in ajaxifyPageNavigation()
445
						// in the mediawiki.page.image.pagination module
446
						$link = Linker::linkKnown(
447
							$this->getTitle(),
448
							$label,
449
							[],
450
							[ 'page' => $page - 1 ]
451
						);
452
						$thumb1 = Linker::makeThumbLinkObj(
453
							$this->getTitle(),
454
							$this->displayImg,
455
							$link,
456
							$label,
457
							'none',
458
							[ 'page' => $page - 1 ]
459
						);
460
					} else {
461
						$thumb1 = '';
462
					}
463
464
					if ( $page < $count ) {
465
						$label = $this->getContext()->msg( 'imgmultipagenext' )->text();
466
						$link = Linker::linkKnown(
467
							$this->getTitle(),
468
							$label,
469
							[],
470
							[ 'page' => $page + 1 ]
471
						);
472
						$thumb2 = Linker::makeThumbLinkObj(
473
							$this->getTitle(),
474
							$this->displayImg,
475
							$link,
476
							$label,
477
							'none',
478
							[ 'page' => $page + 1 ]
479
						);
480
					} else {
481
						$thumb2 = '';
482
					}
483
484
					global $wgScript;
485
486
					$formParams = [
487
						'name' => 'pageselector',
488
						'action' => $wgScript,
489
					];
490
					$options = [];
491
					for ( $i = 1; $i <= $count; $i++ ) {
492
						$options[] = Xml::option( $lang->formatNum( $i ), $i, $i == $page );
493
					}
494
					$select = Xml::tags( 'select',
495
						[ 'id' => 'pageselector', 'name' => 'page' ],
496
						implode( "\n", $options ) );
497
498
					$out->addHTML(
499
						'</td><td><div class="multipageimagenavbox">' .
500
						Xml::openElement( 'form', $formParams ) .
501
						Html::hidden( 'title', $this->getTitle()->getPrefixedDBkey() ) .
502
						$this->getContext()->msg( 'imgmultigoto' )->rawParams( $select )->parse() .
503
						Xml::submitButton( $this->getContext()->msg( 'imgmultigo' )->text() ) .
504
						Xml::closeElement( 'form' ) .
505
						"<hr />$thumb1\n$thumb2<br style=\"clear: both\" /></div></td></tr></table>"
506
					);
507
				}
508
			} elseif ( $this->displayImg->isSafeFile() ) {
509
				# if direct link is allowed but it's not a renderable image, show an icon.
510
				$icon = $this->displayImg->iconThumb();
511
512
				$out->addHTML( '<div class="fullImageLink" id="file">' .
513
					$icon->toHtml( [ 'file-link' => true ] ) .
514
					"</div>\n" );
515
			}
516
517
			$longDesc = $this->getContext()->msg( 'parentheses', $this->displayImg->getLongDesc() )->text();
518
519
			$handler = $this->displayImg->getHandler();
520
521
			// If this is a filetype with potential issues, warn the user.
522
			if ( $handler ) {
523
				$warningConfig = $handler->getWarningConfig( $this->displayImg );
524
525
				if ( $warningConfig !== null ) {
526
					// The warning will be displayed via CSS and JavaScript.
527
					// We just need to tell the client side what message to use.
528
					$output = $this->getContext()->getOutput();
529
					$output->addJsConfigVars( 'wgFileWarning', $warningConfig );
530
					$output->addModules( $warningConfig['module'] );
531
					$output->addModules( 'mediawiki.filewarning' );
532
				}
533
			}
534
535
			$medialink = "[[Media:$filename|$linktext]]";
536
537
			if ( !$this->displayImg->isSafeFile() ) {
538
				$warning = $this->getContext()->msg( 'mediawarning' )->plain();
539
				// dirmark is needed here to separate the file name, which
540
				// most likely ends in Latin characters, from the description,
541
				// which may begin with the file type. In RTL environment
542
				// this will get messy.
543
				// The dirmark, however, must not be immediately adjacent
544
				// to the filename, because it can get copied with it.
545
				// See bug 25277.
546
				// @codingStandardsIgnoreStart Ignore long line
547
				$out->addWikiText( <<<EOT
548
<div class="fullMedia"><span class="dangerousLink">{$medialink}</span> $dirmark<span class="fileInfo">$longDesc</span></div>
549
<div class="mediaWarning">$warning</div>
550
EOT
551
				);
552
				// @codingStandardsIgnoreEnd
553
			} else {
554
				$out->addWikiText( <<<EOT
555
<div class="fullMedia">{$medialink} {$dirmark}<span class="fileInfo">$longDesc</span>
556
</div>
557
EOT
558
				);
559
			}
560
561
			$renderLangOptions = $this->displayImg->getAvailableLanguages();
562
			if ( count( $renderLangOptions ) >= 1 ) {
563
				$currentLanguage = $renderLang;
564
				$defaultLang = $this->displayImg->getDefaultRenderLanguage();
565
				if ( is_null( $currentLanguage ) ) {
566
					$currentLanguage = $defaultLang;
567
				}
568
				$out->addHTML( $this->doRenderLangOpt( $renderLangOptions, $currentLanguage, $defaultLang ) );
569
			}
570
571
			// Add cannot animate thumbnail warning
572
			if ( !$this->displayImg->canAnimateThumbIfAppropriate() ) {
573
				// Include the extension so wiki admins can
574
				// customize it on a per file-type basis
575
				// (aka say things like use format X instead).
576
				// additionally have a specific message for
577
				// file-no-thumb-animation-gif
578
				$ext = $this->displayImg->getExtension();
579
				$noAnimMesg = wfMessageFallback(
580
					'file-no-thumb-animation-' . $ext,
581
					'file-no-thumb-animation'
582
				)->plain();
583
584
				$out->addWikiText( <<<EOT
585
<div class="mw-noanimatethumb">{$noAnimMesg}</div>
586
EOT
587
				);
588
			}
589
590
			if ( !$this->displayImg->isLocal() ) {
591
				$this->printSharedImageText();
592
			}
593
		} else {
594
			# Image does not exist
595 View Code Duplication
			if ( !$this->getId() ) {
596
				# No article exists either
597
				# Show deletion log to be consistent with normal articles
598
				LogEventsList::showLogExtract(
599
					$out,
600
					[ 'delete', 'move' ],
601
					$this->getTitle()->getPrefixedText(),
602
					'',
603
					[ 'lim' => 10,
604
						'conds' => [ "log_action != 'revision'" ],
605
						'showIfEmpty' => false,
606
						'msgKey' => [ 'moveddeleted-notice' ]
607
					]
608
				);
609
			}
610
611
			if ( $wgEnableUploads && $user->isAllowed( 'upload' ) ) {
612
				// Only show an upload link if the user can upload
613
				$uploadTitle = SpecialPage::getTitleFor( 'Upload' );
614
				$nofile = [
615
					'filepage-nofile-link',
616
					$uploadTitle->getFullURL( [ 'wpDestFile' => $this->mPage->getFile()->getName() ] )
617
				];
618
			} else {
619
				$nofile = 'filepage-nofile';
620
			}
621
			// Note, if there is an image description page, but
622
			// no image, then this setRobotPolicy is overridden
623
			// by Article::View().
624
			$out->setRobotPolicy( 'noindex,nofollow' );
625
			$out->wrapWikiMsg( "<div id='mw-imagepage-nofile' class='plainlinks'>\n$1\n</div>", $nofile );
626
			if ( !$this->getId() && $wgSend404Code ) {
627
				// If there is no image, no shared image, and no description page,
628
				// output a 404, to be consistent with Article::showMissingArticle.
629
				$request->response()->statusHeader( 404 );
630
			}
631
		}
632
		$out->setFileVersion( $this->displayImg );
633
	}
634
635
	/**
636
	 * Make the text under the image to say what size preview
637
	 *
638
	 * @param $params array parameters for thumbnail
639
	 * @param $sizeLinkBigImagePreview HTML for the current size
640
	 * @return string HTML output
641
	 */
642
	private function getThumbPrevText( $params, $sizeLinkBigImagePreview ) {
643
		if ( $sizeLinkBigImagePreview ) {
644
			// Show a different message of preview is different format from original.
645
			$previewTypeDiffers = false;
646
			$origExt = $thumbExt = $this->displayImg->getExtension();
647
			if ( $this->displayImg->getHandler() ) {
648
				$origMime = $this->displayImg->getMimeType();
649
				$typeParams = $params;
650
				$this->displayImg->getHandler()->normaliseParams( $this->displayImg, $typeParams );
651
				list( $thumbExt, $thumbMime ) = $this->displayImg->getHandler()->getThumbType(
652
					$origExt, $origMime, $typeParams );
653
				if ( $thumbMime !== $origMime ) {
654
					$previewTypeDiffers = true;
655
				}
656
			}
657
			if ( $previewTypeDiffers ) {
658
				return $this->getContext()->msg( 'show-big-image-preview-differ' )->
659
					rawParams( $sizeLinkBigImagePreview )->
660
					params( strtoupper( $origExt ) )->
661
					params( strtoupper( $thumbExt ) )->
662
					parse();
663
			} else {
664
				return $this->getContext()->msg( 'show-big-image-preview' )->
665
					rawParams( $sizeLinkBigImagePreview )->
666
				parse();
667
			}
668
		} else {
669
			return '';
670
		}
671
	}
672
673
	/**
674
	 * Creates an thumbnail of specified size and returns an HTML link to it
675
	 * @param array $params Scaler parameters
676
	 * @param int $width
677
	 * @param int $height
678
	 * @return string
679
	 */
680
	private function makeSizeLink( $params, $width, $height ) {
681
		$params['width'] = $width;
682
		$params['height'] = $height;
683
		$thumbnail = $this->displayImg->transform( $params );
684
		if ( $thumbnail && !$thumbnail->isError() ) {
685
			return Html::rawElement( 'a', [
686
				'href' => $thumbnail->getUrl(),
687
				'class' => 'mw-thumbnail-link'
688
				], $this->getContext()->msg( 'show-big-image-size' )->numParams(
689
					$thumbnail->getWidth(), $thumbnail->getHeight()
690
				)->parse() );
691
		} else {
692
			return '';
693
		}
694
	}
695
696
	/**
697
	 * Show a notice that the file is from a shared repository
698
	 */
699
	protected function printSharedImageText() {
700
		$out = $this->getContext()->getOutput();
701
		$this->loadFile();
702
703
		$descUrl = $this->mPage->getFile()->getDescriptionUrl();
704
		$descText = $this->mPage->getFile()->getDescriptionText( $this->getContext()->getLanguage() );
705
706
		/* Add canonical to head if there is no local page for this shared file */
707
		if ( $descUrl && $this->mPage->getId() == 0 ) {
708
			$out->setCanonicalUrl( $descUrl );
709
		}
710
711
		$wrap = "<div class=\"sharedUploadNotice\">\n$1\n</div>\n";
712
		$repo = $this->mPage->getFile()->getRepo()->getDisplayName();
713
714
		if ( $descUrl &&
715
			$descText &&
716
			$this->getContext()->msg( 'sharedupload-desc-here' )->plain() !== '-'
717
		) {
718
			$out->wrapWikiMsg( $wrap, [ 'sharedupload-desc-here', $repo, $descUrl ] );
719
		} elseif ( $descUrl &&
720
			$this->getContext()->msg( 'sharedupload-desc-there' )->plain() !== '-'
721
		) {
722
			$out->wrapWikiMsg( $wrap, [ 'sharedupload-desc-there', $repo, $descUrl ] );
723
		} else {
724
			$out->wrapWikiMsg( $wrap, [ 'sharedupload', $repo ], ''/*BACKCOMPAT*/ );
725
		}
726
727
		if ( $descText ) {
728
			$this->mExtraDescription = $descText;
0 ignored issues
show
Documentation Bug introduced by
The property $mExtraDescription was declared of type boolean, but $descText is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
729
		}
730
	}
731
732
	public function getUploadUrl() {
733
		$this->loadFile();
734
		$uploadTitle = SpecialPage::getTitleFor( 'Upload' );
735
		return $uploadTitle->getFullURL( [
736
			'wpDestFile' => $this->mPage->getFile()->getName(),
737
			'wpForReUpload' => 1
738
		] );
739
	}
740
741
	/**
742
	 * Print out the various links at the bottom of the image page, e.g. reupload,
743
	 * external editing (and instructions link) etc.
744
	 */
745
	protected function uploadLinksBox() {
746
		global $wgEnableUploads;
747
748
		if ( !$wgEnableUploads ) {
749
			return;
750
		}
751
752
		$this->loadFile();
753
		if ( !$this->mPage->getFile()->isLocal() ) {
754
			return;
755
		}
756
757
		$out = $this->getContext()->getOutput();
758
		$out->addHTML( "<ul>\n" );
759
760
		# "Upload a new version of this file" link
761
		$canUpload = $this->getTitle()->quickUserCan( 'upload', $this->getContext()->getUser() );
762
		if ( $canUpload && UploadBase::userCanReUpload(
763
				$this->getContext()->getUser(),
764
				$this->mPage->getFile() )
765
		) {
766
			$ulink = Linker::makeExternalLink(
767
				$this->getUploadUrl(),
768
				$this->getContext()->msg( 'uploadnewversion-linktext' )->text()
769
			);
770
			$out->addHTML( "<li id=\"mw-imagepage-reupload-link\">"
771
				. "<div class=\"plainlinks\">{$ulink}</div></li>\n" );
772
		} else {
773
			$out->addHTML( "<li id=\"mw-imagepage-upload-disallowed\">"
774
				. $this->getContext()->msg( 'upload-disallowed-here' )->escaped() . "</li>\n" );
775
		}
776
777
		$out->addHTML( "</ul>\n" );
778
	}
779
780
	/**
781
	 * For overloading
782
	 */
783
	protected function closeShowImage() {
784
	}
785
786
	/**
787
	 * If the page we've just displayed is in the "Image" namespace,
788
	 * we follow it with an upload history of the image and its usage.
789
	 */
790
	protected function imageHistory() {
791
		$this->loadFile();
792
		$out = $this->getContext()->getOutput();
793
		$pager = new ImageHistoryPseudoPager( $this );
794
		$out->addHTML( $pager->getBody() );
795
		$out->preventClickjacking( $pager->getPreventClickjacking() );
796
797
		$this->mPage->getFile()->resetHistory(); // free db resources
798
799
		# Exist check because we don't want to show this on pages where an image
800
		# doesn't exist along with the noimage message, that would suck. -ævar
801
		if ( $this->mPage->getFile()->exists() ) {
802
			$this->uploadLinksBox();
803
		}
804
	}
805
806
	/**
807
	 * @param string $target
808
	 * @param int $limit
809
	 * @return ResultWrapper
810
	 */
811
	protected function queryImageLinks( $target, $limit ) {
812
		$dbr = wfGetDB( DB_SLAVE );
813
814
		return $dbr->select(
815
			[ 'imagelinks', 'page' ],
816
			[ 'page_namespace', 'page_title', 'il_to' ],
817
			[ 'il_to' => $target, 'il_from = page_id' ],
818
			__METHOD__,
819
			[ 'LIMIT' => $limit + 1, 'ORDER BY' => 'il_from', ]
820
		);
821
	}
822
823
	protected function imageLinks() {
824
		$limit = 100;
825
826
		$out = $this->getContext()->getOutput();
827
828
		$rows = [];
829
		$redirects = [];
830
		foreach ( $this->getTitle()->getRedirectsHere( NS_FILE ) as $redir ) {
831
			$redirects[$redir->getDBkey()] = [];
832
			$rows[] = (object)[
833
				'page_namespace' => NS_FILE,
834
				'page_title' => $redir->getDBkey(),
835
			];
836
		}
837
838
		$res = $this->queryImageLinks( $this->getTitle()->getDBkey(), $limit + 1 );
839
		foreach ( $res as $row ) {
840
			$rows[] = $row;
841
		}
842
		$count = count( $rows );
843
844
		$hasMore = $count > $limit;
845
		if ( !$hasMore && count( $redirects ) ) {
846
			$res = $this->queryImageLinks( array_keys( $redirects ),
847
				$limit - count( $rows ) + 1 );
848
			foreach ( $res as $row ) {
849
				$redirects[$row->il_to][] = $row;
850
				$count++;
851
			}
852
			$hasMore = ( $res->numRows() + count( $rows ) ) > $limit;
853
		}
854
855
		if ( $count == 0 ) {
856
			$out->wrapWikiMsg(
857
				Html::rawElement( 'div',
858
					[ 'id' => 'mw-imagepage-nolinkstoimage' ], "\n$1\n" ),
859
				'nolinkstoimage'
860
			);
861
			return;
862
		}
863
864
		$out->addHTML( "<div id='mw-imagepage-section-linkstoimage'>\n" );
865
		if ( !$hasMore ) {
866
			$out->addWikiMsg( 'linkstoimage', $count );
867
		} else {
868
			// More links than the limit. Add a link to [[Special:Whatlinkshere]]
869
			$out->addWikiMsg( 'linkstoimage-more',
870
				$this->getContext()->getLanguage()->formatNum( $limit ),
871
				$this->getTitle()->getPrefixedDBkey()
872
			);
873
		}
874
875
		$out->addHTML(
876
			Html::openElement( 'ul',
877
				[ 'class' => 'mw-imagepage-linkstoimage' ] ) . "\n"
878
		);
879
		$count = 0;
880
881
		// Sort the list by namespace:title
882
		usort( $rows, [ $this, 'compare' ] );
883
884
		// Create links for every element
885
		$currentCount = 0;
886
		foreach ( $rows as $element ) {
887
			$currentCount++;
888
			if ( $currentCount > $limit ) {
889
				break;
890
			}
891
892
			$query = [];
893
			# Add a redirect=no to make redirect pages reachable
894
			if ( isset( $redirects[$element->page_title] ) ) {
895
				$query['redirect'] = 'no';
896
			}
897
			$link = Linker::linkKnown(
898
				Title::makeTitle( $element->page_namespace, $element->page_title ),
899
				null, [], $query
900
			);
901
			if ( !isset( $redirects[$element->page_title] ) ) {
902
				# No redirects
903
				$liContents = $link;
904
			} elseif ( count( $redirects[$element->page_title] ) === 0 ) {
905
				# Redirect without usages
906
				$liContents = $this->getContext()->msg( 'linkstoimage-redirect' )
907
					->rawParams( $link, '' )
908
					->parse();
909
			} else {
910
				# Redirect with usages
911
				$li = '';
912
				foreach ( $redirects[$element->page_title] as $row ) {
913
					$currentCount++;
914
					if ( $currentCount > $limit ) {
915
						break;
916
					}
917
918
					$link2 = Linker::linkKnown( Title::makeTitle( $row->page_namespace, $row->page_title ) );
919
					$li .= Html::rawElement(
920
						'li',
921
						[ 'class' => 'mw-imagepage-linkstoimage-ns' . $element->page_namespace ],
922
						$link2
923
						) . "\n";
924
				}
925
926
				$ul = Html::rawElement(
927
					'ul',
928
					[ 'class' => 'mw-imagepage-redirectstofile' ],
929
					$li
930
					) . "\n";
931
				$liContents = $this->getContext()->msg( 'linkstoimage-redirect' )->rawParams(
932
					$link, $ul )->parse();
933
			}
934
			$out->addHTML( Html::rawElement(
935
					'li',
936
					[ 'class' => 'mw-imagepage-linkstoimage-ns' . $element->page_namespace ],
937
					$liContents
938
				) . "\n"
939
			);
940
941
		};
942
		$out->addHTML( Html::closeElement( 'ul' ) . "\n" );
943
		$res->free();
944
945
		// Add a links to [[Special:Whatlinkshere]]
946
		if ( $count > $limit ) {
947
			$out->addWikiMsg( 'morelinkstoimage', $this->getTitle()->getPrefixedDBkey() );
948
		}
949
		$out->addHTML( Html::closeElement( 'div' ) . "\n" );
950
	}
951
952
	protected function imageDupes() {
953
		$this->loadFile();
954
		$out = $this->getContext()->getOutput();
955
956
		$dupes = $this->mPage->getDuplicates();
957
		if ( count( $dupes ) == 0 ) {
958
			return;
959
		}
960
961
		$out->addHTML( "<div id='mw-imagepage-section-duplicates'>\n" );
962
		$out->addWikiMsg( 'duplicatesoffile',
963
			$this->getContext()->getLanguage()->formatNum( count( $dupes ) ), $this->getTitle()->getDBkey()
964
		);
965
		$out->addHTML( "<ul class='mw-imagepage-duplicates'>\n" );
966
967
		/**
968
		 * @var $file File
969
		 */
970
		foreach ( $dupes as $file ) {
971
			$fromSrc = '';
972
			if ( $file->isLocal() ) {
973
				$link = Linker::linkKnown( $file->getTitle() );
974
			} else {
975
				$link = Linker::makeExternalLink( $file->getDescriptionUrl(),
976
					$file->getTitle()->getPrefixedText() );
977
				$fromSrc = $this->getContext()->msg(
978
					'shared-repo-from',
979
					$file->getRepo()->getDisplayName()
980
				)->text();
981
			}
982
			$out->addHTML( "<li>{$link} {$fromSrc}</li>\n" );
983
		}
984
		$out->addHTML( "</ul></div>\n" );
985
	}
986
987
	/**
988
	 * Delete the file, or an earlier version of it
989
	 */
990
	public function delete() {
991
		$file = $this->mPage->getFile();
992
		if ( !$file->exists() || !$file->isLocal() || $file->getRedirected() ) {
993
			// Standard article deletion
994
			parent::delete();
995
			return;
996
		}
997
998
		$deleter = new FileDeleteForm( $file );
999
		$deleter->execute();
1000
	}
1001
1002
	/**
1003
	 * Display an error with a wikitext description
1004
	 *
1005
	 * @param string $description
1006
	 */
1007
	function showError( $description ) {
1008
		$out = $this->getContext()->getOutput();
1009
		$out->setPageTitle( $this->getContext()->msg( 'internalerror' ) );
1010
		$out->setRobotPolicy( 'noindex,nofollow' );
1011
		$out->setArticleRelated( false );
1012
		$out->enableClientCache( false );
1013
		$out->addWikiText( $description );
1014
	}
1015
1016
	/**
1017
	 * Callback for usort() to do link sorts by (namespace, title)
1018
	 * Function copied from Title::compare()
1019
	 *
1020
	 * @param object $a Object page to compare with
1021
	 * @param object $b Object page to compare with
1022
	 * @return int Result of string comparison, or namespace comparison
1023
	 */
1024
	protected function compare( $a, $b ) {
1025
		if ( $a->page_namespace == $b->page_namespace ) {
1026
			return strcmp( $a->page_title, $b->page_title );
1027
		} else {
1028
			return $a->page_namespace - $b->page_namespace;
1029
		}
1030
	}
1031
1032
	/**
1033
	 * Returns the corresponding $wgImageLimits entry for the selected user option
1034
	 *
1035
	 * @param User $user
1036
	 * @param string $optionName Name of a option to check, typically imagesize or thumbsize
1037
	 * @return array
1038
	 * @since 1.21
1039
	 */
1040
	public function getImageLimitsFromOption( $user, $optionName ) {
1041
		global $wgImageLimits;
1042
1043
		$option = $user->getIntOption( $optionName );
1044
		if ( !isset( $wgImageLimits[$option] ) ) {
1045
			$option = User::getDefaultOption( $optionName );
1046
		}
1047
1048
		// The user offset might still be incorrect, specially if
1049
		// $wgImageLimits got changed (see bug #8858).
1050
		if ( !isset( $wgImageLimits[$option] ) ) {
1051
			// Default to the first offset in $wgImageLimits
1052
			$option = 0;
1053
		}
1054
1055
		return isset( $wgImageLimits[$option] )
1056
			? $wgImageLimits[$option]
1057
			: [ 800, 600 ]; // if nothing is set, fallback to a hardcoded default
1058
	}
1059
1060
	/**
1061
	 * Output a drop-down box for language options for the file
1062
	 *
1063
	 * @param array $langChoices Array of string language codes
1064
	 * @param string $curLang Language code file is being viewed in.
1065
	 * @param string $defaultLang Language code that image is rendered in by default
1066
	 * @return string HTML to insert underneath image.
1067
	 */
1068
	protected function doRenderLangOpt( array $langChoices, $curLang, $defaultLang ) {
1069
		global $wgScript;
1070
		sort( $langChoices );
1071
		$curLang = wfBCP47( $curLang );
1072
		$defaultLang = wfBCP47( $defaultLang );
1073
		$opts = '';
1074
		$haveCurrentLang = false;
1075
		$haveDefaultLang = false;
1076
1077
		// We make a list of all the language choices in the file.
1078
		// Additionally if the default language to render this file
1079
		// is not included as being in this file (for example, in svgs
1080
		// usually the fallback content is the english content) also
1081
		// include a choice for that. Last of all, if we're viewing
1082
		// the file in a language not on the list, add it as a choice.
1083
		foreach ( $langChoices as $lang ) {
1084
			$code = wfBCP47( $lang );
1085
			$name = Language::fetchLanguageName( $code, $this->getContext()->getLanguage()->getCode() );
1086 View Code Duplication
			if ( $name !== '' ) {
1087
				$display = $this->getContext()->msg( 'img-lang-opt', $code, $name )->text();
1088
			} else {
1089
				$display = $code;
1090
			}
1091
			$opts .= "\n" . Xml::option( $display, $code, $curLang === $code );
1092
			if ( $curLang === $code ) {
1093
				$haveCurrentLang = true;
1094
			}
1095
			if ( $defaultLang === $code ) {
1096
				$haveDefaultLang = true;
1097
			}
1098
		}
1099
		if ( !$haveDefaultLang ) {
1100
			// Its hard to know if the content is really in the default language, or
1101
			// if its just unmarked content that could be in any language.
1102
			$opts = Xml::option(
1103
					$this->getContext()->msg( 'img-lang-default' )->text(),
1104
				$defaultLang,
1105
				$defaultLang === $curLang
1106
			) . $opts;
1107
		}
1108
		if ( !$haveCurrentLang && $defaultLang !== $curLang ) {
1109
			$name = Language::fetchLanguageName( $curLang, $this->getContext()->getLanguage()->getCode() );
1110 View Code Duplication
			if ( $name !== '' ) {
1111
				$display = $this->getContext()->msg( 'img-lang-opt', $curLang, $name )->text();
1112
			} else {
1113
				$display = $curLang;
1114
			}
1115
			$opts = Xml::option( $display, $curLang, true ) . $opts;
1116
		}
1117
1118
		$select = Html::rawElement(
1119
			'select',
1120
			[ 'id' => 'mw-imglangselector', 'name' => 'lang' ],
1121
			$opts
1122
		);
1123
		$submit = Xml::submitButton( $this->getContext()->msg( 'img-lang-go' )->text() );
1124
1125
		$formContents = $this->getContext()->msg( 'img-lang-info' )
1126
			->rawParams( $select, $submit )
1127
			->parse();
1128
		$formContents .= Html::hidden( 'title', $this->getTitle()->getPrefixedDBkey() );
1129
1130
		$langSelectLine = Html::rawElement( 'div', [ 'id' => 'mw-imglangselector-line' ],
1131
			Html::rawElement( 'form', [ 'action' => $wgScript ], $formContents )
1132
		);
1133
		return $langSelectLine;
1134
	}
1135
1136
	/**
1137
	 * Get the width and height to display image at.
1138
	 *
1139
	 * @note This method assumes that it is only called if one
1140
	 *  of the dimensions are bigger than the max, or if the
1141
	 *  image is vectorized.
1142
	 *
1143
	 * @param int $maxWidth Max width to display at
1144
	 * @param int $maxHeight Max height to display at
1145
	 * @param int $width Actual width of the image
1146
	 * @param int $height Actual height of the image
1147
	 * @throws MWException
1148
	 * @return array Array (width, height)
1149
	 */
1150
	protected function getDisplayWidthHeight( $maxWidth, $maxHeight, $width, $height ) {
1151
		if ( !$maxWidth || !$maxHeight ) {
1152
			// should never happen
1153
			throw new MWException( 'Using a choice from $wgImageLimits that is 0x0' );
1154
		}
1155
1156
		if ( !$width || !$height ) {
1157
			return [ 0, 0 ];
1158
		}
1159
1160
		# Calculate the thumbnail size.
1161
		if ( $width <= $maxWidth && $height <= $maxHeight ) {
0 ignored issues
show
Unused Code introduced by
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...
1162
			// Vectorized image, do nothing.
1163
		} elseif ( $width / $height >= $maxWidth / $maxHeight ) {
1164
			# The limiting factor is the width, not the height.
1165
			$height = round( $height * $maxWidth / $width );
1166
			$width = $maxWidth;
1167
			# Note that $height <= $maxHeight now.
1168
		} else {
1169
			$newwidth = floor( $width * $maxHeight / $height );
1170
			$height = round( $height * $newwidth / $width );
1171
			$width = $newwidth;
1172
			# Note that $height <= $maxHeight now, but might not be identical
1173
			# because of rounding.
1174
		}
1175
		return [ $width, $height ];
1176
	}
1177
1178
	/**
1179
	 * Get alternative thumbnail sizes.
1180
	 *
1181
	 * @note This will only list several alternatives if thumbnails are rendered on 404
1182
	 * @param int $origWidth Actual width of image
1183
	 * @param int $origHeight Actual height of image
1184
	 * @return array An array of [width, height] pairs.
1185
	 */
1186
	protected function getThumbSizes( $origWidth, $origHeight ) {
1187
		global $wgImageLimits;
1188
		if ( $this->displayImg->getRepo()->canTransformVia404() ) {
1189
			$thumbSizes = $wgImageLimits;
1190
			// Also include the full sized resolution in the list, so
1191
			// that users know they can get it. This will link to the
1192
			// original file asset if mustRender() === false. In the case
1193
			// that we mustRender, some users have indicated that they would
1194
			// find it useful to have the full size image in the rendered
1195
			// image format.
1196
			$thumbSizes[] = [ $origWidth, $origHeight ];
1197
		} else {
1198
			# Creating thumb links triggers thumbnail generation.
1199
			# Just generate the thumb for the current users prefs.
1200
			$thumbSizes = [
1201
				$this->getImageLimitsFromOption( $this->getContext()->getUser(), 'thumbsize' )
1202
			];
1203
			if ( !$this->displayImg->mustRender() ) {
1204
				// We can safely include a link to the "full-size" preview,
1205
				// without actually rendering.
1206
				$thumbSizes[] = [ $origWidth, $origHeight ];
1207
			}
1208
		}
1209
		return $thumbSizes;
1210
	}
1211
1212
	/**
1213
	 * @see WikiFilePage::getFile
1214
	 * @return bool|File
1215
	 */
1216
	public function getFile() {
1217
		return $this->mPage->getFile();
1218
	}
1219
1220
	/**
1221
	 * @see WikiFilePage::isLocal
1222
	 * @return bool
1223
	 */
1224
	public function isLocal() {
1225
		return $this->mPage->isLocal();
1226
	}
1227
1228
	/**
1229
	 * @see WikiFilePage::getDuplicates
1230
	 * @return array|null
1231
	 */
1232
	public function getDuplicates() {
1233
		return $this->mPage->getDuplicates();
1234
	}
1235
1236
	/**
1237
	 * @see WikiFilePage::getForeignCategories
1238
	 * @return TitleArray|Title[]
1239
	 */
1240
	public function getForeignCategories() {
1241
		$this->mPage->getForeignCategories();
1242
	}
1243
1244
}
1245