MediaWiki
REL1_21
|
00001 <?php 00050 abstract class File { 00051 const DELETED_FILE = 1; 00052 const DELETED_COMMENT = 2; 00053 const DELETED_USER = 4; 00054 const DELETED_RESTRICTED = 8; 00055 00057 const RENDER_NOW = 1; 00062 const RENDER_FORCE = 2; 00063 00064 const DELETE_SOURCE = 1; 00065 00066 // Audience options for File::getDescription() 00067 const FOR_PUBLIC = 1; 00068 const FOR_THIS_USER = 2; 00069 const RAW = 3; 00070 00071 // Options for File::thumbName() 00072 const THUMB_FULL_NAME = 1; 00073 00096 var $repo; 00097 00101 var $title; 00102 00103 var $lastError, $redirected, $redirectedTitle; 00104 00108 protected $fsFile; 00109 00113 protected $handler; 00114 00118 protected $url, $extension, $name, $path, $hashPath, $pageCount, $transformScript; 00119 00120 protected $redirectTitle; 00121 00125 protected $canRender, $isSafeFile; 00126 00130 protected $repoClass = 'FileRepo'; 00131 00142 function __construct( $title, $repo ) { 00143 if ( $title !== false ) { // subclasses may not use MW titles 00144 $title = self::normalizeTitle( $title, 'exception' ); 00145 } 00146 $this->title = $title; 00147 $this->repo = $repo; 00148 } 00149 00159 static function normalizeTitle( $title, $exception = false ) { 00160 $ret = $title; 00161 if ( $ret instanceof Title ) { 00162 # Normalize NS_MEDIA -> NS_FILE 00163 if ( $ret->getNamespace() == NS_MEDIA ) { 00164 $ret = Title::makeTitleSafe( NS_FILE, $ret->getDBkey() ); 00165 # Sanity check the title namespace 00166 } elseif ( $ret->getNamespace() !== NS_FILE ) { 00167 $ret = null; 00168 } 00169 } else { 00170 # Convert strings to Title objects 00171 $ret = Title::makeTitleSafe( NS_FILE, (string)$ret ); 00172 } 00173 if ( !$ret && $exception !== false ) { 00174 throw new MWException( "`$title` is not a valid file title." ); 00175 } 00176 return $ret; 00177 } 00178 00179 function __get( $name ) { 00180 $function = array( $this, 'get' . ucfirst( $name ) ); 00181 if ( !is_callable( $function ) ) { 00182 return null; 00183 } else { 00184 $this->$name = call_user_func( $function ); 00185 return $this->$name; 00186 } 00187 } 00188 00196 static function normalizeExtension( $ext ) { 00197 $lower = strtolower( $ext ); 00198 $squish = array( 00199 'htm' => 'html', 00200 'jpeg' => 'jpg', 00201 'mpeg' => 'mpg', 00202 'tiff' => 'tif', 00203 'ogv' => 'ogg' ); 00204 if( isset( $squish[$lower] ) ) { 00205 return $squish[$lower]; 00206 } elseif( preg_match( '/^[0-9a-z]+$/', $lower ) ) { 00207 return $lower; 00208 } else { 00209 return ''; 00210 } 00211 } 00212 00221 static function checkExtensionCompatibility( File $old, $new ) { 00222 $oldMime = $old->getMimeType(); 00223 $n = strrpos( $new, '.' ); 00224 $newExt = self::normalizeExtension( $n ? substr( $new, $n + 1 ) : '' ); 00225 $mimeMagic = MimeMagic::singleton(); 00226 return $mimeMagic->isMatchingExtension( $newExt, $oldMime ); 00227 } 00228 00234 function upgradeRow() {} 00235 00243 public static function splitMime( $mime ) { 00244 if( strpos( $mime, '/' ) !== false ) { 00245 return explode( '/', $mime, 2 ); 00246 } else { 00247 return array( $mime, 'unknown' ); 00248 } 00249 } 00250 00259 public static function compare( File $a, File $b ) { 00260 return strcmp( $a->getName(), $b->getName() ); 00261 } 00262 00268 public function getName() { 00269 if ( !isset( $this->name ) ) { 00270 $this->assertRepoDefined(); 00271 $this->name = $this->repo->getNameFromTitle( $this->title ); 00272 } 00273 return $this->name; 00274 } 00275 00281 function getExtension() { 00282 if ( !isset( $this->extension ) ) { 00283 $n = strrpos( $this->getName(), '.' ); 00284 $this->extension = self::normalizeExtension( 00285 $n ? substr( $this->getName(), $n + 1 ) : '' ); 00286 } 00287 return $this->extension; 00288 } 00289 00295 public function getTitle() { 00296 return $this->title; 00297 } 00298 00304 public function getOriginalTitle() { 00305 if ( $this->redirected ) { 00306 return $this->getRedirectedTitle(); 00307 } 00308 return $this->title; 00309 } 00310 00316 public function getUrl() { 00317 if ( !isset( $this->url ) ) { 00318 $this->assertRepoDefined(); 00319 $ext = $this->getExtension(); 00320 $this->url = $this->repo->getZoneUrl( 'public', $ext ) . '/' . $this->getUrlRel(); 00321 } 00322 return $this->url; 00323 } 00324 00332 public function getFullUrl() { 00333 return wfExpandUrl( $this->getUrl(), PROTO_RELATIVE ); 00334 } 00335 00339 public function getCanonicalUrl() { 00340 return wfExpandUrl( $this->getUrl(), PROTO_CANONICAL ); 00341 } 00342 00346 function getViewURL() { 00347 if ( $this->mustRender() ) { 00348 if ( $this->canRender() ) { 00349 return $this->createThumb( $this->getWidth() ); 00350 } else { 00351 wfDebug( __METHOD__ . ': supposed to render ' . $this->getName() . 00352 ' (' . $this->getMimeType() . "), but can't!\n" ); 00353 return $this->getURL(); #hm... return NULL? 00354 } 00355 } else { 00356 return $this->getURL(); 00357 } 00358 } 00359 00373 public function getPath() { 00374 if ( !isset( $this->path ) ) { 00375 $this->assertRepoDefined(); 00376 $this->path = $this->repo->getZonePath( 'public' ) . '/' . $this->getRel(); 00377 } 00378 return $this->path; 00379 } 00380 00388 public function getLocalRefPath() { 00389 $this->assertRepoDefined(); 00390 if ( !isset( $this->fsFile ) ) { 00391 $this->fsFile = $this->repo->getLocalReference( $this->getPath() ); 00392 if ( !$this->fsFile ) { 00393 $this->fsFile = false; // null => false; cache negative hits 00394 } 00395 } 00396 return ( $this->fsFile ) 00397 ? $this->fsFile->getPath() 00398 : false; 00399 } 00400 00412 public function getWidth( $page = 1 ) { 00413 return false; 00414 } 00415 00427 public function getHeight( $page = 1 ) { 00428 return false; 00429 } 00430 00439 public function getUser( $type = 'text' ) { 00440 return null; 00441 } 00442 00448 public function getLength() { 00449 $handler = $this->getHandler(); 00450 if ( $handler ) { 00451 return $handler->getLength( $this ); 00452 } else { 00453 return 0; 00454 } 00455 } 00456 00462 public function isVectorized() { 00463 $handler = $this->getHandler(); 00464 if ( $handler ) { 00465 return $handler->isVectorized( $this ); 00466 } else { 00467 return false; 00468 } 00469 } 00470 00481 public function canAnimateThumbIfAppropriate() { 00482 $handler = $this->getHandler(); 00483 if ( !$handler ) { 00484 // We cannot handle image whatsoever, thus 00485 // one would not expect it to be animated 00486 // so true. 00487 return true; 00488 } else { 00489 if ( $this->allowInlineDisplay() 00490 && $handler->isAnimatedImage( $this ) 00491 && !$handler->canAnimateThumbnail( $this ) 00492 ) { 00493 // Image is animated, but thumbnail isn't. 00494 // This is unexpected to the user. 00495 return false; 00496 } else { 00497 // Image is not animated, so one would 00498 // not expect thumb to be 00499 return true; 00500 } 00501 } 00502 } 00503 00510 public function getMetadata() { 00511 return false; 00512 } 00513 00521 public function convertMetadataVersion($metadata, $version) { 00522 $handler = $this->getHandler(); 00523 if ( !is_array( $metadata ) ) { 00524 // Just to make the return type consistent 00525 $metadata = unserialize( $metadata ); 00526 } 00527 if ( $handler ) { 00528 return $handler->convertMetadataVersion( $metadata, $version ); 00529 } else { 00530 return $metadata; 00531 } 00532 } 00533 00540 public function getBitDepth() { 00541 return 0; 00542 } 00543 00550 public function getSize() { 00551 return false; 00552 } 00553 00561 function getMimeType() { 00562 return 'unknown/unknown'; 00563 } 00564 00572 function getMediaType() { 00573 return MEDIATYPE_UNKNOWN; 00574 } 00575 00588 function canRender() { 00589 if ( !isset( $this->canRender ) ) { 00590 $this->canRender = $this->getHandler() && $this->handler->canRender( $this ); 00591 } 00592 return $this->canRender; 00593 } 00594 00599 protected function getCanRender() { 00600 return $this->canRender(); 00601 } 00602 00613 function mustRender() { 00614 return $this->getHandler() && $this->handler->mustRender( $this ); 00615 } 00616 00622 function allowInlineDisplay() { 00623 return $this->canRender(); 00624 } 00625 00639 function isSafeFile() { 00640 if ( !isset( $this->isSafeFile ) ) { 00641 $this->isSafeFile = $this->_getIsSafeFile(); 00642 } 00643 return $this->isSafeFile; 00644 } 00645 00651 protected function getIsSafeFile() { 00652 return $this->isSafeFile(); 00653 } 00654 00660 protected function _getIsSafeFile() { 00661 global $wgTrustedMediaFormats; 00662 00663 if ( $this->allowInlineDisplay() ) { 00664 return true; 00665 } 00666 if ( $this->isTrustedFile() ) { 00667 return true; 00668 } 00669 00670 $type = $this->getMediaType(); 00671 $mime = $this->getMimeType(); 00672 #wfDebug( "LocalFile::isSafeFile: type= $type, mime= $mime\n" ); 00673 00674 if ( !$type || $type === MEDIATYPE_UNKNOWN ) { 00675 return false; #unknown type, not trusted 00676 } 00677 if ( in_array( $type, $wgTrustedMediaFormats ) ) { 00678 return true; 00679 } 00680 00681 if ( $mime === "unknown/unknown" ) { 00682 return false; #unknown type, not trusted 00683 } 00684 if ( in_array( $mime, $wgTrustedMediaFormats) ) { 00685 return true; 00686 } 00687 00688 return false; 00689 } 00690 00704 function isTrustedFile() { 00705 #this could be implemented to check a flag in the database, 00706 #look for signatures, etc 00707 return false; 00708 } 00709 00717 public function exists() { 00718 return $this->getPath() && $this->repo->fileExists( $this->path ); 00719 } 00720 00727 public function isVisible() { 00728 return $this->exists(); 00729 } 00730 00734 function getTransformScript() { 00735 if ( !isset( $this->transformScript ) ) { 00736 $this->transformScript = false; 00737 if ( $this->repo ) { 00738 $script = $this->repo->getThumbScriptUrl(); 00739 if ( $script ) { 00740 $this->transformScript = "$script?f=" . urlencode( $this->getName() ); 00741 } 00742 } 00743 } 00744 return $this->transformScript; 00745 } 00746 00754 function getUnscaledThumb( $handlerParams = array() ) { 00755 $hp =& $handlerParams; 00756 $page = isset( $hp['page'] ) ? $hp['page'] : false; 00757 $width = $this->getWidth( $page ); 00758 if ( !$width ) { 00759 return $this->iconThumb(); 00760 } 00761 $hp['width'] = $width; 00762 return $this->transform( $hp ); 00763 } 00764 00774 public function thumbName( $params, $flags = 0 ) { 00775 $name = ( $this->repo && !( $flags & self::THUMB_FULL_NAME ) ) 00776 ? $this->repo->nameForThumb( $this->getName() ) 00777 : $this->getName(); 00778 return $this->generateThumbName( $name, $params ); 00779 } 00780 00789 public function generateThumbName( $name, $params ) { 00790 if ( !$this->getHandler() ) { 00791 return null; 00792 } 00793 $extension = $this->getExtension(); 00794 list( $thumbExt, $thumbMime ) = $this->handler->getThumbType( 00795 $extension, $this->getMimeType(), $params ); 00796 $thumbName = $this->handler->makeParamString( $params ) . '-' . $name; 00797 if ( $thumbExt != $extension ) { 00798 $thumbName .= ".$thumbExt"; 00799 } 00800 return $thumbName; 00801 } 00802 00820 public function createThumb( $width, $height = -1 ) { 00821 $params = array( 'width' => $width ); 00822 if ( $height != -1 ) { 00823 $params['height'] = $height; 00824 } 00825 $thumb = $this->transform( $params ); 00826 if ( is_null( $thumb ) || $thumb->isError() ) { 00827 return ''; 00828 } 00829 return $thumb->getUrl(); 00830 } 00831 00841 protected function transformErrorOutput( $thumbPath, $thumbUrl, $params, $flags ) { 00842 global $wgIgnoreImageErrors; 00843 00844 if ( $wgIgnoreImageErrors && !( $flags & self::RENDER_NOW ) ) { 00845 return $this->getHandler()->getTransform( $this, $thumbPath, $thumbUrl, $params ); 00846 } else { 00847 return new MediaTransformError( 'thumbnail_error', 00848 $params['width'], 0, wfMessage( 'thumbnail-dest-create' )->text() ); 00849 } 00850 } 00851 00860 function transform( $params, $flags = 0 ) { 00861 global $wgUseSquid, $wgIgnoreImageErrors, $wgThumbnailEpoch; 00862 00863 wfProfileIn( __METHOD__ ); 00864 do { 00865 if ( !$this->canRender() ) { 00866 $thumb = $this->iconThumb(); 00867 break; // not a bitmap or renderable image, don't try 00868 } 00869 00870 // Get the descriptionUrl to embed it as comment into the thumbnail. Bug 19791. 00871 $descriptionUrl = $this->getDescriptionUrl(); 00872 if ( $descriptionUrl ) { 00873 $params['descriptionUrl'] = wfExpandUrl( $descriptionUrl, PROTO_CANONICAL ); 00874 } 00875 00876 $handler = $this->getHandler(); 00877 $script = $this->getTransformScript(); 00878 if ( $script && !( $flags & self::RENDER_NOW ) ) { 00879 // Use a script to transform on client request, if possible 00880 $thumb = $handler->getScriptedTransform( $this, $script, $params ); 00881 if ( $thumb ) { 00882 break; 00883 } 00884 } 00885 00886 $normalisedParams = $params; 00887 $handler->normaliseParams( $this, $normalisedParams ); 00888 00889 $thumbName = $this->thumbName( $normalisedParams ); 00890 $thumbUrl = $this->getThumbUrl( $thumbName ); 00891 $thumbPath = $this->getThumbPath( $thumbName ); // final thumb path 00892 00893 if ( $this->repo ) { 00894 // Defer rendering if a 404 handler is set up... 00895 if ( $this->repo->canTransformVia404() && !( $flags & self::RENDER_NOW ) ) { 00896 wfDebug( __METHOD__ . " transformation deferred." ); 00897 // XXX: Pass in the storage path even though we are not rendering anything 00898 // and the path is supposed to be an FS path. This is due to getScalerType() 00899 // getting called on the path and clobbering $thumb->getUrl() if it's false. 00900 $thumb = $handler->getTransform( $this, $thumbPath, $thumbUrl, $params ); 00901 break; 00902 } 00903 // Clean up broken thumbnails as needed 00904 $this->migrateThumbFile( $thumbName ); 00905 // Check if an up-to-date thumbnail already exists... 00906 wfDebug( __METHOD__ . ": Doing stat for $thumbPath\n" ); 00907 if ( !( $flags & self::RENDER_FORCE ) && $this->repo->fileExists( $thumbPath ) ) { 00908 $timestamp = $this->repo->getFileTimestamp( $thumbPath ); 00909 if ( $timestamp !== false && $timestamp >= $wgThumbnailEpoch ) { 00910 // XXX: Pass in the storage path even though we are not rendering anything 00911 // and the path is supposed to be an FS path. This is due to getScalerType() 00912 // getting called on the path and clobbering $thumb->getUrl() if it's false. 00913 $thumb = $handler->getTransform( 00914 $this, $thumbPath, $thumbUrl, $params ); 00915 $thumb->setStoragePath( $thumbPath ); 00916 break; 00917 } 00918 } elseif ( $flags & self::RENDER_FORCE ) { 00919 wfDebug( __METHOD__ . " forcing rendering per flag File::RENDER_FORCE\n" ); 00920 } 00921 } 00922 00923 // If the backend is ready-only, don't keep generating thumbnails 00924 // only to return transformation errors, just return the error now. 00925 if ( $this->repo->getReadOnlyReason() !== false ) { 00926 $thumb = $this->transformErrorOutput( $thumbPath, $thumbUrl, $params, $flags ); 00927 break; 00928 } 00929 00930 // Create a temp FS file with the same extension and the thumbnail 00931 $thumbExt = FileBackend::extensionFromPath( $thumbPath ); 00932 $tmpFile = TempFSFile::factory( 'transform_', $thumbExt ); 00933 if ( !$tmpFile ) { 00934 $thumb = $this->transformErrorOutput( $thumbPath, $thumbUrl, $params, $flags ); 00935 break; 00936 } 00937 $tmpThumbPath = $tmpFile->getPath(); // path of 0-byte temp file 00938 00939 // Actually render the thumbnail... 00940 wfProfileIn( __METHOD__ . '-doTransform' ); 00941 $thumb = $handler->doTransform( $this, $tmpThumbPath, $thumbUrl, $params ); 00942 wfProfileOut( __METHOD__ . '-doTransform' ); 00943 $tmpFile->bind( $thumb ); // keep alive with $thumb 00944 00945 if ( !$thumb ) { // bad params? 00946 $thumb = null; 00947 } elseif ( $thumb->isError() ) { // transform error 00948 $this->lastError = $thumb->toText(); 00949 // Ignore errors if requested 00950 if ( $wgIgnoreImageErrors && !( $flags & self::RENDER_NOW ) ) { 00951 $thumb = $handler->getTransform( $this, $tmpThumbPath, $thumbUrl, $params ); 00952 } 00953 } elseif ( $this->repo && $thumb->hasFile() && !$thumb->fileIsSource() ) { 00954 // Copy the thumbnail from the file system into storage... 00955 $disposition = $this->getThumbDisposition( $thumbName ); 00956 $status = $this->repo->quickImport( $tmpThumbPath, $thumbPath, $disposition ); 00957 if ( $status->isOK() ) { 00958 $thumb->setStoragePath( $thumbPath ); 00959 } else { 00960 $thumb = $this->transformErrorOutput( $thumbPath, $thumbUrl, $params, $flags ); 00961 } 00962 // Give extensions a chance to do something with this thumbnail... 00963 wfRunHooks( 'FileTransformed', array( $this, $thumb, $tmpThumbPath, $thumbPath ) ); 00964 } 00965 00966 // Purge. Useful in the event of Core -> Squid connection failure or squid 00967 // purge collisions from elsewhere during failure. Don't keep triggering for 00968 // "thumbs" which have the main image URL though (bug 13776) 00969 if ( $wgUseSquid ) { 00970 if ( !$thumb || $thumb->isError() || $thumb->getUrl() != $this->getURL() ) { 00971 SquidUpdate::purge( array( $thumbUrl ) ); 00972 } 00973 } 00974 } while ( false ); 00975 00976 wfProfileOut( __METHOD__ ); 00977 return is_object( $thumb ) ? $thumb : false; 00978 } 00979 00984 function getThumbDisposition( $thumbName ) { 00985 $fileName = $this->name; // file name to suggest 00986 $thumbExt = FileBackend::extensionFromPath( $thumbName ); 00987 if ( $thumbExt != '' && $thumbExt !== $this->getExtension() ) { 00988 $fileName .= ".$thumbExt"; 00989 } 00990 return FileBackend::makeContentDisposition( 'inline', $fileName ); 00991 } 00992 00998 function migrateThumbFile( $thumbName ) {} 00999 01005 function getHandler() { 01006 if ( !isset( $this->handler ) ) { 01007 $this->handler = MediaHandler::getHandler( $this->getMimeType() ); 01008 } 01009 return $this->handler; 01010 } 01011 01017 function iconThumb() { 01018 global $wgStylePath, $wgStyleDirectory; 01019 01020 $try = array( 'fileicon-' . $this->getExtension() . '.png', 'fileicon.png' ); 01021 foreach ( $try as $icon ) { 01022 $path = '/common/images/icons/' . $icon; 01023 $filepath = $wgStyleDirectory . $path; 01024 if ( file_exists( $filepath ) ) { // always FS 01025 $params = array( 'width' => 120, 'height' => 120 ); 01026 return new ThumbnailImage( $this, $wgStylePath . $path, false, $params ); 01027 } 01028 } 01029 return null; 01030 } 01031 01036 function getLastError() { 01037 return $this->lastError; 01038 } 01039 01046 function getThumbnails() { 01047 return array(); 01048 } 01049 01057 function purgeCache( $options = array() ) {} 01058 01064 function purgeDescription() { 01065 $title = $this->getTitle(); 01066 if ( $title ) { 01067 $title->invalidateCache(); 01068 $title->purgeSquid(); 01069 } 01070 } 01071 01076 function purgeEverything() { 01077 // Delete thumbnails and refresh file metadata cache 01078 $this->purgeCache(); 01079 $this->purgeDescription(); 01080 01081 // Purge cache of all pages using this file 01082 $title = $this->getTitle(); 01083 if ( $title ) { 01084 $update = new HTMLCacheUpdate( $title, 'imagelinks' ); 01085 $update->doUpdate(); 01086 } 01087 } 01088 01100 function getHistory( $limit = null, $start = null, $end = null, $inc=true ) { 01101 return array(); 01102 } 01103 01113 public function nextHistoryLine() { 01114 return false; 01115 } 01116 01123 public function resetHistory() {} 01124 01132 function getHashPath() { 01133 if ( !isset( $this->hashPath ) ) { 01134 $this->assertRepoDefined(); 01135 $this->hashPath = $this->repo->getHashPath( $this->getName() ); 01136 } 01137 return $this->hashPath; 01138 } 01139 01146 function getRel() { 01147 return $this->getHashPath() . $this->getName(); 01148 } 01149 01157 function getArchiveRel( $suffix = false ) { 01158 $path = 'archive/' . $this->getHashPath(); 01159 if ( $suffix === false ) { 01160 $path = substr( $path, 0, -1 ); 01161 } else { 01162 $path .= $suffix; 01163 } 01164 return $path; 01165 } 01166 01175 function getThumbRel( $suffix = false ) { 01176 $path = $this->getRel(); 01177 if ( $suffix !== false ) { 01178 $path .= '/' . $suffix; 01179 } 01180 return $path; 01181 } 01182 01189 function getUrlRel() { 01190 return $this->getHashPath() . rawurlencode( $this->getName() ); 01191 } 01192 01202 function getArchiveThumbRel( $archiveName, $suffix = false ) { 01203 $path = 'archive/' . $this->getHashPath() . $archiveName . "/"; 01204 if ( $suffix === false ) { 01205 $path = substr( $path, 0, -1 ); 01206 } else { 01207 $path .= $suffix; 01208 } 01209 return $path; 01210 } 01211 01219 function getArchivePath( $suffix = false ) { 01220 $this->assertRepoDefined(); 01221 return $this->repo->getZonePath( 'public' ) . '/' . $this->getArchiveRel( $suffix ); 01222 } 01223 01232 function getArchiveThumbPath( $archiveName, $suffix = false ) { 01233 $this->assertRepoDefined(); 01234 return $this->repo->getZonePath( 'thumb' ) . '/' . 01235 $this->getArchiveThumbRel( $archiveName, $suffix ); 01236 } 01237 01245 function getThumbPath( $suffix = false ) { 01246 $this->assertRepoDefined(); 01247 return $this->repo->getZonePath( 'thumb' ) . '/' . $this->getThumbRel( $suffix ); 01248 } 01249 01257 function getTranscodedPath( $suffix = false ) { 01258 $this->assertRepoDefined(); 01259 return $this->repo->getZonePath( 'transcoded' ) . '/' . $this->getThumbRel( $suffix ); 01260 } 01261 01269 function getArchiveUrl( $suffix = false ) { 01270 $this->assertRepoDefined(); 01271 $ext = $this->getExtension(); 01272 $path = $this->repo->getZoneUrl( 'public', $ext ) . '/archive/' . $this->getHashPath(); 01273 if ( $suffix === false ) { 01274 $path = substr( $path, 0, -1 ); 01275 } else { 01276 $path .= rawurlencode( $suffix ); 01277 } 01278 return $path; 01279 } 01280 01289 function getArchiveThumbUrl( $archiveName, $suffix = false ) { 01290 $this->assertRepoDefined(); 01291 $ext = $this->getExtension(); 01292 $path = $this->repo->getZoneUrl( 'thumb', $ext ) . '/archive/' . 01293 $this->getHashPath() . rawurlencode( $archiveName ) . "/"; 01294 if ( $suffix === false ) { 01295 $path = substr( $path, 0, -1 ); 01296 } else { 01297 $path .= rawurlencode( $suffix ); 01298 } 01299 return $path; 01300 } 01301 01310 function getZoneUrl( $zone, $suffix = false ) { 01311 $this->assertRepoDefined(); 01312 $ext = $this->getExtension(); 01313 $path = $this->repo->getZoneUrl( $zone, $ext ) . '/' . $this->getUrlRel(); 01314 if ( $suffix !== false ) { 01315 $path .= '/' . rawurlencode( $suffix ); 01316 } 01317 return $path; 01318 } 01319 01327 function getThumbUrl( $suffix = false ) { 01328 return $this->getZoneUrl( 'thumb', $suffix ); 01329 } 01330 01338 function getTranscodedUrl( $suffix = false ) { 01339 return $this->getZoneUrl( 'transcoded', $suffix ); 01340 } 01341 01349 function getVirtualUrl( $suffix = false ) { 01350 $this->assertRepoDefined(); 01351 $path = $this->repo->getVirtualUrl() . '/public/' . $this->getUrlRel(); 01352 if ( $suffix !== false ) { 01353 $path .= '/' . rawurlencode( $suffix ); 01354 } 01355 return $path; 01356 } 01357 01365 function getArchiveVirtualUrl( $suffix = false ) { 01366 $this->assertRepoDefined(); 01367 $path = $this->repo->getVirtualUrl() . '/public/archive/' . $this->getHashPath(); 01368 if ( $suffix === false ) { 01369 $path = substr( $path, 0, -1 ); 01370 } else { 01371 $path .= rawurlencode( $suffix ); 01372 } 01373 return $path; 01374 } 01375 01383 function getThumbVirtualUrl( $suffix = false ) { 01384 $this->assertRepoDefined(); 01385 $path = $this->repo->getVirtualUrl() . '/thumb/' . $this->getUrlRel(); 01386 if ( $suffix !== false ) { 01387 $path .= '/' . rawurlencode( $suffix ); 01388 } 01389 return $path; 01390 } 01391 01395 function isHashed() { 01396 $this->assertRepoDefined(); 01397 return (bool)$this->repo->getHashLevels(); 01398 } 01399 01403 function readOnlyError() { 01404 throw new MWException( get_class( $this ) . ': write operations are not supported' ); 01405 } 01406 01422 function recordUpload( $oldver, $desc, $license = '', $copyStatus = '', $source = '', $watch = false, $timestamp = false, User $user = null ) { 01423 $this->readOnlyError(); 01424 } 01425 01448 function publish( $srcPath, $flags = 0, array $options = array() ) { 01449 $this->readOnlyError(); 01450 } 01451 01455 function formatMetadata() { 01456 if ( !$this->getHandler() ) { 01457 return false; 01458 } 01459 return $this->getHandler()->formatMetadata( $this, $this->getMetadata() ); 01460 } 01461 01467 function isLocal() { 01468 return $this->repo && $this->repo->isLocal(); 01469 } 01470 01476 function getRepoName() { 01477 return $this->repo ? $this->repo->getName() : 'unknown'; 01478 } 01479 01485 function getRepo() { 01486 return $this->repo; 01487 } 01488 01495 function isOld() { 01496 return false; 01497 } 01498 01507 function isDeleted( $field ) { 01508 return false; 01509 } 01510 01516 function getVisibility() { 01517 return 0; 01518 } 01519 01525 function wasDeleted() { 01526 $title = $this->getTitle(); 01527 return $title && $title->isDeletedQuick(); 01528 } 01529 01542 function move( $target ) { 01543 $this->readOnlyError(); 01544 } 01545 01560 function delete( $reason, $suppress = false ) { 01561 $this->readOnlyError(); 01562 } 01563 01578 function restore( $versions = array(), $unsuppress = false ) { 01579 $this->readOnlyError(); 01580 } 01581 01589 function isMultipage() { 01590 return $this->getHandler() && $this->handler->isMultiPage( $this ); 01591 } 01592 01599 function pageCount() { 01600 if ( !isset( $this->pageCount ) ) { 01601 if ( $this->getHandler() && $this->handler->isMultiPage( $this ) ) { 01602 $this->pageCount = $this->handler->pageCount( $this ); 01603 } else { 01604 $this->pageCount = false; 01605 } 01606 } 01607 return $this->pageCount; 01608 } 01609 01619 static function scaleHeight( $srcWidth, $srcHeight, $dstWidth ) { 01620 // Exact integer multiply followed by division 01621 if ( $srcWidth == 0 ) { 01622 return 0; 01623 } else { 01624 return round( $srcHeight * $dstWidth / $srcWidth ); 01625 } 01626 } 01627 01635 function getImageSize( $fileName ) { 01636 if ( !$this->getHandler() ) { 01637 return false; 01638 } 01639 return $this->handler->getImageSize( $this, $fileName ); 01640 } 01641 01648 function getDescriptionUrl() { 01649 if ( $this->repo ) { 01650 return $this->repo->getDescriptionUrl( $this->getName() ); 01651 } else { 01652 return false; 01653 } 01654 } 01655 01661 function getDescriptionText() { 01662 global $wgMemc, $wgLang; 01663 if ( !$this->repo || !$this->repo->fetchDescription ) { 01664 return false; 01665 } 01666 $renderUrl = $this->repo->getDescriptionRenderUrl( $this->getName(), $wgLang->getCode() ); 01667 if ( $renderUrl ) { 01668 if ( $this->repo->descriptionCacheExpiry > 0 ) { 01669 wfDebug( "Attempting to get the description from cache..." ); 01670 $key = $this->repo->getLocalCacheKey( 'RemoteFileDescription', 'url', $wgLang->getCode(), 01671 $this->getName() ); 01672 $obj = $wgMemc->get( $key ); 01673 if ( $obj ) { 01674 wfDebug( "success!\n" ); 01675 return $obj; 01676 } 01677 wfDebug( "miss\n" ); 01678 } 01679 wfDebug( "Fetching shared description from $renderUrl\n" ); 01680 $res = Http::get( $renderUrl ); 01681 if ( $res && $this->repo->descriptionCacheExpiry > 0 ) { 01682 $wgMemc->set( $key, $res, $this->repo->descriptionCacheExpiry ); 01683 } 01684 return $res; 01685 } else { 01686 return false; 01687 } 01688 } 01689 01702 function getDescription( $audience = self::FOR_PUBLIC, User $user = null ) { 01703 return null; 01704 } 01705 01711 function getTimestamp() { 01712 $this->assertRepoDefined(); 01713 return $this->repo->getFileTimestamp( $this->getPath() ); 01714 } 01715 01721 function getSha1() { 01722 $this->assertRepoDefined(); 01723 return $this->repo->getFileSha1( $this->getPath() ); 01724 } 01725 01731 function getStorageKey() { 01732 $hash = $this->getSha1(); 01733 if ( !$hash ) { 01734 return false; 01735 } 01736 $ext = $this->getExtension(); 01737 $dotExt = $ext === '' ? '' : ".$ext"; 01738 return $hash . $dotExt; 01739 } 01740 01749 function userCan( $field, User $user = null ) { 01750 return true; 01751 } 01752 01763 static function getPropsFromPath( $path, $ext = true ) { 01764 wfDebug( __METHOD__ . ": Getting file info for $path\n" ); 01765 wfDeprecated( __METHOD__, '1.19' ); 01766 01767 $fsFile = new FSFile( $path ); 01768 return $fsFile->getProps(); 01769 } 01770 01783 static function sha1Base36( $path ) { 01784 wfDeprecated( __METHOD__, '1.19' ); 01785 01786 $fsFile = new FSFile( $path ); 01787 return $fsFile->getSha1Base36(); 01788 } 01789 01793 function getStreamHeaders() { 01794 $handler = $this->getHandler(); 01795 if ( $handler ) { 01796 return $handler->getStreamHeaders( $this->getMetadata() ); 01797 } else { 01798 return array(); 01799 } 01800 } 01801 01805 function getLongDesc() { 01806 $handler = $this->getHandler(); 01807 if ( $handler ) { 01808 return $handler->getLongDesc( $this ); 01809 } else { 01810 return MediaHandler::getGeneralLongDesc( $this ); 01811 } 01812 } 01813 01817 function getShortDesc() { 01818 $handler = $this->getHandler(); 01819 if ( $handler ) { 01820 return $handler->getShortDesc( $this ); 01821 } else { 01822 return MediaHandler::getGeneralShortDesc( $this ); 01823 } 01824 } 01825 01829 function getDimensionsString() { 01830 $handler = $this->getHandler(); 01831 if ( $handler ) { 01832 return $handler->getDimensionsString( $this ); 01833 } else { 01834 return ''; 01835 } 01836 } 01837 01841 function getRedirected() { 01842 return $this->redirected; 01843 } 01844 01848 function getRedirectedTitle() { 01849 if ( $this->redirected ) { 01850 if ( !$this->redirectTitle ) { 01851 $this->redirectTitle = Title::makeTitle( NS_FILE, $this->redirected ); 01852 } 01853 return $this->redirectTitle; 01854 } 01855 return null; 01856 } 01857 01862 function redirectedFrom( $from ) { 01863 $this->redirected = $from; 01864 } 01865 01869 function isMissing() { 01870 return false; 01871 } 01872 01877 public function isCacheable() { 01878 return true; 01879 } 01880 01885 protected function assertRepoDefined() { 01886 if ( !( $this->repo instanceof $this->repoClass ) ) { 01887 throw new MWException( "A {$this->repoClass} object is not set for this File.\n" ); 01888 } 01889 } 01890 01895 protected function assertTitleDefined() { 01896 if ( !( $this->title instanceof Title ) ) { 01897 throw new MWException( "A Title object is not set for this File.\n" ); 01898 } 01899 } 01900 }