MediaWiki
REL1_23
|
00001 <?php 00050 abstract class File { 00051 // Bitfield values akin to the Revision deletion constants 00052 const DELETED_FILE = 1; 00053 const DELETED_COMMENT = 2; 00054 const DELETED_USER = 4; 00055 const DELETED_RESTRICTED = 8; 00056 00058 const RENDER_NOW = 1; 00063 const RENDER_FORCE = 2; 00064 00065 const DELETE_SOURCE = 1; 00066 00067 // Audience options for File::getDescription() 00068 const FOR_PUBLIC = 1; 00069 const FOR_THIS_USER = 2; 00070 const RAW = 3; 00071 00072 // Options for File::thumbName() 00073 const THUMB_FULL_NAME = 1; 00074 00095 public $repo; 00096 00098 protected $title; 00099 00101 protected $lastError; 00102 00104 protected $redirected; 00105 00107 protected $redirectedTitle; 00108 00110 protected $fsFile; 00111 00113 protected $handler; 00114 00116 protected $url; 00117 00119 protected $extension; 00120 00122 protected $name; 00123 00125 protected $path; 00126 00128 protected $hashPath; 00129 00133 protected $pageCount; 00134 00136 protected $transformScript; 00137 00139 protected $redirectTitle; 00140 00142 protected $canRender; 00143 00147 protected $isSafeFile; 00148 00150 protected $repoClass = 'FileRepo'; 00151 00162 function __construct( $title, $repo ) { 00163 if ( $title !== false ) { // subclasses may not use MW titles 00164 $title = self::normalizeTitle( $title, 'exception' ); 00165 } 00166 $this->title = $title; 00167 $this->repo = $repo; 00168 } 00169 00179 static function normalizeTitle( $title, $exception = false ) { 00180 $ret = $title; 00181 if ( $ret instanceof Title ) { 00182 # Normalize NS_MEDIA -> NS_FILE 00183 if ( $ret->getNamespace() == NS_MEDIA ) { 00184 $ret = Title::makeTitleSafe( NS_FILE, $ret->getDBkey() ); 00185 # Sanity check the title namespace 00186 } elseif ( $ret->getNamespace() !== NS_FILE ) { 00187 $ret = null; 00188 } 00189 } else { 00190 # Convert strings to Title objects 00191 $ret = Title::makeTitleSafe( NS_FILE, (string)$ret ); 00192 } 00193 if ( !$ret && $exception !== false ) { 00194 throw new MWException( "`$title` is not a valid file title." ); 00195 } 00196 00197 return $ret; 00198 } 00199 00200 function __get( $name ) { 00201 $function = array( $this, 'get' . ucfirst( $name ) ); 00202 if ( !is_callable( $function ) ) { 00203 return null; 00204 } else { 00205 $this->$name = call_user_func( $function ); 00206 00207 return $this->$name; 00208 } 00209 } 00210 00218 static function normalizeExtension( $ext ) { 00219 $lower = strtolower( $ext ); 00220 $squish = array( 00221 'htm' => 'html', 00222 'jpeg' => 'jpg', 00223 'mpeg' => 'mpg', 00224 'tiff' => 'tif', 00225 'ogv' => 'ogg' ); 00226 if ( isset( $squish[$lower] ) ) { 00227 return $squish[$lower]; 00228 } elseif ( preg_match( '/^[0-9a-z]+$/', $lower ) ) { 00229 return $lower; 00230 } else { 00231 return ''; 00232 } 00233 } 00234 00243 static function checkExtensionCompatibility( File $old, $new ) { 00244 $oldMime = $old->getMimeType(); 00245 $n = strrpos( $new, '.' ); 00246 $newExt = self::normalizeExtension( $n ? substr( $new, $n + 1 ) : '' ); 00247 $mimeMagic = MimeMagic::singleton(); 00248 00249 return $mimeMagic->isMatchingExtension( $newExt, $oldMime ); 00250 } 00251 00257 function upgradeRow() { 00258 } 00259 00267 public static function splitMime( $mime ) { 00268 if ( strpos( $mime, '/' ) !== false ) { 00269 return explode( '/', $mime, 2 ); 00270 } else { 00271 return array( $mime, 'unknown' ); 00272 } 00273 } 00274 00282 public static function compare( File $a, File $b ) { 00283 return strcmp( $a->getName(), $b->getName() ); 00284 } 00285 00291 public function getName() { 00292 if ( !isset( $this->name ) ) { 00293 $this->assertRepoDefined(); 00294 $this->name = $this->repo->getNameFromTitle( $this->title ); 00295 } 00296 00297 return $this->name; 00298 } 00299 00305 function getExtension() { 00306 if ( !isset( $this->extension ) ) { 00307 $n = strrpos( $this->getName(), '.' ); 00308 $this->extension = self::normalizeExtension( 00309 $n ? substr( $this->getName(), $n + 1 ) : '' ); 00310 } 00311 00312 return $this->extension; 00313 } 00314 00320 public function getTitle() { 00321 return $this->title; 00322 } 00323 00329 public function getOriginalTitle() { 00330 if ( $this->redirected ) { 00331 return $this->getRedirectedTitle(); 00332 } 00333 00334 return $this->title; 00335 } 00336 00342 public function getUrl() { 00343 if ( !isset( $this->url ) ) { 00344 $this->assertRepoDefined(); 00345 $ext = $this->getExtension(); 00346 $this->url = $this->repo->getZoneUrl( 'public', $ext ) . '/' . $this->getUrlRel(); 00347 } 00348 00349 return $this->url; 00350 } 00351 00359 public function getFullUrl() { 00360 return wfExpandUrl( $this->getUrl(), PROTO_RELATIVE ); 00361 } 00362 00366 public function getCanonicalUrl() { 00367 return wfExpandUrl( $this->getUrl(), PROTO_CANONICAL ); 00368 } 00369 00373 function getViewURL() { 00374 if ( $this->mustRender() ) { 00375 if ( $this->canRender() ) { 00376 return $this->createThumb( $this->getWidth() ); 00377 } else { 00378 wfDebug( __METHOD__ . ': supposed to render ' . $this->getName() . 00379 ' (' . $this->getMimeType() . "), but can't!\n" ); 00380 00381 return $this->getURL(); #hm... return NULL? 00382 } 00383 } else { 00384 return $this->getURL(); 00385 } 00386 } 00387 00401 public function getPath() { 00402 if ( !isset( $this->path ) ) { 00403 $this->assertRepoDefined(); 00404 $this->path = $this->repo->getZonePath( 'public' ) . '/' . $this->getRel(); 00405 } 00406 00407 return $this->path; 00408 } 00409 00417 public function getLocalRefPath() { 00418 $this->assertRepoDefined(); 00419 if ( !isset( $this->fsFile ) ) { 00420 $this->fsFile = $this->repo->getLocalReference( $this->getPath() ); 00421 if ( !$this->fsFile ) { 00422 $this->fsFile = false; // null => false; cache negative hits 00423 } 00424 } 00425 00426 return ( $this->fsFile ) 00427 ? $this->fsFile->getPath() 00428 : false; 00429 } 00430 00441 public function getWidth( $page = 1 ) { 00442 return false; 00443 } 00444 00455 public function getHeight( $page = 1 ) { 00456 return false; 00457 } 00458 00466 public function getUser( $type = 'text' ) { 00467 return null; 00468 } 00469 00475 public function getLength() { 00476 $handler = $this->getHandler(); 00477 if ( $handler ) { 00478 return $handler->getLength( $this ); 00479 } else { 00480 return 0; 00481 } 00482 } 00483 00489 public function isVectorized() { 00490 $handler = $this->getHandler(); 00491 if ( $handler ) { 00492 return $handler->isVectorized( $this ); 00493 } else { 00494 return false; 00495 } 00496 } 00497 00509 public function getAvailableLanguages() { 00510 $handler = $this->getHandler(); 00511 if ( $handler ) { 00512 return $handler->getAvailableLanguages( $this ); 00513 } else { 00514 return array(); 00515 } 00516 } 00517 00525 public function getDefaultRenderLanguage() { 00526 $handler = $this->getHandler(); 00527 if ( $handler ) { 00528 return $handler->getDefaultRenderLanguage( $this ); 00529 } else { 00530 return null; 00531 } 00532 } 00533 00544 public function canAnimateThumbIfAppropriate() { 00545 $handler = $this->getHandler(); 00546 if ( !$handler ) { 00547 // We cannot handle image whatsoever, thus 00548 // one would not expect it to be animated 00549 // so true. 00550 return true; 00551 } else { 00552 if ( $this->allowInlineDisplay() 00553 && $handler->isAnimatedImage( $this ) 00554 && !$handler->canAnimateThumbnail( $this ) 00555 ) { 00556 // Image is animated, but thumbnail isn't. 00557 // This is unexpected to the user. 00558 return false; 00559 } else { 00560 // Image is not animated, so one would 00561 // not expect thumb to be 00562 return true; 00563 } 00564 } 00565 } 00566 00573 public function getMetadata() { 00574 return false; 00575 } 00576 00583 public function getCommonMetaArray() { 00584 $handler = $this->getHandler(); 00585 00586 if ( !$handler ) { 00587 return false; 00588 } 00589 00590 return $handler->getCommonMetaArray( $this ); 00591 } 00592 00601 public function convertMetadataVersion( $metadata, $version ) { 00602 $handler = $this->getHandler(); 00603 if ( !is_array( $metadata ) ) { 00604 // Just to make the return type consistent 00605 $metadata = unserialize( $metadata ); 00606 } 00607 if ( $handler ) { 00608 return $handler->convertMetadataVersion( $metadata, $version ); 00609 } else { 00610 return $metadata; 00611 } 00612 } 00613 00620 public function getBitDepth() { 00621 return 0; 00622 } 00623 00630 public function getSize() { 00631 return false; 00632 } 00633 00641 function getMimeType() { 00642 return 'unknown/unknown'; 00643 } 00644 00652 function getMediaType() { 00653 return MEDIATYPE_UNKNOWN; 00654 } 00655 00668 function canRender() { 00669 if ( !isset( $this->canRender ) ) { 00670 $this->canRender = $this->getHandler() && $this->handler->canRender( $this ); 00671 } 00672 00673 return $this->canRender; 00674 } 00675 00680 protected function getCanRender() { 00681 return $this->canRender(); 00682 } 00683 00694 function mustRender() { 00695 return $this->getHandler() && $this->handler->mustRender( $this ); 00696 } 00697 00703 function allowInlineDisplay() { 00704 return $this->canRender(); 00705 } 00706 00720 function isSafeFile() { 00721 if ( !isset( $this->isSafeFile ) ) { 00722 $this->isSafeFile = $this->getIsSafeFileUncached(); 00723 } 00724 00725 return $this->isSafeFile; 00726 } 00727 00733 protected function getIsSafeFile() { 00734 return $this->isSafeFile(); 00735 } 00736 00742 protected function getIsSafeFileUncached() { 00743 global $wgTrustedMediaFormats; 00744 00745 if ( $this->allowInlineDisplay() ) { 00746 return true; 00747 } 00748 if ( $this->isTrustedFile() ) { 00749 return true; 00750 } 00751 00752 $type = $this->getMediaType(); 00753 $mime = $this->getMimeType(); 00754 #wfDebug( "LocalFile::isSafeFile: type= $type, mime= $mime\n" ); 00755 00756 if ( !$type || $type === MEDIATYPE_UNKNOWN ) { 00757 return false; #unknown type, not trusted 00758 } 00759 if ( in_array( $type, $wgTrustedMediaFormats ) ) { 00760 return true; 00761 } 00762 00763 if ( $mime === "unknown/unknown" ) { 00764 return false; #unknown type, not trusted 00765 } 00766 if ( in_array( $mime, $wgTrustedMediaFormats ) ) { 00767 return true; 00768 } 00769 00770 return false; 00771 } 00772 00786 function isTrustedFile() { 00787 #this could be implemented to check a flag in the database, 00788 #look for signatures, etc 00789 return false; 00790 } 00791 00799 public function exists() { 00800 return $this->getPath() && $this->repo->fileExists( $this->path ); 00801 } 00802 00809 public function isVisible() { 00810 return $this->exists(); 00811 } 00812 00816 function getTransformScript() { 00817 if ( !isset( $this->transformScript ) ) { 00818 $this->transformScript = false; 00819 if ( $this->repo ) { 00820 $script = $this->repo->getThumbScriptUrl(); 00821 if ( $script ) { 00822 $this->transformScript = wfAppendQuery( $script, array( 'f' => $this->getName() ) ); 00823 } 00824 } 00825 } 00826 00827 return $this->transformScript; 00828 } 00829 00837 function getUnscaledThumb( $handlerParams = array() ) { 00838 $hp =& $handlerParams; 00839 $page = isset( $hp['page'] ) ? $hp['page'] : false; 00840 $width = $this->getWidth( $page ); 00841 if ( !$width ) { 00842 return $this->iconThumb(); 00843 } 00844 $hp['width'] = $width; 00845 00846 return $this->transform( $hp ); 00847 } 00848 00858 public function thumbName( $params, $flags = 0 ) { 00859 $name = ( $this->repo && !( $flags & self::THUMB_FULL_NAME ) ) 00860 ? $this->repo->nameForThumb( $this->getName() ) 00861 : $this->getName(); 00862 00863 return $this->generateThumbName( $name, $params ); 00864 } 00865 00873 public function generateThumbName( $name, $params ) { 00874 if ( !$this->getHandler() ) { 00875 return null; 00876 } 00877 $extension = $this->getExtension(); 00878 list( $thumbExt, ) = $this->handler->getThumbType( 00879 $extension, $this->getMimeType(), $params ); 00880 $thumbName = $this->handler->makeParamString( $params ) . '-' . $name; 00881 if ( $thumbExt != $extension ) { 00882 $thumbName .= ".$thumbExt"; 00883 } 00884 00885 return $thumbName; 00886 } 00887 00905 public function createThumb( $width, $height = -1 ) { 00906 $params = array( 'width' => $width ); 00907 if ( $height != -1 ) { 00908 $params['height'] = $height; 00909 } 00910 $thumb = $this->transform( $params ); 00911 if ( !$thumb || $thumb->isError() ) { 00912 return ''; 00913 } 00914 00915 return $thumb->getUrl(); 00916 } 00917 00927 protected function transformErrorOutput( $thumbPath, $thumbUrl, $params, $flags ) { 00928 global $wgIgnoreImageErrors; 00929 00930 $handler = $this->getHandler(); 00931 if ( $handler && $wgIgnoreImageErrors && !( $flags & self::RENDER_NOW ) ) { 00932 return $handler->getTransform( $this, $thumbPath, $thumbUrl, $params ); 00933 } else { 00934 return new MediaTransformError( 'thumbnail_error', 00935 $params['width'], 0, wfMessage( 'thumbnail-dest-create' )->text() ); 00936 } 00937 } 00938 00947 function transform( $params, $flags = 0 ) { 00948 global $wgUseSquid, $wgIgnoreImageErrors, $wgThumbnailEpoch; 00949 00950 wfProfileIn( __METHOD__ ); 00951 do { 00952 if ( !$this->canRender() ) { 00953 $thumb = $this->iconThumb(); 00954 break; // not a bitmap or renderable image, don't try 00955 } 00956 00957 // Get the descriptionUrl to embed it as comment into the thumbnail. Bug 19791. 00958 $descriptionUrl = $this->getDescriptionUrl(); 00959 if ( $descriptionUrl ) { 00960 $params['descriptionUrl'] = wfExpandUrl( $descriptionUrl, PROTO_CANONICAL ); 00961 } 00962 00963 $handler = $this->getHandler(); 00964 $script = $this->getTransformScript(); 00965 if ( $script && !( $flags & self::RENDER_NOW ) ) { 00966 // Use a script to transform on client request, if possible 00967 $thumb = $handler->getScriptedTransform( $this, $script, $params ); 00968 if ( $thumb ) { 00969 break; 00970 } 00971 } 00972 00973 $normalisedParams = $params; 00974 $handler->normaliseParams( $this, $normalisedParams ); 00975 00976 $thumbName = $this->thumbName( $normalisedParams ); 00977 $thumbUrl = $this->getThumbUrl( $thumbName ); 00978 $thumbPath = $this->getThumbPath( $thumbName ); // final thumb path 00979 00980 if ( $this->repo ) { 00981 // Defer rendering if a 404 handler is set up... 00982 if ( $this->repo->canTransformVia404() && !( $flags & self::RENDER_NOW ) ) { 00983 wfDebug( __METHOD__ . " transformation deferred.\n" ); 00984 // XXX: Pass in the storage path even though we are not rendering anything 00985 // and the path is supposed to be an FS path. This is due to getScalerType() 00986 // getting called on the path and clobbering $thumb->getUrl() if it's false. 00987 $thumb = $handler->getTransform( $this, $thumbPath, $thumbUrl, $params ); 00988 break; 00989 } 00990 // Clean up broken thumbnails as needed 00991 $this->migrateThumbFile( $thumbName ); 00992 // Check if an up-to-date thumbnail already exists... 00993 wfDebug( __METHOD__ . ": Doing stat for $thumbPath\n" ); 00994 if ( !( $flags & self::RENDER_FORCE ) && $this->repo->fileExists( $thumbPath ) ) { 00995 $timestamp = $this->repo->getFileTimestamp( $thumbPath ); 00996 if ( $timestamp !== false && $timestamp >= $wgThumbnailEpoch ) { 00997 // XXX: Pass in the storage path even though we are not rendering anything 00998 // and the path is supposed to be an FS path. This is due to getScalerType() 00999 // getting called on the path and clobbering $thumb->getUrl() if it's false. 01000 $thumb = $handler->getTransform( $this, $thumbPath, $thumbUrl, $params ); 01001 $thumb->setStoragePath( $thumbPath ); 01002 break; 01003 } 01004 } elseif ( $flags & self::RENDER_FORCE ) { 01005 wfDebug( __METHOD__ . " forcing rendering per flag File::RENDER_FORCE\n" ); 01006 } 01007 } 01008 01009 // If the backend is ready-only, don't keep generating thumbnails 01010 // only to return transformation errors, just return the error now. 01011 if ( $this->repo->getReadOnlyReason() !== false ) { 01012 $thumb = $this->transformErrorOutput( $thumbPath, $thumbUrl, $params, $flags ); 01013 break; 01014 } 01015 01016 // Create a temp FS file with the same extension and the thumbnail 01017 $thumbExt = FileBackend::extensionFromPath( $thumbPath ); 01018 $tmpFile = TempFSFile::factory( 'transform_', $thumbExt ); 01019 if ( !$tmpFile ) { 01020 $thumb = $this->transformErrorOutput( $thumbPath, $thumbUrl, $params, $flags ); 01021 break; 01022 } 01023 $tmpThumbPath = $tmpFile->getPath(); // path of 0-byte temp file 01024 01025 // Actually render the thumbnail... 01026 wfProfileIn( __METHOD__ . '-doTransform' ); 01027 $thumb = $handler->doTransform( $this, $tmpThumbPath, $thumbUrl, $params ); 01028 wfProfileOut( __METHOD__ . '-doTransform' ); 01029 $tmpFile->bind( $thumb ); // keep alive with $thumb 01030 01031 if ( !$thumb ) { // bad params? 01032 $thumb = null; 01033 } elseif ( $thumb->isError() ) { // transform error 01034 $this->lastError = $thumb->toText(); 01035 // Ignore errors if requested 01036 if ( $wgIgnoreImageErrors && !( $flags & self::RENDER_NOW ) ) { 01037 $thumb = $handler->getTransform( $this, $tmpThumbPath, $thumbUrl, $params ); 01038 } 01039 } elseif ( $this->repo && $thumb->hasFile() && !$thumb->fileIsSource() ) { 01040 // Copy the thumbnail from the file system into storage... 01041 $disposition = $this->getThumbDisposition( $thumbName ); 01042 $status = $this->repo->quickImport( $tmpThumbPath, $thumbPath, $disposition ); 01043 if ( $status->isOK() ) { 01044 $thumb->setStoragePath( $thumbPath ); 01045 } else { 01046 $thumb = $this->transformErrorOutput( $thumbPath, $thumbUrl, $params, $flags ); 01047 } 01048 // Give extensions a chance to do something with this thumbnail... 01049 wfRunHooks( 'FileTransformed', array( $this, $thumb, $tmpThumbPath, $thumbPath ) ); 01050 } 01051 01052 // Purge. Useful in the event of Core -> Squid connection failure or squid 01053 // purge collisions from elsewhere during failure. Don't keep triggering for 01054 // "thumbs" which have the main image URL though (bug 13776) 01055 if ( $wgUseSquid ) { 01056 if ( !$thumb || $thumb->isError() || $thumb->getUrl() != $this->getURL() ) { 01057 SquidUpdate::purge( array( $thumbUrl ) ); 01058 } 01059 } 01060 } while ( false ); 01061 01062 wfProfileOut( __METHOD__ ); 01063 01064 return is_object( $thumb ) ? $thumb : false; 01065 } 01066 01072 function getThumbDisposition( $thumbName, $dispositionType = 'inline' ) { 01073 $fileName = $this->name; // file name to suggest 01074 $thumbExt = FileBackend::extensionFromPath( $thumbName ); 01075 if ( $thumbExt != '' && $thumbExt !== $this->getExtension() ) { 01076 $fileName .= ".$thumbExt"; 01077 } 01078 01079 return FileBackend::makeContentDisposition( $dispositionType, $fileName ); 01080 } 01081 01087 function migrateThumbFile( $thumbName ) { 01088 } 01089 01096 function getHandler() { 01097 if ( !isset( $this->handler ) ) { 01098 $this->handler = MediaHandler::getHandler( $this->getMimeType() ); 01099 } 01100 01101 return $this->handler; 01102 } 01103 01109 function iconThumb() { 01110 global $wgStylePath, $wgStyleDirectory; 01111 01112 $try = array( 'fileicon-' . $this->getExtension() . '.png', 'fileicon.png' ); 01113 foreach ( $try as $icon ) { 01114 $path = '/common/images/icons/' . $icon; 01115 $filepath = $wgStyleDirectory . $path; 01116 if ( file_exists( $filepath ) ) { // always FS 01117 $params = array( 'width' => 120, 'height' => 120 ); 01118 01119 return new ThumbnailImage( $this, $wgStylePath . $path, false, $params ); 01120 } 01121 } 01122 01123 return null; 01124 } 01125 01130 function getLastError() { 01131 return $this->lastError; 01132 } 01133 01140 function getThumbnails() { 01141 return array(); 01142 } 01143 01151 function purgeCache( $options = array() ) { 01152 } 01153 01159 function purgeDescription() { 01160 $title = $this->getTitle(); 01161 if ( $title ) { 01162 $title->invalidateCache(); 01163 $title->purgeSquid(); 01164 } 01165 } 01166 01171 function purgeEverything() { 01172 // Delete thumbnails and refresh file metadata cache 01173 $this->purgeCache(); 01174 $this->purgeDescription(); 01175 01176 // Purge cache of all pages using this file 01177 $title = $this->getTitle(); 01178 if ( $title ) { 01179 $update = new HTMLCacheUpdate( $title, 'imagelinks' ); 01180 $update->doUpdate(); 01181 } 01182 } 01183 01195 function getHistory( $limit = null, $start = null, $end = null, $inc = true ) { 01196 return array(); 01197 } 01198 01208 public function nextHistoryLine() { 01209 return false; 01210 } 01211 01218 public function resetHistory() { 01219 } 01220 01228 function getHashPath() { 01229 if ( !isset( $this->hashPath ) ) { 01230 $this->assertRepoDefined(); 01231 $this->hashPath = $this->repo->getHashPath( $this->getName() ); 01232 } 01233 01234 return $this->hashPath; 01235 } 01236 01243 function getRel() { 01244 return $this->getHashPath() . $this->getName(); 01245 } 01246 01254 function getArchiveRel( $suffix = false ) { 01255 $path = 'archive/' . $this->getHashPath(); 01256 if ( $suffix === false ) { 01257 $path = substr( $path, 0, -1 ); 01258 } else { 01259 $path .= $suffix; 01260 } 01261 01262 return $path; 01263 } 01264 01272 function getThumbRel( $suffix = false ) { 01273 $path = $this->getRel(); 01274 if ( $suffix !== false ) { 01275 $path .= '/' . $suffix; 01276 } 01277 01278 return $path; 01279 } 01280 01287 function getUrlRel() { 01288 return $this->getHashPath() . rawurlencode( $this->getName() ); 01289 } 01290 01299 function getArchiveThumbRel( $archiveName, $suffix = false ) { 01300 $path = 'archive/' . $this->getHashPath() . $archiveName . "/"; 01301 if ( $suffix === false ) { 01302 $path = substr( $path, 0, -1 ); 01303 } else { 01304 $path .= $suffix; 01305 } 01306 01307 return $path; 01308 } 01309 01316 function getArchivePath( $suffix = false ) { 01317 $this->assertRepoDefined(); 01318 01319 return $this->repo->getZonePath( 'public' ) . '/' . $this->getArchiveRel( $suffix ); 01320 } 01321 01329 function getArchiveThumbPath( $archiveName, $suffix = false ) { 01330 $this->assertRepoDefined(); 01331 01332 return $this->repo->getZonePath( 'thumb' ) . '/' . 01333 $this->getArchiveThumbRel( $archiveName, $suffix ); 01334 } 01335 01342 function getThumbPath( $suffix = false ) { 01343 $this->assertRepoDefined(); 01344 01345 return $this->repo->getZonePath( 'thumb' ) . '/' . $this->getThumbRel( $suffix ); 01346 } 01347 01354 function getTranscodedPath( $suffix = false ) { 01355 $this->assertRepoDefined(); 01356 01357 return $this->repo->getZonePath( 'transcoded' ) . '/' . $this->getThumbRel( $suffix ); 01358 } 01359 01366 function getArchiveUrl( $suffix = false ) { 01367 $this->assertRepoDefined(); 01368 $ext = $this->getExtension(); 01369 $path = $this->repo->getZoneUrl( 'public', $ext ) . '/archive/' . $this->getHashPath(); 01370 if ( $suffix === false ) { 01371 $path = substr( $path, 0, -1 ); 01372 } else { 01373 $path .= rawurlencode( $suffix ); 01374 } 01375 01376 return $path; 01377 } 01378 01386 function getArchiveThumbUrl( $archiveName, $suffix = false ) { 01387 $this->assertRepoDefined(); 01388 $ext = $this->getExtension(); 01389 $path = $this->repo->getZoneUrl( 'thumb', $ext ) . '/archive/' . 01390 $this->getHashPath() . rawurlencode( $archiveName ) . "/"; 01391 if ( $suffix === false ) { 01392 $path = substr( $path, 0, -1 ); 01393 } else { 01394 $path .= rawurlencode( $suffix ); 01395 } 01396 01397 return $path; 01398 } 01399 01407 function getZoneUrl( $zone, $suffix = false ) { 01408 $this->assertRepoDefined(); 01409 $ext = $this->getExtension(); 01410 $path = $this->repo->getZoneUrl( $zone, $ext ) . '/' . $this->getUrlRel(); 01411 if ( $suffix !== false ) { 01412 $path .= '/' . rawurlencode( $suffix ); 01413 } 01414 01415 return $path; 01416 } 01417 01424 function getThumbUrl( $suffix = false ) { 01425 return $this->getZoneUrl( 'thumb', $suffix ); 01426 } 01427 01434 function getTranscodedUrl( $suffix = false ) { 01435 return $this->getZoneUrl( 'transcoded', $suffix ); 01436 } 01437 01444 function getVirtualUrl( $suffix = false ) { 01445 $this->assertRepoDefined(); 01446 $path = $this->repo->getVirtualUrl() . '/public/' . $this->getUrlRel(); 01447 if ( $suffix !== false ) { 01448 $path .= '/' . rawurlencode( $suffix ); 01449 } 01450 01451 return $path; 01452 } 01453 01460 function getArchiveVirtualUrl( $suffix = false ) { 01461 $this->assertRepoDefined(); 01462 $path = $this->repo->getVirtualUrl() . '/public/archive/' . $this->getHashPath(); 01463 if ( $suffix === false ) { 01464 $path = substr( $path, 0, -1 ); 01465 } else { 01466 $path .= rawurlencode( $suffix ); 01467 } 01468 01469 return $path; 01470 } 01471 01478 function getThumbVirtualUrl( $suffix = false ) { 01479 $this->assertRepoDefined(); 01480 $path = $this->repo->getVirtualUrl() . '/thumb/' . $this->getUrlRel(); 01481 if ( $suffix !== false ) { 01482 $path .= '/' . rawurlencode( $suffix ); 01483 } 01484 01485 return $path; 01486 } 01487 01491 function isHashed() { 01492 $this->assertRepoDefined(); 01493 01494 return (bool)$this->repo->getHashLevels(); 01495 } 01496 01500 function readOnlyError() { 01501 throw new MWException( get_class( $this ) . ': write operations are not supported' ); 01502 } 01503 01519 function recordUpload( $oldver, $desc, $license = '', $copyStatus = '', $source = '', 01520 $watch = false, $timestamp = false, User $user = null 01521 ) { 01522 $this->readOnlyError(); 01523 } 01524 01546 function publish( $srcPath, $flags = 0, array $options = array() ) { 01547 $this->readOnlyError(); 01548 } 01549 01553 function formatMetadata() { 01554 if ( !$this->getHandler() ) { 01555 return false; 01556 } 01557 01558 return $this->getHandler()->formatMetadata( $this, $this->getMetadata() ); 01559 } 01560 01566 function isLocal() { 01567 return $this->repo && $this->repo->isLocal(); 01568 } 01569 01575 function getRepoName() { 01576 return $this->repo ? $this->repo->getName() : 'unknown'; 01577 } 01578 01584 function getRepo() { 01585 return $this->repo; 01586 } 01587 01594 function isOld() { 01595 return false; 01596 } 01597 01605 function isDeleted( $field ) { 01606 return false; 01607 } 01608 01614 function getVisibility() { 01615 return 0; 01616 } 01617 01623 function wasDeleted() { 01624 $title = $this->getTitle(); 01625 01626 return $title && $title->isDeletedQuick(); 01627 } 01628 01641 function move( $target ) { 01642 $this->readOnlyError(); 01643 } 01644 01659 function delete( $reason, $suppress = false ) { 01660 $this->readOnlyError(); 01661 } 01662 01677 function restore( $versions = array(), $unsuppress = false ) { 01678 $this->readOnlyError(); 01679 } 01680 01688 function isMultipage() { 01689 return $this->getHandler() && $this->handler->isMultiPage( $this ); 01690 } 01691 01698 function pageCount() { 01699 if ( !isset( $this->pageCount ) ) { 01700 if ( $this->getHandler() && $this->handler->isMultiPage( $this ) ) { 01701 $this->pageCount = $this->handler->pageCount( $this ); 01702 } else { 01703 $this->pageCount = false; 01704 } 01705 } 01706 01707 return $this->pageCount; 01708 } 01709 01719 static function scaleHeight( $srcWidth, $srcHeight, $dstWidth ) { 01720 // Exact integer multiply followed by division 01721 if ( $srcWidth == 0 ) { 01722 return 0; 01723 } else { 01724 return round( $srcHeight * $dstWidth / $srcWidth ); 01725 } 01726 } 01727 01735 function getImageSize( $fileName ) { 01736 if ( !$this->getHandler() ) { 01737 return false; 01738 } 01739 01740 return $this->handler->getImageSize( $this, $fileName ); 01741 } 01742 01749 function getDescriptionUrl() { 01750 if ( $this->repo ) { 01751 return $this->repo->getDescriptionUrl( $this->getName() ); 01752 } else { 01753 return false; 01754 } 01755 } 01756 01763 function getDescriptionText( $lang = false ) { 01764 global $wgMemc, $wgLang; 01765 if ( !$this->repo || !$this->repo->fetchDescription ) { 01766 return false; 01767 } 01768 if ( !$lang ) { 01769 $lang = $wgLang; 01770 } 01771 $renderUrl = $this->repo->getDescriptionRenderUrl( $this->getName(), $lang->getCode() ); 01772 if ( $renderUrl ) { 01773 if ( $this->repo->descriptionCacheExpiry > 0 ) { 01774 wfDebug( "Attempting to get the description from cache..." ); 01775 $key = $this->repo->getLocalCacheKey( 01776 'RemoteFileDescription', 01777 'url', 01778 $lang->getCode(), 01779 $this->getName() 01780 ); 01781 $obj = $wgMemc->get( $key ); 01782 if ( $obj ) { 01783 wfDebug( "success!\n" ); 01784 01785 return $obj; 01786 } 01787 wfDebug( "miss\n" ); 01788 } 01789 wfDebug( "Fetching shared description from $renderUrl\n" ); 01790 $res = Http::get( $renderUrl ); 01791 if ( $res && $this->repo->descriptionCacheExpiry > 0 ) { 01792 $wgMemc->set( $key, $res, $this->repo->descriptionCacheExpiry ); 01793 } 01794 01795 return $res; 01796 } else { 01797 return false; 01798 } 01799 } 01800 01813 function getDescription( $audience = self::FOR_PUBLIC, User $user = null ) { 01814 return null; 01815 } 01816 01822 function getTimestamp() { 01823 $this->assertRepoDefined(); 01824 01825 return $this->repo->getFileTimestamp( $this->getPath() ); 01826 } 01827 01833 function getSha1() { 01834 $this->assertRepoDefined(); 01835 01836 return $this->repo->getFileSha1( $this->getPath() ); 01837 } 01838 01844 function getStorageKey() { 01845 $hash = $this->getSha1(); 01846 if ( !$hash ) { 01847 return false; 01848 } 01849 $ext = $this->getExtension(); 01850 $dotExt = $ext === '' ? '' : ".$ext"; 01851 01852 return $hash . $dotExt; 01853 } 01854 01863 function userCan( $field, User $user = null ) { 01864 return true; 01865 } 01866 01877 static function getPropsFromPath( $path, $ext = true ) { 01878 wfDebug( __METHOD__ . ": Getting file info for $path\n" ); 01879 wfDeprecated( __METHOD__, '1.19' ); 01880 01881 $fsFile = new FSFile( $path ); 01882 01883 return $fsFile->getProps(); 01884 } 01885 01897 static function sha1Base36( $path ) { 01898 wfDeprecated( __METHOD__, '1.19' ); 01899 01900 $fsFile = new FSFile( $path ); 01901 01902 return $fsFile->getSha1Base36(); 01903 } 01904 01908 function getStreamHeaders() { 01909 $handler = $this->getHandler(); 01910 if ( $handler ) { 01911 return $handler->getStreamHeaders( $this->getMetadata() ); 01912 } else { 01913 return array(); 01914 } 01915 } 01916 01920 function getLongDesc() { 01921 $handler = $this->getHandler(); 01922 if ( $handler ) { 01923 return $handler->getLongDesc( $this ); 01924 } else { 01925 return MediaHandler::getGeneralLongDesc( $this ); 01926 } 01927 } 01928 01932 function getShortDesc() { 01933 $handler = $this->getHandler(); 01934 if ( $handler ) { 01935 return $handler->getShortDesc( $this ); 01936 } else { 01937 return MediaHandler::getGeneralShortDesc( $this ); 01938 } 01939 } 01940 01944 function getDimensionsString() { 01945 $handler = $this->getHandler(); 01946 if ( $handler ) { 01947 return $handler->getDimensionsString( $this ); 01948 } else { 01949 return ''; 01950 } 01951 } 01952 01956 function getRedirected() { 01957 return $this->redirected; 01958 } 01959 01963 function getRedirectedTitle() { 01964 if ( $this->redirected ) { 01965 if ( !$this->redirectTitle ) { 01966 $this->redirectTitle = Title::makeTitle( NS_FILE, $this->redirected ); 01967 } 01968 01969 return $this->redirectTitle; 01970 } 01971 01972 return null; 01973 } 01974 01979 function redirectedFrom( $from ) { 01980 $this->redirected = $from; 01981 } 01982 01986 function isMissing() { 01987 return false; 01988 } 01989 01994 public function isCacheable() { 01995 return true; 01996 } 01997 02002 protected function assertRepoDefined() { 02003 if ( !( $this->repo instanceof $this->repoClass ) ) { 02004 throw new MWException( "A {$this->repoClass} object is not set for this File.\n" ); 02005 } 02006 } 02007 02012 protected function assertTitleDefined() { 02013 if ( !( $this->title instanceof Title ) ) { 02014 throw new MWException( "A Title object is not set for this File.\n" ); 02015 } 02016 } 02017 }