MediaWiki
REL1_22
|
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 00097 var $repo; 00098 00102 var $title; 00103 00104 var $lastError, $redirected, $redirectedTitle; 00105 00109 protected $fsFile; 00110 00114 protected $handler; 00115 00119 protected $url, $extension, $name, $path, $hashPath, $pageCount, $transformScript; 00120 00121 protected $redirectTitle; 00122 00126 protected $canRender, $isSafeFile; 00127 00131 protected $repoClass = 'FileRepo'; 00132 00143 function __construct( $title, $repo ) { 00144 if ( $title !== false ) { // subclasses may not use MW titles 00145 $title = self::normalizeTitle( $title, 'exception' ); 00146 } 00147 $this->title = $title; 00148 $this->repo = $repo; 00149 } 00150 00160 static function normalizeTitle( $title, $exception = false ) { 00161 $ret = $title; 00162 if ( $ret instanceof Title ) { 00163 # Normalize NS_MEDIA -> NS_FILE 00164 if ( $ret->getNamespace() == NS_MEDIA ) { 00165 $ret = Title::makeTitleSafe( NS_FILE, $ret->getDBkey() ); 00166 # Sanity check the title namespace 00167 } elseif ( $ret->getNamespace() !== NS_FILE ) { 00168 $ret = null; 00169 } 00170 } else { 00171 # Convert strings to Title objects 00172 $ret = Title::makeTitleSafe( NS_FILE, (string)$ret ); 00173 } 00174 if ( !$ret && $exception !== false ) { 00175 throw new MWException( "`$title` is not a valid file title." ); 00176 } 00177 return $ret; 00178 } 00179 00180 function __get( $name ) { 00181 $function = array( $this, 'get' . ucfirst( $name ) ); 00182 if ( !is_callable( $function ) ) { 00183 return null; 00184 } else { 00185 $this->$name = call_user_func( $function ); 00186 return $this->$name; 00187 } 00188 } 00189 00197 static function normalizeExtension( $ext ) { 00198 $lower = strtolower( $ext ); 00199 $squish = array( 00200 'htm' => 'html', 00201 'jpeg' => 'jpg', 00202 'mpeg' => 'mpg', 00203 'tiff' => 'tif', 00204 'ogv' => 'ogg' ); 00205 if ( isset( $squish[$lower] ) ) { 00206 return $squish[$lower]; 00207 } elseif ( preg_match( '/^[0-9a-z]+$/', $lower ) ) { 00208 return $lower; 00209 } else { 00210 return ''; 00211 } 00212 } 00213 00222 static function checkExtensionCompatibility( File $old, $new ) { 00223 $oldMime = $old->getMimeType(); 00224 $n = strrpos( $new, '.' ); 00225 $newExt = self::normalizeExtension( $n ? substr( $new, $n + 1 ) : '' ); 00226 $mimeMagic = MimeMagic::singleton(); 00227 return $mimeMagic->isMatchingExtension( $newExt, $oldMime ); 00228 } 00229 00235 function upgradeRow() {} 00236 00244 public static function splitMime( $mime ) { 00245 if ( strpos( $mime, '/' ) !== false ) { 00246 return explode( '/', $mime, 2 ); 00247 } else { 00248 return array( $mime, 'unknown' ); 00249 } 00250 } 00251 00260 public static function compare( File $a, File $b ) { 00261 return strcmp( $a->getName(), $b->getName() ); 00262 } 00263 00269 public function getName() { 00270 if ( !isset( $this->name ) ) { 00271 $this->assertRepoDefined(); 00272 $this->name = $this->repo->getNameFromTitle( $this->title ); 00273 } 00274 return $this->name; 00275 } 00276 00282 function getExtension() { 00283 if ( !isset( $this->extension ) ) { 00284 $n = strrpos( $this->getName(), '.' ); 00285 $this->extension = self::normalizeExtension( 00286 $n ? substr( $this->getName(), $n + 1 ) : '' ); 00287 } 00288 return $this->extension; 00289 } 00290 00296 public function getTitle() { 00297 return $this->title; 00298 } 00299 00305 public function getOriginalTitle() { 00306 if ( $this->redirected ) { 00307 return $this->getRedirectedTitle(); 00308 } 00309 return $this->title; 00310 } 00311 00317 public function getUrl() { 00318 if ( !isset( $this->url ) ) { 00319 $this->assertRepoDefined(); 00320 $ext = $this->getExtension(); 00321 $this->url = $this->repo->getZoneUrl( 'public', $ext ) . '/' . $this->getUrlRel(); 00322 } 00323 return $this->url; 00324 } 00325 00333 public function getFullUrl() { 00334 return wfExpandUrl( $this->getUrl(), PROTO_RELATIVE ); 00335 } 00336 00340 public function getCanonicalUrl() { 00341 return wfExpandUrl( $this->getUrl(), PROTO_CANONICAL ); 00342 } 00343 00347 function getViewURL() { 00348 if ( $this->mustRender() ) { 00349 if ( $this->canRender() ) { 00350 return $this->createThumb( $this->getWidth() ); 00351 } else { 00352 wfDebug( __METHOD__ . ': supposed to render ' . $this->getName() . 00353 ' (' . $this->getMimeType() . "), but can't!\n" ); 00354 return $this->getURL(); #hm... return NULL? 00355 } 00356 } else { 00357 return $this->getURL(); 00358 } 00359 } 00360 00374 public function getPath() { 00375 if ( !isset( $this->path ) ) { 00376 $this->assertRepoDefined(); 00377 $this->path = $this->repo->getZonePath( 'public' ) . '/' . $this->getRel(); 00378 } 00379 return $this->path; 00380 } 00381 00389 public function getLocalRefPath() { 00390 $this->assertRepoDefined(); 00391 if ( !isset( $this->fsFile ) ) { 00392 $this->fsFile = $this->repo->getLocalReference( $this->getPath() ); 00393 if ( !$this->fsFile ) { 00394 $this->fsFile = false; // null => false; cache negative hits 00395 } 00396 } 00397 return ( $this->fsFile ) 00398 ? $this->fsFile->getPath() 00399 : false; 00400 } 00401 00413 public function getWidth( $page = 1 ) { 00414 return false; 00415 } 00416 00428 public function getHeight( $page = 1 ) { 00429 return false; 00430 } 00431 00440 public function getUser( $type = 'text' ) { 00441 return null; 00442 } 00443 00449 public function getLength() { 00450 $handler = $this->getHandler(); 00451 if ( $handler ) { 00452 return $handler->getLength( $this ); 00453 } else { 00454 return 0; 00455 } 00456 } 00457 00463 public function isVectorized() { 00464 $handler = $this->getHandler(); 00465 if ( $handler ) { 00466 return $handler->isVectorized( $this ); 00467 } else { 00468 return false; 00469 } 00470 } 00471 00482 public function canAnimateThumbIfAppropriate() { 00483 $handler = $this->getHandler(); 00484 if ( !$handler ) { 00485 // We cannot handle image whatsoever, thus 00486 // one would not expect it to be animated 00487 // so true. 00488 return true; 00489 } else { 00490 if ( $this->allowInlineDisplay() 00491 && $handler->isAnimatedImage( $this ) 00492 && !$handler->canAnimateThumbnail( $this ) 00493 ) { 00494 // Image is animated, but thumbnail isn't. 00495 // This is unexpected to the user. 00496 return false; 00497 } else { 00498 // Image is not animated, so one would 00499 // not expect thumb to be 00500 return true; 00501 } 00502 } 00503 } 00504 00511 public function getMetadata() { 00512 return false; 00513 } 00514 00522 public function convertMetadataVersion( $metadata, $version ) { 00523 $handler = $this->getHandler(); 00524 if ( !is_array( $metadata ) ) { 00525 // Just to make the return type consistent 00526 $metadata = unserialize( $metadata ); 00527 } 00528 if ( $handler ) { 00529 return $handler->convertMetadataVersion( $metadata, $version ); 00530 } else { 00531 return $metadata; 00532 } 00533 } 00534 00541 public function getBitDepth() { 00542 return 0; 00543 } 00544 00551 public function getSize() { 00552 return false; 00553 } 00554 00562 function getMimeType() { 00563 return 'unknown/unknown'; 00564 } 00565 00573 function getMediaType() { 00574 return MEDIATYPE_UNKNOWN; 00575 } 00576 00589 function canRender() { 00590 if ( !isset( $this->canRender ) ) { 00591 $this->canRender = $this->getHandler() && $this->handler->canRender( $this ); 00592 } 00593 return $this->canRender; 00594 } 00595 00600 protected function getCanRender() { 00601 return $this->canRender(); 00602 } 00603 00614 function mustRender() { 00615 return $this->getHandler() && $this->handler->mustRender( $this ); 00616 } 00617 00623 function allowInlineDisplay() { 00624 return $this->canRender(); 00625 } 00626 00640 function isSafeFile() { 00641 if ( !isset( $this->isSafeFile ) ) { 00642 $this->isSafeFile = $this->_getIsSafeFile(); 00643 } 00644 return $this->isSafeFile; 00645 } 00646 00652 protected function getIsSafeFile() { 00653 return $this->isSafeFile(); 00654 } 00655 00661 protected function _getIsSafeFile() { 00662 global $wgTrustedMediaFormats; 00663 00664 if ( $this->allowInlineDisplay() ) { 00665 return true; 00666 } 00667 if ( $this->isTrustedFile() ) { 00668 return true; 00669 } 00670 00671 $type = $this->getMediaType(); 00672 $mime = $this->getMimeType(); 00673 #wfDebug( "LocalFile::isSafeFile: type= $type, mime= $mime\n" ); 00674 00675 if ( !$type || $type === MEDIATYPE_UNKNOWN ) { 00676 return false; #unknown type, not trusted 00677 } 00678 if ( in_array( $type, $wgTrustedMediaFormats ) ) { 00679 return true; 00680 } 00681 00682 if ( $mime === "unknown/unknown" ) { 00683 return false; #unknown type, not trusted 00684 } 00685 if ( in_array( $mime, $wgTrustedMediaFormats ) ) { 00686 return true; 00687 } 00688 00689 return false; 00690 } 00691 00705 function isTrustedFile() { 00706 #this could be implemented to check a flag in the database, 00707 #look for signatures, etc 00708 return false; 00709 } 00710 00718 public function exists() { 00719 return $this->getPath() && $this->repo->fileExists( $this->path ); 00720 } 00721 00728 public function isVisible() { 00729 return $this->exists(); 00730 } 00731 00735 function getTransformScript() { 00736 if ( !isset( $this->transformScript ) ) { 00737 $this->transformScript = false; 00738 if ( $this->repo ) { 00739 $script = $this->repo->getThumbScriptUrl(); 00740 if ( $script ) { 00741 $this->transformScript = wfAppendQuery( $script, array( 'f' => $this->getName() ) ); 00742 } 00743 } 00744 } 00745 return $this->transformScript; 00746 } 00747 00755 function getUnscaledThumb( $handlerParams = array() ) { 00756 $hp =& $handlerParams; 00757 $page = isset( $hp['page'] ) ? $hp['page'] : false; 00758 $width = $this->getWidth( $page ); 00759 if ( !$width ) { 00760 return $this->iconThumb(); 00761 } 00762 $hp['width'] = $width; 00763 return $this->transform( $hp ); 00764 } 00765 00775 public function thumbName( $params, $flags = 0 ) { 00776 $name = ( $this->repo && !( $flags & self::THUMB_FULL_NAME ) ) 00777 ? $this->repo->nameForThumb( $this->getName() ) 00778 : $this->getName(); 00779 return $this->generateThumbName( $name, $params ); 00780 } 00781 00790 public function generateThumbName( $name, $params ) { 00791 if ( !$this->getHandler() ) { 00792 return null; 00793 } 00794 $extension = $this->getExtension(); 00795 list( $thumbExt, $thumbMime ) = $this->handler->getThumbType( 00796 $extension, $this->getMimeType(), $params ); 00797 $thumbName = $this->handler->makeParamString( $params ) . '-' . $name; 00798 if ( $thumbExt != $extension ) { 00799 $thumbName .= ".$thumbExt"; 00800 } 00801 return $thumbName; 00802 } 00803 00821 public function createThumb( $width, $height = -1 ) { 00822 $params = array( 'width' => $width ); 00823 if ( $height != -1 ) { 00824 $params['height'] = $height; 00825 } 00826 $thumb = $this->transform( $params ); 00827 if ( is_null( $thumb ) || $thumb->isError() ) { 00828 return ''; 00829 } 00830 return $thumb->getUrl(); 00831 } 00832 00842 protected function transformErrorOutput( $thumbPath, $thumbUrl, $params, $flags ) { 00843 global $wgIgnoreImageErrors; 00844 00845 $handler = $this->getHandler(); 00846 if ( $handler && $wgIgnoreImageErrors && !( $flags & self::RENDER_NOW ) ) { 00847 return $handler->getTransform( $this, $thumbPath, $thumbUrl, $params ); 00848 } else { 00849 return new MediaTransformError( 'thumbnail_error', 00850 $params['width'], 0, wfMessage( 'thumbnail-dest-create' )->text() ); 00851 } 00852 } 00853 00862 function transform( $params, $flags = 0 ) { 00863 global $wgUseSquid, $wgIgnoreImageErrors, $wgThumbnailEpoch; 00864 00865 wfProfileIn( __METHOD__ ); 00866 do { 00867 if ( !$this->canRender() ) { 00868 $thumb = $this->iconThumb(); 00869 break; // not a bitmap or renderable image, don't try 00870 } 00871 00872 // Get the descriptionUrl to embed it as comment into the thumbnail. Bug 19791. 00873 $descriptionUrl = $this->getDescriptionUrl(); 00874 if ( $descriptionUrl ) { 00875 $params['descriptionUrl'] = wfExpandUrl( $descriptionUrl, PROTO_CANONICAL ); 00876 } 00877 00878 $handler = $this->getHandler(); 00879 $script = $this->getTransformScript(); 00880 if ( $script && !( $flags & self::RENDER_NOW ) ) { 00881 // Use a script to transform on client request, if possible 00882 $thumb = $handler->getScriptedTransform( $this, $script, $params ); 00883 if ( $thumb ) { 00884 break; 00885 } 00886 } 00887 00888 $normalisedParams = $params; 00889 $handler->normaliseParams( $this, $normalisedParams ); 00890 00891 $thumbName = $this->thumbName( $normalisedParams ); 00892 $thumbUrl = $this->getThumbUrl( $thumbName ); 00893 $thumbPath = $this->getThumbPath( $thumbName ); // final thumb path 00894 00895 if ( $this->repo ) { 00896 // Defer rendering if a 404 handler is set up... 00897 if ( $this->repo->canTransformVia404() && !( $flags & self::RENDER_NOW ) ) { 00898 wfDebug( __METHOD__ . " transformation deferred." ); 00899 // XXX: Pass in the storage path even though we are not rendering anything 00900 // and the path is supposed to be an FS path. This is due to getScalerType() 00901 // getting called on the path and clobbering $thumb->getUrl() if it's false. 00902 $thumb = $handler->getTransform( $this, $thumbPath, $thumbUrl, $params ); 00903 break; 00904 } 00905 // Clean up broken thumbnails as needed 00906 $this->migrateThumbFile( $thumbName ); 00907 // Check if an up-to-date thumbnail already exists... 00908 wfDebug( __METHOD__ . ": Doing stat for $thumbPath\n" ); 00909 if ( !( $flags & self::RENDER_FORCE ) && $this->repo->fileExists( $thumbPath ) ) { 00910 $timestamp = $this->repo->getFileTimestamp( $thumbPath ); 00911 if ( $timestamp !== false && $timestamp >= $wgThumbnailEpoch ) { 00912 // XXX: Pass in the storage path even though we are not rendering anything 00913 // and the path is supposed to be an FS path. This is due to getScalerType() 00914 // getting called on the path and clobbering $thumb->getUrl() if it's false. 00915 $thumb = $handler->getTransform( $this, $thumbPath, $thumbUrl, $params ); 00916 $thumb->setStoragePath( $thumbPath ); 00917 break; 00918 } 00919 } elseif ( $flags & self::RENDER_FORCE ) { 00920 wfDebug( __METHOD__ . " forcing rendering per flag File::RENDER_FORCE\n" ); 00921 } 00922 } 00923 00924 // If the backend is ready-only, don't keep generating thumbnails 00925 // only to return transformation errors, just return the error now. 00926 if ( $this->repo->getReadOnlyReason() !== false ) { 00927 $thumb = $this->transformErrorOutput( $thumbPath, $thumbUrl, $params, $flags ); 00928 break; 00929 } 00930 00931 // Create a temp FS file with the same extension and the thumbnail 00932 $thumbExt = FileBackend::extensionFromPath( $thumbPath ); 00933 $tmpFile = TempFSFile::factory( 'transform_', $thumbExt ); 00934 if ( !$tmpFile ) { 00935 $thumb = $this->transformErrorOutput( $thumbPath, $thumbUrl, $params, $flags ); 00936 break; 00937 } 00938 $tmpThumbPath = $tmpFile->getPath(); // path of 0-byte temp file 00939 00940 // Actually render the thumbnail... 00941 wfProfileIn( __METHOD__ . '-doTransform' ); 00942 $thumb = $handler->doTransform( $this, $tmpThumbPath, $thumbUrl, $params ); 00943 wfProfileOut( __METHOD__ . '-doTransform' ); 00944 $tmpFile->bind( $thumb ); // keep alive with $thumb 00945 00946 if ( !$thumb ) { // bad params? 00947 $thumb = null; 00948 } elseif ( $thumb->isError() ) { // transform error 00949 $this->lastError = $thumb->toText(); 00950 // Ignore errors if requested 00951 if ( $wgIgnoreImageErrors && !( $flags & self::RENDER_NOW ) ) { 00952 $thumb = $handler->getTransform( $this, $tmpThumbPath, $thumbUrl, $params ); 00953 } 00954 } elseif ( $this->repo && $thumb->hasFile() && !$thumb->fileIsSource() ) { 00955 // Copy the thumbnail from the file system into storage... 00956 $disposition = $this->getThumbDisposition( $thumbName ); 00957 $status = $this->repo->quickImport( $tmpThumbPath, $thumbPath, $disposition ); 00958 if ( $status->isOK() ) { 00959 $thumb->setStoragePath( $thumbPath ); 00960 } else { 00961 $thumb = $this->transformErrorOutput( $thumbPath, $thumbUrl, $params, $flags ); 00962 } 00963 // Give extensions a chance to do something with this thumbnail... 00964 wfRunHooks( 'FileTransformed', array( $this, $thumb, $tmpThumbPath, $thumbPath ) ); 00965 } 00966 00967 // Purge. Useful in the event of Core -> Squid connection failure or squid 00968 // purge collisions from elsewhere during failure. Don't keep triggering for 00969 // "thumbs" which have the main image URL though (bug 13776) 00970 if ( $wgUseSquid ) { 00971 if ( !$thumb || $thumb->isError() || $thumb->getUrl() != $this->getURL() ) { 00972 SquidUpdate::purge( array( $thumbUrl ) ); 00973 } 00974 } 00975 } while ( false ); 00976 00977 wfProfileOut( __METHOD__ ); 00978 return is_object( $thumb ) ? $thumb : false; 00979 } 00980 00985 function getThumbDisposition( $thumbName ) { 00986 $fileName = $this->name; // file name to suggest 00987 $thumbExt = FileBackend::extensionFromPath( $thumbName ); 00988 if ( $thumbExt != '' && $thumbExt !== $this->getExtension() ) { 00989 $fileName .= ".$thumbExt"; 00990 } 00991 return FileBackend::makeContentDisposition( 'inline', $fileName ); 00992 } 00993 00999 function migrateThumbFile( $thumbName ) {} 01000 01006 function getHandler() { 01007 if ( !isset( $this->handler ) ) { 01008 $this->handler = MediaHandler::getHandler( $this->getMimeType() ); 01009 } 01010 return $this->handler; 01011 } 01012 01018 function iconThumb() { 01019 global $wgStylePath, $wgStyleDirectory; 01020 01021 $try = array( 'fileicon-' . $this->getExtension() . '.png', 'fileicon.png' ); 01022 foreach ( $try as $icon ) { 01023 $path = '/common/images/icons/' . $icon; 01024 $filepath = $wgStyleDirectory . $path; 01025 if ( file_exists( $filepath ) ) { // always FS 01026 $params = array( 'width' => 120, 'height' => 120 ); 01027 return new ThumbnailImage( $this, $wgStylePath . $path, false, $params ); 01028 } 01029 } 01030 return null; 01031 } 01032 01037 function getLastError() { 01038 return $this->lastError; 01039 } 01040 01047 function getThumbnails() { 01048 return array(); 01049 } 01050 01058 function purgeCache( $options = array() ) {} 01059 01065 function purgeDescription() { 01066 $title = $this->getTitle(); 01067 if ( $title ) { 01068 $title->invalidateCache(); 01069 $title->purgeSquid(); 01070 } 01071 } 01072 01077 function purgeEverything() { 01078 // Delete thumbnails and refresh file metadata cache 01079 $this->purgeCache(); 01080 $this->purgeDescription(); 01081 01082 // Purge cache of all pages using this file 01083 $title = $this->getTitle(); 01084 if ( $title ) { 01085 $update = new HTMLCacheUpdate( $title, 'imagelinks' ); 01086 $update->doUpdate(); 01087 } 01088 } 01089 01101 function getHistory( $limit = null, $start = null, $end = null, $inc = true ) { 01102 return array(); 01103 } 01104 01114 public function nextHistoryLine() { 01115 return false; 01116 } 01117 01124 public function resetHistory() {} 01125 01133 function getHashPath() { 01134 if ( !isset( $this->hashPath ) ) { 01135 $this->assertRepoDefined(); 01136 $this->hashPath = $this->repo->getHashPath( $this->getName() ); 01137 } 01138 return $this->hashPath; 01139 } 01140 01147 function getRel() { 01148 return $this->getHashPath() . $this->getName(); 01149 } 01150 01158 function getArchiveRel( $suffix = false ) { 01159 $path = 'archive/' . $this->getHashPath(); 01160 if ( $suffix === false ) { 01161 $path = substr( $path, 0, -1 ); 01162 } else { 01163 $path .= $suffix; 01164 } 01165 return $path; 01166 } 01167 01176 function getThumbRel( $suffix = false ) { 01177 $path = $this->getRel(); 01178 if ( $suffix !== false ) { 01179 $path .= '/' . $suffix; 01180 } 01181 return $path; 01182 } 01183 01190 function getUrlRel() { 01191 return $this->getHashPath() . rawurlencode( $this->getName() ); 01192 } 01193 01203 function getArchiveThumbRel( $archiveName, $suffix = false ) { 01204 $path = 'archive/' . $this->getHashPath() . $archiveName . "/"; 01205 if ( $suffix === false ) { 01206 $path = substr( $path, 0, -1 ); 01207 } else { 01208 $path .= $suffix; 01209 } 01210 return $path; 01211 } 01212 01220 function getArchivePath( $suffix = false ) { 01221 $this->assertRepoDefined(); 01222 return $this->repo->getZonePath( 'public' ) . '/' . $this->getArchiveRel( $suffix ); 01223 } 01224 01233 function getArchiveThumbPath( $archiveName, $suffix = false ) { 01234 $this->assertRepoDefined(); 01235 return $this->repo->getZonePath( 'thumb' ) . '/' . 01236 $this->getArchiveThumbRel( $archiveName, $suffix ); 01237 } 01238 01246 function getThumbPath( $suffix = false ) { 01247 $this->assertRepoDefined(); 01248 return $this->repo->getZonePath( 'thumb' ) . '/' . $this->getThumbRel( $suffix ); 01249 } 01250 01258 function getTranscodedPath( $suffix = false ) { 01259 $this->assertRepoDefined(); 01260 return $this->repo->getZonePath( 'transcoded' ) . '/' . $this->getThumbRel( $suffix ); 01261 } 01262 01270 function getArchiveUrl( $suffix = false ) { 01271 $this->assertRepoDefined(); 01272 $ext = $this->getExtension(); 01273 $path = $this->repo->getZoneUrl( 'public', $ext ) . '/archive/' . $this->getHashPath(); 01274 if ( $suffix === false ) { 01275 $path = substr( $path, 0, -1 ); 01276 } else { 01277 $path .= rawurlencode( $suffix ); 01278 } 01279 return $path; 01280 } 01281 01290 function getArchiveThumbUrl( $archiveName, $suffix = false ) { 01291 $this->assertRepoDefined(); 01292 $ext = $this->getExtension(); 01293 $path = $this->repo->getZoneUrl( 'thumb', $ext ) . '/archive/' . 01294 $this->getHashPath() . rawurlencode( $archiveName ) . "/"; 01295 if ( $suffix === false ) { 01296 $path = substr( $path, 0, -1 ); 01297 } else { 01298 $path .= rawurlencode( $suffix ); 01299 } 01300 return $path; 01301 } 01302 01311 function getZoneUrl( $zone, $suffix = false ) { 01312 $this->assertRepoDefined(); 01313 $ext = $this->getExtension(); 01314 $path = $this->repo->getZoneUrl( $zone, $ext ) . '/' . $this->getUrlRel(); 01315 if ( $suffix !== false ) { 01316 $path .= '/' . rawurlencode( $suffix ); 01317 } 01318 return $path; 01319 } 01320 01328 function getThumbUrl( $suffix = false ) { 01329 return $this->getZoneUrl( 'thumb', $suffix ); 01330 } 01331 01339 function getTranscodedUrl( $suffix = false ) { 01340 return $this->getZoneUrl( 'transcoded', $suffix ); 01341 } 01342 01350 function getVirtualUrl( $suffix = false ) { 01351 $this->assertRepoDefined(); 01352 $path = $this->repo->getVirtualUrl() . '/public/' . $this->getUrlRel(); 01353 if ( $suffix !== false ) { 01354 $path .= '/' . rawurlencode( $suffix ); 01355 } 01356 return $path; 01357 } 01358 01366 function getArchiveVirtualUrl( $suffix = false ) { 01367 $this->assertRepoDefined(); 01368 $path = $this->repo->getVirtualUrl() . '/public/archive/' . $this->getHashPath(); 01369 if ( $suffix === false ) { 01370 $path = substr( $path, 0, -1 ); 01371 } else { 01372 $path .= rawurlencode( $suffix ); 01373 } 01374 return $path; 01375 } 01376 01384 function getThumbVirtualUrl( $suffix = false ) { 01385 $this->assertRepoDefined(); 01386 $path = $this->repo->getVirtualUrl() . '/thumb/' . $this->getUrlRel(); 01387 if ( $suffix !== false ) { 01388 $path .= '/' . rawurlencode( $suffix ); 01389 } 01390 return $path; 01391 } 01392 01396 function isHashed() { 01397 $this->assertRepoDefined(); 01398 return (bool)$this->repo->getHashLevels(); 01399 } 01400 01404 function readOnlyError() { 01405 throw new MWException( get_class( $this ) . ': write operations are not supported' ); 01406 } 01407 01423 function recordUpload( $oldver, $desc, $license = '', $copyStatus = '', $source = '', $watch = false, $timestamp = false, User $user = null ) { 01424 $this->readOnlyError(); 01425 } 01426 01449 function publish( $srcPath, $flags = 0, array $options = array() ) { 01450 $this->readOnlyError(); 01451 } 01452 01456 function formatMetadata() { 01457 if ( !$this->getHandler() ) { 01458 return false; 01459 } 01460 return $this->getHandler()->formatMetadata( $this, $this->getMetadata() ); 01461 } 01462 01468 function isLocal() { 01469 return $this->repo && $this->repo->isLocal(); 01470 } 01471 01477 function getRepoName() { 01478 return $this->repo ? $this->repo->getName() : 'unknown'; 01479 } 01480 01486 function getRepo() { 01487 return $this->repo; 01488 } 01489 01496 function isOld() { 01497 return false; 01498 } 01499 01508 function isDeleted( $field ) { 01509 return false; 01510 } 01511 01517 function getVisibility() { 01518 return 0; 01519 } 01520 01526 function wasDeleted() { 01527 $title = $this->getTitle(); 01528 return $title && $title->isDeletedQuick(); 01529 } 01530 01543 function move( $target ) { 01544 $this->readOnlyError(); 01545 } 01546 01561 function delete( $reason, $suppress = false ) { 01562 $this->readOnlyError(); 01563 } 01564 01579 function restore( $versions = array(), $unsuppress = false ) { 01580 $this->readOnlyError(); 01581 } 01582 01590 function isMultipage() { 01591 return $this->getHandler() && $this->handler->isMultiPage( $this ); 01592 } 01593 01600 function pageCount() { 01601 if ( !isset( $this->pageCount ) ) { 01602 if ( $this->getHandler() && $this->handler->isMultiPage( $this ) ) { 01603 $this->pageCount = $this->handler->pageCount( $this ); 01604 } else { 01605 $this->pageCount = false; 01606 } 01607 } 01608 return $this->pageCount; 01609 } 01610 01620 static function scaleHeight( $srcWidth, $srcHeight, $dstWidth ) { 01621 // Exact integer multiply followed by division 01622 if ( $srcWidth == 0 ) { 01623 return 0; 01624 } else { 01625 return round( $srcHeight * $dstWidth / $srcWidth ); 01626 } 01627 } 01628 01636 function getImageSize( $fileName ) { 01637 if ( !$this->getHandler() ) { 01638 return false; 01639 } 01640 return $this->handler->getImageSize( $this, $fileName ); 01641 } 01642 01649 function getDescriptionUrl() { 01650 if ( $this->repo ) { 01651 return $this->repo->getDescriptionUrl( $this->getName() ); 01652 } else { 01653 return false; 01654 } 01655 } 01656 01663 function getDescriptionText( $lang = false ) { 01664 global $wgMemc, $wgLang; 01665 if ( !$this->repo || !$this->repo->fetchDescription ) { 01666 return false; 01667 } 01668 if ( !$lang ) { 01669 $lang = $wgLang; 01670 } 01671 $renderUrl = $this->repo->getDescriptionRenderUrl( $this->getName(), $lang->getCode() ); 01672 if ( $renderUrl ) { 01673 if ( $this->repo->descriptionCacheExpiry > 0 ) { 01674 wfDebug( "Attempting to get the description from cache..." ); 01675 $key = $this->repo->getLocalCacheKey( 'RemoteFileDescription', 'url', $lang->getCode(), 01676 $this->getName() ); 01677 $obj = $wgMemc->get( $key ); 01678 if ( $obj ) { 01679 wfDebug( "success!\n" ); 01680 return $obj; 01681 } 01682 wfDebug( "miss\n" ); 01683 } 01684 wfDebug( "Fetching shared description from $renderUrl\n" ); 01685 $res = Http::get( $renderUrl ); 01686 if ( $res && $this->repo->descriptionCacheExpiry > 0 ) { 01687 $wgMemc->set( $key, $res, $this->repo->descriptionCacheExpiry ); 01688 } 01689 return $res; 01690 } else { 01691 return false; 01692 } 01693 } 01694 01707 function getDescription( $audience = self::FOR_PUBLIC, User $user = null ) { 01708 return null; 01709 } 01710 01716 function getTimestamp() { 01717 $this->assertRepoDefined(); 01718 return $this->repo->getFileTimestamp( $this->getPath() ); 01719 } 01720 01726 function getSha1() { 01727 $this->assertRepoDefined(); 01728 return $this->repo->getFileSha1( $this->getPath() ); 01729 } 01730 01736 function getStorageKey() { 01737 $hash = $this->getSha1(); 01738 if ( !$hash ) { 01739 return false; 01740 } 01741 $ext = $this->getExtension(); 01742 $dotExt = $ext === '' ? '' : ".$ext"; 01743 return $hash . $dotExt; 01744 } 01745 01754 function userCan( $field, User $user = null ) { 01755 return true; 01756 } 01757 01768 static function getPropsFromPath( $path, $ext = true ) { 01769 wfDebug( __METHOD__ . ": Getting file info for $path\n" ); 01770 wfDeprecated( __METHOD__, '1.19' ); 01771 01772 $fsFile = new FSFile( $path ); 01773 return $fsFile->getProps(); 01774 } 01775 01788 static function sha1Base36( $path ) { 01789 wfDeprecated( __METHOD__, '1.19' ); 01790 01791 $fsFile = new FSFile( $path ); 01792 return $fsFile->getSha1Base36(); 01793 } 01794 01798 function getStreamHeaders() { 01799 $handler = $this->getHandler(); 01800 if ( $handler ) { 01801 return $handler->getStreamHeaders( $this->getMetadata() ); 01802 } else { 01803 return array(); 01804 } 01805 } 01806 01810 function getLongDesc() { 01811 $handler = $this->getHandler(); 01812 if ( $handler ) { 01813 return $handler->getLongDesc( $this ); 01814 } else { 01815 return MediaHandler::getGeneralLongDesc( $this ); 01816 } 01817 } 01818 01822 function getShortDesc() { 01823 $handler = $this->getHandler(); 01824 if ( $handler ) { 01825 return $handler->getShortDesc( $this ); 01826 } else { 01827 return MediaHandler::getGeneralShortDesc( $this ); 01828 } 01829 } 01830 01834 function getDimensionsString() { 01835 $handler = $this->getHandler(); 01836 if ( $handler ) { 01837 return $handler->getDimensionsString( $this ); 01838 } else { 01839 return ''; 01840 } 01841 } 01842 01846 function getRedirected() { 01847 return $this->redirected; 01848 } 01849 01853 function getRedirectedTitle() { 01854 if ( $this->redirected ) { 01855 if ( !$this->redirectTitle ) { 01856 $this->redirectTitle = Title::makeTitle( NS_FILE, $this->redirected ); 01857 } 01858 return $this->redirectTitle; 01859 } 01860 return null; 01861 } 01862 01867 function redirectedFrom( $from ) { 01868 $this->redirected = $from; 01869 } 01870 01874 function isMissing() { 01875 return false; 01876 } 01877 01882 public function isCacheable() { 01883 return true; 01884 } 01885 01890 protected function assertRepoDefined() { 01891 if ( !( $this->repo instanceof $this->repoClass ) ) { 01892 throw new MWException( "A {$this->repoClass} object is not set for this File.\n" ); 01893 } 01894 } 01895 01900 protected function assertTitleDefined() { 01901 if ( !( $this->title instanceof Title ) ) { 01902 throw new MWException( "A Title object is not set for this File.\n" ); 01903 } 01904 } 01905 }