* Parser now only adds the redirect source to imagelinks, like it does in pagelinks * ImagePage now shows the file redirects in-line with the normal "The following pages link to this file:" ** Added message linkstoimage-redirect ** Removed the separate file redirects section and removed associated message redirectstofile ** ImagePage::imageLinks will first fetch image links to the file, determine which are redirects, and if there are fewer links than the limit, fetch the redirect target links.
1320 lines
37 KiB
PHP
1320 lines
37 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Special handling for image description pages
|
|
*
|
|
* @ingroup Media
|
|
*/
|
|
class ImagePage extends Article {
|
|
|
|
/**
|
|
* @var File
|
|
*/
|
|
private $img;
|
|
private $displayImg;
|
|
private $repo;
|
|
private $fileLoaded;
|
|
|
|
var $mExtraDescription = false;
|
|
var $dupes;
|
|
|
|
function __construct( $title ) {
|
|
parent::__construct( $title );
|
|
$this->dupes = null;
|
|
$this->repo = null;
|
|
}
|
|
|
|
/**
|
|
* @param $file File:
|
|
* @return void
|
|
*/
|
|
public function setFile( $file ) {
|
|
$this->displayImg = $file;
|
|
$this->img = $file;
|
|
$this->fileLoaded = true;
|
|
}
|
|
|
|
protected function loadFile() {
|
|
if ( $this->fileLoaded ) {
|
|
return true;
|
|
}
|
|
$this->fileLoaded = true;
|
|
|
|
$this->displayImg = $this->img = false;
|
|
wfRunHooks( 'ImagePageFindFile', array( $this, &$this->img, &$this->displayImg ) );
|
|
if ( !$this->img ) {
|
|
$this->img = wfFindFile( $this->mTitle );
|
|
if ( !$this->img ) {
|
|
$this->img = wfLocalFile( $this->mTitle );
|
|
}
|
|
}
|
|
if ( !$this->displayImg ) {
|
|
$this->displayImg = $this->img;
|
|
}
|
|
$this->repo = $this->img->getRepo();
|
|
}
|
|
|
|
/**
|
|
* Handler for action=render
|
|
* Include body text only; none of the image extras
|
|
*/
|
|
public function render() {
|
|
global $wgOut;
|
|
$wgOut->setArticleBodyOnly( true );
|
|
parent::view();
|
|
}
|
|
|
|
public function view() {
|
|
global $wgOut, $wgShowEXIF, $wgRequest, $wgUser;
|
|
|
|
$diff = $wgRequest->getVal( 'diff' );
|
|
$diffOnly = $wgRequest->getBool( 'diffonly', $wgUser->getOption( 'diffonly' ) );
|
|
|
|
if ( $this->mTitle->getNamespace() != NS_FILE || ( isset( $diff ) && $diffOnly ) ) {
|
|
return parent::view();
|
|
}
|
|
|
|
$this->loadFile();
|
|
|
|
if ( $this->mTitle->getNamespace() == NS_FILE && $this->img->getRedirected() ) {
|
|
if ( $this->mTitle->getDBkey() == $this->img->getName() || isset( $diff ) ) {
|
|
// mTitle is the same as the redirect target so ask Article
|
|
// to perform the redirect for us.
|
|
$wgRequest->setVal( 'diffonly', 'true' );
|
|
return parent::view();
|
|
} else {
|
|
// mTitle is not the same as the redirect target so it is
|
|
// probably the redirect page itself. Fake the redirect symbol
|
|
$wgOut->setPageTitle( $this->mTitle->getPrefixedText() );
|
|
$wgOut->addHTML( $this->viewRedirect( Title::makeTitle( NS_FILE, $this->img->getName() ),
|
|
/* $appendSubtitle */ true, /* $forceKnown */ true ) );
|
|
$this->viewUpdates();
|
|
return;
|
|
}
|
|
}
|
|
|
|
$this->showRedirectedFromHeader();
|
|
|
|
if ( $wgShowEXIF && $this->displayImg->exists() ) {
|
|
// FIXME: bad interface, see note on MediaHandler::formatMetadata().
|
|
$formattedMetadata = $this->displayImg->formatMetadata();
|
|
$showmeta = $formattedMetadata !== false;
|
|
} else {
|
|
$showmeta = false;
|
|
}
|
|
|
|
if ( !$diff && $this->displayImg->exists() ) {
|
|
$wgOut->addHTML( $this->showTOC( $showmeta ) );
|
|
}
|
|
|
|
if ( !$diff ) {
|
|
$this->openShowImage();
|
|
}
|
|
|
|
# No need to display noarticletext, we use our own message, output in openShowImage()
|
|
if ( $this->getID() ) {
|
|
parent::view();
|
|
} else {
|
|
# Just need to set the right headers
|
|
$wgOut->setArticleFlag( true );
|
|
$wgOut->setPageTitle( $this->mTitle->getPrefixedText() );
|
|
$this->viewUpdates();
|
|
}
|
|
|
|
# Show shared description, if needed
|
|
if ( $this->mExtraDescription ) {
|
|
$fol = wfMessage( 'shareddescriptionfollows' );
|
|
if ( !$fol->isDisabled() ) {
|
|
$wgOut->addWikiText( $fol->plain() );
|
|
}
|
|
$wgOut->addHTML( '<div id="shared-image-desc">' . $this->mExtraDescription . "</div>\n" );
|
|
}
|
|
|
|
$this->closeShowImage();
|
|
$this->imageHistory();
|
|
// TODO: Cleanup the following
|
|
|
|
$wgOut->addHTML( Xml::element( 'h2',
|
|
array( 'id' => 'filelinks' ),
|
|
wfMsg( 'imagelinks' ) ) . "\n" );
|
|
$this->imageDupes();
|
|
# TODO! FIXME! For some freaky reason, we can't redirect to foreign images.
|
|
# Yet we return metadata about the target. Definitely an issue in the FileRepo
|
|
$this->imageLinks();
|
|
|
|
# Allow extensions to add something after the image links
|
|
$html = '';
|
|
wfRunHooks( 'ImagePageAfterImageLinks', array( $this, &$html ) );
|
|
if ( $html ) {
|
|
$wgOut->addHTML( $html );
|
|
}
|
|
|
|
if ( $showmeta ) {
|
|
$wgOut->addHTML( Xml::element( 'h2', array( 'id' => 'metadata' ), wfMsg( 'metadata' ) ) . "\n" );
|
|
$wgOut->addWikiText( $this->makeMetadataTable( $formattedMetadata ) );
|
|
$wgOut->addModules( array( 'mediawiki.action.view.metadata' ) );
|
|
}
|
|
|
|
$css = $this->repo->getDescriptionStylesheetUrl();
|
|
if ( $css ) {
|
|
$wgOut->addStyle( $css );
|
|
}
|
|
}
|
|
|
|
public function getRedirectTarget() {
|
|
$this->loadFile();
|
|
if ( $this->img->isLocal() ) {
|
|
return parent::getRedirectTarget();
|
|
}
|
|
// Foreign image page
|
|
$from = $this->img->getRedirected();
|
|
$to = $this->img->getName();
|
|
if ( $from == $to ) {
|
|
return null;
|
|
}
|
|
return $this->mRedirectTarget = Title::makeTitle( NS_FILE, $to );
|
|
}
|
|
|
|
public function followRedirect() {
|
|
$this->loadFile();
|
|
if ( $this->img->isLocal() ) {
|
|
return parent::followRedirect();
|
|
}
|
|
$from = $this->img->getRedirected();
|
|
$to = $this->img->getName();
|
|
if ( $from == $to ) {
|
|
return false;
|
|
}
|
|
return Title::makeTitle( NS_FILE, $to );
|
|
}
|
|
|
|
public function isRedirect( $text = false ) {
|
|
$this->loadFile();
|
|
if ( $this->img->isLocal() ) {
|
|
return parent::isRedirect( $text );
|
|
}
|
|
|
|
return (bool)$this->img->getRedirected();
|
|
}
|
|
|
|
public function isLocal() {
|
|
$this->loadFile();
|
|
return $this->img->isLocal();
|
|
}
|
|
|
|
public function getFile() {
|
|
$this->loadFile();
|
|
return $this->img;
|
|
}
|
|
|
|
public function getDisplayedFile() {
|
|
$this->loadFile();
|
|
return $this->displayImg;
|
|
}
|
|
|
|
public function getDuplicates() {
|
|
$this->loadFile();
|
|
if ( !is_null( $this->dupes ) ) {
|
|
return $this->dupes;
|
|
}
|
|
$hash = $this->img->getSha1();
|
|
if ( !( $hash ) ) {
|
|
return $this->dupes = array();
|
|
}
|
|
$dupes = RepoGroup::singleton()->findBySha1( $hash );
|
|
// Remove duplicates with self and non matching file sizes
|
|
$self = $this->img->getRepoName() . ':' . $this->img->getName();
|
|
$size = $this->img->getSize();
|
|
foreach ( $dupes as $index => $file ) {
|
|
$key = $file->getRepoName() . ':' . $file->getName();
|
|
if ( $key == $self ) {
|
|
unset( $dupes[$index] );
|
|
}
|
|
if ( $file->getSize() != $size ) {
|
|
unset( $dupes[$index] );
|
|
}
|
|
}
|
|
return $this->dupes = $dupes;
|
|
}
|
|
|
|
/**
|
|
* Create the TOC
|
|
*
|
|
* @param $metadata Boolean: whether or not to show the metadata link
|
|
* @return String
|
|
*/
|
|
protected function showTOC( $metadata ) {
|
|
$r = array(
|
|
'<li><a href="#file">' . wfMsgHtml( 'file-anchor-link' ) . '</a></li>',
|
|
'<li><a href="#filehistory">' . wfMsgHtml( 'filehist' ) . '</a></li>',
|
|
'<li><a href="#filelinks">' . wfMsgHtml( 'imagelinks' ) . '</a></li>',
|
|
);
|
|
if ( $metadata ) {
|
|
$r[] = '<li><a href="#metadata">' . wfMsgHtml( 'metadata' ) . '</a></li>';
|
|
}
|
|
|
|
wfRunHooks( 'ImagePageShowTOC', array( $this, &$r ) );
|
|
|
|
return '<ul id="filetoc">' . implode( "\n", $r ) . '</ul>';
|
|
}
|
|
|
|
/**
|
|
* Make a table with metadata to be shown in the output page.
|
|
*
|
|
* FIXME: bad interface, see note on MediaHandler::formatMetadata().
|
|
*
|
|
* @param $metadata Array: the array containing the EXIF data
|
|
* @return String The metadata table. This is treated as Wikitext (!)
|
|
*/
|
|
protected function makeMetadataTable( $metadata ) {
|
|
$r = "<div class=\"mw-imagepage-section-metadata\">";
|
|
$r .= wfMsgNoTrans( 'metadata-help' );
|
|
$r .= "<table id=\"mw_metadata\" class=\"mw_metadata\">\n";
|
|
foreach ( $metadata as $type => $stuff ) {
|
|
foreach ( $stuff as $v ) {
|
|
# FIXME, why is this using escapeId for a class?!
|
|
$class = Sanitizer::escapeId( $v['id'] );
|
|
if ( $type == 'collapsed' ) {
|
|
$class .= ' collapsable';
|
|
}
|
|
$r .= "<tr class=\"$class\">\n";
|
|
$r .= "<th>{$v['name']}</th>\n";
|
|
$r .= "<td>{$v['value']}</td>\n</tr>";
|
|
}
|
|
}
|
|
$r .= "</table>\n</div>\n";
|
|
return $r;
|
|
}
|
|
|
|
/**
|
|
* Overloading Article's getContent method.
|
|
*
|
|
* Omit noarticletext if sharedupload; text will be fetched from the
|
|
* shared upload server if possible.
|
|
*/
|
|
public function getContent() {
|
|
$this->loadFile();
|
|
if ( $this->img && !$this->img->isLocal() && 0 == $this->getID() ) {
|
|
return '';
|
|
}
|
|
return parent::getContent();
|
|
}
|
|
|
|
protected function openShowImage() {
|
|
global $wgOut, $wgUser, $wgImageLimits, $wgRequest,
|
|
$wgLang, $wgContLang, $wgEnableUploads;
|
|
|
|
$this->loadFile();
|
|
|
|
$sizeSel = intval( $wgUser->getOption( 'imagesize' ) );
|
|
if ( !isset( $wgImageLimits[$sizeSel] ) ) {
|
|
$sizeSel = User::getDefaultOption( 'imagesize' );
|
|
|
|
// The user offset might still be incorrect, specially if
|
|
// $wgImageLimits got changed (see bug #8858).
|
|
if ( !isset( $wgImageLimits[$sizeSel] ) ) {
|
|
// Default to the first offset in $wgImageLimits
|
|
$sizeSel = 0;
|
|
}
|
|
}
|
|
$max = $wgImageLimits[$sizeSel];
|
|
$maxWidth = $max[0];
|
|
$maxHeight = $max[1];
|
|
$sk = $wgUser->getSkin();
|
|
$dirmark = $wgContLang->getDirMark();
|
|
|
|
if ( $this->displayImg->exists() ) {
|
|
# image
|
|
$page = $wgRequest->getIntOrNull( 'page' );
|
|
if ( is_null( $page ) ) {
|
|
$params = array();
|
|
$page = 1;
|
|
} else {
|
|
$params = array( 'page' => $page );
|
|
}
|
|
$width_orig = $this->displayImg->getWidth( $page );
|
|
$width = $width_orig;
|
|
$height_orig = $this->displayImg->getHeight( $page );
|
|
$height = $height_orig;
|
|
|
|
$longDesc = wfMsg( 'parentheses', $this->displayImg->getLongDesc() );
|
|
|
|
wfRunHooks( 'ImageOpenShowImageInlineBefore', array( &$this, &$wgOut ) );
|
|
|
|
if ( $this->displayImg->allowInlineDisplay() ) {
|
|
# image
|
|
|
|
# "Download high res version" link below the image
|
|
# $msgsize = wfMsgHtml( 'file-info-size', $width_orig, $height_orig, $sk->formatSize( $this->displayImg->getSize() ), $mime );
|
|
# We'll show a thumbnail of this image
|
|
if ( $width > $maxWidth || $height > $maxHeight ) {
|
|
# Calculate the thumbnail size.
|
|
# First case, the limiting factor is the width, not the height.
|
|
if ( $width / $height >= $maxWidth / $maxHeight ) {
|
|
$height = round( $height * $maxWidth / $width );
|
|
$width = $maxWidth;
|
|
# Note that $height <= $maxHeight now.
|
|
} else {
|
|
$newwidth = floor( $width * $maxHeight / $height );
|
|
$height = round( $height * $newwidth / $width );
|
|
$width = $newwidth;
|
|
# Note that $height <= $maxHeight now, but might not be identical
|
|
# because of rounding.
|
|
}
|
|
$msgbig = wfMsgHtml( 'show-big-image' );
|
|
$otherSizes = array();
|
|
foreach ( $wgImageLimits as $size ) {
|
|
if ( $size[0] < $width_orig && $size[1] < $height_orig &&
|
|
$size[0] != $width && $size[1] != $height ) {
|
|
$otherSizes[] = $this->makeSizeLink( $params, $size[0], $size[1] );
|
|
}
|
|
}
|
|
$msgsmall = wfMessage( 'show-big-image-preview' )->
|
|
rawParams( $this->makeSizeLink( $params, $width, $height ) )->
|
|
parse() . ' ' .
|
|
wfMessage( 'show-big-image-other' )->
|
|
rawParams( $wgLang->pipeList( $otherSizes ) )->parse();
|
|
} else {
|
|
# Image is small enough to show full size on image page
|
|
$msgsmall = wfMsgExt( 'file-nohires', array( 'parseinline' ) );
|
|
}
|
|
|
|
$params['width'] = $width;
|
|
$params['height'] = $height;
|
|
$thumbnail = $this->displayImg->transform( $params );
|
|
|
|
$showLink = true;
|
|
$anchorclose = '<br />' . $msgsmall;
|
|
|
|
$isMulti = $this->displayImg->isMultipage() && $this->displayImg->pageCount() > 1;
|
|
if ( $isMulti ) {
|
|
$wgOut->addHTML( '<table class="multipageimage"><tr><td>' );
|
|
}
|
|
|
|
if ( $thumbnail ) {
|
|
$options = array(
|
|
'alt' => $this->displayImg->getTitle()->getPrefixedText(),
|
|
'file-link' => true,
|
|
);
|
|
$wgOut->addHTML( '<div class="fullImageLink" id="file">' .
|
|
$thumbnail->toHtml( $options ) .
|
|
$anchorclose . "</div>\n" );
|
|
}
|
|
|
|
if ( $isMulti ) {
|
|
$count = $this->displayImg->pageCount();
|
|
|
|
if ( $page > 1 ) {
|
|
$label = $wgOut->parse( wfMsg( 'imgmultipageprev' ), false );
|
|
$link = $sk->link(
|
|
$this->mTitle,
|
|
$label,
|
|
array(),
|
|
array( 'page' => $page - 1 ),
|
|
array( 'known', 'noclasses' )
|
|
);
|
|
$thumb1 = $sk->makeThumbLinkObj( $this->mTitle, $this->displayImg, $link, $label, 'none',
|
|
array( 'page' => $page - 1 ) );
|
|
} else {
|
|
$thumb1 = '';
|
|
}
|
|
|
|
if ( $page < $count ) {
|
|
$label = wfMsg( 'imgmultipagenext' );
|
|
$link = $sk->link(
|
|
$this->mTitle,
|
|
$label,
|
|
array(),
|
|
array( 'page' => $page + 1 ),
|
|
array( 'known', 'noclasses' )
|
|
);
|
|
$thumb2 = $sk->makeThumbLinkObj( $this->mTitle, $this->displayImg, $link, $label, 'none',
|
|
array( 'page' => $page + 1 ) );
|
|
} else {
|
|
$thumb2 = '';
|
|
}
|
|
|
|
global $wgScript;
|
|
|
|
$formParams = array(
|
|
'name' => 'pageselector',
|
|
'action' => $wgScript,
|
|
'onchange' => 'document.pageselector.submit();',
|
|
);
|
|
|
|
for ( $i = 1; $i <= $count; $i++ ) {
|
|
$options[] = Xml::option( $wgLang->formatNum( $i ), $i, $i == $page );
|
|
}
|
|
$select = Xml::tags( 'select',
|
|
array( 'id' => 'pageselector', 'name' => 'page' ),
|
|
implode( "\n", $options ) );
|
|
|
|
$wgOut->addHTML(
|
|
'</td><td><div class="multipageimagenavbox">' .
|
|
Xml::openElement( 'form', $formParams ) .
|
|
Html::hidden( 'title', $this->getTitle()->getPrefixedDBkey() ) .
|
|
wfMsgExt( 'imgmultigoto', array( 'parseinline', 'replaceafter' ), $select ) .
|
|
Xml::submitButton( wfMsg( 'imgmultigo' ) ) .
|
|
Xml::closeElement( 'form' ) .
|
|
"<hr />$thumb1\n$thumb2<br clear=\"all\" /></div></td></tr></table>"
|
|
);
|
|
}
|
|
} else {
|
|
# if direct link is allowed but it's not a renderable image, show an icon.
|
|
if ( $this->displayImg->isSafeFile() ) {
|
|
$icon = $this->displayImg->iconThumb();
|
|
|
|
$wgOut->addHTML( '<div class="fullImageLink" id="file">' .
|
|
$icon->toHtml( array( 'file-link' => true ) ) .
|
|
"</div>\n" );
|
|
}
|
|
|
|
$showLink = true;
|
|
}
|
|
|
|
if ( $showLink ) {
|
|
$filename = wfEscapeWikiText( $this->displayImg->getName() );
|
|
$linktext = $filename;
|
|
if ( isset( $msgbig ) ) {
|
|
$linktext = wfEscapeWikiText( $msgbig );
|
|
}
|
|
$medialink = "[[Media:$filename|$linktext]]";
|
|
|
|
if ( !$this->displayImg->isSafeFile() ) {
|
|
$warning = wfMsgNoTrans( 'mediawarning' );
|
|
$wgOut->addWikiText( <<<EOT
|
|
<div class="fullMedia"><span class="dangerousLink">{$medialink}</span>$dirmark <span class="fileInfo">$longDesc</span></div>
|
|
<div class="mediaWarning">$warning</div>
|
|
EOT
|
|
);
|
|
} else {
|
|
$wgOut->addWikiText( <<<EOT
|
|
<div class="fullMedia">{$medialink}{$dirmark} <span class="fileInfo">$longDesc</span>
|
|
</div>
|
|
EOT
|
|
);
|
|
}
|
|
}
|
|
|
|
if ( !$this->displayImg->isLocal() ) {
|
|
$this->printSharedImageText();
|
|
}
|
|
} else {
|
|
# Image does not exist
|
|
if ( $wgEnableUploads && $wgUser->isAllowed( 'upload' ) ) {
|
|
// Only show an upload link if the user can upload
|
|
$uploadTitle = SpecialPage::getTitleFor( 'Upload' );
|
|
$nofile = array(
|
|
'filepage-nofile-link',
|
|
$uploadTitle->getFullURL( array( 'wpDestFile' => $this->img->getName() ) )
|
|
);
|
|
} else {
|
|
$nofile = 'filepage-nofile';
|
|
}
|
|
// Note, if there is an image description page, but
|
|
// no image, then this setRobotPolicy is overriden
|
|
// by Article::View().
|
|
$wgOut->setRobotPolicy( 'noindex,nofollow' );
|
|
$wgOut->wrapWikiMsg( "<div id='mw-imagepage-nofile' class='plainlinks'>\n$1\n</div>", $nofile );
|
|
if ( !$this->getID() ) {
|
|
// If there is no image, no shared image, and no description page,
|
|
// output a 404, to be consistent with articles.
|
|
$wgRequest->response()->header( 'HTTP/1.1 404 Not Found' );
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates an thumbnail of specified size and returns an HTML link to it
|
|
* @param array $params Scaler parameters
|
|
* @param int $width
|
|
* @param int $height
|
|
*/
|
|
private function makeSizeLink( $params, $width, $height ) {
|
|
$params['width'] = $width;
|
|
$params['height'] = $height;
|
|
$thumbnail = $this->displayImg->transform( $params );
|
|
if ( $thumbnail && !$thumbnail->isError() ) {
|
|
return Html::rawElement( 'a', array(
|
|
'href' => $thumbnail->getUrl(),
|
|
'class' => 'mw-thumbnail-link'
|
|
), wfMessage( 'show-big-image-size' )->numParams(
|
|
$thumbnail->getWidth(), $thumbnail->getHeight()
|
|
)->parse() );
|
|
} else {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Show a notice that the file is from a shared repository
|
|
*/
|
|
protected function printSharedImageText() {
|
|
global $wgOut;
|
|
|
|
$this->loadFile();
|
|
|
|
$descUrl = $this->img->getDescriptionUrl();
|
|
$descText = $this->img->getDescriptionText();
|
|
|
|
/* Add canonical to head if there is no local page for this shared file */
|
|
if( $descUrl && $this->getID() == 0 ) {
|
|
$wgOut->addLink( array( 'rel' => 'canonical', 'href' => $descUrl ) );
|
|
}
|
|
|
|
$wrap = "<div class=\"sharedUploadNotice\">\n$1\n</div>\n";
|
|
$repo = $this->img->getRepo()->getDisplayName();
|
|
|
|
if ( $descUrl && $descText && wfMsgNoTrans( 'sharedupload-desc-here' ) !== '-' ) {
|
|
$wgOut->wrapWikiMsg( $wrap, array( 'sharedupload-desc-here', $repo, $descUrl ) );
|
|
} elseif ( $descUrl && wfMsgNoTrans( 'sharedupload-desc-there' ) !== '-' ) {
|
|
$wgOut->wrapWikiMsg( $wrap, array( 'sharedupload-desc-there', $repo, $descUrl ) );
|
|
} else {
|
|
$wgOut->wrapWikiMsg( $wrap, array( 'sharedupload', $repo ), ''/*BACKCOMPAT*/ );
|
|
}
|
|
|
|
if ( $descText ) {
|
|
$this->mExtraDescription = $descText;
|
|
}
|
|
}
|
|
|
|
public function getUploadUrl() {
|
|
$this->loadFile();
|
|
$uploadTitle = SpecialPage::getTitleFor( 'Upload' );
|
|
return $uploadTitle->getFullURL( array(
|
|
'wpDestFile' => $this->img->getName(),
|
|
'wpForReUpload' => 1
|
|
) );
|
|
}
|
|
|
|
/**
|
|
* Print out the various links at the bottom of the image page, e.g. reupload,
|
|
* external editing (and instructions link) etc.
|
|
*/
|
|
protected function uploadLinksBox() {
|
|
global $wgUser, $wgOut, $wgEnableUploads, $wgUseExternalEditor;
|
|
|
|
if ( !$wgEnableUploads ) {
|
|
return;
|
|
}
|
|
|
|
$this->loadFile();
|
|
if ( !$this->img->isLocal() ) {
|
|
return;
|
|
}
|
|
|
|
$sk = $wgUser->getSkin();
|
|
|
|
$wgOut->addHTML( "<br /><ul>\n" );
|
|
|
|
# "Upload a new version of this file" link
|
|
if ( UploadBase::userCanReUpload( $wgUser, $this->img->name ) ) {
|
|
$ulink = $sk->makeExternalLink( $this->getUploadUrl(), wfMsg( 'uploadnewversion-linktext' ) );
|
|
$wgOut->addHTML( "<li id=\"mw-imagepage-reupload-link\"><div class=\"plainlinks\">{$ulink}</div></li>\n" );
|
|
}
|
|
|
|
# External editing link
|
|
if ( $wgUseExternalEditor ) {
|
|
$elink = $sk->link(
|
|
$this->mTitle,
|
|
wfMsgHtml( 'edit-externally' ),
|
|
array(),
|
|
array(
|
|
'action' => 'edit',
|
|
'externaledit' => 'true',
|
|
'mode' => 'file'
|
|
),
|
|
array( 'known', 'noclasses' )
|
|
);
|
|
$wgOut->addHTML(
|
|
'<li id="mw-imagepage-edit-external">' . $elink . ' <small>' .
|
|
wfMsgExt( 'edit-externally-help', array( 'parseinline' ) ) .
|
|
"</small></li>\n"
|
|
);
|
|
}
|
|
|
|
$wgOut->addHTML( "</ul>\n" );
|
|
}
|
|
|
|
protected function closeShowImage() { } # For overloading
|
|
|
|
/**
|
|
* If the page we've just displayed is in the "Image" namespace,
|
|
* we follow it with an upload history of the image and its usage.
|
|
*/
|
|
protected function imageHistory() {
|
|
global $wgOut;
|
|
|
|
$this->loadFile();
|
|
$pager = new ImageHistoryPseudoPager( $this );
|
|
$wgOut->addHTML( $pager->getBody() );
|
|
$wgOut->preventClickjacking( $pager->getPreventClickjacking() );
|
|
|
|
$this->img->resetHistory(); // free db resources
|
|
|
|
# Exist check because we don't want to show this on pages where an image
|
|
# doesn't exist along with the noimage message, that would suck. -ævar
|
|
if ( $this->img->exists() ) {
|
|
$this->uploadLinksBox();
|
|
}
|
|
}
|
|
|
|
protected function queryImageLinks( $target, $limit ) {
|
|
$dbr = wfGetDB( DB_SLAVE );
|
|
|
|
return $dbr->select(
|
|
array( 'imagelinks', 'page' ),
|
|
array( 'page_namespace', 'page_title', 'page_is_redirect', 'il_to' ),
|
|
array( 'il_to' => $target, 'il_from = page_id' ),
|
|
__METHOD__,
|
|
array( 'LIMIT' => $limit + 1 )
|
|
);
|
|
}
|
|
|
|
protected function imageLinks() {
|
|
global $wgUser, $wgOut, $wgLang;
|
|
|
|
$limit = 100;
|
|
|
|
$res = $this->queryImageLinks( $this->mTitle->getDbKey(), $limit + 1);
|
|
$rows = array();
|
|
$redirects = array();
|
|
foreach ( $res as $row ) {
|
|
if ( $row->page_is_redirect ) {
|
|
$redirects[$row->page_title] = array();
|
|
}
|
|
$rows[] = $row;
|
|
}
|
|
$count = count( $rows );
|
|
|
|
$hasMore = $count > $limit;
|
|
if ( !$hasMore && count( $redirects ) ) {
|
|
$res = $this->queryImageLinks( array_keys( $redirects ),
|
|
$limit - count( $rows ) + 1 );
|
|
foreach ( $res as $row ) {
|
|
$redirects[$row->il_to][] = $row;
|
|
$count++;
|
|
}
|
|
$hasMore = ( $res->numRows() + count( $rows ) ) > $limit;
|
|
}
|
|
|
|
if ( $count == 0 ) {
|
|
$wgOut->wrapWikiMsg(
|
|
Html::rawElement( 'div',
|
|
array( 'id' => 'mw-imagepage-nolinkstoimage' ), "\n$1\n" ),
|
|
'nolinkstoimage'
|
|
);
|
|
return;
|
|
}
|
|
|
|
$wgOut->addHTML( "<div id='mw-imagepage-section-linkstoimage'>\n" );
|
|
if ( !$hasMore ) {
|
|
$wgOut->addWikiMsg( 'linkstoimage', $count );
|
|
} else {
|
|
// More links than the limit. Add a link to [[Special:Whatlinkshere]]
|
|
$wgOut->addWikiMsg( 'linkstoimage-more',
|
|
$wgLang->formatNum( $limit ),
|
|
$this->mTitle->getPrefixedDBkey()
|
|
);
|
|
}
|
|
|
|
$wgOut->addHTML(
|
|
Html::openElement( 'ul',
|
|
array( 'class' => 'mw-imagepage-linkstoimage' ) ) . "\n"
|
|
);
|
|
$sk = $wgUser->getSkin();
|
|
$count = 0;
|
|
|
|
// Sort the list by namespace:title
|
|
usort( $rows, array( $this, 'compare' ) );
|
|
|
|
// Create links for every element
|
|
$currentCount = 0;
|
|
foreach( $rows as $element ) {
|
|
$currentCount++;
|
|
if ( $currentCount > $limit ) {
|
|
break;
|
|
}
|
|
|
|
$link = $sk->linkKnown( Title::makeTitle( $element->page_namespace, $element->page_title ) );
|
|
if ( !isset( $redirects[$element->page_title] ) ) {
|
|
$liContents = $link;
|
|
} else {
|
|
$ul = "<ul class='mw-imagepage-redirectstofile'>\n";
|
|
foreach ( $redirects[$element->page_title] as $row ) {
|
|
$currentCount++;
|
|
if ( $currentCount > $limit ) {
|
|
break;
|
|
}
|
|
|
|
$link2 = $sk->linkKnown( Title::makeTitle( $row->page_namespace, $row->page_title ) );
|
|
$ul .= Html::rawElement(
|
|
'li',
|
|
array( 'id' => 'mw-imagepage-linkstoimage-ns' . $element->page_namespace ),
|
|
$link2
|
|
) . "\n";
|
|
}
|
|
$ul .= '</ul>';
|
|
$liContents = wfMessage( 'linkstoimage-redirect' )->rawParams(
|
|
$link, $ul )->parse();
|
|
}
|
|
$wgOut->addHTML( Html::rawElement(
|
|
'li',
|
|
array( 'id' => 'mw-imagepage-linkstoimage-ns' . $element->page_namespace ),
|
|
$liContents
|
|
) . "\n"
|
|
);
|
|
|
|
};
|
|
$wgOut->addHTML( Html::closeElement( 'ul' ) . "\n" );
|
|
$res->free();
|
|
|
|
// Add a links to [[Special:Whatlinkshere]]
|
|
if ( $count > $limit ) {
|
|
$wgOut->addWikiMsg( 'morelinkstoimage', $this->mTitle->getPrefixedDBkey() );
|
|
}
|
|
$wgOut->addHTML( Html::closeElement( 'div' ) . "\n" );
|
|
}
|
|
|
|
protected function imageDupes() {
|
|
global $wgOut, $wgUser, $wgLang;
|
|
|
|
$this->loadFile();
|
|
|
|
$dupes = $this->getDuplicates();
|
|
if ( count( $dupes ) == 0 ) {
|
|
return;
|
|
}
|
|
|
|
$wgOut->addHTML( "<div id='mw-imagepage-section-duplicates'>\n" );
|
|
$wgOut->addWikiMsg( 'duplicatesoffile',
|
|
$wgLang->formatNum( count( $dupes ) ), $this->mTitle->getDBkey()
|
|
);
|
|
$wgOut->addHTML( "<ul class='mw-imagepage-duplicates'>\n" );
|
|
|
|
$sk = $wgUser->getSkin();
|
|
foreach ( $dupes as $file ) {
|
|
$fromSrc = '';
|
|
if ( $file->isLocal() ) {
|
|
$link = $sk->link(
|
|
$file->getTitle(),
|
|
null,
|
|
array(),
|
|
array(),
|
|
array( 'known', 'noclasses' )
|
|
);
|
|
} else {
|
|
$link = $sk->makeExternalLink( $file->getDescriptionUrl(),
|
|
$file->getTitle()->getPrefixedText() );
|
|
$fromSrc = wfMsg( 'shared-repo-from', $file->getRepo()->getDisplayName() );
|
|
}
|
|
$wgOut->addHTML( "<li>{$link} {$fromSrc}</li>\n" );
|
|
}
|
|
$wgOut->addHTML( "</ul></div>\n" );
|
|
}
|
|
|
|
/**
|
|
* Delete the file, or an earlier version of it
|
|
*/
|
|
public function delete() {
|
|
global $wgUploadMaintenance;
|
|
if ( $wgUploadMaintenance && $this->mTitle && $this->mTitle->getNamespace() == NS_FILE ) {
|
|
global $wgOut;
|
|
$wgOut->wrapWikiMsg( "<div class='error'>\n$1\n</div>\n", array( 'filedelete-maintenance' ) );
|
|
return;
|
|
}
|
|
|
|
$this->loadFile();
|
|
if ( !$this->img->exists() || !$this->img->isLocal() || $this->img->getRedirected() ) {
|
|
// Standard article deletion
|
|
parent::delete();
|
|
return;
|
|
}
|
|
$deleter = new FileDeleteForm( $this->img );
|
|
$deleter->execute();
|
|
}
|
|
|
|
/**
|
|
* Revert the file to an earlier version
|
|
*/
|
|
public function revert() {
|
|
$this->loadFile();
|
|
$reverter = new FileRevertForm( $this->img );
|
|
$reverter->execute();
|
|
}
|
|
|
|
/**
|
|
* Override handling of action=purge
|
|
*/
|
|
public function doPurge() {
|
|
$this->loadFile();
|
|
if ( $this->img->exists() ) {
|
|
wfDebug( 'ImagePage::doPurge purging ' . $this->img->getName() . "\n" );
|
|
$update = new HTMLCacheUpdate( $this->mTitle, 'imagelinks' );
|
|
$update->doUpdate();
|
|
$this->img->upgradeRow();
|
|
$this->img->purgeCache();
|
|
} else {
|
|
wfDebug( 'ImagePage::doPurge no image for ' . $this->img->getName() . "; limiting purge to cache only\n" );
|
|
// even if the file supposedly doesn't exist, force any cached information
|
|
// to be updated (in case the cached information is wrong)
|
|
$this->img->purgeCache();
|
|
}
|
|
parent::doPurge();
|
|
}
|
|
|
|
/**
|
|
* Display an error with a wikitext description
|
|
*/
|
|
function showError( $description ) {
|
|
global $wgOut;
|
|
$wgOut->setPageTitle( wfMsg( 'internalerror' ) );
|
|
$wgOut->setRobotPolicy( 'noindex,nofollow' );
|
|
$wgOut->setArticleRelated( false );
|
|
$wgOut->enableClientCache( false );
|
|
$wgOut->addWikiText( $description );
|
|
}
|
|
|
|
/**
|
|
* Callback for usort() to do link sorts by (namespace, title)
|
|
* Function copied from Title::compare()
|
|
*
|
|
* @param $a object page to compare with
|
|
* @param $b object page to compare with
|
|
* @return Integer: result of string comparison, or namespace comparison
|
|
*/
|
|
protected function compare( $a, $b ) {
|
|
if ( $a->page_namespace == $b->page_namespace ) {
|
|
return strcmp( $a->page_title, $b->page_title );
|
|
} else {
|
|
return $a->page_namespace - $b->page_namespace;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Builds the image revision log shown on image pages
|
|
*
|
|
* @ingroup Media
|
|
*/
|
|
class ImageHistoryList {
|
|
|
|
/**
|
|
* @var Title
|
|
*/
|
|
protected $title;
|
|
|
|
/**
|
|
* @var File
|
|
*/
|
|
protected $img;
|
|
|
|
/**
|
|
* @var ImagePage
|
|
*/
|
|
protected $imagePage;
|
|
|
|
/**
|
|
* @var Skin
|
|
*/
|
|
protected $skin;
|
|
|
|
protected $repo, $showThumb;
|
|
protected $preventClickjacking = false;
|
|
|
|
/**
|
|
* @param ImagePage $imagePage
|
|
*/
|
|
public function __construct( $imagePage ) {
|
|
global $wgUser, $wgShowArchiveThumbnails;
|
|
$this->skin = $wgUser->getSkin();
|
|
$this->current = $imagePage->getFile();
|
|
$this->img = $imagePage->getDisplayedFile();
|
|
$this->title = $imagePage->getTitle();
|
|
$this->imagePage = $imagePage;
|
|
$this->showThumb = $wgShowArchiveThumbnails && $this->img->canRender();
|
|
}
|
|
|
|
public function getImagePage() {
|
|
return $this->imagePage;
|
|
}
|
|
|
|
public function getSkin() {
|
|
return $this->skin;
|
|
}
|
|
|
|
public function getFile() {
|
|
return $this->img;
|
|
}
|
|
|
|
public function beginImageHistoryList( $navLinks = '' ) {
|
|
global $wgOut, $wgUser;
|
|
return Xml::element( 'h2', array( 'id' => 'filehistory' ), wfMsg( 'filehist' ) ) . "\n"
|
|
. "<div id=\"mw-imagepage-section-filehistory\">\n"
|
|
. $wgOut->parse( wfMsgNoTrans( 'filehist-help' ) )
|
|
. $navLinks . "\n"
|
|
. Xml::openElement( 'table', array( 'class' => 'wikitable filehistory' ) ) . "\n"
|
|
. '<tr><td></td>'
|
|
. ( $this->current->isLocal() && ( $wgUser->isAllowedAny( 'delete', 'deletedhistory' ) ) ? '<td></td>' : '' )
|
|
. '<th>' . wfMsgHtml( 'filehist-datetime' ) . '</th>'
|
|
. ( $this->showThumb ? '<th>' . wfMsgHtml( 'filehist-thumb' ) . '</th>' : '' )
|
|
. '<th>' . wfMsgHtml( 'filehist-dimensions' ) . '</th>'
|
|
. '<th>' . wfMsgHtml( 'filehist-user' ) . '</th>'
|
|
. '<th>' . wfMsgHtml( 'filehist-comment' ) . '</th>'
|
|
. "</tr>\n";
|
|
}
|
|
|
|
public function endImageHistoryList( $navLinks = '' ) {
|
|
return "</table>\n$navLinks\n</div>\n";
|
|
}
|
|
|
|
/**
|
|
* @param $iscur
|
|
* @param $file File
|
|
* @return string
|
|
*/
|
|
public function imageHistoryLine( $iscur, $file ) {
|
|
global $wgUser, $wgLang;
|
|
|
|
$timestamp = wfTimestamp( TS_MW, $file->getTimestamp() );
|
|
$img = $iscur ? $file->getName() : $file->getArchiveName();
|
|
$user = $file->getUser( 'id' );
|
|
$usertext = $file->getUser( 'text' );
|
|
$description = $file->getDescription();
|
|
|
|
$local = $this->current->isLocal();
|
|
$row = $selected = '';
|
|
|
|
// Deletion link
|
|
if ( $local && ( $wgUser->isAllowedAny( 'delete', 'deletedhistory' ) ) ) {
|
|
$row .= '<td>';
|
|
# Link to remove from history
|
|
if ( $wgUser->isAllowed( 'delete' ) ) {
|
|
$q = array( 'action' => 'delete' );
|
|
if ( !$iscur ) {
|
|
$q['oldimage'] = $img;
|
|
}
|
|
$row .= $this->skin->link(
|
|
$this->title,
|
|
wfMsgHtml( $iscur ? 'filehist-deleteall' : 'filehist-deleteone' ),
|
|
array(), $q, array( 'known' )
|
|
);
|
|
}
|
|
# Link to hide content. Don't show useless link to people who cannot hide revisions.
|
|
$canHide = $wgUser->isAllowed( 'deleterevision' );
|
|
if ( $canHide || ( $wgUser->isAllowed( 'deletedhistory' ) && $file->getVisibility() ) ) {
|
|
if ( $wgUser->isAllowed( 'delete' ) ) {
|
|
$row .= '<br />';
|
|
}
|
|
// If file is top revision or locked from this user, don't link
|
|
if ( $iscur || !$file->userCan( File::DELETED_RESTRICTED ) ) {
|
|
$del = $this->skin->revDeleteLinkDisabled( $canHide );
|
|
} else {
|
|
list( $ts, $name ) = explode( '!', $img, 2 );
|
|
$query = array(
|
|
'type' => 'oldimage',
|
|
'target' => $this->title->getPrefixedText(),
|
|
'ids' => $ts,
|
|
);
|
|
$del = $this->skin->revDeleteLink( $query,
|
|
$file->isDeleted( File::DELETED_RESTRICTED ), $canHide );
|
|
}
|
|
$row .= $del;
|
|
}
|
|
$row .= '</td>';
|
|
}
|
|
|
|
// Reversion link/current indicator
|
|
$row .= '<td>';
|
|
if ( $iscur ) {
|
|
$row .= wfMsgHtml( 'filehist-current' );
|
|
} elseif ( $local && $wgUser->isLoggedIn() && $this->title->userCan( 'edit' ) ) {
|
|
if ( $file->isDeleted( File::DELETED_FILE ) ) {
|
|
$row .= wfMsgHtml( 'filehist-revert' );
|
|
} else {
|
|
$row .= $this->skin->link(
|
|
$this->title,
|
|
wfMsgHtml( 'filehist-revert' ),
|
|
array(),
|
|
array(
|
|
'action' => 'revert',
|
|
'oldimage' => $img,
|
|
'wpEditToken' => $wgUser->editToken( $img )
|
|
),
|
|
array( 'known', 'noclasses' )
|
|
);
|
|
}
|
|
}
|
|
$row .= '</td>';
|
|
|
|
// Date/time and image link
|
|
if ( $file->getTimestamp() === $this->img->getTimestamp() ) {
|
|
$selected = "class='filehistory-selected'";
|
|
}
|
|
$row .= "<td $selected style='white-space: nowrap;'>";
|
|
if ( !$file->userCan( File::DELETED_FILE ) ) {
|
|
# Don't link to unviewable files
|
|
$row .= '<span class="history-deleted">' . $wgLang->timeAndDate( $timestamp, true ) . '</span>';
|
|
} elseif ( $file->isDeleted( File::DELETED_FILE ) ) {
|
|
if ( $local ) {
|
|
$this->preventClickjacking();
|
|
$revdel = SpecialPage::getTitleFor( 'Revisiondelete' );
|
|
# Make a link to review the image
|
|
$url = $this->skin->link(
|
|
$revdel,
|
|
$wgLang->timeAndDate( $timestamp, true ),
|
|
array(),
|
|
array(
|
|
'target' => $this->title->getPrefixedText(),
|
|
'file' => $img,
|
|
'token' => $wgUser->editToken( $img )
|
|
),
|
|
array( 'known', 'noclasses' )
|
|
);
|
|
} else {
|
|
$url = $wgLang->timeAndDate( $timestamp, true );
|
|
}
|
|
$row .= '<span class="history-deleted">' . $url . '</span>';
|
|
} else {
|
|
$url = $iscur ? $this->current->getUrl() : $this->current->getArchiveUrl( $img );
|
|
$row .= Xml::element( 'a', array( 'href' => $url ), $wgLang->timeAndDate( $timestamp, true ) );
|
|
}
|
|
$row .= "</td>";
|
|
|
|
// Thumbnail
|
|
if ( $this->showThumb ) {
|
|
$row .= '<td>' . $this->getThumbForLine( $file ) . '</td>';
|
|
}
|
|
|
|
// Image dimensions + size
|
|
$row .= '<td>';
|
|
$row .= htmlspecialchars( $file->getDimensionsString() );
|
|
$row .= ' <span style="white-space: nowrap;">(' . $this->skin->formatSize( $file->getSize() ) . ')</span>';
|
|
$row .= '</td>';
|
|
|
|
// Uploading user
|
|
$row .= '<td>';
|
|
// Hide deleted usernames
|
|
if ( $file->isDeleted( File::DELETED_USER ) ) {
|
|
$row .= '<span class="history-deleted">' . wfMsgHtml( 'rev-deleted-user' ) . '</span>';
|
|
} else {
|
|
if ( $local ) {
|
|
$row .= $this->skin->userLink( $user, $usertext ) . ' <span style="white-space: nowrap;">' .
|
|
$this->skin->userToolLinks( $user, $usertext ) . '</span>';
|
|
} else {
|
|
$row .= htmlspecialchars( $usertext );
|
|
}
|
|
}
|
|
$row .= '</td><td>';
|
|
|
|
// Don't show deleted descriptions
|
|
if ( $file->isDeleted( File::DELETED_COMMENT ) ) {
|
|
$row .= '<span class="history-deleted">' . wfMsgHtml( 'rev-deleted-comment' ) . '</span>';
|
|
} else {
|
|
$row .= $this->skin->commentBlock( $description, $this->title, false, false );
|
|
}
|
|
$row .= '</td>';
|
|
|
|
$rowClass = null;
|
|
wfRunHooks( 'ImagePageFileHistoryLine', array( $this, $file, &$row, &$rowClass ) );
|
|
$classAttr = $rowClass ? " class='$rowClass'" : '';
|
|
|
|
return "<tr{$classAttr}>{$row}</tr>\n";
|
|
}
|
|
|
|
/**
|
|
* @param $file File
|
|
* @return string
|
|
*/
|
|
protected function getThumbForLine( $file ) {
|
|
global $wgLang;
|
|
|
|
if ( $file->allowInlineDisplay() && $file->userCan( File::DELETED_FILE ) && !$file->isDeleted( File::DELETED_FILE ) ) {
|
|
$params = array(
|
|
'width' => '120',
|
|
'height' => '120',
|
|
);
|
|
$timestamp = wfTimestamp( TS_MW, $file->getTimestamp() );
|
|
|
|
$thumbnail = $file->transform( $params );
|
|
$options = array(
|
|
'alt' => wfMsg( 'filehist-thumbtext',
|
|
$wgLang->timeAndDate( $timestamp, true ),
|
|
$wgLang->date( $timestamp, true ),
|
|
$wgLang->time( $timestamp, true ) ),
|
|
'file-link' => true,
|
|
);
|
|
|
|
if ( !$thumbnail ) {
|
|
return wfMsgHtml( 'filehist-nothumb' );
|
|
}
|
|
|
|
return $thumbnail->toHtml( $options );
|
|
} else {
|
|
return wfMsgHtml( 'filehist-nothumb' );
|
|
}
|
|
}
|
|
|
|
protected function preventClickjacking( $enable = true ) {
|
|
$this->preventClickjacking = $enable;
|
|
}
|
|
|
|
public function getPreventClickjacking() {
|
|
return $this->preventClickjacking;
|
|
}
|
|
}
|
|
|
|
class ImageHistoryPseudoPager extends ReverseChronologicalPager {
|
|
protected $preventClickjacking = false;
|
|
|
|
/**
|
|
* @var File
|
|
*/
|
|
protected $mImg;
|
|
|
|
/**
|
|
* @var Title
|
|
*/
|
|
protected $mTitle;
|
|
|
|
/**
|
|
* @param ImagePage $imagePage
|
|
*/
|
|
function __construct( $imagePage ) {
|
|
parent::__construct();
|
|
$this->mImagePage = $imagePage;
|
|
$this->mTitle = clone ( $imagePage->getTitle() );
|
|
$this->mTitle->setFragment( '#filehistory' );
|
|
$this->mImg = null;
|
|
$this->mHist = array();
|
|
$this->mRange = array( 0, 0 ); // display range
|
|
}
|
|
|
|
function getTitle() {
|
|
return $this->mTitle;
|
|
}
|
|
|
|
function getQueryInfo() {
|
|
return false;
|
|
}
|
|
|
|
function getIndexField() {
|
|
return '';
|
|
}
|
|
|
|
function formatRow( $row ) {
|
|
return '';
|
|
}
|
|
|
|
function getBody() {
|
|
$s = '';
|
|
$this->doQuery();
|
|
if ( count( $this->mHist ) ) {
|
|
$list = new ImageHistoryList( $this->mImagePage );
|
|
# Generate prev/next links
|
|
$navLink = $this->getNavigationBar();
|
|
$s = $list->beginImageHistoryList( $navLink );
|
|
// Skip rows there just for paging links
|
|
for ( $i = $this->mRange[0]; $i <= $this->mRange[1]; $i++ ) {
|
|
$file = $this->mHist[$i];
|
|
$s .= $list->imageHistoryLine( !$file->isOld(), $file );
|
|
}
|
|
$s .= $list->endImageHistoryList( $navLink );
|
|
|
|
if ( $list->getPreventClickjacking() ) {
|
|
$this->preventClickjacking();
|
|
}
|
|
}
|
|
return $s;
|
|
}
|
|
|
|
function doQuery() {
|
|
if ( $this->mQueryDone ) {
|
|
return;
|
|
}
|
|
$this->mImg = $this->mImagePage->getFile(); // ensure loading
|
|
if ( !$this->mImg->exists() ) {
|
|
return;
|
|
}
|
|
$queryLimit = $this->mLimit + 1; // limit plus extra row
|
|
if ( $this->mIsBackwards ) {
|
|
// Fetch the file history
|
|
$this->mHist = $this->mImg->getHistory( $queryLimit, null, $this->mOffset, false );
|
|
// The current rev may not meet the offset/limit
|
|
$numRows = count( $this->mHist );
|
|
if ( $numRows <= $this->mLimit && $this->mImg->getTimestamp() > $this->mOffset ) {
|
|
$this->mHist = array_merge( array( $this->mImg ), $this->mHist );
|
|
}
|
|
} else {
|
|
// The current rev may not meet the offset
|
|
if ( !$this->mOffset || $this->mImg->getTimestamp() < $this->mOffset ) {
|
|
$this->mHist[] = $this->mImg;
|
|
}
|
|
// Old image versions (fetch extra row for nav links)
|
|
$oiLimit = count( $this->mHist ) ? $this->mLimit : $this->mLimit + 1;
|
|
// Fetch the file history
|
|
$this->mHist = array_merge( $this->mHist,
|
|
$this->mImg->getHistory( $oiLimit, $this->mOffset, null, false ) );
|
|
}
|
|
$numRows = count( $this->mHist ); // Total number of query results
|
|
if ( $numRows ) {
|
|
# Index value of top item in the list
|
|
$firstIndex = $this->mIsBackwards ?
|
|
$this->mHist[$numRows - 1]->getTimestamp() : $this->mHist[0]->getTimestamp();
|
|
# Discard the extra result row if there is one
|
|
if ( $numRows > $this->mLimit && $numRows > 1 ) {
|
|
if ( $this->mIsBackwards ) {
|
|
# Index value of item past the index
|
|
$this->mPastTheEndIndex = $this->mHist[0]->getTimestamp();
|
|
# Index value of bottom item in the list
|
|
$lastIndex = $this->mHist[1]->getTimestamp();
|
|
# Display range
|
|
$this->mRange = array( 1, $numRows - 1 );
|
|
} else {
|
|
# Index value of item past the index
|
|
$this->mPastTheEndIndex = $this->mHist[$numRows - 1]->getTimestamp();
|
|
# Index value of bottom item in the list
|
|
$lastIndex = $this->mHist[$numRows - 2]->getTimestamp();
|
|
# Display range
|
|
$this->mRange = array( 0, $numRows - 2 );
|
|
}
|
|
} else {
|
|
# Setting indexes to an empty string means that they will be
|
|
# omitted if they would otherwise appear in URLs. It just so
|
|
# happens that this is the right thing to do in the standard
|
|
# UI, in all the relevant cases.
|
|
$this->mPastTheEndIndex = '';
|
|
# Index value of bottom item in the list
|
|
$lastIndex = $this->mIsBackwards ?
|
|
$this->mHist[0]->getTimestamp() : $this->mHist[$numRows - 1]->getTimestamp();
|
|
# Display range
|
|
$this->mRange = array( 0, $numRows - 1 );
|
|
}
|
|
} else {
|
|
$firstIndex = '';
|
|
$lastIndex = '';
|
|
$this->mPastTheEndIndex = '';
|
|
}
|
|
if ( $this->mIsBackwards ) {
|
|
$this->mIsFirst = ( $numRows < $queryLimit );
|
|
$this->mIsLast = ( $this->mOffset == '' );
|
|
$this->mLastShown = $firstIndex;
|
|
$this->mFirstShown = $lastIndex;
|
|
} else {
|
|
$this->mIsFirst = ( $this->mOffset == '' );
|
|
$this->mIsLast = ( $numRows < $queryLimit );
|
|
$this->mLastShown = $lastIndex;
|
|
$this->mFirstShown = $firstIndex;
|
|
}
|
|
$this->mQueryDone = true;
|
|
}
|
|
|
|
protected function preventClickjacking( $enable = true ) {
|
|
$this->preventClickjacking = $enable;
|
|
}
|
|
|
|
public function getPreventClickjacking() {
|
|
return $this->preventClickjacking;
|
|
}
|
|
|
|
}
|