MediaWiki
master
|
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->handler->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 $script = $this->getTransformScript(); 00877 if ( $script && !( $flags & self::RENDER_NOW ) ) { 00878 // Use a script to transform on client request, if possible 00879 $thumb = $this->handler->getScriptedTransform( $this, $script, $params ); 00880 if ( $thumb ) { 00881 break; 00882 } 00883 } 00884 00885 $normalisedParams = $params; 00886 $this->handler->normaliseParams( $this, $normalisedParams ); 00887 00888 $thumbName = $this->thumbName( $normalisedParams ); 00889 $thumbUrl = $this->getThumbUrl( $thumbName ); 00890 $thumbPath = $this->getThumbPath( $thumbName ); // final thumb path 00891 00892 if ( $this->repo ) { 00893 // Defer rendering if a 404 handler is set up... 00894 if ( $this->repo->canTransformVia404() && !( $flags & self::RENDER_NOW ) ) { 00895 wfDebug( __METHOD__ . " transformation deferred." ); 00896 // XXX: Pass in the storage path even though we are not rendering anything 00897 // and the path is supposed to be an FS path. This is due to getScalerType() 00898 // getting called on the path and clobbering $thumb->getUrl() if it's false. 00899 $thumb = $this->handler->getTransform( $this, $thumbPath, $thumbUrl, $params ); 00900 break; 00901 } 00902 // Clean up broken thumbnails as needed 00903 $this->migrateThumbFile( $thumbName ); 00904 // Check if an up-to-date thumbnail already exists... 00905 wfDebug( __METHOD__.": Doing stat for $thumbPath\n" ); 00906 if ( $this->repo->fileExists( $thumbPath ) && !( $flags & self::RENDER_FORCE ) ) { 00907 $timestamp = $this->repo->getFileTimestamp( $thumbPath ); 00908 if ( $timestamp !== false && $timestamp >= $wgThumbnailEpoch ) { 00909 // XXX: Pass in the storage path even though we are not rendering anything 00910 // and the path is supposed to be an FS path. This is due to getScalerType() 00911 // getting called on the path and clobbering $thumb->getUrl() if it's false. 00912 $thumb = $this->handler->getTransform( $this, $thumbPath, $thumbUrl, $params ); 00913 $thumb->setStoragePath( $thumbPath ); 00914 break; 00915 } 00916 } elseif ( $flags & self::RENDER_FORCE ) { 00917 wfDebug( __METHOD__ . " forcing rendering per flag File::RENDER_FORCE\n" ); 00918 } 00919 } 00920 00921 // If the backend is ready-only, don't keep generating thumbnails 00922 // only to return transformation errors, just return the error now. 00923 if ( $this->repo->getReadOnlyReason() !== false ) { 00924 $thumb = $this->transformErrorOutput( $thumbPath, $thumbUrl, $params, $flags ); 00925 break; 00926 } 00927 00928 // Create a temp FS file with the same extension and the thumbnail 00929 $thumbExt = FileBackend::extensionFromPath( $thumbPath ); 00930 $tmpFile = TempFSFile::factory( 'transform_', $thumbExt ); 00931 if ( !$tmpFile ) { 00932 $thumb = $this->transformErrorOutput( $thumbPath, $thumbUrl, $params, $flags ); 00933 break; 00934 } 00935 $tmpThumbPath = $tmpFile->getPath(); // path of 0-byte temp file 00936 00937 // Actually render the thumbnail... 00938 wfProfileIn( __METHOD__ . '-doTransform' ); 00939 $thumb = $this->handler->doTransform( $this, $tmpThumbPath, $thumbUrl, $params ); 00940 wfProfileOut( __METHOD__ . '-doTransform' ); 00941 $tmpFile->bind( $thumb ); // keep alive with $thumb 00942 00943 if ( !$thumb ) { // bad params? 00944 $thumb = null; 00945 } elseif ( $thumb->isError() ) { // transform error 00946 $this->lastError = $thumb->toText(); 00947 // Ignore errors if requested 00948 if ( $wgIgnoreImageErrors && !( $flags & self::RENDER_NOW ) ) { 00949 $thumb = $this->handler->getTransform( $this, $tmpThumbPath, $thumbUrl, $params ); 00950 } 00951 } elseif ( $this->repo && $thumb->hasFile() && !$thumb->fileIsSource() ) { 00952 // Copy the thumbnail from the file system into storage... 00953 $disposition = $this->getThumbDisposition( $thumbName ); 00954 $status = $this->repo->quickImport( $tmpThumbPath, $thumbPath, $disposition ); 00955 if ( $status->isOK() ) { 00956 $thumb->setStoragePath( $thumbPath ); 00957 } else { 00958 $thumb = $this->transformErrorOutput( $thumbPath, $thumbUrl, $params, $flags ); 00959 } 00960 // Give extensions a chance to do something with this thumbnail... 00961 wfRunHooks( 'FileTransformed', array( $this, $thumb, $tmpThumbPath, $thumbPath ) ); 00962 } 00963 00964 // Purge. Useful in the event of Core -> Squid connection failure or squid 00965 // purge collisions from elsewhere during failure. Don't keep triggering for 00966 // "thumbs" which have the main image URL though (bug 13776) 00967 if ( $wgUseSquid ) { 00968 if ( !$thumb || $thumb->isError() || $thumb->getUrl() != $this->getURL() ) { 00969 SquidUpdate::purge( array( $thumbUrl ) ); 00970 } 00971 } 00972 } while ( false ); 00973 00974 wfProfileOut( __METHOD__ ); 00975 return is_object( $thumb ) ? $thumb : false; 00976 } 00977 00982 function getThumbDisposition( $thumbName ) { 00983 $fileName = $this->name; // file name to suggest 00984 $thumbExt = FileBackend::extensionFromPath( $thumbName ); 00985 if ( $thumbExt != '' && $thumbExt !== $this->getExtension() ) { 00986 $fileName .= ".$thumbExt"; 00987 } 00988 return FileBackend::makeContentDisposition( 'inline', $fileName ); 00989 } 00990 00996 function migrateThumbFile( $thumbName ) {} 00997 01003 function getHandler() { 01004 if ( !isset( $this->handler ) ) { 01005 $this->handler = MediaHandler::getHandler( $this->getMimeType() ); 01006 } 01007 return $this->handler; 01008 } 01009 01015 function iconThumb() { 01016 global $wgStylePath, $wgStyleDirectory; 01017 01018 $try = array( 'fileicon-' . $this->getExtension() . '.png', 'fileicon.png' ); 01019 foreach ( $try as $icon ) { 01020 $path = '/common/images/icons/' . $icon; 01021 $filepath = $wgStyleDirectory . $path; 01022 if ( file_exists( $filepath ) ) { // always FS 01023 $params = array( 'width' => 120, 'height' => 120 ); 01024 return new ThumbnailImage( $this, $wgStylePath . $path, false, $params ); 01025 } 01026 } 01027 return null; 01028 } 01029 01034 function getLastError() { 01035 return $this->lastError; 01036 } 01037 01044 function getThumbnails() { 01045 return array(); 01046 } 01047 01055 function purgeCache( $options = array() ) {} 01056 01062 function purgeDescription() { 01063 $title = $this->getTitle(); 01064 if ( $title ) { 01065 $title->invalidateCache(); 01066 $title->purgeSquid(); 01067 } 01068 } 01069 01074 function purgeEverything() { 01075 // Delete thumbnails and refresh file metadata cache 01076 $this->purgeCache(); 01077 $this->purgeDescription(); 01078 01079 // Purge cache of all pages using this file 01080 $title = $this->getTitle(); 01081 if ( $title ) { 01082 $update = new HTMLCacheUpdate( $title, 'imagelinks' ); 01083 $update->doUpdate(); 01084 } 01085 } 01086 01098 function getHistory( $limit = null, $start = null, $end = null, $inc=true ) { 01099 return array(); 01100 } 01101 01111 public function nextHistoryLine() { 01112 return false; 01113 } 01114 01121 public function resetHistory() {} 01122 01130 function getHashPath() { 01131 if ( !isset( $this->hashPath ) ) { 01132 $this->assertRepoDefined(); 01133 $this->hashPath = $this->repo->getHashPath( $this->getName() ); 01134 } 01135 return $this->hashPath; 01136 } 01137 01144 function getRel() { 01145 return $this->getHashPath() . $this->getName(); 01146 } 01147 01155 function getArchiveRel( $suffix = false ) { 01156 $path = 'archive/' . $this->getHashPath(); 01157 if ( $suffix === false ) { 01158 $path = substr( $path, 0, -1 ); 01159 } else { 01160 $path .= $suffix; 01161 } 01162 return $path; 01163 } 01164 01173 function getThumbRel( $suffix = false ) { 01174 $path = $this->getRel(); 01175 if ( $suffix !== false ) { 01176 $path .= '/' . $suffix; 01177 } 01178 return $path; 01179 } 01180 01187 function getUrlRel() { 01188 return $this->getHashPath() . rawurlencode( $this->getName() ); 01189 } 01190 01200 function getArchiveThumbRel( $archiveName, $suffix = false ) { 01201 $path = 'archive/' . $this->getHashPath() . $archiveName . "/"; 01202 if ( $suffix === false ) { 01203 $path = substr( $path, 0, -1 ); 01204 } else { 01205 $path .= $suffix; 01206 } 01207 return $path; 01208 } 01209 01217 function getArchivePath( $suffix = false ) { 01218 $this->assertRepoDefined(); 01219 return $this->repo->getZonePath( 'public' ) . '/' . $this->getArchiveRel( $suffix ); 01220 } 01221 01230 function getArchiveThumbPath( $archiveName, $suffix = false ) { 01231 $this->assertRepoDefined(); 01232 return $this->repo->getZonePath( 'thumb' ) . '/' . 01233 $this->getArchiveThumbRel( $archiveName, $suffix ); 01234 } 01235 01243 function getThumbPath( $suffix = false ) { 01244 $this->assertRepoDefined(); 01245 return $this->repo->getZonePath( 'thumb' ) . '/' . $this->getThumbRel( $suffix ); 01246 } 01247 01255 function getArchiveUrl( $suffix = false ) { 01256 $this->assertRepoDefined(); 01257 $ext = $this->getExtension(); 01258 $path = $this->repo->getZoneUrl( 'public', $ext ) . '/archive/' . $this->getHashPath(); 01259 if ( $suffix === false ) { 01260 $path = substr( $path, 0, -1 ); 01261 } else { 01262 $path .= rawurlencode( $suffix ); 01263 } 01264 return $path; 01265 } 01266 01275 function getArchiveThumbUrl( $archiveName, $suffix = false ) { 01276 $this->assertRepoDefined(); 01277 $ext = $this->getExtension(); 01278 $path = $this->repo->getZoneUrl( 'thumb', $ext ) . '/archive/' . 01279 $this->getHashPath() . rawurlencode( $archiveName ) . "/"; 01280 if ( $suffix === false ) { 01281 $path = substr( $path, 0, -1 ); 01282 } else { 01283 $path .= rawurlencode( $suffix ); 01284 } 01285 return $path; 01286 } 01287 01295 function getThumbUrl( $suffix = false ) { 01296 $this->assertRepoDefined(); 01297 $ext = $this->getExtension(); 01298 $path = $this->repo->getZoneUrl( 'thumb', $ext ) . '/' . $this->getUrlRel(); 01299 if ( $suffix !== false ) { 01300 $path .= '/' . rawurlencode( $suffix ); 01301 } 01302 return $path; 01303 } 01304 01312 function getVirtualUrl( $suffix = false ) { 01313 $this->assertRepoDefined(); 01314 $path = $this->repo->getVirtualUrl() . '/public/' . $this->getUrlRel(); 01315 if ( $suffix !== false ) { 01316 $path .= '/' . rawurlencode( $suffix ); 01317 } 01318 return $path; 01319 } 01320 01328 function getArchiveVirtualUrl( $suffix = false ) { 01329 $this->assertRepoDefined(); 01330 $path = $this->repo->getVirtualUrl() . '/public/archive/' . $this->getHashPath(); 01331 if ( $suffix === false ) { 01332 $path = substr( $path, 0, -1 ); 01333 } else { 01334 $path .= rawurlencode( $suffix ); 01335 } 01336 return $path; 01337 } 01338 01346 function getThumbVirtualUrl( $suffix = false ) { 01347 $this->assertRepoDefined(); 01348 $path = $this->repo->getVirtualUrl() . '/thumb/' . $this->getUrlRel(); 01349 if ( $suffix !== false ) { 01350 $path .= '/' . rawurlencode( $suffix ); 01351 } 01352 return $path; 01353 } 01354 01358 function isHashed() { 01359 $this->assertRepoDefined(); 01360 return (bool)$this->repo->getHashLevels(); 01361 } 01362 01366 function readOnlyError() { 01367 throw new MWException( get_class($this) . ': write operations are not supported' ); 01368 } 01369 01381 function recordUpload( $oldver, $desc, $license = '', $copyStatus = '', $source = '', $watch = false ) { 01382 $this->readOnlyError(); 01383 } 01384 01403 function publish( $srcPath, $flags = 0 ) { 01404 $this->readOnlyError(); 01405 } 01406 01410 function formatMetadata() { 01411 if ( !$this->getHandler() ) { 01412 return false; 01413 } 01414 return $this->getHandler()->formatMetadata( $this, $this->getMetadata() ); 01415 } 01416 01422 function isLocal() { 01423 return $this->repo && $this->repo->isLocal(); 01424 } 01425 01431 function getRepoName() { 01432 return $this->repo ? $this->repo->getName() : 'unknown'; 01433 } 01434 01440 function getRepo() { 01441 return $this->repo; 01442 } 01443 01450 function isOld() { 01451 return false; 01452 } 01453 01462 function isDeleted( $field ) { 01463 return false; 01464 } 01465 01471 function getVisibility() { 01472 return 0; 01473 } 01474 01480 function wasDeleted() { 01481 $title = $this->getTitle(); 01482 return $title && $title->isDeletedQuick(); 01483 } 01484 01497 function move( $target ) { 01498 $this->readOnlyError(); 01499 } 01500 01515 function delete( $reason, $suppress = false ) { 01516 $this->readOnlyError(); 01517 } 01518 01533 function restore( $versions = array(), $unsuppress = false ) { 01534 $this->readOnlyError(); 01535 } 01536 01544 function isMultipage() { 01545 return $this->getHandler() && $this->handler->isMultiPage( $this ); 01546 } 01547 01554 function pageCount() { 01555 if ( !isset( $this->pageCount ) ) { 01556 if ( $this->getHandler() && $this->handler->isMultiPage( $this ) ) { 01557 $this->pageCount = $this->handler->pageCount( $this ); 01558 } else { 01559 $this->pageCount = false; 01560 } 01561 } 01562 return $this->pageCount; 01563 } 01564 01574 static function scaleHeight( $srcWidth, $srcHeight, $dstWidth ) { 01575 // Exact integer multiply followed by division 01576 if ( $srcWidth == 0 ) { 01577 return 0; 01578 } else { 01579 return round( $srcHeight * $dstWidth / $srcWidth ); 01580 } 01581 } 01582 01590 function getImageSize( $fileName ) { 01591 if ( !$this->getHandler() ) { 01592 return false; 01593 } 01594 return $this->handler->getImageSize( $this, $fileName ); 01595 } 01596 01603 function getDescriptionUrl() { 01604 if ( $this->repo ) { 01605 return $this->repo->getDescriptionUrl( $this->getName() ); 01606 } else { 01607 return false; 01608 } 01609 } 01610 01616 function getDescriptionText() { 01617 global $wgMemc, $wgLang; 01618 if ( !$this->repo || !$this->repo->fetchDescription ) { 01619 return false; 01620 } 01621 $renderUrl = $this->repo->getDescriptionRenderUrl( $this->getName(), $wgLang->getCode() ); 01622 if ( $renderUrl ) { 01623 if ( $this->repo->descriptionCacheExpiry > 0 ) { 01624 wfDebug("Attempting to get the description from cache..."); 01625 $key = $this->repo->getLocalCacheKey( 'RemoteFileDescription', 'url', $wgLang->getCode(), 01626 $this->getName() ); 01627 $obj = $wgMemc->get($key); 01628 if ($obj) { 01629 wfDebug("success!\n"); 01630 return $obj; 01631 } 01632 wfDebug("miss\n"); 01633 } 01634 wfDebug( "Fetching shared description from $renderUrl\n" ); 01635 $res = Http::get( $renderUrl ); 01636 if ( $res && $this->repo->descriptionCacheExpiry > 0 ) { 01637 $wgMemc->set( $key, $res, $this->repo->descriptionCacheExpiry ); 01638 } 01639 return $res; 01640 } else { 01641 return false; 01642 } 01643 } 01644 01657 function getDescription( $audience = self::FOR_PUBLIC, User $user = null ) { 01658 return null; 01659 } 01660 01666 function getTimestamp() { 01667 $this->assertRepoDefined(); 01668 return $this->repo->getFileTimestamp( $this->getPath() ); 01669 } 01670 01676 function getSha1() { 01677 $this->assertRepoDefined(); 01678 return $this->repo->getFileSha1( $this->getPath() ); 01679 } 01680 01686 function getStorageKey() { 01687 $hash = $this->getSha1(); 01688 if ( !$hash ) { 01689 return false; 01690 } 01691 $ext = $this->getExtension(); 01692 $dotExt = $ext === '' ? '' : ".$ext"; 01693 return $hash . $dotExt; 01694 } 01695 01704 function userCan( $field, User $user = null ) { 01705 return true; 01706 } 01707 01717 static function getPropsFromPath( $path, $ext = true ) { 01718 wfDebug( __METHOD__.": Getting file info for $path\n" ); 01719 wfDeprecated( __METHOD__, '1.19' ); 01720 01721 $fsFile = new FSFile( $path ); 01722 return $fsFile->getProps(); 01723 } 01724 01736 static function sha1Base36( $path ) { 01737 wfDeprecated( __METHOD__, '1.19' ); 01738 01739 $fsFile = new FSFile( $path ); 01740 return $fsFile->getSha1Base36(); 01741 } 01742 01746 function getLongDesc() { 01747 $handler = $this->getHandler(); 01748 if ( $handler ) { 01749 return $handler->getLongDesc( $this ); 01750 } else { 01751 return MediaHandler::getGeneralLongDesc( $this ); 01752 } 01753 } 01754 01758 function getShortDesc() { 01759 $handler = $this->getHandler(); 01760 if ( $handler ) { 01761 return $handler->getShortDesc( $this ); 01762 } else { 01763 return MediaHandler::getGeneralShortDesc( $this ); 01764 } 01765 } 01766 01770 function getDimensionsString() { 01771 $handler = $this->getHandler(); 01772 if ( $handler ) { 01773 return $handler->getDimensionsString( $this ); 01774 } else { 01775 return ''; 01776 } 01777 } 01778 01782 function getRedirected() { 01783 return $this->redirected; 01784 } 01785 01789 function getRedirectedTitle() { 01790 if ( $this->redirected ) { 01791 if ( !$this->redirectTitle ) { 01792 $this->redirectTitle = Title::makeTitle( NS_FILE, $this->redirected ); 01793 } 01794 return $this->redirectTitle; 01795 } 01796 } 01797 01802 function redirectedFrom( $from ) { 01803 $this->redirected = $from; 01804 } 01805 01809 function isMissing() { 01810 return false; 01811 } 01812 01817 public function isCacheable() { 01818 return true; 01819 } 01820 01825 protected function assertRepoDefined() { 01826 if ( !( $this->repo instanceof $this->repoClass ) ) { 01827 throw new MWException( "A {$this->repoClass} object is not set for this File.\n" ); 01828 } 01829 } 01830 01835 protected function assertTitleDefined() { 01836 if ( !( $this->title instanceof Title ) ) { 01837 throw new MWException( "A Title object is not set for this File.\n" ); 01838 } 01839 } 01840 }