MediaWiki
REL1_20
|
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 $this->url = $this->repo->getZoneUrl( 'public' ) . '/' . $this->getUrlRel(); 00320 } 00321 return $this->url; 00322 } 00323 00331 public function getFullUrl() { 00332 return wfExpandUrl( $this->getUrl(), PROTO_RELATIVE ); 00333 } 00334 00338 public function getCanonicalUrl() { 00339 return wfExpandUrl( $this->getUrl(), PROTO_CANONICAL ); 00340 } 00341 00345 function getViewURL() { 00346 if ( $this->mustRender() ) { 00347 if ( $this->canRender() ) { 00348 return $this->createThumb( $this->getWidth() ); 00349 } else { 00350 wfDebug( __METHOD__.': supposed to render ' . $this->getName() . 00351 ' (' . $this->getMimeType() . "), but can't!\n" ); 00352 return $this->getURL(); #hm... return NULL? 00353 } 00354 } else { 00355 return $this->getURL(); 00356 } 00357 } 00358 00372 public function getPath() { 00373 if ( !isset( $this->path ) ) { 00374 $this->assertRepoDefined(); 00375 $this->path = $this->repo->getZonePath( 'public' ) . '/' . $this->getRel(); 00376 } 00377 return $this->path; 00378 } 00379 00387 public function getLocalRefPath() { 00388 $this->assertRepoDefined(); 00389 if ( !isset( $this->fsFile ) ) { 00390 $this->fsFile = $this->repo->getLocalReference( $this->getPath() ); 00391 if ( !$this->fsFile ) { 00392 $this->fsFile = false; // null => false; cache negative hits 00393 } 00394 } 00395 return ( $this->fsFile ) 00396 ? $this->fsFile->getPath() 00397 : false; 00398 } 00399 00411 public function getWidth( $page = 1 ) { 00412 return false; 00413 } 00414 00426 public function getHeight( $page = 1 ) { 00427 return false; 00428 } 00429 00438 public function getUser( $type = 'text' ) { 00439 return null; 00440 } 00441 00447 public function getLength() { 00448 $handler = $this->getHandler(); 00449 if ( $handler ) { 00450 return $handler->getLength( $this ); 00451 } else { 00452 return 0; 00453 } 00454 } 00455 00461 public function isVectorized() { 00462 $handler = $this->getHandler(); 00463 if ( $handler ) { 00464 return $handler->isVectorized( $this ); 00465 } else { 00466 return false; 00467 } 00468 } 00469 00480 public function canAnimateThumbIfAppropriate() { 00481 $handler = $this->getHandler(); 00482 if ( !$handler ) { 00483 // We cannot handle image whatsoever, thus 00484 // one would not expect it to be animated 00485 // so true. 00486 return true; 00487 } else { 00488 if ( $this->allowInlineDisplay() 00489 && $handler->isAnimatedImage( $this ) 00490 && !$handler->canAnimateThumbnail( $this ) 00491 ) { 00492 // Image is animated, but thumbnail isn't. 00493 // This is unexpected to the user. 00494 return false; 00495 } else { 00496 // Image is not animated, so one would 00497 // not expect thumb to be 00498 return true; 00499 } 00500 } 00501 } 00502 00509 public function getMetadata() { 00510 return false; 00511 } 00512 00520 public function convertMetadataVersion($metadata, $version) { 00521 $handler = $this->getHandler(); 00522 if ( !is_array( $metadata ) ) { 00523 // Just to make the return type consistent 00524 $metadata = unserialize( $metadata ); 00525 } 00526 if ( $handler ) { 00527 return $handler->convertMetadataVersion( $metadata, $version ); 00528 } else { 00529 return $metadata; 00530 } 00531 } 00532 00539 public function getBitDepth() { 00540 return 0; 00541 } 00542 00549 public function getSize() { 00550 return false; 00551 } 00552 00560 function getMimeType() { 00561 return 'unknown/unknown'; 00562 } 00563 00571 function getMediaType() { 00572 return MEDIATYPE_UNKNOWN; 00573 } 00574 00587 function canRender() { 00588 if ( !isset( $this->canRender ) ) { 00589 $this->canRender = $this->getHandler() && $this->handler->canRender( $this ); 00590 } 00591 return $this->canRender; 00592 } 00593 00598 protected function getCanRender() { 00599 return $this->canRender(); 00600 } 00601 00612 function mustRender() { 00613 return $this->getHandler() && $this->handler->mustRender( $this ); 00614 } 00615 00621 function allowInlineDisplay() { 00622 return $this->canRender(); 00623 } 00624 00638 function isSafeFile() { 00639 if ( !isset( $this->isSafeFile ) ) { 00640 $this->isSafeFile = $this->_getIsSafeFile(); 00641 } 00642 return $this->isSafeFile; 00643 } 00644 00650 protected function getIsSafeFile() { 00651 return $this->isSafeFile(); 00652 } 00653 00659 protected function _getIsSafeFile() { 00660 global $wgTrustedMediaFormats; 00661 00662 if ( $this->allowInlineDisplay() ) { 00663 return true; 00664 } 00665 if ($this->isTrustedFile()) { 00666 return true; 00667 } 00668 00669 $type = $this->getMediaType(); 00670 $mime = $this->getMimeType(); 00671 #wfDebug("LocalFile::isSafeFile: type= $type, mime= $mime\n"); 00672 00673 if ( !$type || $type === MEDIATYPE_UNKNOWN ) { 00674 return false; #unknown type, not trusted 00675 } 00676 if ( in_array( $type, $wgTrustedMediaFormats ) ) { 00677 return true; 00678 } 00679 00680 if ( $mime === "unknown/unknown" ) { 00681 return false; #unknown type, not trusted 00682 } 00683 if ( in_array( $mime, $wgTrustedMediaFormats) ) { 00684 return true; 00685 } 00686 00687 return false; 00688 } 00689 00703 function isTrustedFile() { 00704 #this could be implemented to check a flag in the database, 00705 #look for signatures, etc 00706 return false; 00707 } 00708 00716 public function exists() { 00717 return $this->getPath() && $this->repo->fileExists( $this->path ); 00718 } 00719 00726 public function isVisible() { 00727 return $this->exists(); 00728 } 00729 00733 function getTransformScript() { 00734 if ( !isset( $this->transformScript ) ) { 00735 $this->transformScript = false; 00736 if ( $this->repo ) { 00737 $script = $this->repo->getThumbScriptUrl(); 00738 if ( $script ) { 00739 $this->transformScript = "$script?f=" . urlencode( $this->getName() ); 00740 } 00741 } 00742 } 00743 return $this->transformScript; 00744 } 00745 00753 function getUnscaledThumb( $handlerParams = array() ) { 00754 $hp =& $handlerParams; 00755 $page = isset( $hp['page'] ) ? $hp['page'] : false; 00756 $width = $this->getWidth( $page ); 00757 if ( !$width ) { 00758 return $this->iconThumb(); 00759 } 00760 $hp['width'] = $width; 00761 return $this->transform( $hp ); 00762 } 00763 00773 public function thumbName( $params, $flags = 0 ) { 00774 $name = ( $this->repo && !( $flags & self::THUMB_FULL_NAME ) ) 00775 ? $this->repo->nameForThumb( $this->getName() ) 00776 : $this->getName(); 00777 return $this->generateThumbName( $name, $params ); 00778 } 00779 00788 public function generateThumbName( $name, $params ) { 00789 if ( !$this->getHandler() ) { 00790 return null; 00791 } 00792 $extension = $this->getExtension(); 00793 list( $thumbExt, $thumbMime ) = $this->handler->getThumbType( 00794 $extension, $this->getMimeType(), $params ); 00795 $thumbName = $this->handler->makeParamString( $params ) . '-' . $name; 00796 if ( $thumbExt != $extension ) { 00797 $thumbName .= ".$thumbExt"; 00798 } 00799 return $thumbName; 00800 } 00801 00819 public function createThumb( $width, $height = -1 ) { 00820 $params = array( 'width' => $width ); 00821 if ( $height != -1 ) { 00822 $params['height'] = $height; 00823 } 00824 $thumb = $this->transform( $params ); 00825 if ( is_null( $thumb ) || $thumb->isError() ) { 00826 return ''; 00827 } 00828 return $thumb->getUrl(); 00829 } 00830 00840 protected function transformErrorOutput( $thumbPath, $thumbUrl, $params, $flags ) { 00841 global $wgIgnoreImageErrors; 00842 00843 if ( $wgIgnoreImageErrors && !( $flags & self::RENDER_NOW ) ) { 00844 return $this->handler->getTransform( $this, $thumbPath, $thumbUrl, $params ); 00845 } else { 00846 return new MediaTransformError( 'thumbnail_error', 00847 $params['width'], 0, wfMessage( 'thumbnail-dest-create' )->text() ); 00848 } 00849 } 00850 00859 function transform( $params, $flags = 0 ) { 00860 global $wgUseSquid, $wgIgnoreImageErrors, $wgThumbnailEpoch; 00861 00862 wfProfileIn( __METHOD__ ); 00863 do { 00864 if ( !$this->canRender() ) { 00865 $thumb = $this->iconThumb(); 00866 break; // not a bitmap or renderable image, don't try 00867 } 00868 00869 // Get the descriptionUrl to embed it as comment into the thumbnail. Bug 19791. 00870 $descriptionUrl = $this->getDescriptionUrl(); 00871 if ( $descriptionUrl ) { 00872 $params['descriptionUrl'] = wfExpandUrl( $descriptionUrl, PROTO_CANONICAL ); 00873 } 00874 00875 $script = $this->getTransformScript(); 00876 if ( $script && !( $flags & self::RENDER_NOW ) ) { 00877 // Use a script to transform on client request, if possible 00878 $thumb = $this->handler->getScriptedTransform( $this, $script, $params ); 00879 if ( $thumb ) { 00880 break; 00881 } 00882 } 00883 00884 $normalisedParams = $params; 00885 $this->handler->normaliseParams( $this, $normalisedParams ); 00886 00887 $thumbName = $this->thumbName( $normalisedParams ); 00888 $thumbUrl = $this->getThumbUrl( $thumbName ); 00889 $thumbPath = $this->getThumbPath( $thumbName ); // final thumb path 00890 00891 if ( $this->repo ) { 00892 // Defer rendering if a 404 handler is set up... 00893 if ( $this->repo->canTransformVia404() && !( $flags & self::RENDER_NOW ) ) { 00894 wfDebug( __METHOD__ . " transformation deferred." ); 00895 // XXX: Pass in the storage path even though we are not rendering anything 00896 // and the path is supposed to be an FS path. This is due to getScalerType() 00897 // getting called on the path and clobbering $thumb->getUrl() if it's false. 00898 $thumb = $this->handler->getTransform( $this, $thumbPath, $thumbUrl, $params ); 00899 break; 00900 } 00901 // Clean up broken thumbnails as needed 00902 $this->migrateThumbFile( $thumbName ); 00903 // Check if an up-to-date thumbnail already exists... 00904 wfDebug( __METHOD__.": Doing stat for $thumbPath\n" ); 00905 if ( $this->repo->fileExists( $thumbPath ) && !( $flags & self::RENDER_FORCE ) ) { 00906 $timestamp = $this->repo->getFileTimestamp( $thumbPath ); 00907 if ( $timestamp !== false && $timestamp >= $wgThumbnailEpoch ) { 00908 // XXX: Pass in the storage path even though we are not rendering anything 00909 // and the path is supposed to be an FS path. This is due to getScalerType() 00910 // getting called on the path and clobbering $thumb->getUrl() if it's false. 00911 $thumb = $this->handler->getTransform( $this, $thumbPath, $thumbUrl, $params ); 00912 $thumb->setStoragePath( $thumbPath ); 00913 break; 00914 } 00915 } elseif ( $flags & self::RENDER_FORCE ) { 00916 wfDebug( __METHOD__ . " forcing rendering per flag File::RENDER_FORCE\n" ); 00917 } 00918 } 00919 00920 // If the backend is ready-only, don't keep generating thumbnails 00921 // only to return transformation errors, just return the error now. 00922 if ( $this->repo->getReadOnlyReason() !== false ) { 00923 $thumb = $this->transformErrorOutput( $thumbPath, $thumbUrl, $params, $flags ); 00924 break; 00925 } 00926 00927 // Create a temp FS file with the same extension and the thumbnail 00928 $thumbExt = FileBackend::extensionFromPath( $thumbPath ); 00929 $tmpFile = TempFSFile::factory( 'transform_', $thumbExt ); 00930 if ( !$tmpFile ) { 00931 $thumb = $this->transformErrorOutput( $thumbPath, $thumbUrl, $params, $flags ); 00932 break; 00933 } 00934 $tmpThumbPath = $tmpFile->getPath(); // path of 0-byte temp file 00935 00936 // Actually render the thumbnail... 00937 wfProfileIn( __METHOD__ . '-doTransform' ); 00938 $thumb = $this->handler->doTransform( $this, $tmpThumbPath, $thumbUrl, $params ); 00939 wfProfileOut( __METHOD__ . '-doTransform' ); 00940 $tmpFile->bind( $thumb ); // keep alive with $thumb 00941 00942 if ( !$thumb ) { // bad params? 00943 $thumb = null; 00944 } elseif ( $thumb->isError() ) { // transform error 00945 $this->lastError = $thumb->toText(); 00946 // Ignore errors if requested 00947 if ( $wgIgnoreImageErrors && !( $flags & self::RENDER_NOW ) ) { 00948 $thumb = $this->handler->getTransform( $this, $tmpThumbPath, $thumbUrl, $params ); 00949 } 00950 } elseif ( $this->repo && $thumb->hasFile() && !$thumb->fileIsSource() ) { 00951 // Copy the thumbnail from the file system into storage... 00952 $disposition = $this->getThumbDisposition( $thumbName ); 00953 $status = $this->repo->quickImport( $tmpThumbPath, $thumbPath, $disposition ); 00954 if ( $status->isOK() ) { 00955 $thumb->setStoragePath( $thumbPath ); 00956 } else { 00957 $thumb = $this->transformErrorOutput( $thumbPath, $thumbUrl, $params, $flags ); 00958 } 00959 // Give extensions a chance to do something with this thumbnail... 00960 wfRunHooks( 'FileTransformed', array( $this, $thumb, $tmpThumbPath, $thumbPath ) ); 00961 } 00962 00963 // Purge. Useful in the event of Core -> Squid connection failure or squid 00964 // purge collisions from elsewhere during failure. Don't keep triggering for 00965 // "thumbs" which have the main image URL though (bug 13776) 00966 if ( $wgUseSquid ) { 00967 if ( !$thumb || $thumb->isError() || $thumb->getUrl() != $this->getURL() ) { 00968 SquidUpdate::purge( array( $thumbUrl ) ); 00969 } 00970 } 00971 } while ( false ); 00972 00973 wfProfileOut( __METHOD__ ); 00974 return is_object( $thumb ) ? $thumb : false; 00975 } 00976 00981 function getThumbDisposition( $thumbName ) { 00982 $fileName = $this->name; // file name to suggest 00983 $thumbExt = FileBackend::extensionFromPath( $thumbName ); 00984 if ( $thumbExt != '' && $thumbExt !== $this->getExtension() ) { 00985 $fileName .= ".$thumbExt"; 00986 } 00987 return FileBackend::makeContentDisposition( 'inline', $fileName ); 00988 } 00989 00995 function migrateThumbFile( $thumbName ) {} 00996 01002 function getHandler() { 01003 if ( !isset( $this->handler ) ) { 01004 $this->handler = MediaHandler::getHandler( $this->getMimeType() ); 01005 } 01006 return $this->handler; 01007 } 01008 01014 function iconThumb() { 01015 global $wgStylePath, $wgStyleDirectory; 01016 01017 $try = array( 'fileicon-' . $this->getExtension() . '.png', 'fileicon.png' ); 01018 foreach ( $try as $icon ) { 01019 $path = '/common/images/icons/' . $icon; 01020 $filepath = $wgStyleDirectory . $path; 01021 if ( file_exists( $filepath ) ) { // always FS 01022 $params = array( 'width' => 120, 'height' => 120 ); 01023 return new ThumbnailImage( $this, $wgStylePath . $path, false, $params ); 01024 } 01025 } 01026 return null; 01027 } 01028 01033 function getLastError() { 01034 return $this->lastError; 01035 } 01036 01043 function getThumbnails() { 01044 return array(); 01045 } 01046 01054 function purgeCache( $options = array() ) {} 01055 01061 function purgeDescription() { 01062 $title = $this->getTitle(); 01063 if ( $title ) { 01064 $title->invalidateCache(); 01065 $title->purgeSquid(); 01066 } 01067 } 01068 01073 function purgeEverything() { 01074 // Delete thumbnails and refresh file metadata cache 01075 $this->purgeCache(); 01076 $this->purgeDescription(); 01077 01078 // Purge cache of all pages using this file 01079 $title = $this->getTitle(); 01080 if ( $title ) { 01081 $update = new HTMLCacheUpdate( $title, 'imagelinks' ); 01082 $update->doUpdate(); 01083 } 01084 } 01085 01097 function getHistory( $limit = null, $start = null, $end = null, $inc=true ) { 01098 return array(); 01099 } 01100 01110 public function nextHistoryLine() { 01111 return false; 01112 } 01113 01120 public function resetHistory() {} 01121 01129 function getHashPath() { 01130 if ( !isset( $this->hashPath ) ) { 01131 $this->assertRepoDefined(); 01132 $this->hashPath = $this->repo->getHashPath( $this->getName() ); 01133 } 01134 return $this->hashPath; 01135 } 01136 01143 function getRel() { 01144 return $this->getHashPath() . $this->getName(); 01145 } 01146 01154 function getArchiveRel( $suffix = false ) { 01155 $path = 'archive/' . $this->getHashPath(); 01156 if ( $suffix === false ) { 01157 $path = substr( $path, 0, -1 ); 01158 } else { 01159 $path .= $suffix; 01160 } 01161 return $path; 01162 } 01163 01172 function getThumbRel( $suffix = false ) { 01173 $path = $this->getRel(); 01174 if ( $suffix !== false ) { 01175 $path .= '/' . $suffix; 01176 } 01177 return $path; 01178 } 01179 01186 function getUrlRel() { 01187 return $this->getHashPath() . rawurlencode( $this->getName() ); 01188 } 01189 01199 function getArchiveThumbRel( $archiveName, $suffix = false ) { 01200 $path = 'archive/' . $this->getHashPath() . $archiveName . "/"; 01201 if ( $suffix === false ) { 01202 $path = substr( $path, 0, -1 ); 01203 } else { 01204 $path .= $suffix; 01205 } 01206 return $path; 01207 } 01208 01216 function getArchivePath( $suffix = false ) { 01217 $this->assertRepoDefined(); 01218 return $this->repo->getZonePath( 'public' ) . '/' . $this->getArchiveRel( $suffix ); 01219 } 01220 01229 function getArchiveThumbPath( $archiveName, $suffix = false ) { 01230 $this->assertRepoDefined(); 01231 return $this->repo->getZonePath( 'thumb' ) . '/' . 01232 $this->getArchiveThumbRel( $archiveName, $suffix ); 01233 } 01234 01242 function getThumbPath( $suffix = false ) { 01243 $this->assertRepoDefined(); 01244 return $this->repo->getZonePath( 'thumb' ) . '/' . $this->getThumbRel( $suffix ); 01245 } 01246 01254 function getArchiveUrl( $suffix = false ) { 01255 $this->assertRepoDefined(); 01256 $path = $this->repo->getZoneUrl( 'public' ) . '/archive/' . $this->getHashPath(); 01257 if ( $suffix === false ) { 01258 $path = substr( $path, 0, -1 ); 01259 } else { 01260 $path .= rawurlencode( $suffix ); 01261 } 01262 return $path; 01263 } 01264 01273 function getArchiveThumbUrl( $archiveName, $suffix = false ) { 01274 $this->assertRepoDefined(); 01275 $path = $this->repo->getZoneUrl( 'thumb' ) . '/archive/' . 01276 $this->getHashPath() . rawurlencode( $archiveName ) . "/"; 01277 if ( $suffix === false ) { 01278 $path = substr( $path, 0, -1 ); 01279 } else { 01280 $path .= rawurlencode( $suffix ); 01281 } 01282 return $path; 01283 } 01284 01292 function getThumbUrl( $suffix = false ) { 01293 $this->assertRepoDefined(); 01294 $path = $this->repo->getZoneUrl( 'thumb' ) . '/' . $this->getUrlRel(); 01295 if ( $suffix !== false ) { 01296 $path .= '/' . rawurlencode( $suffix ); 01297 } 01298 return $path; 01299 } 01300 01308 function getVirtualUrl( $suffix = false ) { 01309 $this->assertRepoDefined(); 01310 $path = $this->repo->getVirtualUrl() . '/public/' . $this->getUrlRel(); 01311 if ( $suffix !== false ) { 01312 $path .= '/' . rawurlencode( $suffix ); 01313 } 01314 return $path; 01315 } 01316 01324 function getArchiveVirtualUrl( $suffix = false ) { 01325 $this->assertRepoDefined(); 01326 $path = $this->repo->getVirtualUrl() . '/public/archive/' . $this->getHashPath(); 01327 if ( $suffix === false ) { 01328 $path = substr( $path, 0, -1 ); 01329 } else { 01330 $path .= rawurlencode( $suffix ); 01331 } 01332 return $path; 01333 } 01334 01342 function getThumbVirtualUrl( $suffix = false ) { 01343 $this->assertRepoDefined(); 01344 $path = $this->repo->getVirtualUrl() . '/thumb/' . $this->getUrlRel(); 01345 if ( $suffix !== false ) { 01346 $path .= '/' . rawurlencode( $suffix ); 01347 } 01348 return $path; 01349 } 01350 01354 function isHashed() { 01355 $this->assertRepoDefined(); 01356 return (bool)$this->repo->getHashLevels(); 01357 } 01358 01362 function readOnlyError() { 01363 throw new MWException( get_class($this) . ': write operations are not supported' ); 01364 } 01365 01377 function recordUpload( $oldver, $desc, $license = '', $copyStatus = '', $source = '', $watch = false ) { 01378 $this->readOnlyError(); 01379 } 01380 01399 function publish( $srcPath, $flags = 0 ) { 01400 $this->readOnlyError(); 01401 } 01402 01406 function formatMetadata() { 01407 if ( !$this->getHandler() ) { 01408 return false; 01409 } 01410 return $this->getHandler()->formatMetadata( $this, $this->getMetadata() ); 01411 } 01412 01418 function isLocal() { 01419 return $this->repo && $this->repo->isLocal(); 01420 } 01421 01427 function getRepoName() { 01428 return $this->repo ? $this->repo->getName() : 'unknown'; 01429 } 01430 01436 function getRepo() { 01437 return $this->repo; 01438 } 01439 01446 function isOld() { 01447 return false; 01448 } 01449 01458 function isDeleted( $field ) { 01459 return false; 01460 } 01461 01467 function getVisibility() { 01468 return 0; 01469 } 01470 01476 function wasDeleted() { 01477 $title = $this->getTitle(); 01478 return $title && $title->isDeletedQuick(); 01479 } 01480 01493 function move( $target ) { 01494 $this->readOnlyError(); 01495 } 01496 01511 function delete( $reason, $suppress = false ) { 01512 $this->readOnlyError(); 01513 } 01514 01529 function restore( $versions = array(), $unsuppress = false ) { 01530 $this->readOnlyError(); 01531 } 01532 01540 function isMultipage() { 01541 return $this->getHandler() && $this->handler->isMultiPage( $this ); 01542 } 01543 01550 function pageCount() { 01551 if ( !isset( $this->pageCount ) ) { 01552 if ( $this->getHandler() && $this->handler->isMultiPage( $this ) ) { 01553 $this->pageCount = $this->handler->pageCount( $this ); 01554 } else { 01555 $this->pageCount = false; 01556 } 01557 } 01558 return $this->pageCount; 01559 } 01560 01570 static function scaleHeight( $srcWidth, $srcHeight, $dstWidth ) { 01571 // Exact integer multiply followed by division 01572 if ( $srcWidth == 0 ) { 01573 return 0; 01574 } else { 01575 return round( $srcHeight * $dstWidth / $srcWidth ); 01576 } 01577 } 01578 01586 function getImageSize( $fileName ) { 01587 if ( !$this->getHandler() ) { 01588 return false; 01589 } 01590 return $this->handler->getImageSize( $this, $fileName ); 01591 } 01592 01599 function getDescriptionUrl() { 01600 if ( $this->repo ) { 01601 return $this->repo->getDescriptionUrl( $this->getName() ); 01602 } else { 01603 return false; 01604 } 01605 } 01606 01612 function getDescriptionText() { 01613 global $wgMemc, $wgLang; 01614 if ( !$this->repo || !$this->repo->fetchDescription ) { 01615 return false; 01616 } 01617 $renderUrl = $this->repo->getDescriptionRenderUrl( $this->getName(), $wgLang->getCode() ); 01618 if ( $renderUrl ) { 01619 if ( $this->repo->descriptionCacheExpiry > 0 ) { 01620 wfDebug("Attempting to get the description from cache..."); 01621 $key = $this->repo->getLocalCacheKey( 'RemoteFileDescription', 'url', $wgLang->getCode(), 01622 $this->getName() ); 01623 $obj = $wgMemc->get($key); 01624 if ($obj) { 01625 wfDebug("success!\n"); 01626 return $obj; 01627 } 01628 wfDebug("miss\n"); 01629 } 01630 wfDebug( "Fetching shared description from $renderUrl\n" ); 01631 $res = Http::get( $renderUrl ); 01632 if ( $res && $this->repo->descriptionCacheExpiry > 0 ) { 01633 $wgMemc->set( $key, $res, $this->repo->descriptionCacheExpiry ); 01634 } 01635 return $res; 01636 } else { 01637 return false; 01638 } 01639 } 01640 01653 function getDescription( $audience = self::FOR_PUBLIC, User $user = null ) { 01654 return null; 01655 } 01656 01662 function getTimestamp() { 01663 $this->assertRepoDefined(); 01664 return $this->repo->getFileTimestamp( $this->getPath() ); 01665 } 01666 01672 function getSha1() { 01673 $this->assertRepoDefined(); 01674 return $this->repo->getFileSha1( $this->getPath() ); 01675 } 01676 01682 function getStorageKey() { 01683 $hash = $this->getSha1(); 01684 if ( !$hash ) { 01685 return false; 01686 } 01687 $ext = $this->getExtension(); 01688 $dotExt = $ext === '' ? '' : ".$ext"; 01689 return $hash . $dotExt; 01690 } 01691 01700 function userCan( $field, User $user = null ) { 01701 return true; 01702 } 01703 01713 static function getPropsFromPath( $path, $ext = true ) { 01714 wfDebug( __METHOD__.": Getting file info for $path\n" ); 01715 wfDeprecated( __METHOD__, '1.19' ); 01716 01717 $fsFile = new FSFile( $path ); 01718 return $fsFile->getProps(); 01719 } 01720 01732 static function sha1Base36( $path ) { 01733 wfDeprecated( __METHOD__, '1.19' ); 01734 01735 $fsFile = new FSFile( $path ); 01736 return $fsFile->getSha1Base36(); 01737 } 01738 01742 function getLongDesc() { 01743 $handler = $this->getHandler(); 01744 if ( $handler ) { 01745 return $handler->getLongDesc( $this ); 01746 } else { 01747 return MediaHandler::getGeneralLongDesc( $this ); 01748 } 01749 } 01750 01754 function getShortDesc() { 01755 $handler = $this->getHandler(); 01756 if ( $handler ) { 01757 return $handler->getShortDesc( $this ); 01758 } else { 01759 return MediaHandler::getGeneralShortDesc( $this ); 01760 } 01761 } 01762 01766 function getDimensionsString() { 01767 $handler = $this->getHandler(); 01768 if ( $handler ) { 01769 return $handler->getDimensionsString( $this ); 01770 } else { 01771 return ''; 01772 } 01773 } 01774 01778 function getRedirected() { 01779 return $this->redirected; 01780 } 01781 01785 function getRedirectedTitle() { 01786 if ( $this->redirected ) { 01787 if ( !$this->redirectTitle ) { 01788 $this->redirectTitle = Title::makeTitle( NS_FILE, $this->redirected ); 01789 } 01790 return $this->redirectTitle; 01791 } 01792 } 01793 01798 function redirectedFrom( $from ) { 01799 $this->redirected = $from; 01800 } 01801 01805 function isMissing() { 01806 return false; 01807 } 01808 01813 public function isCacheable() { 01814 return true; 01815 } 01816 01821 protected function assertRepoDefined() { 01822 if ( !( $this->repo instanceof $this->repoClass ) ) { 01823 throw new MWException( "A {$this->repoClass} object is not set for this File.\n" ); 01824 } 01825 } 01826 01831 protected function assertTitleDefined() { 01832 if ( !( $this->title instanceof Title ) ) { 01833 throw new MWException( "A Title object is not set for this File.\n" ); 01834 } 01835 } 01836 }