X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;ds=sidebyside;f=includes%2FImagePage.php;h=55e63baffe8912536f4e7e8bd98d48c1b8a94b41;hb=06adafb3d506a347cc7cffbe50a3c15dc8368912;hp=e593082eccc3b8e43b7269e74eb3edbb134bcb1a;hpb=f83d881fb16025437543141f5ef396c0ea3e7e19;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/ImagePage.php b/includes/ImagePage.php index e593082ecc..55e63baffe 100644 --- a/includes/ImagePage.php +++ b/includes/ImagePage.php @@ -1,22 +1,47 @@ dupes = null; + $this->repo = null; + } + + 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 @@ -25,13 +50,28 @@ class ImagePage extends Article { function render() { global $wgOut; $wgOut->setArticleBodyOnly( true ); - $wgOut->addSecondaryWikitext( $this->getContent() ); + parent::view(); } function view() { global $wgOut, $wgShowEXIF, $wgRequest, $wgUser; + $this->loadFile(); - $this->img = new Image( $this->mTitle ); + if ( $this->mTitle->getNamespace() == NS_IMAGE && $this->img->getRedirected() ) { + if ( $this->mTitle->getDBkey() == $this->img->getName() ) { + // mTitle is the same as the redirect target so ask Article + // to perform the redirect for us. + return Article::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() ); + $this->viewRedirect( Title::makeTitle( NS_IMAGE, $this->img->getName() ), + /* $overwriteSubtitle */ true, /* $forceKnown */ true ); + $this->viewUpdates(); + return; + } + } $diff = $wgRequest->getVal( 'diff' ); $diffOnly = $wgRequest->getBool( 'diffonly', $wgUser->getOption( 'diffonly' ) ); @@ -39,16 +79,16 @@ class ImagePage extends Article { if ( $this->mTitle->getNamespace() != NS_IMAGE || ( isset( $diff ) && $diffOnly ) ) return Article::view(); - if ($wgShowEXIF && $this->img->exists()) { - $exif = $this->img->getExifData(); - $showmeta = count($exif) ? true : false; + if ( $wgShowEXIF && $this->displayImg->exists() ) { + // FIXME: bad interface, see note on MediaHandler::formatMetadata(). + $formattedMetadata = $this->displayImg->formatMetadata(); + $showmeta = $formattedMetadata !== false; } else { - $exif = false; $showmeta = false; } - if ($this->img->exists()) - $wgOut->addHTML($this->showTOC($showmeta)); + if ( $this->displayImg->exists() ) + $wgOut->addHTML( $this->showTOC($showmeta) ); $this->openShowImage(); @@ -58,35 +98,120 @@ class ImagePage extends Article { } else { # Just need to set the right headers $wgOut->setArticleFlag( true ); - $wgOut->setRobotpolicy( 'index,follow' ); + $wgOut->setRobotpolicy( 'noindex,nofollow' ); $wgOut->setPageTitle( $this->mTitle->getPrefixedText() ); $this->viewUpdates(); } # Show shared description, if needed if ( $this->mExtraDescription ) { - $fol = wfMsg( 'shareddescriptionfollows' ); + $fol = wfMsgNoTrans( 'shareddescriptionfollows' ); if( $fol != '-' && !wfEmptyMsg( 'shareddescriptionfollows', $fol ) ) { $wgOut->addWikiText( $fol ); } $wgOut->addHTML( '
' . $this->mExtraDescription . '
' ); + } else { + $this->checkSharedConflict(); } $this->closeShowImage(); $this->imageHistory(); + // TODO: Cleanup the following + + $wgOut->addHTML( Xml::element( 'h2', + array( 'id' => 'filelinks' ), + wfMsg( 'imagelinks' ) ) . "\n" ); + $this->imageDupes(); + // TODO: We may want to find local images redirecting to a foreign + // file: "The following local files redirect to this file" + if ( $this->img->isLocal() ) { + $this->imageRedirects(); + } $this->imageLinks(); - if ( $exif ) { + if ( $showmeta ) { global $wgStylePath, $wgStyleVersion; $expand = htmlspecialchars( wfEscapeJsString( wfMsg( 'metadata-expand' ) ) ); $collapse = htmlspecialchars( wfEscapeJsString( wfMsg( 'metadata-collapse' ) ) ); $wgOut->addHTML( Xml::element( 'h2', array( 'id' => 'metadata' ), wfMsg( 'metadata' ) ). "\n" ); - $wgOut->addWikiText( $this->makeMetadataTable( $exif ) ); + $wgOut->addWikiText( $this->makeMetadataTable( $formattedMetadata ) ); + $wgOut->addScriptFile( 'metadata.js' ); $wgOut->addHTML( - "\n" . "\n" ); } } + + 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_IMAGE, $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_IMAGE, $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; + } + if ( !( $hash = $this->img->getSha1() ) ) { + 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 @@ -100,9 +225,9 @@ class ImagePage extends Article { global $wgLang; $r = ''; return $r; } @@ -110,66 +235,51 @@ class ImagePage extends Article { /** * Make a table with metadata to be shown in the output page. * + * FIXME: bad interface, see note on MediaHandler::formatMetadata(). + * * @access private * * @param array $exif The array containing the EXIF data * @return string */ - function makeMetadataTable( $exif ) { + function makeMetadataTable( $metadata ) { $r = wfMsg( 'metadata-help' ) . "\n\n"; $r .= "{| id=mw_metadata class=mw_metadata\n"; - $visibleFields = $this->visibleMetadataFields(); - foreach( $exif as $k => $v ) { - $tag = strtolower( $k ); - $msg = wfMsg( "exif-$tag" ); - $class = "exif-$tag"; - if( !in_array( $tag, $visibleFields ) ) { - $class .= ' collapsable'; + foreach ( $metadata as $type => $stuff ) { + foreach ( $stuff as $v ) { + $class = Sanitizer::escapeId( $v['id'] ); + if( $type == 'collapsed' ) { + $class .= ' collapsable'; + } + $r .= "|- class=\"$class\"\n"; + $r .= "!| {$v['name']}\n"; + $r .= "|| {$v['value']}\n"; } - $r .= "|- class=\"$class\"\n"; - $r .= "!| $msg\n"; - $r .= "|| $v\n"; } $r .= '|}'; return $r; } - /** - * Get a list of EXIF metadata items which should be displayed when - * the metadata table is collapsed. - * - * @return array of strings - * @access private - */ - function visibleMetadataFields() { - $fields = array(); - $lines = explode( "\n", wfMsgForContent( 'metadata-fields' ) ); - foreach( $lines as $line ) { - $matches = array(); - if( preg_match( '/^\\*\s*(.*?)\s*$/', $line, $matches ) ) { - $fields[] = $matches[1]; - } - } - return $fields; - } - /** * Overloading Article's getContent method. - * + * * Omit noarticletext if sharedupload; text will be fetched from the * shared upload server if possible. */ function getContent() { - if( $this->img && $this->img->fromSharedDirectory && 0 == $this->getID() ) { + $this->loadFile(); + if( $this->img && !$this->img->isLocal() && 0 == $this->getID() ) { return ''; } return Article::getContent(); } function openShowImage() { - global $wgOut, $wgUser, $wgImageLimits, $wgRequest, $wgLang; + global $wgOut, $wgUser, $wgImageLimits, $wgRequest, $wgLang, $wgContLang; - $full_url = $this->img->getURL(); + $this->loadFile(); + + $full_url = $this->displayImg->getURL(); $linkAttribs = false; $sizeSel = intval( $wgUser->getOption( 'imagesize') ); if( !isset( $wgImageLimits[$sizeSel] ) ) { @@ -186,8 +296,9 @@ class ImagePage extends Article { $maxWidth = $max[0]; $maxHeight = $max[1]; $sk = $wgUser->getSkin(); + $dirmark = $wgContLang->getDirMark(); - if ( $this->img->exists() ) { + if ( $this->displayImg->exists() ) { # image $page = $wgRequest->getIntOrNull( 'page' ); if ( is_null( $page ) ) { @@ -196,19 +307,22 @@ class ImagePage extends Article { } else { $params = array( 'page' => $page ); } - $width_orig = $this->img->getWidth(); + $width_orig = $this->displayImg->getWidth(); $width = $width_orig; - $height_orig = $this->img->getHeight(); + $height_orig = $this->displayImg->getHeight(); $height = $height_orig; - $mime = $this->img->getMimeType(); + $mime = $this->displayImg->getMimeType(); $showLink = false; $linkAttribs = array( 'href' => $full_url ); + $longDesc = $this->displayImg->getLongDesc(); + + wfRunHooks( 'ImageOpenShowImageInlineBefore', array( &$this , &$wgOut ) ) ; - if ( $this->img->allowInlineDisplay() and $width and $height) { + 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->img->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. @@ -226,47 +340,46 @@ class ImagePage extends Article { } $msgbig = wfMsgHtml( 'show-big-image' ); $msgsmall = wfMsgExt( 'show-big-image-thumb', - array( 'parseinline' ), $width, $height ); + array( 'parseinline' ), $wgLang->formatNum( $width ), $wgLang->formatNum( $height ) ); } else { # Image is small enough to show full size on image page - $msgbig = htmlspecialchars( $this->img->getName() ); + $msgbig = htmlspecialchars( $this->displayImg->getName() ); $msgsmall = wfMsgExt( 'file-nohires', array( 'parseinline' ) ); } $params['width'] = $width; - $thumbnail = $this->img->transform( $params ); + $thumbnail = $this->displayImg->transform( $params ); $anchorclose = "
"; - if( $this->img->mustRender() ) { + if( $this->displayImg->mustRender() ) { $showLink = true; } else { - $anchorclose .= + $anchorclose .= $msgsmall . - '
' . Xml::tags( 'a', $linkAttribs, $msgbig ) . ' ' . $msgsize; + '
' . Xml::tags( 'a', $linkAttribs, $msgbig ) . "$dirmark " . $longDesc; } - if ( $this->img->isMultipage() ) { + if ( $this->displayImg->isMultipage() ) { $wgOut->addHTML( '
' ); } - $imgAttribs = array( - 'border' => 0, - 'alt' => $this->img->getTitle()->getPrefixedText() - ); - if ( $thumbnail ) { - $wgOut->addHTML( '
' . - "$select
$thumb1\n$thumb2
" ); + $select = Xml::tags( 'select', + array( 'id' => 'pageselector', 'name' => 'page' ), + implode( "\n", $options ) ); + + $wgOut->addHTML( + '
' . + Xml::openElement( 'form', $formParams ) . + Xml::hidden( 'title', $this->getTitle()->getPrefixedDbKey() ) . + wfMsgExt( 'imgmultigoto', array( 'parseinline', 'replaceafter' ), $select ) . + Xml::submitButton( wfMsg( 'imgmultigo' ) ) . + Xml::closeElement( 'form' ) . + "
$thumb1\n$thumb2
" + ); } } else { #if direct link is allowed but it's not a renderable image, show an icon. - if ($this->img->isSafeFile()) { - $icon= $this->img->iconThumb(); + if ( $this->displayImg->isSafeFile() ) { + $icon= $this->displayImg->iconThumb(); - $wgOut->addHTML( '' ); + $wgOut->addHTML( '' ); } $showLink = true; @@ -314,42 +435,30 @@ class ImagePage extends Article { if ($showLink) { - // Workaround for incorrect MIME type on SVGs uploaded in previous versions - if ($mime == 'image/svg') $mime = 'image/svg+xml'; + $filename = wfEscapeWikiText( $this->displayImg->getName() ); - $filename = wfEscapeWikiText( $this->img->getName() ); - $info = wfMsg( 'file-info', $sk->formatSize( $this->img->getSize() ), $mime ); - $infores = ''; - - // Check for MIME type. Other types may have more information in the future. - if (substr($mime,0,9) == 'image/svg' ) { - $infores = wfMsg('file-svg', $width_orig, $height_orig ) . '
'; - } - - global $wgContLang; - $dirmark = $wgContLang->getDirMark(); - if (!$this->img->isSafeFile()) { - $warning = wfMsg( 'mediawarning' ); - $wgOut->addWikiText( <<$infores + if ( !$this->displayImg->isSafeFile() ) { + $warning = wfMsgNoTrans( 'mediawarning' ); + $wgOut->addWikiText( << [[Media:$filename|$filename]]$dirmark - $info + $longDesc
$warning
-END +EOT ); } else { - $wgOut->addWikiText( <<$infores -[[Media:$filename|$filename]]$dirmark $info + $wgOut->addWikiText( << +[[Media:$filename|$filename]]$dirmark $longDesc -END +EOT ); } } - if($this->img->fromSharedDirectory) { + if( !$this->displayImg->isLocal() ) { $this->printSharedImageText(); } } else { @@ -357,40 +466,92 @@ END $title = SpecialPage::getTitleFor( 'Upload' ); $link = $sk->makeKnownLinkObj($title, wfMsgHtml('noimage-linktext'), - 'wpDestFile=' . urlencode( $this->img->getName() ) ); + 'wpDestFile=' . urlencode( $this->displayImg->getName() ) ); $wgOut->addHTML( wfMsgWikiHtml( 'noimage', $link ) ); } } + /** + * Show a notice that the file is from a shared repository + */ function printSharedImageText() { - global $wgRepositoryBaseUrl, $wgFetchCommonsDescriptions, $wgOut, $wgUser; + global $wgOut, $wgUser; - $url = $wgRepositoryBaseUrl . urlencode($this->mTitle->getDBkey()); - $sharedtext = "
" . wfMsgWikiHtml("sharedupload"); - if ($wgRepositoryBaseUrl && !$wgFetchCommonsDescriptions) { + $this->loadFile(); + $descUrl = $this->img->getDescriptionUrl(); + $descText = $this->img->getDescriptionText(); + $s = "
" . wfMsgWikiHtml( 'sharedupload' ); + if ( $descUrl ) { $sk = $wgUser->getSkin(); - $title = SpecialPage::getTitleFor( 'Upload' ); - $link = $sk->makeKnownLinkObj($title, wfMsgHtml('shareduploadwiki-linktext'), - array( 'wpDestFile' => urlencode( $this->img->getName() ))); - $sharedtext .= " " . wfMsgWikiHtml('shareduploadwiki', $link); + $link = $sk->makeExternalLink( $descUrl, wfMsg( 'shareduploadwiki-linktext' ) ); + $msg = ( $descText ) ? 'shareduploadwiki-desc' : 'shareduploadwiki'; + $msg = wfMsgExt( $msg, array( 'parseinline', 'replaceafter' ), $link ); + if ( $msg != '-' ) { + # Show message only if not voided by local sysops + $s .= $msg; + } + } + $s .= "
"; + $wgOut->addHTML( $s ); + + if ( $descText ) { + $this->mExtraDescription = $descText; + } + } + + /* + * Check for files with the same name on the foreign repos. + */ + function checkSharedConflict() { + global $wgOut, $wgUser; + + $repoGroup = RepoGroup::singleton(); + if( !$repoGroup->hasForeignRepos() ) { + return; + } + + $this->loadFile(); + if( !$this->img->isLocal() ) { + return; + } + + $this->dupFile = null; + $repoGroup->forEachForeignRepo( array( $this, 'checkSharedConflictCallback' ) ); + + if( !$this->dupFile ) + return; + $dupfile = $this->dupFile; + $same = ( + ($this->img->getSha1() == $dupfile->getSha1()) && + ($this->img->getSize() == $dupfile->getSize()) + ); + + $sk = $wgUser->getSkin(); + $descUrl = $dupfile->getDescriptionUrl(); + if( $same ) { + $link = $sk->makeExternalLink( $descUrl, wfMsg( 'shareduploadduplicate-linktext' ) ); + $wgOut->addHTML( '
' . wfMsgWikiHtml( 'shareduploadduplicate', $link ) . '
' ); + } else { + $link = $sk->makeExternalLink( $descUrl, wfMsg( 'shareduploadconflict-linktext' ) ); + $wgOut->addHTML( '
' . wfMsgWikiHtml( 'shareduploadconflict', $link ) . '
' ); } - $sharedtext .= "
"; - $wgOut->addHTML($sharedtext); + } - if ($wgRepositoryBaseUrl && $wgFetchCommonsDescriptions) { - $renderUrl = wfAppendQuery( $url, 'action=render' ); - wfDebug( "Fetching shared description from $renderUrl\n" ); - $text = Http::get( $renderUrl ); - if ($text) - $this->mExtraDescription = $text; + function checkSharedConflictCallback( $repo ) { + $this->loadFile(); + $dupfile = $repo->newFile( $this->img->getTitle() ); + if( $dupfile && $dupfile->exists() ) { + $this->dupFile = $dupfile; + return $dupfile->exists(); } + return false; } function getUploadUrl() { - global $wgServer; + $this->loadFile(); $uploadTitle = SpecialPage::getTitleFor( 'Upload' ); - return $wgServer . $uploadTitle->getLocalUrl( 'wpDestFile=' . urlencode( $this->img->getName() ) ); + return $uploadTitle->getFullUrl( 'wpDestFile=' . urlencode( $this->img->getName() ) ); } /** @@ -400,23 +561,28 @@ END function uploadLinksBox() { global $wgUser, $wgOut; - if( $this->img->fromSharedDirectory ) + $this->loadFile(); + if( !$this->img->isLocal() ) return; $sk = $wgUser->getSkin(); - + $wgOut->addHtml( '
    ' ); - + # "Upload a new version of this file" link - if( $wgUser->isAllowed( 'reupload' ) ) { + if( UploadForm::userCanReUpload($wgUser,$this->img->name) ) { $ulink = $sk->makeExternalLink( $this->getUploadUrl(), wfMsg( 'uploadnewversion-linktext' ) ); $wgOut->addHtml( "
  • " ); } - + + # Link to Special:FileDuplicateSearch + $dupeLink = $sk->makeKnownLinkObj( SpecialPage::getTitleFor( 'FileDuplicateSearch', $this->mTitle->getDBkey() ), wfMsgHtml( 'imagepage-searchdupe' ) ); + $wgOut->addHtml( "
  • {$dupeLink}
  • " ); + # External editing link $elink = $sk->makeKnownLinkObj( $this->mTitle, wfMsgHtml( 'edit-externally' ), 'action=edit&externaledit=true&mode=file' ); $wgOut->addHtml( '
  • ' . $elink . '
    ' . wfMsgWikiHtml( 'edit-externally-help' ) . '
  • ' ); - + $wgOut->addHtml( '
' ); } @@ -432,32 +598,27 @@ END */ function imageHistory() { - global $wgUser, $wgOut, $wgUseExternalEditor; - - $sk = $wgUser->getSkin(); + global $wgOut, $wgUseExternalEditor; - $line = $this->img->nextHistoryLine(); - - if ( $line ) { - $list = new ImageHistoryList( $sk ); - $s = $list->beginImageHistoryList() . - $list->imageHistoryLine( true, wfTimestamp(TS_MW, $line->img_timestamp), - $this->mTitle->getDBkey(), $line->img_user, - $line->img_user_text, $line->img_size, $line->img_description, - $line->img_width, $line->img_height - ); - - while ( $line = $this->img->nextHistoryLine() ) { - $s .= $list->imageHistoryLine( false, $line->img_timestamp, - $line->oi_archive_name, $line->img_user, - $line->img_user_text, $line->img_size, $line->img_description, - $line->img_width, $line->img_height - ); + $this->loadFile(); + if ( $this->img->exists() ) { + $list = new ImageHistoryList( $this ); + $file = $this->img; + $dims = $file->getDimensionsString(); + $s = $list->beginImageHistoryList(); + $s .= $list->imageHistoryLine( true, $file ); + // old image versions + $hist = $this->img->getHistory(); + foreach( $hist as $file ) { + $dims = $file->getDimensionsString(); + $s .= $list->imageHistoryLine( false, $file ); } $s .= $list->endImageHistoryList(); } else { $s=''; } $wgOut->addHTML( $s ); + $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( $wgUseExternalEditor && $this->img->exists() ) { @@ -469,238 +630,125 @@ END function imageLinks() { global $wgUser, $wgOut; - - $wgOut->addHTML( Xml::element( 'h2', array( 'id' => 'filelinks' ), wfMsg( 'imagelinks' ) ) . "\n" ); + + $limit = 100; $dbr = wfGetDB( DB_SLAVE ); - $page = $dbr->tableName( 'page' ); - $imagelinks = $dbr->tableName( 'imagelinks' ); - $sql = "SELECT page_namespace,page_title FROM $imagelinks,$page WHERE il_to=" . - $dbr->addQuotes( $this->mTitle->getDBkey() ) . " AND il_from=page_id"; - $sql = $dbr->limitResult($sql, 500, 0); - $res = $dbr->query( $sql, "ImagePage::imageLinks" ); + $res = $dbr->select( + array( 'imagelinks', 'page' ), + array( 'page_namespace', 'page_title' ), + array( 'il_to' => $this->mTitle->getDBkey(), 'il_from = page_id' ), + __METHOD__, + array( 'LIMIT' => $limit + 1) + ); if ( 0 == $dbr->numRows( $res ) ) { - $wgOut->addHtml( '

' . wfMsg( "nolinkstoimage" ) . "

\n" ); + $wgOut->addHTML( "
\n" ); + $wgOut->addWikiMsg( 'nolinkstoimage' ); + $wgOut->addHTML( "
\n" ); return; } - $wgOut->addHTML( '

' . wfMsg( 'linkstoimage' ) . "

\n
    " ); + $wgOut->addHTML( "
    \n" ); + $wgOut->addWikiMsg( 'linkstoimage' ); + $wgOut->addHTML( "
      \n" ); $sk = $wgUser->getSkin(); - while ( $s = $dbr->fetchObject( $res ) ) { - $name = Title::MakeTitle( $s->page_namespace, $s->page_title ); - $link = $sk->makeKnownLinkObj( $name, "" ); - $wgOut->addHTML( "
    • {$link}
    • \n" ); + $count = 0; + while ( $s = $res->fetchObject() ) { + $count++; + if ( $count <= $limit ) { + // We have not yet reached the extra one that tells us there is more to fetch + $name = Title::makeTitle( $s->page_namespace, $s->page_title ); + $link = $sk->makeKnownLinkObj( $name, "" ); + $wgOut->addHTML( "
    • {$link}
    • \n" ); + } } - $wgOut->addHTML( "
    \n" ); + $wgOut->addHTML( "
\n" ); + $res->free(); + + // Add a links to [[Special:Whatlinkshere]] + if ( $count > $limit ) + $wgOut->addWikiMsg( 'morelinkstoimage', $this->mTitle->getPrefixedDBkey() ); } - - function delete() + + function imageRedirects() { - global $wgUser, $wgOut, $wgRequest; - - $confirm = $wgRequest->wasPosted(); - $reason = $wgRequest->getVal( 'wpReason' ); - $image = $wgRequest->getVal( 'image' ); - $oldimage = $wgRequest->getVal( 'oldimage' ); - - # Only sysops can delete images. Previously ordinary users could delete - # old revisions, but this is no longer the case. - if ( !$wgUser->isAllowed('delete') ) { - $wgOut->permissionRequired( 'delete' ); - return; - } - if ( $wgUser->isBlocked() ) { - return $this->blockedIPpage(); - } - if ( wfReadOnly() ) { - $wgOut->readOnlyPage(); - return; - } - - # Better double-check that it hasn't been deleted yet! - $wgOut->setPagetitle( wfMsg( 'confirmdelete' ) ); - if ( ( !is_null( $image ) ) - && ( '' == trim( $image ) ) ) { - $wgOut->showFatalError( wfMsg( 'cannotdelete' ) ); - return; - } + global $wgUser, $wgOut; + + $redirects = $this->getTitle()->getRedirectsHere( NS_IMAGE ); + if ( count( $redirects ) == 0 ) return; - $this->img = new Image( $this->mTitle ); + $wgOut->addHTML( "
\n" ); + $wgOut->addWikiMsg( 'redirectstofile' ); + $wgOut->addHTML( "
    \n" ); - # Deleting old images doesn't require confirmation - if ( !is_null( $oldimage ) || $confirm ) { - if( $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ), $oldimage ) ) { - $this->doDelete( $reason ); - } else { - $wgOut->showFatalError( wfMsg( 'sessionfailure' ) ); - } - return; + $sk = $wgUser->getSkin(); + foreach ( $redirects as $title ) { + $link = $sk->makeKnownLinkObj( $title, "" ); + $wgOut->addHTML( "
  • {$link}
  • \n" ); } + $wgOut->addHTML( "
\n" ); - if ( !is_null( $image ) ) { - $q = '&image=' . urlencode( $image ); - } else if ( !is_null( $oldimage ) ) { - $q = '&oldimage=' . urlencode( $oldimage ); - } else { - $q = ''; - } - return $this->confirmDelete( $q, $wgRequest->getText( 'wpReason' ) ); } - - /* - * Delete an image. - * @param $reason User provided reason for deletion. - */ - function doDelete( $reason ) { - global $wgOut, $wgRequest; - - $oldimage = $wgRequest->getVal( 'oldimage' ); - - if ( !is_null( $oldimage ) ) { - if ( strlen( $oldimage ) < 16 ) { - $wgOut->showUnexpectedValueError( 'oldimage', htmlspecialchars($oldimage) ); - return; - } - if ( strstr( $oldimage, "/" ) || strstr( $oldimage, "\\" ) ) { - $wgOut->showUnexpectedValueError( 'oldimage', htmlspecialchars($oldimage) ); - return; - } - if ( !$this->doDeleteOldImage( $oldimage ) ) { - return; - } - $deleted = $oldimage; - } else { - $ok = $this->img->delete( $reason ); - if( !$ok ) { - # If the deletion operation actually failed, bug out: - $wgOut->showFileDeleteError( $this->img->getName() ); - return; - } - - # Image itself is now gone, and database is cleaned. - # Now we remove the image description page. - $article = new Article( $this->mTitle ); - $article->doDeleteArticle( $reason ); # ignore errors - - $deleted = $this->img->getName(); - } + function imageDupes() { + global $wgOut, $wgUser; - $wgOut->setPagetitle( wfMsg( 'actioncomplete' ) ); - $wgOut->setRobotpolicy( 'noindex,nofollow' ); + $this->loadFile(); - $loglink = '[[Special:Log/delete|' . wfMsg( 'deletionlog' ) . ']]'; - $text = wfMsg( 'deletedtext', $deleted, $loglink ); + $dupes = $this->getDuplicates(); + if ( count( $dupes ) == 0 ) return; - $wgOut->addWikiText( $text ); + $wgOut->addHTML( "
\n" ); + $wgOut->addWikiMsg( 'duplicatesoffile' ); + $wgOut->addHTML( "
    \n" ); - $wgOut->returnToMain( false, $this->mTitle->getPrefixedText() ); + $sk = $wgUser->getSkin(); + foreach ( $dupes as $file ) { + if ( $file->isLocal() ) + $link = $sk->makeKnownLinkObj( $file->getTitle(), "" ); + else + $link = $sk->makeExternalLink( $file->getDescriptionUrl(), + $file->getTitle()->getPrefixedText() ); + $wgOut->addHTML( "
  • {$link}
  • \n" ); + } + $wgOut->addHTML( "
\n" ); } /** - * @return success + * Delete the file, or an earlier version of it */ - function doDeleteOldImage( $oldimage ) - { - global $wgOut; - - $ok = $this->img->deleteOld( $oldimage, '' ); - if( !$ok ) { - # If we actually have a file and can't delete it, throw an error. - # Something went awry... - $wgOut->showFileDeleteError( "$oldimage" ); - } else { - # Log the deletion - $log = new LogPage( 'delete' ); - $log->addEntry( 'delete', $this->mTitle, wfMsg('deletedrevision',$oldimage) ); - } - return $ok; - } - - function revert() { - global $wgOut, $wgRequest, $wgUser; - - $oldimage = $wgRequest->getText( 'oldimage' ); - if ( strlen( $oldimage ) < 16 ) { - $wgOut->showUnexpectedValueError( 'oldimage', htmlspecialchars($oldimage) ); - return; - } - if ( strstr( $oldimage, "/" ) || strstr( $oldimage, "\\" ) ) { - $wgOut->showUnexpectedValueError( 'oldimage', htmlspecialchars($oldimage) ); + public function delete() { + $this->loadFile(); + if( !$this->img->exists() || !$this->img->isLocal() || $this->img->getRedirected() ) { + // Standard article deletion + Article::delete(); return; } - - if ( wfReadOnly() ) { - $wgOut->readOnlyPage(); - return; - } - if( $wgUser->isAnon() ) { - $wgOut->showErrorPage( 'uploadnologin', 'uploadnologintext' ); - return; - } - if ( ! $this->mTitle->userCan( 'edit' ) ) { - $wgOut->readOnlyPage( $this->getContent(), true ); - return; - } - if ( $wgUser->isBlocked() ) { - return $this->blockedIPpage(); - } - if( !$wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ), $oldimage ) ) { - $wgOut->showErrorPage( 'internalerror', 'sessionfailure' ); - return; - } - $name = substr( $oldimage, 15 ); - - $dest = wfImageDir( $name ); - $archive = wfImageArchiveDir( $name ); - $curfile = "{$dest}/{$name}"; - - if ( !is_dir( $dest ) ) wfMkdirParents( $dest ); - if ( !is_dir( $archive ) ) wfMkdirParents( $archive ); - - if ( ! is_file( $curfile ) ) { - $wgOut->showFileNotFoundError( htmlspecialchars( $curfile ) ); - return; - } - $oldver = wfTimestampNow() . "!{$name}"; - - if ( ! rename( $curfile, "${archive}/{$oldver}" ) ) { - $wgOut->showFileRenameError( $curfile, "${archive}/{$oldver}" ); - return; - } - if ( ! copy( "{$archive}/{$oldimage}", $curfile ) ) { - $wgOut->showFileCopyError( "${archive}/{$oldimage}", $curfile ); - return; - } - - # Record upload and update metadata cache - $img = Image::newFromName( $name ); - $img->recordUpload( $oldver, wfMsg( "reverted" ) ); - - $wgOut->setPagetitle( wfMsg( 'actioncomplete' ) ); - $wgOut->setRobotpolicy( 'noindex,nofollow' ); - $wgOut->addHTML( wfMsg( 'imagereverted' ) ); - - $descTitle = $img->getTitle(); - $wgOut->returnToMain( false, $descTitle->getPrefixedText() ); + $deleter = new FileDeleteForm( $this->img ); + $deleter->execute(); } - function blockedIPpage() { - $edit = new EditPage( $this ); - return $edit->blockedIPpage(); + /** + * Revert the file to an earlier version + */ + public function revert() { + $this->loadFile(); + $reverter = new FileRevertForm( $this->img ); + $reverter->execute(); } - + /** * Override handling of action=purge */ function doPurge() { - $this->img = new Image( $this->mTitle ); + $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\n" ); @@ -708,82 +756,194 @@ END 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 ); + } + } /** - * @todo document - * @addtogroup Media + * Builds the image revision log shown on image pages + * + * @ingroup Media */ class ImageHistoryList { - function ImageHistoryList( &$skin ) { - $this->skin =& $skin; + + protected $imagePage, $img, $skin, $title, $repo; + + public function __construct( $imagePage ) { + global $wgUser; + $this->skin = $wgUser->getSkin(); + $this->current = $imagePage->getFile(); + $this->img = $imagePage->getDisplayedFile(); + $this->title = $imagePage->getTitle(); + $this->imagePage = $imagePage; } - function beginImageHistoryList() { - $s = "\n" . - Xml::element( 'h2', array( 'id' => 'filehistory' ), wfMsg( 'imghistory' ) ) . - "\n

" . wfMsg( 'imghistlegend' ) . "

\n".'
    '; - return $s; + function getImagePage() { + return $this->imagePage; } - function endImageHistoryList() { - $s = "
\n"; - return $s; + function getSkin() { + return $this->skin; } - function imageHistoryLine( $iscur, $timestamp, $img, $user, $usertext, $size, $description, $width, $height ) { - global $wgUser, $wgLang, $wgTitle, $wgContLang; + function getFile() { + return $this->img; + } - $datetime = $wgLang->timeanddate( $timestamp, true ); - $del = wfMsgHtml( 'deleteimg' ); - $delall = wfMsgHtml( 'deleteimgcompletely' ); - $cur = wfMsgHtml( 'cur' ); + public function beginImageHistoryList() { + global $wgOut, $wgUser; + return Xml::element( 'h2', array( 'id' => 'filehistory' ), wfMsg( 'filehist' ) ) + . $wgOut->parse( wfMsgNoTrans( 'filehist-help' ) ) + . Xml::openElement( 'table', array( 'class' => 'filehistory' ) ) . "\n" + . '' + . ( $this->current->isLocal() && ($wgUser->isAllowed('delete') || $wgUser->isAllowed('deleterevision') ) ? '' : '' ) + . '' . wfMsgHtml( 'filehist-datetime' ) . '' + . '' . wfMsgHtml( 'filehist-dimensions' ) . '' + . '' . wfMsgHtml( 'filehist-user' ) . '' + . '' . wfMsgHtml( 'filehist-comment' ) . '' + . "\n"; + } - if ( $iscur ) { - $url = Image::imageUrl( $img ); - $rlink = $cur; - if ( $wgUser->isAllowed('delete') ) { - $link = $wgTitle->escapeLocalURL( 'image=' . $wgTitle->getPartialURL() . - '&action=delete' ); - $style = $this->skin->getInternalLinkAttributes( $link, $delall ); + public function endImageHistoryList() { + return "\n"; + } + + public function imageHistoryLine( $iscur, $file ) { + global $wgUser, $wgLang, $wgContLang, $wgTitle; + + $timestamp = wfTimestamp(TS_MW, $file->getTimestamp()); + $img = $iscur ? $file->getName() : $file->getArchiveName(); + $user = $file->getUser('id'); + $usertext = $file->getUser('text'); + $size = $file->getSize(); + $description = $file->getDescription(); + $dims = $file->getDimensionsString(); + $sha1 = $file->getSha1(); + + $local = $this->current->isLocal(); + $row = $css = $selected = ''; + + // Deletion link + if( $local && ($wgUser->isAllowed('delete') || $wgUser->isAllowed('deleterevision') ) ) { + $row .= ''; + # Link to remove from history + if( $wgUser->isAllowed( 'delete' ) ) { + $q = array(); + $q[] = 'action=delete'; + if( !$iscur ) + $q[] = 'oldimage=' . urlencode( $img ); + $row .= $this->skin->makeKnownLinkObj( + $this->title, + wfMsgHtml( $iscur ? 'filehist-deleteall' : 'filehist-deleteone' ), + implode( '&', $q ) + ); + } + # Link to hide content + if( $wgUser->isAllowed( 'deleterevision' ) ) { + if( $wgUser->isAllowed('delete') ) { + $row .= '
'; + } + $revdel = SpecialPage::getTitleFor( 'Revisiondelete' ); + // If file is top revision or locked from this user, don't link + if( $iscur || !$file->userCan(File::DELETED_RESTRICTED) ) { + $del = wfMsgHtml( 'rev-delundel' ); + } else { + // If the file was hidden, link to sha-1 + list($ts,$name) = explode('!',$img,2); + $del = $this->skin->makeKnownLinkObj( $revdel, wfMsg( 'rev-delundel' ), + 'target=' . urlencode( $wgTitle->getPrefixedText() ) . + '&oldimage=' . urlencode( $ts ) ); + // Bolden oversighted content + if( $file->isDeleted(File::DELETED_RESTRICTED) ) + $del = "$del"; + } + $row .= "$del"; + } + $row .= ''; + } - $dlink = ''.$delall.''; + // Reversion link/current indicator + $row .= ''; + 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 { - $dlink = $del; + $q = array(); + $q[] = 'action=revert'; + $q[] = 'oldimage=' . urlencode( $img ); + $q[] = 'wpEditToken=' . urlencode( $wgUser->editToken( $img ) ); + $row .= $this->skin->makeKnownLinkObj( $this->title, + wfMsgHtml( 'filehist-revert' ), + implode( '&', $q ) ); } + } + $row .= ''; + + // Date/time and image link + if( $file->getTimestamp() === $this->img->getTimestamp() ) { + $selected = "class='filehistory-selected'"; + } + $row .= ""; + if( !$file->userCan(File::DELETED_FILE) ) { + # Don't link to unviewable files + $row .= '' . $wgLang->timeAndDate( $timestamp, true ) . ''; + } else if( $file->isDeleted(File::DELETED_FILE) ) { + $revdel = SpecialPage::getTitleFor( 'Revisiondelete' ); + # Make a link to review the image + $url = $this->skin->makeKnownLinkObj( $revdel, $wgLang->timeAndDate( $timestamp, true ), + "target=".$wgTitle->getPrefixedText()."&file=$sha1.".$this->current->getExtension() ); + $row .= ''.$url.''; } else { - $url = htmlspecialchars( wfImageArchiveUrl( $img ) ); - if( $wgUser->getID() != 0 && $wgTitle->userCan( 'edit' ) ) { - $token = urlencode( $wgUser->editToken( $img ) ); - $rlink = $this->skin->makeKnownLinkObj( $wgTitle, - wfMsgHtml( 'revertimg' ), 'action=revert&oldimage=' . - urlencode( $img ) . "&wpEditToken=$token" ); - $dlink = $this->skin->makeKnownLinkObj( $wgTitle, - $del, 'action=delete&oldimage=' . urlencode( $img ) . - "&wpEditToken=$token" ); + $url = $iscur ? $this->current->getUrl() : $this->current->getArchiveUrl( $img ); + $row .= Xml::element( 'a', array( 'href' => $url ), $wgLang->timeAndDate( $timestamp, true ) ); + } + + $row .= ""; + + // Image dimensions + $row .= htmlspecialchars( $dims ); + + // File size + $row .= " (" . $this->skin->formatSize( $size ) . ')'; + + // Uploading user + $row .= ''; + if( $local ) { + // Hide deleted usernames + if( $file->isDeleted(File::DELETED_USER) ) { + $row .= '' . wfMsgHtml( 'rev-deleted-user' ) . ''; } else { - # Having live active links for non-logged in users - # means that bots and spiders crawling our site can - # inadvertently change content. Baaaad idea. - $rlink = wfMsgHtml( 'revertimg' ); - $dlink = $del; + $row .= $this->skin->userLink( $user, $usertext ) . " " . + $this->skin->userToolLinks( $user, $usertext ) . ""; } + } else { + $row .= htmlspecialchars( $usertext ); } - - $userlink = $this->skin->userLink( $user, $usertext ) . $this->skin->userToolLinks( $user, $usertext ); - $nbytes = wfMsgExt( 'nbytes', array( 'parsemag', 'escape' ), - $wgLang->formatNum( $size ) ); - $widthheight = wfMsgHtml( 'widthheight', $width, $height ); - $style = $this->skin->getInternalLinkAttributes( $url, $datetime ); + $row .= ''; - $s = "
  • ({$dlink}) ({$rlink}) {$datetime} . . {$userlink} . . {$widthheight} ({$nbytes})"; + // Don't show deleted descriptions + if ( $file->isDeleted(File::DELETED_COMMENT) ) { + $row .= '' . wfMsgHtml('rev-deleted-comment') . ''; + } else { + $row .= $this->skin->commentBlock( $description, $this->title ); + } + $row .= ''; - $s .= $this->skin->commentBlock( $description, $wgTitle ); - $s .= "
  • \n"; - return $s; - } + wfRunHooks( 'ImagePageFileHistoryLine', array( $this, $file, &$row, &$rowClass ) ); + $classAttr = $rowClass ? " class='$rowClass'" : ""; + return "{$row}\n"; + } } - - -?>