MediaWiki  REL1_19
ImagePage.php
Go to the documentation of this file.
00001 <?php
00007 class ImagePage extends Article {
00008 
00012         private $displayImg;
00016         private $repo;
00017         private $fileLoaded;
00018 
00019         var $mExtraDescription = false;
00020 
00025         protected function newPage( Title $title ) {
00026                 // Overload mPage with a file-specific page
00027                 return new WikiFilePage( $title );
00028         }
00029 
00034         public static function newFromID( $id ) {
00035                 $t = Title::newFromID( $id );
00036                 # @todo FIXME: Doesn't inherit right
00037                 return $t == null ? null : new self( $t );
00038                 # return $t == null ? null : new static( $t ); // PHP 5.3
00039         }
00040 
00045         public function setFile( $file ) {
00046                 $this->mPage->setFile( $file );
00047                 $this->displayImg = $file;
00048                 $this->fileLoaded = true;
00049         }
00050 
00051         protected function loadFile() {
00052                 if ( $this->fileLoaded ) {
00053                         return true;
00054                 }
00055                 $this->fileLoaded = true;
00056 
00057                 $this->displayImg = $img = false;
00058                 wfRunHooks( 'ImagePageFindFile', array( $this, &$img, &$this->displayImg ) );
00059                 if ( !$img ) { // not set by hook?
00060                         $img = wfFindFile( $this->getTitle() );
00061                         if ( !$img ) {
00062                                 $img = wfLocalFile( $this->getTitle() );
00063                         }
00064                 }
00065                 $this->mPage->setFile( $img );
00066                 if ( !$this->displayImg ) { // not set by hook?
00067                         $this->displayImg = $img;
00068                 }
00069                 $this->repo = $img->getRepo();
00070         }
00071 
00076         public function render() {
00077                 global $wgOut;
00078                 $wgOut->setArticleBodyOnly( true );
00079                 parent::view();
00080         }
00081 
00082         public function view() {
00083                 global $wgOut, $wgShowEXIF, $wgRequest, $wgUser;
00084 
00085                 $diff = $wgRequest->getVal( 'diff' );
00086                 $diffOnly = $wgRequest->getBool( 'diffonly', $wgUser->getOption( 'diffonly' ) );
00087 
00088                 if ( $this->getTitle()->getNamespace() != NS_FILE || ( isset( $diff ) && $diffOnly ) ) {
00089                         return parent::view();
00090                 }
00091 
00092                 $this->loadFile();
00093 
00094                 if ( $this->getTitle()->getNamespace() == NS_FILE && $this->mPage->getFile()->getRedirected() ) {
00095                         if ( $this->getTitle()->getDBkey() == $this->mPage->getFile()->getName() || isset( $diff ) ) {
00096                                 // mTitle is the same as the redirect target so ask Article
00097                                 // to perform the redirect for us.
00098                                 $wgRequest->setVal( 'diffonly', 'true' );
00099                                 return parent::view();
00100                         } else {
00101                                 // mTitle is not the same as the redirect target so it is
00102                                 // probably the redirect page itself. Fake the redirect symbol
00103                                 $wgOut->setPageTitle( $this->getTitle()->getPrefixedText() );
00104                                 $wgOut->addHTML( $this->viewRedirect( Title::makeTitle( NS_FILE, $this->mPage->getFile()->getName() ),
00105                                         /* $appendSubtitle */ true, /* $forceKnown */ true ) );
00106                                 $this->mPage->doViewUpdates( $this->getContext()->getUser() );
00107                                 return;
00108                         }
00109                 }
00110 
00111                 if ( $wgShowEXIF && $this->displayImg->exists() ) {
00112                         // @todo FIXME: Bad interface, see note on MediaHandler::formatMetadata().
00113                         $formattedMetadata = $this->displayImg->formatMetadata();
00114                         $showmeta = $formattedMetadata !== false;
00115                 } else {
00116                         $showmeta = false;
00117                 }
00118 
00119                 if ( !$diff && $this->displayImg->exists() ) {
00120                         $wgOut->addHTML( $this->showTOC( $showmeta ) );
00121                 }
00122 
00123                 if ( !$diff ) {
00124                         $this->openShowImage();
00125                 }
00126 
00127                 # No need to display noarticletext, we use our own message, output in openShowImage()
00128                 if ( $this->mPage->getID() ) {
00129                         # NS_FILE is in the user language, but this section (the actual wikitext)
00130                         # should be in page content language
00131                         $pageLang = $this->getTitle()->getPageLanguage();
00132                         $wgOut->addHTML( Xml::openElement( 'div', array( 'id' => 'mw-imagepage-content',
00133                                 'lang' => $pageLang->getCode(), 'dir' => $pageLang->getDir(),
00134                                 'class' => 'mw-content-'.$pageLang->getDir() ) ) );
00135                         parent::view();
00136                         $wgOut->addHTML( Xml::closeElement( 'div' ) );
00137                 } else {
00138                         # Just need to set the right headers
00139                         $wgOut->setArticleFlag( true );
00140                         $wgOut->setPageTitle( $this->getTitle()->getPrefixedText() );
00141                         $this->mPage->doViewUpdates( $this->getContext()->getUser() );
00142                 }
00143 
00144                 # Show shared description, if needed
00145                 if ( $this->mExtraDescription ) {
00146                         $fol = wfMessage( 'shareddescriptionfollows' );
00147                         if ( !$fol->isDisabled() ) {
00148                                 $wgOut->addWikiText( $fol->plain() );
00149                         }
00150                         $wgOut->addHTML( '<div id="shared-image-desc">' . $this->mExtraDescription . "</div>\n" );
00151                 }
00152 
00153                 $this->closeShowImage();
00154                 $this->imageHistory();
00155                 // TODO: Cleanup the following
00156 
00157                 $wgOut->addHTML( Xml::element( 'h2',
00158                         array( 'id' => 'filelinks' ),
00159                         wfMsg( 'imagelinks' ) ) . "\n" );
00160                 $this->imageDupes();
00161                 # @todo FIXME: For some freaky reason, we can't redirect to foreign images.
00162                 # Yet we return metadata about the target. Definitely an issue in the FileRepo
00163                 $this->imageLinks();
00164 
00165                 # Allow extensions to add something after the image links
00166                 $html = '';
00167                 wfRunHooks( 'ImagePageAfterImageLinks', array( $this, &$html ) );
00168                 if ( $html ) {
00169                         $wgOut->addHTML( $html );
00170                 }
00171 
00172                 if ( $showmeta ) {
00173                         $wgOut->addHTML( Xml::element( 'h2', array( 'id' => 'metadata' ), wfMsg( 'metadata' ) ) . "\n" );
00174                         $wgOut->addWikiText( $this->makeMetadataTable( $formattedMetadata ) );
00175                         $wgOut->addModules( array( 'mediawiki.action.view.metadata' ) );
00176                 }
00177 
00178                 // Add remote Filepage.css
00179                 if( !$this->repo->isLocal() ) {
00180                         $css = $this->repo->getDescriptionStylesheetUrl();
00181                         if ( $css ) {
00182                                 $wgOut->addStyle( $css );
00183                         }
00184                 }
00185                 // always show the local local Filepage.css, bug 29277
00186                 $wgOut->addModuleStyles( 'filepage' );
00187         }
00188 
00192         public function getDisplayedFile() {
00193                 $this->loadFile();
00194                 return $this->displayImg;
00195         }
00196 
00203         protected function showTOC( $metadata ) {
00204                 $r = array(
00205                         '<li><a href="#file">' . wfMsgHtml( 'file-anchor-link' ) . '</a></li>',
00206                         '<li><a href="#filehistory">' . wfMsgHtml( 'filehist' ) . '</a></li>',
00207                         '<li><a href="#filelinks">' . wfMsgHtml( 'imagelinks' ) . '</a></li>',
00208                 );
00209                 if ( $metadata ) {
00210                         $r[] = '<li><a href="#metadata">' . wfMsgHtml( 'metadata' ) . '</a></li>';
00211                 }
00212 
00213                 wfRunHooks( 'ImagePageShowTOC', array( $this, &$r ) );
00214 
00215                 return '<ul id="filetoc">' . implode( "\n", $r ) . '</ul>';
00216         }
00217 
00226         protected function makeMetadataTable( $metadata ) {
00227                 $r = "<div class=\"mw-imagepage-section-metadata\">";
00228                 $r .= wfMsgNoTrans( 'metadata-help' );
00229                 $r .= "<table id=\"mw_metadata\" class=\"mw_metadata\">\n";
00230                 foreach ( $metadata as $type => $stuff ) {
00231                         foreach ( $stuff as $v ) {
00232                                 # @todo FIXME: Why is this using escapeId for a class?!
00233                                 $class = Sanitizer::escapeId( $v['id'] );
00234                                 if ( $type == 'collapsed' ) {
00235                                         $class .= ' collapsable';
00236                                 }
00237                                 $r .= "<tr class=\"$class\">\n";
00238                                 $r .= "<th>{$v['name']}</th>\n";
00239                                 $r .= "<td>{$v['value']}</td>\n</tr>";
00240                         }
00241                 }
00242                 $r .= "</table>\n</div>\n";
00243                 return $r;
00244         }
00245 
00253         public function getContent() {
00254                 $this->loadFile();
00255                 if ( $this->mPage->getFile() && !$this->mPage->getFile()->isLocal() && 0 == $this->getID() ) {
00256                         return '';
00257                 }
00258                 return parent::getContent();
00259         }
00260 
00261         protected function openShowImage() {
00262                 global $wgOut, $wgUser, $wgImageLimits, $wgRequest,
00263                         $wgLang, $wgEnableUploads, $wgSend404Code;
00264 
00265                 $this->loadFile();
00266 
00267                 $sizeSel = intval( $wgUser->getOption( 'imagesize' ) );
00268                 if ( !isset( $wgImageLimits[$sizeSel] ) ) {
00269                         $sizeSel = User::getDefaultOption( 'imagesize' );
00270 
00271                         // The user offset might still be incorrect, specially if
00272                         // $wgImageLimits got changed (see bug #8858).
00273                         if ( !isset( $wgImageLimits[$sizeSel] ) ) {
00274                                 // Default to the first offset in $wgImageLimits
00275                                 $sizeSel = 0;
00276                         }
00277                 }
00278                 $max = $wgImageLimits[$sizeSel];
00279                 $maxWidth = $max[0];
00280                 $maxHeight = $max[1];
00281                 $dirmark = $wgLang->getDirMark();
00282 
00283                 if ( $this->displayImg->exists() ) {
00284                         # image
00285                         $page = $wgRequest->getIntOrNull( 'page' );
00286                         if ( is_null( $page ) ) {
00287                                 $params = array();
00288                                 $page = 1;
00289                         } else {
00290                                 $params = array( 'page' => $page );
00291                         }
00292                         $width_orig = $this->displayImg->getWidth( $page );
00293                         $width = $width_orig;
00294                         $height_orig = $this->displayImg->getHeight( $page );
00295                         $height = $height_orig;
00296 
00297                         $longDesc = wfMsg( 'parentheses', $this->displayImg->getLongDesc() );
00298 
00299                         wfRunHooks( 'ImageOpenShowImageInlineBefore', array( &$this, &$wgOut ) );
00300 
00301                         if ( $this->displayImg->allowInlineDisplay() ) {
00302                                 # image
00303 
00304                                 # "Download high res version" link below the image
00305                                 # $msgsize = wfMsgHtml( 'file-info-size', $width_orig, $height_orig, Linker::formatSize( $this->displayImg->getSize() ), $mime );
00306                                 # We'll show a thumbnail of this image
00307                                 if ( $width > $maxWidth || $height > $maxHeight ) {
00308                                         # Calculate the thumbnail size.
00309                                         # First case, the limiting factor is the width, not the height.
00310                                         if ( $width / $height >= $maxWidth / $maxHeight ) {
00311                                                 $height = round( $height * $maxWidth / $width );
00312                                                 $width = $maxWidth;
00313                                                 # Note that $height <= $maxHeight now.
00314                                         } else {
00315                                                 $newwidth = floor( $width * $maxHeight / $height );
00316                                                 $height = round( $height * $newwidth / $width );
00317                                                 $width = $newwidth;
00318                                                 # Note that $height <= $maxHeight now, but might not be identical
00319                                                 # because of rounding.
00320                                         }
00321                                         $msgbig  = wfMsgHtml( 'show-big-image' );
00322                                         $otherSizes = array();
00323                                         foreach ( $wgImageLimits as $size ) {
00324                                                 if ( $size[0] < $width_orig && $size[1] < $height_orig &&
00325                                                                 $size[0] != $width && $size[1] != $height ) {
00326                                                         $otherSizes[] = $this->makeSizeLink( $params, $size[0], $size[1] );
00327                                                 }
00328                                         }
00329                                         $msgsmall = wfMessage( 'show-big-image-preview' )->
00330                                                 rawParams( $this->makeSizeLink( $params, $width, $height ) )->
00331                                                 parse();
00332                                         if ( count( $otherSizes ) && $this->displayImg->getRepo()->canTransformVia404() ) {
00333                                                 $msgsmall .= ' ' .
00334                                                 Html::rawElement( 'span', array( 'class' => 'mw-filepage-other-resolutions' ),
00335                                                         wfMessage( 'show-big-image-other' )->rawParams( $wgLang->pipeList( $otherSizes ) )->
00336                                                         params( count( $otherSizes ) )->parse()
00337                                                 );
00338                                         }
00339                                 } elseif ( $width == 0 && $height == 0 ){
00340                                         # Some sort of audio file that doesn't have dimensions
00341                                         # Don't output a no hi res message for such a file
00342                                         $msgsmall = '';
00343                                 } else {
00344                                         # Image is small enough to show full size on image page
00345                                         $msgsmall = wfMessage( 'file-nohires' )->parse();
00346                                 }
00347 
00348                                 $params['width'] = $width;
00349                                 $params['height'] = $height;
00350                                 $thumbnail = $this->displayImg->transform( $params );
00351 
00352                                 $showLink = true;
00353                                 $anchorclose = Html::rawElement( 'div', array( 'class' => 'mw-filepage-resolutioninfo' ), $msgsmall );
00354 
00355                                 $isMulti = $this->displayImg->isMultipage() && $this->displayImg->pageCount() > 1;
00356                                 if ( $isMulti ) {
00357                                         $wgOut->addHTML( '<table class="multipageimage"><tr><td>' );
00358                                 }
00359 
00360                                 if ( $thumbnail ) {
00361                                         $options = array(
00362                                                 'alt' => $this->displayImg->getTitle()->getPrefixedText(),
00363                                                 'file-link' => true,
00364                                         );
00365                                         $wgOut->addHTML( '<div class="fullImageLink" id="file">' .
00366                                                 $thumbnail->toHtml( $options ) .
00367                                                 $anchorclose . "</div>\n" );
00368                                 }
00369 
00370                                 if ( $isMulti ) {
00371                                         $count = $this->displayImg->pageCount();
00372 
00373                                         if ( $page > 1 ) {
00374                                                 $label = $wgOut->parse( wfMsg( 'imgmultipageprev' ), false );
00375                                                 $link = Linker::link(
00376                                                         $this->getTitle(),
00377                                                         $label,
00378                                                         array(),
00379                                                         array( 'page' => $page - 1 ),
00380                                                         array( 'known', 'noclasses' )
00381                                                 );
00382                                                 $thumb1 = Linker::makeThumbLinkObj( $this->getTitle(), $this->displayImg, $link, $label, 'none',
00383                                                         array( 'page' => $page - 1 ) );
00384                                         } else {
00385                                                 $thumb1 = '';
00386                                         }
00387 
00388                                         if ( $page < $count ) {
00389                                                 $label = wfMsg( 'imgmultipagenext' );
00390                                                 $link = Linker::link(
00391                                                         $this->getTitle(),
00392                                                         $label,
00393                                                         array(),
00394                                                         array( 'page' => $page + 1 ),
00395                                                         array( 'known', 'noclasses' )
00396                                                 );
00397                                                 $thumb2 = Linker::makeThumbLinkObj( $this->getTitle(), $this->displayImg, $link, $label, 'none',
00398                                                         array( 'page' => $page + 1 ) );
00399                                         } else {
00400                                                 $thumb2 = '';
00401                                         }
00402 
00403                                         global $wgScript;
00404 
00405                                         $formParams = array(
00406                                                 'name' => 'pageselector',
00407                                                 'action' => $wgScript,
00408                                                 'onchange' => 'document.pageselector.submit();',
00409                                         );
00410                                         $options = array();
00411                                         for ( $i = 1; $i <= $count; $i++ ) {
00412                                                 $options[] = Xml::option( $wgLang->formatNum( $i ), $i, $i == $page );
00413                                         }
00414                                         $select = Xml::tags( 'select',
00415                                                 array( 'id' => 'pageselector', 'name' => 'page' ),
00416                                                 implode( "\n", $options ) );
00417 
00418                                         $wgOut->addHTML(
00419                                                 '</td><td><div class="multipageimagenavbox">' .
00420                                                 Xml::openElement( 'form', $formParams ) .
00421                                                 Html::hidden( 'title', $this->getTitle()->getPrefixedDBkey() ) .
00422                                                 wfMsgExt( 'imgmultigoto', array( 'parseinline', 'replaceafter' ), $select ) .
00423                                                 Xml::submitButton( wfMsg( 'imgmultigo' ) ) .
00424                                                 Xml::closeElement( 'form' ) .
00425                                                 "<hr />$thumb1\n$thumb2<br style=\"clear: both\" /></div></td></tr></table>"
00426                                         );
00427                                 }
00428                         } else {
00429                                 # if direct link is allowed but it's not a renderable image, show an icon.
00430                                 if ( $this->displayImg->isSafeFile() ) {
00431                                         $icon = $this->displayImg->iconThumb();
00432 
00433                                         $wgOut->addHTML( '<div class="fullImageLink" id="file">' .
00434                                                 $icon->toHtml( array( 'file-link' => true ) ) .
00435                                                 "</div>\n" );
00436                                 }
00437 
00438                                 $showLink = true;
00439                         }
00440 
00441                         if ( $showLink ) {
00442                                 $filename = wfEscapeWikiText( $this->displayImg->getName() );
00443                                 $linktext = $filename;
00444                                 if ( isset( $msgbig ) ) {
00445                                         $linktext = wfEscapeWikiText( $msgbig );
00446                                 }
00447                                 $medialink = "[[Media:$filename|$linktext]]";
00448 
00449                                 if ( !$this->displayImg->isSafeFile() ) {
00450                                         $warning = wfMsgNoTrans( 'mediawarning' );
00451                                         $wgOut->addWikiText( <<<EOT
00452 <div class="fullMedia"><span class="dangerousLink">{$medialink}</span>$dirmark <span class="fileInfo">$longDesc</span></div>
00453 <div class="mediaWarning">$warning</div>
00454 EOT
00455                                                 );
00456                                 } else {
00457                                         $wgOut->addWikiText( <<<EOT
00458 <div class="fullMedia">{$medialink}{$dirmark} <span class="fileInfo">$longDesc</span>
00459 </div>
00460 EOT
00461                                         );
00462                                 }
00463                         }
00464 
00465                         if ( !$this->displayImg->isLocal() ) {
00466                                 $this->printSharedImageText();
00467                         }
00468                 } else {
00469                         # Image does not exist
00470                         if ( $wgEnableUploads && $wgUser->isAllowed( 'upload' ) ) {
00471                                 // Only show an upload link if the user can upload
00472                                 $uploadTitle = SpecialPage::getTitleFor( 'Upload' );
00473                                 $nofile = array(
00474                                         'filepage-nofile-link',
00475                                         $uploadTitle->getFullURL( array( 'wpDestFile' => $this->mPage->getFile()->getName() ) )
00476                                 );
00477                         } else {
00478                                 $nofile = 'filepage-nofile';
00479                         }
00480                         // Note, if there is an image description page, but
00481                         // no image, then this setRobotPolicy is overriden
00482                         // by Article::View().
00483                         $wgOut->setRobotPolicy( 'noindex,nofollow' );
00484                         $wgOut->wrapWikiMsg( "<div id='mw-imagepage-nofile' class='plainlinks'>\n$1\n</div>", $nofile );
00485                         if ( !$this->getID() && $wgSend404Code ) {
00486                                 // If there is no image, no shared image, and no description page,
00487                                 // output a 404, to be consistent with articles.
00488                                 $wgRequest->response()->header( 'HTTP/1.1 404 Not Found' );
00489                         }
00490                 }
00491                 $wgOut->setFileVersion( $this->displayImg );
00492         }
00493 
00501         private function makeSizeLink( $params, $width, $height ) {
00502                 $params['width'] = $width;
00503                 $params['height'] = $height;
00504                 $thumbnail = $this->displayImg->transform( $params );
00505                 if ( $thumbnail && !$thumbnail->isError() ) {
00506                         return Html::rawElement( 'a', array(
00507                                 'href' => $thumbnail->getUrl(),
00508                                 'class' => 'mw-thumbnail-link'
00509                                 ), wfMessage( 'show-big-image-size' )->numParams(
00510                                         $thumbnail->getWidth(), $thumbnail->getHeight()
00511                                 )->parse() );
00512                 } else {
00513                         return '';
00514                 }
00515         }
00516 
00520         protected function printSharedImageText() {
00521                 global $wgOut;
00522 
00523                 $this->loadFile();
00524 
00525                 $descUrl = $this->mPage->getFile()->getDescriptionUrl();
00526                 $descText = $this->mPage->getFile()->getDescriptionText();
00527 
00528                 /* Add canonical to head if there is no local page for this shared file */
00529                 if( $descUrl && $this->mPage->getID() == 0 ) {
00530                         $wgOut->addLink( array( 'rel' => 'canonical', 'href' => $descUrl ) );
00531                 }
00532 
00533                 $wrap = "<div class=\"sharedUploadNotice\">\n$1\n</div>\n";
00534                 $repo = $this->mPage->getFile()->getRepo()->getDisplayName();
00535 
00536                 if ( $descUrl && $descText && wfMsgNoTrans( 'sharedupload-desc-here' ) !== '-'  ) {
00537                         $wgOut->wrapWikiMsg( $wrap, array( 'sharedupload-desc-here', $repo, $descUrl ) );
00538                 } elseif ( $descUrl && wfMsgNoTrans( 'sharedupload-desc-there' ) !== '-' ) {
00539                         $wgOut->wrapWikiMsg( $wrap, array( 'sharedupload-desc-there', $repo, $descUrl ) );
00540                 } else {
00541                         $wgOut->wrapWikiMsg( $wrap, array( 'sharedupload', $repo ), ''/*BACKCOMPAT*/ );
00542                 }
00543 
00544                 if ( $descText ) {
00545                         $this->mExtraDescription = $descText;
00546                 }
00547         }
00548 
00549         public function getUploadUrl() {
00550                 $this->loadFile();
00551                 $uploadTitle = SpecialPage::getTitleFor( 'Upload' );
00552                 return $uploadTitle->getFullURL( array(
00553                         'wpDestFile' => $this->mPage->getFile()->getName(),
00554                         'wpForReUpload' => 1
00555                  ) );
00556         }
00557 
00562         protected function uploadLinksBox() {
00563                 global $wgUser, $wgOut, $wgEnableUploads, $wgUseExternalEditor;
00564 
00565                 if ( !$wgEnableUploads ) {
00566                         return;
00567                 }
00568 
00569                 $this->loadFile();
00570                 if ( !$this->mPage->getFile()->isLocal() ) {
00571                         return;
00572                 }
00573 
00574                 $wgOut->addHTML( "<br /><ul>\n" );
00575 
00576                 # "Upload a new version of this file" link
00577                 if ( UploadBase::userCanReUpload( $wgUser, $this->mPage->getFile()->name ) ) {
00578                         $ulink = Linker::makeExternalLink( $this->getUploadUrl(), wfMsg( 'uploadnewversion-linktext' ) );
00579                         $wgOut->addHTML( "<li id=\"mw-imagepage-reupload-link\"><div class=\"plainlinks\">{$ulink}</div></li>\n" );
00580                 }
00581 
00582                 # External editing link
00583                 if ( $wgUseExternalEditor ) {
00584                         $elink = Linker::link(
00585                                 $this->getTitle(),
00586                                 wfMsgHtml( 'edit-externally' ),
00587                                 array(),
00588                                 array(
00589                                         'action' => 'edit',
00590                                         'externaledit' => 'true',
00591                                         'mode' => 'file'
00592                                 ),
00593                                 array( 'known', 'noclasses' )
00594                         );
00595                         $wgOut->addHTML(
00596                                 '<li id="mw-imagepage-edit-external">' . $elink . ' <small>' .
00597                                 wfMsgExt( 'edit-externally-help', array( 'parseinline' ) ) .
00598                                 "</small></li>\n"
00599                         );
00600                 }
00601 
00602                 $wgOut->addHTML( "</ul>\n" );
00603         }
00604 
00605         protected function closeShowImage() { } # For overloading
00606 
00611         protected function imageHistory() {
00612                 global $wgOut;
00613 
00614                 $this->loadFile();
00615                 $pager = new ImageHistoryPseudoPager( $this );
00616                 $wgOut->addHTML( $pager->getBody() );
00617                 $wgOut->preventClickjacking( $pager->getPreventClickjacking() );
00618 
00619                 $this->mPage->getFile()->resetHistory(); // free db resources
00620 
00621                 # Exist check because we don't want to show this on pages where an image
00622                 # doesn't exist along with the noimage message, that would suck. -ævar
00623                 if ( $this->mPage->getFile()->exists() ) {
00624                         $this->uploadLinksBox();
00625                 }
00626         }
00627 
00633         protected function queryImageLinks( $target, $limit ) {
00634                 $dbr = wfGetDB( DB_SLAVE );
00635 
00636                 return $dbr->select(
00637                         array( 'imagelinks', 'page' ),
00638                         array( 'page_namespace', 'page_title', 'page_is_redirect', 'il_to' ),
00639                         array( 'il_to' => $target, 'il_from = page_id' ),
00640                         __METHOD__,
00641                         array( 'LIMIT' => $limit + 1, 'ORDER BY' => 'il_from', )
00642                 );
00643         }
00644 
00645         protected function imageLinks() {
00646                 global $wgOut, $wgLang;
00647 
00648                 $limit = 100;
00649 
00650                 $res = $this->queryImageLinks( $this->getTitle()->getDbKey(), $limit + 1);
00651                 $rows = array();
00652                 $redirects = array();
00653                 foreach ( $res as $row ) {
00654                         if ( $row->page_is_redirect ) {
00655                                 $redirects[$row->page_title] = array();
00656                         }
00657                         $rows[] = $row;
00658                 }
00659                 $count = count( $rows );
00660 
00661                 $hasMore = $count > $limit;
00662                 if ( !$hasMore && count( $redirects ) ) {
00663                         $res = $this->queryImageLinks( array_keys( $redirects ),
00664                                 $limit - count( $rows ) + 1 );
00665                         foreach ( $res as $row ) {
00666                                 $redirects[$row->il_to][] = $row;
00667                                 $count++;
00668                         }
00669                         $hasMore = ( $res->numRows() + count( $rows ) ) > $limit;
00670                 }
00671 
00672                 if ( $count == 0 ) {
00673                         $wgOut->wrapWikiMsg(
00674                                 Html::rawElement( 'div',
00675                                         array( 'id' => 'mw-imagepage-nolinkstoimage' ), "\n$1\n" ),
00676                                 'nolinkstoimage'
00677                         );
00678                         return;
00679                 }
00680 
00681                 $wgOut->addHTML( "<div id='mw-imagepage-section-linkstoimage'>\n" );
00682                 if ( !$hasMore ) {
00683                         $wgOut->addWikiMsg( 'linkstoimage', $count );
00684                 } else {
00685                         // More links than the limit. Add a link to [[Special:Whatlinkshere]]
00686                         $wgOut->addWikiMsg( 'linkstoimage-more',
00687                                 $wgLang->formatNum( $limit ),
00688                                 $this->getTitle()->getPrefixedDBkey()
00689                         );
00690                 }
00691 
00692                 $wgOut->addHTML(
00693                         Html::openElement( 'ul',
00694                                 array( 'class' => 'mw-imagepage-linkstoimage' ) ) . "\n"
00695                 );
00696                 $count = 0;
00697 
00698                 // Sort the list by namespace:title
00699                 usort( $rows, array( $this, 'compare' ) );
00700 
00701                 // Create links for every element
00702                 $currentCount = 0;
00703                 foreach( $rows as $element ) {
00704                         $currentCount++;
00705                         if ( $currentCount > $limit ) {
00706                                 break;
00707                         }
00708 
00709                         $link = Linker::linkKnown( Title::makeTitle( $element->page_namespace, $element->page_title ) );
00710                         if ( !isset( $redirects[$element->page_title] ) ) {
00711                                 $liContents = $link;
00712                         } else {
00713                                 $ul = "<ul class='mw-imagepage-redirectstofile'>\n";
00714                                 foreach ( $redirects[$element->page_title] as $row ) {
00715                                         $currentCount++;
00716                                         if ( $currentCount > $limit ) {
00717                                                 break;
00718                                         }
00719 
00720                                         $link2 = Linker::linkKnown( Title::makeTitle( $row->page_namespace, $row->page_title ) );
00721                                         $ul .= Html::rawElement(
00722                                                 'li',
00723                                                 array( 'id' => 'mw-imagepage-linkstoimage-ns' . $element->page_namespace ),
00724                                                 $link2
00725                                                 ) . "\n";
00726                                 }
00727                                 $ul .= '</ul>';
00728                                 $liContents = wfMessage( 'linkstoimage-redirect' )->rawParams(
00729                                         $link, $ul )->parse();
00730                         }
00731                         $wgOut->addHTML( Html::rawElement(
00732                                         'li',
00733                                         array( 'id' => 'mw-imagepage-linkstoimage-ns' . $element->page_namespace ),
00734                                         $liContents
00735                                 ) . "\n"
00736                         );
00737 
00738                 };
00739                 $wgOut->addHTML( Html::closeElement( 'ul' ) . "\n" );
00740                 $res->free();
00741 
00742                 // Add a links to [[Special:Whatlinkshere]]
00743                 if ( $count > $limit ) {
00744                         $wgOut->addWikiMsg( 'morelinkstoimage', $this->getTitle()->getPrefixedDBkey() );
00745                 }
00746                 $wgOut->addHTML( Html::closeElement( 'div' ) . "\n" );
00747         }
00748 
00749         protected function imageDupes() {
00750                 global $wgOut, $wgLang;
00751 
00752                 $this->loadFile();
00753 
00754                 $dupes = $this->mPage->getDuplicates();
00755                 if ( count( $dupes ) == 0 ) {
00756                         return;
00757                 }
00758 
00759                 $wgOut->addHTML( "<div id='mw-imagepage-section-duplicates'>\n" );
00760                 $wgOut->addWikiMsg( 'duplicatesoffile',
00761                         $wgLang->formatNum( count( $dupes ) ), $this->getTitle()->getDBkey()
00762                 );
00763                 $wgOut->addHTML( "<ul class='mw-imagepage-duplicates'>\n" );
00764 
00768                 foreach ( $dupes as $file ) {
00769                         $fromSrc = '';
00770                         if ( $file->isLocal() ) {
00771                                 $link = Linker::link(
00772                                         $file->getTitle(),
00773                                         null,
00774                                         array(),
00775                                         array(),
00776                                         array( 'known', 'noclasses' )
00777                                 );
00778                         } else {
00779                                 $link = Linker::makeExternalLink( $file->getDescriptionUrl(),
00780                                         $file->getTitle()->getPrefixedText() );
00781                                 $fromSrc = wfMsg( 'shared-repo-from', $file->getRepo()->getDisplayName() );
00782                         }
00783                         $wgOut->addHTML( "<li>{$link} {$fromSrc}</li>\n" );
00784                 }
00785                 $wgOut->addHTML( "</ul></div>\n" );
00786         }
00787 
00791         public function delete() {
00792                 $file = $this->mPage->getFile();
00793                 if ( !$file->exists() || !$file->isLocal() || $file->getRedirected() ) {
00794                         // Standard article deletion
00795                         parent::delete();
00796                         return;
00797                 }
00798 
00799                 $deleter = new FileDeleteForm( $file );
00800                 $deleter->execute();
00801         }
00802 
00808         function showError( $description ) {
00809                 global $wgOut;
00810                 $wgOut->setPageTitle( wfMessage( 'internalerror' ) );
00811                 $wgOut->setRobotPolicy( 'noindex,nofollow' );
00812                 $wgOut->setArticleRelated( false );
00813                 $wgOut->enableClientCache( false );
00814                 $wgOut->addWikiText( $description );
00815         }
00816 
00825         protected function compare( $a, $b ) {
00826                 if ( $a->page_namespace == $b->page_namespace ) {
00827                         return strcmp( $a->page_title, $b->page_title );
00828                 } else {
00829                         return $a->page_namespace - $b->page_namespace;
00830                 }
00831         }
00832 }
00833 
00839 class ImageHistoryList {
00840 
00844         protected $title;
00845 
00849         protected $img;
00850 
00854         protected $imagePage;
00855 
00859         protected $current;
00860 
00861         protected $repo, $showThumb;
00862         protected $preventClickjacking = false;
00863 
00867         public function __construct( $imagePage ) {
00868                 global $wgShowArchiveThumbnails;
00869                 $this->current = $imagePage->getFile();
00870                 $this->img = $imagePage->getDisplayedFile();
00871                 $this->title = $imagePage->getTitle();
00872                 $this->imagePage = $imagePage;
00873                 $this->showThumb = $wgShowArchiveThumbnails && $this->img->canRender();
00874         }
00875 
00879         public function getImagePage() {
00880                 return $this->imagePage;
00881         }
00882 
00886         public function getFile() {
00887                 return $this->img;
00888         }
00889 
00894         public function beginImageHistoryList( $navLinks = '' ) {
00895                 global $wgOut, $wgUser;
00896                 return Xml::element( 'h2', array( 'id' => 'filehistory' ), wfMsg( 'filehist' ) ) . "\n"
00897                         . "<div id=\"mw-imagepage-section-filehistory\">\n"
00898                         . $wgOut->parse( wfMsgNoTrans( 'filehist-help' ) )
00899                         . $navLinks . "\n"
00900                         . Xml::openElement( 'table', array( 'class' => 'wikitable filehistory' ) ) . "\n"
00901                         . '<tr><td></td>'
00902                         . ( $this->current->isLocal() && ( $wgUser->isAllowedAny( 'delete', 'deletedhistory' ) ) ? '<td></td>' : '' )
00903                         . '<th>' . wfMsgHtml( 'filehist-datetime' ) . '</th>'
00904                         . ( $this->showThumb ? '<th>' . wfMsgHtml( 'filehist-thumb' ) . '</th>' : '' )
00905                         . '<th>' . wfMsgHtml( 'filehist-dimensions' ) . '</th>'
00906                         . '<th>' . wfMsgHtml( 'filehist-user' ) . '</th>'
00907                         . '<th>' . wfMsgHtml( 'filehist-comment' ) . '</th>'
00908                         . "</tr>\n";
00909         }
00910 
00915         public function endImageHistoryList( $navLinks = '' ) {
00916                 return "</table>\n$navLinks\n</div>\n";
00917         }
00918 
00924         public function imageHistoryLine( $iscur, $file ) {
00925                 global $wgUser, $wgLang, $wgContLang;
00926 
00927                 $timestamp = wfTimestamp( TS_MW, $file->getTimestamp() );
00928                 $img = $iscur ? $file->getName() : $file->getArchiveName();
00929                 $user = $file->getUser( 'id' );
00930                 $usertext = $file->getUser( 'text' );
00931                 $description = $file->getDescription();
00932 
00933                 $local = $this->current->isLocal();
00934                 $row = $selected = '';
00935 
00936                 // Deletion link
00937                 if ( $local && ( $wgUser->isAllowedAny( 'delete', 'deletedhistory' ) ) ) {
00938                         $row .= '<td>';
00939                         # Link to remove from history
00940                         if ( $wgUser->isAllowed( 'delete' ) ) {
00941                                 $q = array( 'action' => 'delete' );
00942                                 if ( !$iscur ) {
00943                                         $q['oldimage'] = $img;
00944                                 }
00945                                 $row .= Linker::link(
00946                                         $this->title,
00947                                         wfMsgHtml( $iscur ? 'filehist-deleteall' : 'filehist-deleteone' ),
00948                                         array(), $q, array( 'known' )
00949                                 );
00950                         }
00951                         # Link to hide content. Don't show useless link to people who cannot hide revisions.
00952                         $canHide = $wgUser->isAllowed( 'deleterevision' );
00953                         if ( $canHide || ( $wgUser->isAllowed( 'deletedhistory' ) && $file->getVisibility() ) ) {
00954                                 if ( $wgUser->isAllowed( 'delete' ) ) {
00955                                         $row .= '<br />';
00956                                 }
00957                                 // If file is top revision or locked from this user, don't link
00958                                 if ( $iscur || !$file->userCan( File::DELETED_RESTRICTED ) ) {
00959                                         $del = Linker::revDeleteLinkDisabled( $canHide );
00960                                 } else {
00961                                         list( $ts, $name ) = explode( '!', $img, 2 );
00962                                         $query = array(
00963                                                 'type'   => 'oldimage',
00964                                                 'target' => $this->title->getPrefixedText(),
00965                                                 'ids'    => $ts,
00966                                         );
00967                                         $del = Linker::revDeleteLink( $query,
00968                                                 $file->isDeleted( File::DELETED_RESTRICTED ), $canHide );
00969                                 }
00970                                 $row .= $del;
00971                         }
00972                         $row .= '</td>';
00973                 }
00974 
00975                 // Reversion link/current indicator
00976                 $row .= '<td>';
00977                 if ( $iscur ) {
00978                         $row .= wfMsgHtml( 'filehist-current' );
00979                 } elseif ( $local && $wgUser->isLoggedIn() && $this->title->userCan( 'edit' ) ) {
00980                         if ( $file->isDeleted( File::DELETED_FILE ) ) {
00981                                 $row .= wfMsgHtml( 'filehist-revert' );
00982                         } else {
00983                                 $row .= Linker::link(
00984                                         $this->title,
00985                                         wfMsgHtml( 'filehist-revert' ),
00986                                         array(),
00987                                         array(
00988                                                 'action' => 'revert',
00989                                                 'oldimage' => $img,
00990                                                 'wpEditToken' => $wgUser->getEditToken( $img )
00991                                         ),
00992                                         array( 'known', 'noclasses' )
00993                                 );
00994                         }
00995                 }
00996                 $row .= '</td>';
00997 
00998                 // Date/time and image link
00999                 if ( $file->getTimestamp() === $this->img->getTimestamp() ) {
01000                         $selected = "class='filehistory-selected'";
01001                 }
01002                 $row .= "<td $selected style='white-space: nowrap;'>";
01003                 if ( !$file->userCan( File::DELETED_FILE ) ) {
01004                         # Don't link to unviewable files
01005                         $row .= '<span class="history-deleted">' . $wgLang->timeanddate( $timestamp, true ) . '</span>';
01006                 } elseif ( $file->isDeleted( File::DELETED_FILE ) ) {
01007                         if ( $local ) {
01008                                 $this->preventClickjacking();
01009                                 $revdel = SpecialPage::getTitleFor( 'Revisiondelete' );
01010                                 # Make a link to review the image
01011                                 $url = Linker::link(
01012                                         $revdel,
01013                                         $wgLang->timeanddate( $timestamp, true ),
01014                                         array(),
01015                                         array(
01016                                                 'target' => $this->title->getPrefixedText(),
01017                                                 'file' => $img,
01018                                                 'token' => $wgUser->getEditToken( $img )
01019                                         ),
01020                                         array( 'known', 'noclasses' )
01021                                 );
01022                         } else {
01023                                 $url = $wgLang->timeanddate( $timestamp, true );
01024                         }
01025                         $row .= '<span class="history-deleted">' . $url . '</span>';
01026                 } else {
01027                         $url = $iscur ? $this->current->getUrl() : $this->current->getArchiveUrl( $img );
01028                         $row .= Xml::element( 'a', array( 'href' => $url ), $wgLang->timeanddate( $timestamp, true ) );
01029                 }
01030                 $row .= "</td>";
01031 
01032                 // Thumbnail
01033                 if ( $this->showThumb ) {
01034                         $row .= '<td>' . $this->getThumbForLine( $file ) . '</td>';
01035                 }
01036 
01037                 // Image dimensions + size
01038                 $row .= '<td>';
01039                 $row .= htmlspecialchars( $file->getDimensionsString() );
01040                 $row .= ' <span style="white-space: nowrap;">(' . Linker::formatSize( $file->getSize() ) . ')</span>';
01041                 $row .= '</td>';
01042 
01043                 // Uploading user
01044                 $row .= '<td>';
01045                 // Hide deleted usernames
01046                 if ( $file->isDeleted( File::DELETED_USER ) ) {
01047                         $row .= '<span class="history-deleted">' . wfMsgHtml( 'rev-deleted-user' ) . '</span>';
01048                 } else {
01049                         if ( $local ) {
01050                                 $row .= Linker::userLink( $user, $usertext ) . ' <span style="white-space: nowrap;">' .
01051                                 Linker::userToolLinks( $user, $usertext ) . '</span>';
01052                         } else {
01053                                 $row .= htmlspecialchars( $usertext );
01054                         }
01055                 }
01056                 $row .= '</td>';
01057 
01058                 // Don't show deleted descriptions
01059                 if ( $file->isDeleted( File::DELETED_COMMENT ) ) {
01060                         $row .= '<td><span class="history-deleted">' . wfMsgHtml( 'rev-deleted-comment' ) . '</span></td>';
01061                 } else {
01062                         $row .= '<td dir="' . $wgContLang->getDir() . '">' . Linker::formatComment( $description, $this->title ) . '</td>';
01063                 }
01064 
01065                 $rowClass = null;
01066                 wfRunHooks( 'ImagePageFileHistoryLine', array( $this, $file, &$row, &$rowClass ) );
01067                 $classAttr = $rowClass ? " class='$rowClass'" : '';
01068 
01069                 return "<tr{$classAttr}>{$row}</tr>\n";
01070         }
01071 
01076         protected function getThumbForLine( $file ) {
01077                 global $wgLang;
01078 
01079                 if ( $file->allowInlineDisplay() && $file->userCan( File::DELETED_FILE ) && !$file->isDeleted( File::DELETED_FILE ) ) {
01080                         $params = array(
01081                                 'width' => '120',
01082                                 'height' => '120',
01083                         );
01084                         $timestamp = wfTimestamp( TS_MW, $file->getTimestamp() );
01085 
01086                         $thumbnail = $file->transform( $params );
01087                         $options = array(
01088                                 'alt' => wfMsg( 'filehist-thumbtext',
01089                                         $wgLang->timeanddate( $timestamp, true ),
01090                                         $wgLang->date( $timestamp, true ),
01091                                         $wgLang->time( $timestamp, true ) ),
01092                                 'file-link' => true,
01093                         );
01094 
01095                         if ( !$thumbnail ) {
01096                                 return wfMsgHtml( 'filehist-nothumb' );
01097                         }
01098 
01099                         return $thumbnail->toHtml( $options );
01100                 } else {
01101                         return wfMsgHtml( 'filehist-nothumb' );
01102                 }
01103         }
01104 
01108         protected function preventClickjacking( $enable = true ) {
01109                 $this->preventClickjacking = $enable;
01110         }
01111 
01115         public function getPreventClickjacking() {
01116                 return $this->preventClickjacking;
01117         }
01118 }
01119 
01120 class ImageHistoryPseudoPager extends ReverseChronologicalPager {
01121         protected $preventClickjacking = false;
01122 
01126         protected $mImg;
01127 
01131         protected $mTitle;
01132 
01136         function __construct( $imagePage ) {
01137                 parent::__construct();
01138                 $this->mImagePage = $imagePage;
01139                 $this->mTitle = clone ( $imagePage->getTitle() );
01140                 $this->mTitle->setFragment( '#filehistory' );
01141                 $this->mImg = null;
01142                 $this->mHist = array();
01143                 $this->mRange = array( 0, 0 ); // display range
01144         }
01145 
01149         function getTitle() {
01150                 return $this->mTitle;
01151         }
01152 
01153         function getQueryInfo() {
01154                 return false;
01155         }
01156 
01160         function getIndexField() {
01161                 return '';
01162         }
01163 
01167         function formatRow( $row ) {
01168                 return '';
01169         }
01170 
01174         function getBody() {
01175                 $s = '';
01176                 $this->doQuery();
01177                 if ( count( $this->mHist ) ) {
01178                         $list = new ImageHistoryList( $this->mImagePage );
01179                         # Generate prev/next links
01180                         $navLink = $this->getNavigationBar();
01181                         $s = $list->beginImageHistoryList( $navLink );
01182                         // Skip rows there just for paging links
01183                         for ( $i = $this->mRange[0]; $i <= $this->mRange[1]; $i++ ) {
01184                                 $file = $this->mHist[$i];
01185                                 $s .= $list->imageHistoryLine( !$file->isOld(), $file );
01186                         }
01187                         $s .= $list->endImageHistoryList( $navLink );
01188 
01189                         if ( $list->getPreventClickjacking() ) {
01190                                 $this->preventClickjacking();
01191                         }
01192                 }
01193                 return $s;
01194         }
01195 
01196         function doQuery() {
01197                 if ( $this->mQueryDone ) {
01198                         return;
01199                 }
01200                 $this->mImg = $this->mImagePage->getFile(); // ensure loading
01201                 if ( !$this->mImg->exists() ) {
01202                         return;
01203                 }
01204                 $queryLimit = $this->mLimit + 1; // limit plus extra row
01205                 if ( $this->mIsBackwards ) {
01206                         // Fetch the file history
01207                         $this->mHist = $this->mImg->getHistory( $queryLimit, null, $this->mOffset, false );
01208                         // The current rev may not meet the offset/limit
01209                         $numRows = count( $this->mHist );
01210                         if ( $numRows <= $this->mLimit && $this->mImg->getTimestamp() > $this->mOffset ) {
01211                                 $this->mHist = array_merge( array( $this->mImg ), $this->mHist );
01212                         }
01213                 } else {
01214                         // The current rev may not meet the offset
01215                         if ( !$this->mOffset || $this->mImg->getTimestamp() < $this->mOffset ) {
01216                                 $this->mHist[] = $this->mImg;
01217                         }
01218                         // Old image versions (fetch extra row for nav links)
01219                         $oiLimit = count( $this->mHist ) ? $this->mLimit : $this->mLimit + 1;
01220                         // Fetch the file history
01221                         $this->mHist = array_merge( $this->mHist,
01222                                 $this->mImg->getHistory( $oiLimit, $this->mOffset, null, false ) );
01223                 }
01224                 $numRows = count( $this->mHist ); // Total number of query results
01225                 if ( $numRows ) {
01226                         # Index value of top item in the list
01227                         $firstIndex = $this->mIsBackwards ?
01228                                 $this->mHist[$numRows - 1]->getTimestamp() : $this->mHist[0]->getTimestamp();
01229                         # Discard the extra result row if there is one
01230                         if ( $numRows > $this->mLimit && $numRows > 1 ) {
01231                                 if ( $this->mIsBackwards ) {
01232                                         # Index value of item past the index
01233                                         $this->mPastTheEndIndex = $this->mHist[0]->getTimestamp();
01234                                         # Index value of bottom item in the list
01235                                         $lastIndex = $this->mHist[1]->getTimestamp();
01236                                         # Display range
01237                                         $this->mRange = array( 1, $numRows - 1 );
01238                                 } else {
01239                                         # Index value of item past the index
01240                                         $this->mPastTheEndIndex = $this->mHist[$numRows - 1]->getTimestamp();
01241                                         # Index value of bottom item in the list
01242                                         $lastIndex = $this->mHist[$numRows - 2]->getTimestamp();
01243                                         # Display range
01244                                         $this->mRange = array( 0, $numRows - 2 );
01245                                 }
01246                         } else {
01247                                 # Setting indexes to an empty string means that they will be
01248                                 # omitted if they would otherwise appear in URLs. It just so
01249                                 # happens that this  is the right thing to do in the standard
01250                                 # UI, in all the relevant cases.
01251                                 $this->mPastTheEndIndex = '';
01252                                 # Index value of bottom item in the list
01253                                 $lastIndex = $this->mIsBackwards ?
01254                                         $this->mHist[0]->getTimestamp() : $this->mHist[$numRows - 1]->getTimestamp();
01255                                 # Display range
01256                                 $this->mRange = array( 0, $numRows - 1 );
01257                         }
01258                 } else {
01259                         $firstIndex = '';
01260                         $lastIndex = '';
01261                         $this->mPastTheEndIndex = '';
01262                 }
01263                 if ( $this->mIsBackwards ) {
01264                         $this->mIsFirst = ( $numRows < $queryLimit );
01265                         $this->mIsLast = ( $this->mOffset == '' );
01266                         $this->mLastShown = $firstIndex;
01267                         $this->mFirstShown = $lastIndex;
01268                 } else {
01269                         $this->mIsFirst = ( $this->mOffset == '' );
01270                         $this->mIsLast = ( $numRows < $queryLimit );
01271                         $this->mLastShown = $lastIndex;
01272                         $this->mFirstShown = $firstIndex;
01273                 }
01274                 $this->mQueryDone = true;
01275         }
01276 
01280         protected function preventClickjacking( $enable = true ) {
01281                 $this->preventClickjacking = $enable;
01282         }
01283 
01287         public function getPreventClickjacking() {
01288                 return $this->preventClickjacking;
01289         }
01290 
01291 }