<?php
-if ( !defined( 'MEDIAWIKI' ) )
- die( 1 );
-
/**
* Special handling for image description pages
*
*/
class ImagePage extends Article {
- /* private */ var $img; // Image object
- /* private */ var $displayImg;
- /* private */ var $repo;
- /* private */ var $fileLoaded;
+ /**
+ * @var File
+ */
+ private $img;
+ private $displayImg;
+ private $repo;
+ private $fileLoaded;
+
var $mExtraDescription = false;
var $dupes;
$this->dupes = null;
$this->repo = null;
}
-
+
+ /**
+ * @param $file File:
+ * @return void
+ */
public function setFile( $file ) {
$this->displayImg = $file;
$this->img = $file;
$showmeta = false;
}
- if ( !$diff && $this->displayImg->exists() )
+ if ( !$diff && $this->displayImg->exists() ) {
$wgOut->addHTML( $this->showTOC( $showmeta ) );
+ }
- if ( !$diff )
+ if ( !$diff ) {
$this->openShowImage();
+ }
# No need to display noarticletext, we use our own message, output in openShowImage()
if ( $this->getID() ) {
# Show shared description, if needed
if ( $this->mExtraDescription ) {
- $fol = wfMsgNoTrans( 'shareddescriptionfollows' );
- if ( $fol != '-' && !wfEmptyMsg( 'shareddescriptionfollows', $fol ) ) {
- $wgOut->addWikiText( $fol );
+ $fol = wfMessage( 'shareddescriptionfollows' );
+ if ( !$fol->isDisabled() ) {
+ $wgOut->addWikiText( $fol->plain() );
}
$wgOut->addHTML( '<div id="shared-image-desc">' . $this->mExtraDescription . "</div>\n" );
}
# Allow extensions to add something after the image links
$html = '';
wfRunHooks( 'ImagePageAfterImageLinks', array( $this, &$html ) );
- if ( $html )
+ if ( $html ) {
$wgOut->addHTML( $html );
+ }
if ( $showmeta ) {
- $expand = htmlspecialchars( Xml::escapeJsString( wfMsg( 'metadata-expand' ) ) );
- $collapse = htmlspecialchars( Xml::escapeJsString( wfMsg( 'metadata-collapse' ) ) );
$wgOut->addHTML( Xml::element( 'h2', array( 'id' => 'metadata' ), wfMsg( 'metadata' ) ) . "\n" );
$wgOut->addWikiText( $this->makeMetadataTable( $formattedMetadata ) );
- $wgOut->addModules( array( 'mediawiki.legacy.metadata' ) );
- $wgOut->addHTML(
- "<script type=\"text/javascript\">attachMetadataToggle('mw_metadata', '$expand', '$collapse');</script>\n" );
+ $wgOut->addModules( array( 'mediawiki.action.view.metadata' ) );
}
$css = $this->repo->getDescriptionStylesheetUrl();
}
return $this->mRedirectTarget = Title::makeTitle( NS_FILE, $to );
}
+
public function followRedirect() {
$this->loadFile();
if ( $this->img->isLocal() ) {
}
return Title::makeTitle( NS_FILE, $to );
}
+
public function isRedirect( $text = false ) {
$this->loadFile();
- if ( $this->img->isLocal() )
+ if ( $this->img->isLocal() ) {
return parent::isRedirect( $text );
+ }
return (bool)$this->img->getRedirected();
}
if ( !is_null( $this->dupes ) ) {
return $this->dupes;
}
- if ( !( $hash = $this->img->getSha1() ) ) {
+ $hash = $this->img->getSha1();
+ if ( !( $hash ) ) {
return $this->dupes = array();
}
$dupes = RepoGroup::singleton()->findBySha1( $hash );
$size = $this->img->getSize();
foreach ( $dupes as $index => $file ) {
$key = $file->getRepoName() . ':' . $file->getName();
- if ( $key == $self )
+ if ( $key == $self ) {
unset( $dupes[$index] );
- if ( $file->getSize() != $size )
+ }
+ if ( $file->getSize() != $size ) {
unset( $dupes[$index] );
+ }
}
return $this->dupes = $dupes;
-
}
-
/**
* Create the TOC
*/
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>',
+ '<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>';
* FIXME: bad interface, see note on MediaHandler::formatMetadata().
*
* @param $metadata Array: the array containing the EXIF data
- * @return String
+ * @return String The metadata table. This is treated as Wikitext (!)
*/
protected function makeMetadataTable( $metadata ) {
$r = "<div class=\"mw-imagepage-section-metadata\">";
if ( $this->img && !$this->img->isLocal() && 0 == $this->getID() ) {
return '';
}
- return Article::getContent();
+ return parent::getContent();
}
protected function openShowImage() {
$this->loadFile();
- $full_url = $this->displayImg->getURL();
$sizeSel = intval( $wgUser->getOption( 'imagesize' ) );
if ( !isset( $wgImageLimits[$sizeSel] ) ) {
$sizeSel = User::getDefaultOption( 'imagesize' );
$height_orig = $this->displayImg->getHeight( $page );
$height = $height_orig;
- $showLink = false;
- $linkAttribs = array( 'href' => $full_url );
- $longDesc = $this->displayImg->getLongDesc();
+ $longDesc = wfMsg( 'parentheses', $this->displayImg->getLongDesc() );
wfRunHooks( 'ImageOpenShowImageInlineBefore', array( &$this, &$wgOut ) );
# image
# "Download high res version" link below the image
- # $msgsize = wfMsgHtml('file-info-size', $width_orig, $height_orig, $sk->formatSize( $this->displayImg->getSize() ), $mime );
+ # $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.
# because of rounding.
}
$msgbig = wfMsgHtml( 'show-big-image' );
- $msgsmall = wfMsgExt( 'show-big-image-thumb', 'parseinline',
- $wgLang->formatNum( $width ),
- $wgLang->formatNum( $height )
- );
+ $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 = '';
- if ( !$this->displayImg->mustRender() ) {
- $anchorclose = "<br />" . $msgsmall;
- }
+ $anchorclose = '<br />' . $msgsmall;
$isMulti = $this->displayImg->isMultipage() && $this->displayImg->pageCount() > 1;
if ( $isMulti ) {
$wgOut->addHTML(
'</td><td><div class="multipageimagenavbox">' .
Xml::openElement( 'form', $formParams ) .
- Xml::hidden( 'title', $this->getTitle()->getPrefixedDbKey() ) .
+ Html::hidden( 'title', $this->getTitle()->getPrefixedDBkey() ) .
wfMsgExt( 'imgmultigoto', array( 'parseinline', 'replaceafter' ), $select ) .
Xml::submitButton( wfMsg( 'imgmultigo' ) ) .
Xml::closeElement( 'form' ) .
$icon = $this->displayImg->iconThumb();
$wgOut->addHTML( '<div class="fullImageLink" id="file">' .
- $icon->toHtml( array( 'file-link' => true ) ) .
- "</div>\n" );
+ $icon->toHtml( array( 'file-link' => true ) ) .
+ "</div>\n" );
}
$showLink = true;
}
-
if ( $showLink ) {
$filename = wfEscapeWikiText( $this->displayImg->getName() );
$linktext = $filename;
$uploadTitle = SpecialPage::getTitleFor( 'Upload' );
$nofile = array(
'filepage-nofile-link',
- $uploadTitle->getFullUrl( array( 'wpDestFile' => $this->img->getName() ) )
+ $uploadTitle->getFullURL( array( 'wpDestFile' => $this->img->getName() ) )
);
- }
- else
- {
+ } 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 '';
}
}
$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();
public function getUploadUrl() {
$this->loadFile();
$uploadTitle = SpecialPage::getTitleFor( 'Upload' );
- return $uploadTitle->getFullUrl( array(
+ return $uploadTitle->getFullURL( array(
'wpDestFile' => $this->img->getName(),
'wpForReUpload' => 1
) );
protected function uploadLinksBox() {
global $wgUser, $wgOut, $wgEnableUploads, $wgUseExternalEditor;
- if ( !$wgEnableUploads ) { return; }
+ if ( !$wgEnableUploads ) {
+ return;
+ }
$this->loadFile();
- if ( !$this->img->isLocal() )
+ if ( !$this->img->isLocal() ) {
return;
+ }
$sk = $wgUser->getSkin();
),
array( 'known', 'noclasses' )
);
- $wgOut->addHTML( '<li id="mw-imagepage-edit-external">' . $elink . ' <small>' . wfMsgExt( 'edit-externally-help', array( 'parseinline' ) ) . "</small></li>\n" );
+ $wgOut->addHTML(
+ '<li id="mw-imagepage-edit-external">' . $elink . ' <small>' .
+ wfMsgExt( 'edit-externally-help', array( 'parseinline' ) ) .
+ "</small></li>\n"
+ );
}
$wgOut->addHTML( "</ul>\n" );
$this->loadFile();
$pager = new ImageHistoryPseudoPager( $this );
$wgOut->addHTML( $pager->getBody() );
+ $wgOut->preventClickjacking( $pager->getPreventClickjacking() );
$this->img->resetHistory(); // free db resources
);
$count = $dbr->numRows( $res );
if ( $count == 0 ) {
- $wgOut->wrapWikiMsg( Html::rawElement( 'div', array ( 'id' => 'mw-imagepage-nolinkstoimage' ), "\n$1\n" ), 'nolinkstoimage' );
+ $wgOut->wrapWikiMsg(
+ Html::rawElement( 'div',
+ array( 'id' => 'mw-imagepage-nolinkstoimage' ), "\n$1\n" ),
+ 'nolinkstoimage'
+ );
return;
}
);
}
- $wgOut->addHTML( Html::openElement( 'ul', array( 'class' => 'mw-imagepage-linkstoimage' ) ) . "\n" );
+ $wgOut->addHTML(
+ Html::openElement( 'ul',
+ array( 'class' => 'mw-imagepage-linkstoimage' ) ) . "\n"
+ );
$sk = $wgUser->getSkin();
$count = 0;
$elements = array();
- while ( $s = $res->fetchObject() ) {
+ foreach ( $res as $s ) {
$count++;
if ( $count <= $limit ) {
// We have not yet reached the extra one that tells us there is more to fetch
}
// Sort the list by namespace:title
- usort ( $elements, array( $this, 'compare' ) );
+ usort( $elements, array( $this, 'compare' ) );
// Create links for every element
foreach( $elements as $element ) {
$link = $sk->linkKnown( Title::makeTitle( $element->page_namespace, $element->page_title ) );
$wgOut->addHTML( Html::rawElement(
- 'li',
- array( 'id' => 'mw-imagepage-linkstoimage-ns' . $element->page_namespace ),
- $link
- ) . "\n"
+ 'li',
+ array( 'id' => 'mw-imagepage-linkstoimage-ns' . $element->page_namespace ),
+ $link
+ ) . "\n"
);
};
global $wgUser, $wgOut, $wgLang;
$redirects = $this->getTitle()->getRedirectsHere( NS_FILE );
- if ( count( $redirects ) == 0 ) return;
+ if ( count( $redirects ) == 0 ) {
+ return;
+ }
$wgOut->addHTML( "<div id='mw-imagepage-section-redirectstofile'>\n" );
$wgOut->addWikiMsg( 'redirectstofile',
$wgOut->addHTML( "<li>{$link}</li>\n" );
}
$wgOut->addHTML( "</ul></div>\n" );
-
}
protected function imageDupes() {
$this->loadFile();
$dupes = $this->getDuplicates();
- if ( count( $dupes ) == 0 ) return;
+ if ( count( $dupes ) == 0 ) {
+ return;
+ }
$wgOut->addHTML( "<div id='mw-imagepage-section-duplicates'>\n" );
$wgOut->addWikiMsg( 'duplicatesoffile',
public function doPurge() {
$this->loadFile();
if ( $this->img->exists() ) {
- wfDebug( "ImagePage::doPurge purging " . $this->img->getName() . "\n" );
+ 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" );
+ 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();
*/
function showError( $description ) {
global $wgOut;
- $wgOut->setPageTitle( wfMsg( "internalerror" ) );
- $wgOut->setRobotPolicy( "noindex,nofollow" );
+ $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()
*/
class ImageHistoryList {
- protected $imagePage, $img, $skin, $title, $repo, $showThumb;
+ /**
+ * @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();
. $navLinks . "\n"
. Xml::openElement( 'table', array( 'class' => 'wikitable filehistory' ) ) . "\n"
. '<tr><td></td>'
- . ( $this->current->isLocal() && ( $wgUser->isAllowed( 'delete' ) || $wgUser->isAllowed( 'deletedhistory' ) ) ? '<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>'
return "</table>\n$navLinks\n</div>\n";
}
+ /**
+ * @param $iscur
+ * @param $file File
+ * @return string
+ */
public function imageHistoryLine( $iscur, $file ) {
global $wgUser, $wgLang;
$row = $selected = '';
// Deletion link
- if ( $local && ( $wgUser->isAllowed( 'delete' ) || $wgUser->isAllowed( 'deletedhistory' ) ) ) {
+ if ( $local && ( $wgUser->isAllowedAny( 'delete', 'deletedhistory' ) ) ) {
$row .= '<td>';
# Link to remove from history
if ( $wgUser->isAllowed( 'delete' ) ) {
$q = array( 'action' => 'delete' );
- if ( !$iscur )
+ if ( !$iscur ) {
$q['oldimage'] = $img;
+ }
$row .= $this->skin->link(
$this->title,
wfMsgHtml( $iscur ? 'filehist-deleteall' : 'filehist-deleteone' ),
# Don't link to unviewable files
$row .= '<span class="history-deleted">' . $wgLang->timeAndDate( $timestamp, true ) . '</span>';
} elseif ( $file->isDeleted( File::DELETED_FILE ) ) {
- $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' )
- );
+ 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 );
// Image dimensions + size
$row .= '<td>';
$row .= htmlspecialchars( $file->getDimensionsString() );
- $row .= " <span style='white-space: nowrap;'>(" . $this->skin->formatSize( $file->getSize() ) . ')</span>';
+ $row .= ' <span style="white-space: nowrap;">(' . $this->skin->formatSize( $file->getSize() ) . ')</span>';
$row .= '</td>';
// Uploading user
$row .= '<td>';
- if ( $local ) {
- // Hide deleted usernames
- if ( $file->isDeleted( File::DELETED_USER ) ) {
- $row .= '<span class="history-deleted">' . wfMsgHtml( 'rev-deleted-user' ) . '</span>';
+ // 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 .= $this->skin->userLink( $user, $usertext ) . " <span style='white-space: nowrap;'>" .
- $this->skin->userToolLinks( $user, $usertext ) . "</span>";
+ $row .= htmlspecialchars( $usertext );
}
- } else {
- $row .= htmlspecialchars( $usertext );
}
$row .= '</td><td>';
}
$row .= '</td>';
+ $rowClass = null;
wfRunHooks( 'ImagePageFileHistoryLine', array( $this, $file, &$row, &$rowClass ) );
- $classAttr = $rowClass ? " class='$rowClass'" : "";
+ $classAttr = $rowClass ? " class='$rowClass'" : '';
return "<tr{$classAttr}>{$row}</tr>\n";
}
+ /**
+ * @param $file File
+ * @return string
+ */
protected function getThumbForLine( $file ) {
global $wgLang;
'file-link' => true,
);
- if ( !$thumbnail ) return wfMsgHtml( 'filehist-nothumb' );
+ 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;
$s .= $list->imageHistoryLine( !$file->isOld(), $file );
}
$s .= $list->endImageHistoryList( $navLink );
+
+ if ( $list->getPreventClickjacking() ) {
+ $this->preventClickjacking();
+ }
}
return $s;
}
function doQuery() {
- if ( $this->mQueryDone ) return;
+ if ( $this->mQueryDone ) {
+ return;
+ }
$this->mImg = $this->mImagePage->getFile(); // ensure loading
if ( !$this->mImg->exists() ) {
return;
}
$this->mQueryDone = true;
}
+
+ protected function preventClickjacking( $enable = true ) {
+ $this->preventClickjacking = $enable;
+ }
+
+ public function getPreventClickjacking() {
+ return $this->preventClickjacking;
+ }
+
}