summaryrefslogtreecommitdiff
path: root/includes/filerepo/file/File.php
diff options
context:
space:
mode:
Diffstat (limited to 'includes/filerepo/file/File.php')
-rw-r--r--includes/filerepo/file/File.php827
1 files changed, 568 insertions, 259 deletions
diff --git a/includes/filerepo/file/File.php b/includes/filerepo/file/File.php
index ec5f927b..b574c5e7 100644
--- a/includes/filerepo/file/File.php
+++ b/includes/filerepo/file/File.php
@@ -91,45 +91,67 @@ abstract class File {
* The following member variables are not lazy-initialised
*/
- /**
- * @var FileRepo|bool
- */
- var $repo;
+ /** @var FileRepo|LocalRepo|ForeignAPIRepo|bool */
+ public $repo;
- /**
- * @var Title
- */
- var $title;
+ /** @var Title|string|bool */
+ protected $title;
- var $lastError, $redirected, $redirectedTitle;
+ /** @var string Text of last error */
+ protected $lastError;
- /**
- * @var FSFile|bool False if undefined
- */
+ /** @var string Main part of the title, with underscores (Title::getDBkey) */
+ protected $redirected;
+
+ /** @var Title */
+ protected $redirectedTitle;
+
+ /** @var FSFile|bool False if undefined */
protected $fsFile;
- /**
- * @var MediaHandler
- */
+ /** @var MediaHandler */
protected $handler;
- /**
- * @var string
+ /** @var string The URL corresponding to one of the four basic zones */
+ protected $url;
+
+ /** @var string File extension */
+ protected $extension;
+
+ /** @var string The name of a file from its title object */
+ protected $name;
+
+ /** @var string The storage path corresponding to one of the zones */
+ protected $path;
+
+ /** @var string Relative path including trailing slash */
+ protected $hashPath;
+
+ /** @var string Number of pages of a multipage document, or false for
+ * documents which aren't multipage documents
*/
- protected $url, $extension, $name, $path, $hashPath, $pageCount, $transformScript;
+ protected $pageCount;
+ /** @var string URL of transformscript (for example thumb.php) */
+ protected $transformScript;
+
+ /** @var Title */
protected $redirectTitle;
- /**
- * @var bool
- */
- protected $canRender, $isSafeFile;
+ /** @var bool Wether the output of transform() for this file is likely to be valid. */
+ protected $canRender;
- /**
- * @var string Required Repository class type
+ /** @var bool Wether this media file is in a format that is unlikely to
+ * contain viruses or malicious content
*/
+ protected $isSafeFile;
+
+ /** @var string Required Repository class type */
protected $repoClass = 'FileRepo';
+ /** @var array Cache of tmp filepaths pointing to generated bucket thumbnails, keyed by width */
+ protected $tmpBucketedThumbCache = array();
+
/**
* Call this constructor from child classes.
*
@@ -137,8 +159,8 @@ abstract class File {
* may return false or throw exceptions if they are not set.
* Most subclasses will want to call assertRepoDefined() here.
*
- * @param $title Title|string|bool
- * @param $repo FileRepo|bool
+ * @param Title|string|bool $title
+ * @param FileRepo|bool $repo
*/
function __construct( $title, $repo ) {
if ( $title !== false ) { // subclasses may not use MW titles
@@ -152,7 +174,7 @@ abstract class File {
* Given a string or Title object return either a
* valid Title object with namespace NS_FILE or null
*
- * @param $title Title|string
+ * @param Title|string $title
* @param string|bool $exception Use 'exception' to throw an error on bad titles
* @throws MWException
* @return Title|null
@@ -174,6 +196,7 @@ abstract class File {
if ( !$ret && $exception !== false ) {
throw new MWException( "`$title` is not a valid file title." );
}
+
return $ret;
}
@@ -183,6 +206,7 @@ abstract class File {
return null;
} else {
$this->$name = call_user_func( $function );
+
return $this->$name;
}
}
@@ -214,7 +238,7 @@ abstract class File {
/**
* Checks if file extensions are compatible
*
- * @param $old File Old file
+ * @param File $old Old file
* @param string $new New name
*
* @return bool|null
@@ -224,6 +248,7 @@ abstract class File {
$n = strrpos( $new, '.' );
$newExt = self::normalizeExtension( $n ? substr( $new, $n + 1 ) : '' );
$mimeMagic = MimeMagic::singleton();
+
return $mimeMagic->isMatchingExtension( $newExt, $oldMime );
}
@@ -232,7 +257,8 @@ abstract class File {
* Called by ImagePage
* STUB
*/
- function upgradeRow() {}
+ function upgradeRow() {
+ }
/**
* Split an internet media type into its two components; if not
@@ -252,10 +278,9 @@ abstract class File {
/**
* Callback for usort() to do file sorts by name
*
- * @param $a File
- * @param $b File
- *
- * @return Integer: result of name comparison
+ * @param File $a
+ * @param File $b
+ * @return int Result of name comparison
*/
public static function compare( File $a, File $b ) {
return strcmp( $a->getName(), $b->getName() );
@@ -271,6 +296,7 @@ abstract class File {
$this->assertRepoDefined();
$this->name = $this->repo->getNameFromTitle( $this->title );
}
+
return $this->name;
}
@@ -285,6 +311,7 @@ abstract class File {
$this->extension = self::normalizeExtension(
$n ? substr( $this->getName(), $n + 1 ) : '' );
}
+
return $this->extension;
}
@@ -306,6 +333,7 @@ abstract class File {
if ( $this->redirected ) {
return $this->getRedirectedTitle();
}
+
return $this->title;
}
@@ -320,6 +348,7 @@ abstract class File {
$ext = $this->getExtension();
$this->url = $this->repo->getZoneUrl( 'public', $ext ) . '/' . $this->getUrlRel();
}
+
return $this->url;
}
@@ -328,7 +357,7 @@ abstract class File {
* Upload URL paths _may or may not_ be fully qualified, so
* we check. Local paths are assumed to belong on $wgServer.
*
- * @return String
+ * @return string
*/
public function getFullUrl() {
return wfExpandUrl( $this->getUrl(), PROTO_RELATIVE );
@@ -351,6 +380,7 @@ abstract class File {
} else {
wfDebug( __METHOD__ . ': supposed to render ' . $this->getName() .
' (' . $this->getMimeType() . "), but can't!\n" );
+
return $this->getURL(); #hm... return NULL?
}
} else {
@@ -376,6 +406,7 @@ abstract class File {
$this->assertRepoDefined();
$this->path = $this->repo->getZonePath( 'public' ) . '/' . $this->getRel();
}
+
return $this->path;
}
@@ -394,6 +425,7 @@ abstract class File {
$this->fsFile = false; // null => false; cache negative hits
}
}
+
return ( $this->fsFile )
? $this->fsFile->getPath()
: false;
@@ -406,9 +438,8 @@ abstract class File {
* STUB
* Overridden by LocalFile, UnregisteredLocalFile
*
- * @param $page int
- *
- * @return number
+ * @param int $page
+ * @return int|bool
*/
public function getWidth( $page = 1 ) {
return false;
@@ -421,20 +452,62 @@ abstract class File {
* STUB
* Overridden by LocalFile, UnregisteredLocalFile
*
- * @param $page int
- *
- * @return bool|number False on failure
+ * @param int $page
+ * @return bool|int False on failure
*/
public function getHeight( $page = 1 ) {
return false;
}
/**
+ * Return the smallest bucket from $wgThumbnailBuckets which is at least
+ * $wgThumbnailMinimumBucketDistance larger than $desiredWidth. The returned bucket, if any,
+ * will always be bigger than $desiredWidth.
+ *
+ * @param int $desiredWidth
+ * @param int $page
+ * @return bool|int
+ */
+ public function getThumbnailBucket( $desiredWidth, $page = 1 ) {
+ global $wgThumbnailBuckets, $wgThumbnailMinimumBucketDistance;
+
+ $imageWidth = $this->getWidth( $page );
+
+ if ( $imageWidth === false ) {
+ return false;
+ }
+
+ if ( $desiredWidth > $imageWidth ) {
+ return false;
+ }
+
+ if ( !$wgThumbnailBuckets ) {
+ return false;
+ }
+
+ $sortedBuckets = $wgThumbnailBuckets;
+
+ sort( $sortedBuckets );
+
+ foreach ( $sortedBuckets as $bucket ) {
+ if ( $bucket > $imageWidth ) {
+ return false;
+ }
+
+ if ( $bucket - $wgThumbnailMinimumBucketDistance > $desiredWidth ) {
+ return $bucket;
+ }
+ }
+
+ // Image is bigger than any available bucket
+ return false;
+ }
+
+ /**
* Returns ID or name of user who uploaded the file
* STUB
*
* @param string $type 'text' or 'id'
- *
* @return string|int
*/
public function getUser( $type = 'text' ) {
@@ -444,7 +517,7 @@ abstract class File {
/**
* Get the duration of a media file in seconds
*
- * @return number
+ * @return int
*/
public function getLength() {
$handler = $this->getHandler();
@@ -470,11 +543,47 @@ abstract class File {
}
/**
+ * Gives a (possibly empty) list of languages to render
+ * the file in.
+ *
+ * If the file doesn't have translations, or if the file
+ * format does not support that sort of thing, returns
+ * an empty array.
+ *
+ * @return array
+ * @since 1.23
+ */
+ public function getAvailableLanguages() {
+ $handler = $this->getHandler();
+ if ( $handler ) {
+ return $handler->getAvailableLanguages( $this );
+ } else {
+ return array();
+ }
+ }
+
+ /**
+ * In files that support multiple language, what is the default language
+ * to use if none specified.
+ *
+ * @return string Lang code, or null if filetype doesn't support multiple languages.
+ * @since 1.23
+ */
+ public function getDefaultRenderLanguage() {
+ $handler = $this->getHandler();
+ if ( $handler ) {
+ return $handler->getDefaultRenderLanguage( $this );
+ } else {
+ return null;
+ }
+ }
+
+ /**
* Will the thumbnail be animated if one would expect it to be.
*
* Currently used to add a warning to the image description page
*
- * @return bool false if the main image is both animated
+ * @return bool False if the main image is both animated
* and the thumbnail is not. In all other cases must return
* true. If image is not renderable whatsoever, should
* return true.
@@ -506,18 +615,35 @@ abstract class File {
* Get handler-specific metadata
* Overridden by LocalFile, UnregisteredLocalFile
* STUB
- * @return bool
+ * @return bool|array
*/
public function getMetadata() {
return false;
}
/**
+ * Like getMetadata but returns a handler independent array of common values.
+ * @see MediaHandler::getCommonMetaArray()
+ * @return array|bool Array or false if not supported
+ * @since 1.23
+ */
+ public function getCommonMetaArray() {
+ $handler = $this->getHandler();
+
+ if ( !$handler ) {
+ return false;
+ }
+
+ return $handler->getCommonMetaArray( $this );
+ }
+
+ /**
* get versioned metadata
*
- * @param $metadata Mixed Array or String of (serialized) metadata
- * @param $version integer version number.
- * @return Array containing metadata, or what was passed to it on fail (unserializing if not array)
+ * @param array|string $metadata Array or string of (serialized) metadata
+ * @param int $version Version number.
+ * @return array Array containing metadata, or what was passed to it on fail
+ * (unserializing if not array)
*/
public function convertMetadataVersion( $metadata, $version ) {
$handler = $this->getHandler();
@@ -553,7 +679,7 @@ abstract class File {
}
/**
- * Returns the mime type of the file.
+ * Returns the MIME type of the file.
* Overridden by LocalFile, UnregisteredLocalFile
* STUB
*
@@ -588,8 +714,9 @@ abstract class File {
*/
function canRender() {
if ( !isset( $this->canRender ) ) {
- $this->canRender = $this->getHandler() && $this->handler->canRender( $this );
+ $this->canRender = $this->getHandler() && $this->handler->canRender( $this ) && $this->exists();
}
+
return $this->canRender;
}
@@ -639,8 +766,9 @@ abstract class File {
*/
function isSafeFile() {
if ( !isset( $this->isSafeFile ) ) {
- $this->isSafeFile = $this->_getIsSafeFile();
+ $this->isSafeFile = $this->getIsSafeFileUncached();
}
+
return $this->isSafeFile;
}
@@ -658,7 +786,7 @@ abstract class File {
*
* @return bool
*/
- protected function _getIsSafeFile() {
+ protected function getIsSafeFileUncached() {
global $wgTrustedMediaFormats;
if ( $this->allowInlineDisplay() ) {
@@ -713,7 +841,7 @@ abstract class File {
*
* Overridden by LocalFile to avoid unnecessary stat calls.
*
- * @return boolean Whether file exists in the repository.
+ * @return bool Whether file exists in the repository.
*/
public function exists() {
return $this->getPath() && $this->repo->fileExists( $this->path );
@@ -723,7 +851,7 @@ abstract class File {
* Returns true if file exists in the repository and can be included in a page.
* It would be unsafe to include private images, making public thumbnails inadvertently
*
- * @return boolean Whether file exists in the repository and is includable.
+ * @return bool Whether file exists in the repository and is includable.
*/
public function isVisible() {
return $this->exists();
@@ -742,13 +870,14 @@ abstract class File {
}
}
}
+
return $this->transformScript;
}
/**
* Get a ThumbnailImage which is the same size as the source
*
- * @param $handlerParams array
+ * @param array $handlerParams
*
* @return string
*/
@@ -760,6 +889,9 @@ abstract class File {
return $this->iconThumb();
}
$hp['width'] = $width;
+ // be sure to ignore any height specification as well (bug 62258)
+ unset( $hp['height'] );
+
return $this->transform( $hp );
}
@@ -768,14 +900,15 @@ abstract class File {
* Use File::THUMB_FULL_NAME to always get a name like "<params>-<source>".
* Otherwise, the format may be "<params>-<source>" or "<params>-thumbnail.<ext>".
*
- * @param array $params handler-specific parameters
- * @param $flags integer Bitfield that supports THUMB_* constants
+ * @param array $params Handler-specific parameters
+ * @param int $flags Bitfield that supports THUMB_* constants
* @return string
*/
public function thumbName( $params, $flags = 0 ) {
$name = ( $this->repo && !( $flags & self::THUMB_FULL_NAME ) )
? $this->repo->nameForThumb( $this->getName() )
: $this->getName();
+
return $this->generateThumbName( $name, $params );
}
@@ -784,7 +917,6 @@ abstract class File {
*
* @param string $name
* @param array $params Parameters which will be passed to MediaHandler::makeParamString
- *
* @return string
*/
public function generateThumbName( $name, $params ) {
@@ -792,12 +924,13 @@ abstract class File {
return null;
}
$extension = $this->getExtension();
- list( $thumbExt, $thumbMime ) = $this->handler->getThumbType(
+ list( $thumbExt, ) = $this->getHandler()->getThumbType(
$extension, $this->getMimeType(), $params );
- $thumbName = $this->handler->makeParamString( $params ) . '-' . $name;
+ $thumbName = $this->getHandler()->makeParamString( $params ) . '-' . $name;
if ( $thumbExt != $extension ) {
$thumbName .= ".$thumbExt";
}
+
return $thumbName;
}
@@ -813,8 +946,8 @@ abstract class File {
* specified, the generated image will be no bigger than width x height,
* and will also have correct aspect ratio.
*
- * @param $width Integer: maximum width of the generated thumbnail
- * @param $height Integer: maximum height of the image (optional)
+ * @param int $width Maximum width of the generated thumbnail
+ * @param int $height Maximum height of the image (optional)
*
* @return string
*/
@@ -824,9 +957,10 @@ abstract class File {
$params['height'] = $height;
}
$thumb = $this->transform( $params );
- if ( is_null( $thumb ) || $thumb->isError() ) {
+ if ( !$thumb || $thumb->isError() ) {
return '';
}
+
return $thumb->getUrl();
}
@@ -835,8 +969,8 @@ abstract class File {
*
* @param string $thumbPath Thumbnail storage path
* @param string $thumbUrl Thumbnail URL
- * @param $params Array
- * @param $flags integer
+ * @param array $params
+ * @param int $flags
* @return MediaTransformOutput
*/
protected function transformErrorOutput( $thumbPath, $thumbUrl, $params, $flags ) {
@@ -854,13 +988,13 @@ abstract class File {
/**
* Transform a media file
*
- * @param array $params an associative array of handler-specific parameters.
- * Typical keys are width, height and page.
- * @param $flags Integer: a bitfield, may contain self::RENDER_NOW to force rendering
+ * @param array $params An associative array of handler-specific parameters.
+ * Typical keys are width, height and page.
+ * @param int $flags A bitfield, may contain self::RENDER_NOW to force rendering
* @return MediaTransformOutput|bool False on failure
*/
function transform( $params, $flags = 0 ) {
- global $wgUseSquid, $wgIgnoreImageErrors, $wgThumbnailEpoch;
+ global $wgThumbnailEpoch;
wfProfileIn( __METHOD__ );
do {
@@ -895,15 +1029,13 @@ abstract class File {
if ( $this->repo ) {
// Defer rendering if a 404 handler is set up...
if ( $this->repo->canTransformVia404() && !( $flags & self::RENDER_NOW ) ) {
- wfDebug( __METHOD__ . " transformation deferred." );
+ wfDebug( __METHOD__ . " transformation deferred.\n" );
// XXX: Pass in the storage path even though we are not rendering anything
// and the path is supposed to be an FS path. This is due to getScalerType()
// getting called on the path and clobbering $thumb->getUrl() if it's false.
$thumb = $handler->getTransform( $this, $thumbPath, $thumbUrl, $params );
break;
}
- // Clean up broken thumbnails as needed
- $this->migrateThumbFile( $thumbName );
// Check if an up-to-date thumbnail already exists...
wfDebug( __METHOD__ . ": Doing stat for $thumbPath\n" );
if ( !( $flags & self::RENDER_FORCE ) && $this->repo->fileExists( $thumbPath ) ) {
@@ -919,94 +1051,274 @@ abstract class File {
} elseif ( $flags & self::RENDER_FORCE ) {
wfDebug( __METHOD__ . " forcing rendering per flag File::RENDER_FORCE\n" );
}
- }
- // If the backend is ready-only, don't keep generating thumbnails
- // only to return transformation errors, just return the error now.
- if ( $this->repo->getReadOnlyReason() !== false ) {
- $thumb = $this->transformErrorOutput( $thumbPath, $thumbUrl, $params, $flags );
- break;
+ // If the backend is ready-only, don't keep generating thumbnails
+ // only to return transformation errors, just return the error now.
+ if ( $this->repo->getReadOnlyReason() !== false ) {
+ $thumb = $this->transformErrorOutput( $thumbPath, $thumbUrl, $params, $flags );
+ break;
+ }
}
- // Create a temp FS file with the same extension and the thumbnail
- $thumbExt = FileBackend::extensionFromPath( $thumbPath );
- $tmpFile = TempFSFile::factory( 'transform_', $thumbExt );
+ $tmpFile = $this->makeTransformTmpFile( $thumbPath );
+
if ( !$tmpFile ) {
$thumb = $this->transformErrorOutput( $thumbPath, $thumbUrl, $params, $flags );
- break;
+ } else {
+ $thumb = $this->generateAndSaveThumb( $tmpFile, $params, $flags );
}
- $tmpThumbPath = $tmpFile->getPath(); // path of 0-byte temp file
-
- // Actually render the thumbnail...
- wfProfileIn( __METHOD__ . '-doTransform' );
- $thumb = $handler->doTransform( $this, $tmpThumbPath, $thumbUrl, $params );
- wfProfileOut( __METHOD__ . '-doTransform' );
- $tmpFile->bind( $thumb ); // keep alive with $thumb
-
- if ( !$thumb ) { // bad params?
- $thumb = null;
- } elseif ( $thumb->isError() ) { // transform error
- $this->lastError = $thumb->toText();
- // Ignore errors if requested
- if ( $wgIgnoreImageErrors && !( $flags & self::RENDER_NOW ) ) {
- $thumb = $handler->getTransform( $this, $tmpThumbPath, $thumbUrl, $params );
- }
- } elseif ( $this->repo && $thumb->hasFile() && !$thumb->fileIsSource() ) {
- // Copy the thumbnail from the file system into storage...
- $disposition = $this->getThumbDisposition( $thumbName );
- $status = $this->repo->quickImport( $tmpThumbPath, $thumbPath, $disposition );
- if ( $status->isOK() ) {
- $thumb->setStoragePath( $thumbPath );
- } else {
- $thumb = $this->transformErrorOutput( $thumbPath, $thumbUrl, $params, $flags );
+ } while ( false );
+
+ wfProfileOut( __METHOD__ );
+
+ return is_object( $thumb ) ? $thumb : false;
+ }
+
+ /**
+ * Generates a thumbnail according to the given parameters and saves it to storage
+ * @param TempFSFile $tmpFile Temporary file where the rendered thumbnail will be saved
+ * @param array $transformParams
+ * @param int $flags
+ * @return bool|MediaTransformOutput
+ */
+ public function generateAndSaveThumb( $tmpFile, $transformParams, $flags ) {
+ global $wgUseSquid, $wgIgnoreImageErrors;
+
+ $handler = $this->getHandler();
+
+ $normalisedParams = $transformParams;
+ $handler->normaliseParams( $this, $normalisedParams );
+
+ $thumbName = $this->thumbName( $normalisedParams );
+ $thumbUrl = $this->getThumbUrl( $thumbName );
+ $thumbPath = $this->getThumbPath( $thumbName ); // final thumb path
+
+ $tmpThumbPath = $tmpFile->getPath();
+
+ if ( $handler->supportsBucketing() ) {
+ $this->generateBucketsIfNeeded( $normalisedParams, $flags );
+ }
+
+ // Actually render the thumbnail...
+ wfProfileIn( __METHOD__ . '-doTransform' );
+ $thumb = $handler->doTransform( $this, $tmpThumbPath, $thumbUrl, $transformParams );
+ wfProfileOut( __METHOD__ . '-doTransform' );
+ $tmpFile->bind( $thumb ); // keep alive with $thumb
+
+ if ( !$thumb ) { // bad params?
+ $thumb = false;
+ } elseif ( $thumb->isError() ) { // transform error
+ $this->lastError = $thumb->toText();
+ // Ignore errors if requested
+ if ( $wgIgnoreImageErrors && !( $flags & self::RENDER_NOW ) ) {
+ $thumb = $handler->getTransform( $this, $tmpThumbPath, $thumbUrl, $transformParams );
+ }
+ } elseif ( $this->repo && $thumb->hasFile() && !$thumb->fileIsSource() ) {
+ // Copy the thumbnail from the file system into storage...
+ $disposition = $this->getThumbDisposition( $thumbName );
+ $status = $this->repo->quickImport( $tmpThumbPath, $thumbPath, $disposition );
+ if ( $status->isOK() ) {
+ $thumb->setStoragePath( $thumbPath );
+ } else {
+ $thumb = $this->transformErrorOutput( $thumbPath, $thumbUrl, $transformParams, $flags );
+ }
+ // Give extensions a chance to do something with this thumbnail...
+ wfRunHooks( 'FileTransformed', array( $this, $thumb, $tmpThumbPath, $thumbPath ) );
+ }
+
+ // Purge. Useful in the event of Core -> Squid connection failure or squid
+ // purge collisions from elsewhere during failure. Don't keep triggering for
+ // "thumbs" which have the main image URL though (bug 13776)
+ if ( $wgUseSquid ) {
+ if ( !$thumb || $thumb->isError() || $thumb->getUrl() != $this->getURL() ) {
+ SquidUpdate::purge( array( $thumbUrl ) );
+ }
+ }
+
+ return $thumb;
+ }
+
+ /**
+ * Generates chained bucketed thumbnails if needed
+ * @param array $params
+ * @param int $flags
+ * @return bool Whether at least one bucket was generated
+ */
+ protected function generateBucketsIfNeeded( $params, $flags = 0 ) {
+ if ( !$this->repo
+ || !isset( $params['physicalWidth'] )
+ || !isset( $params['physicalHeight'] )
+ || !( $bucket = $this->getThumbnailBucket( $params['physicalWidth'] ) )
+ || $bucket == $params['physicalWidth'] ) {
+ return false;
+ }
+
+ $bucketPath = $this->getBucketThumbPath( $bucket );
+
+ if ( $this->repo->fileExists( $bucketPath ) ) {
+ return false;
+ }
+
+ $params['physicalWidth'] = $bucket;
+ $params['width'] = $bucket;
+
+ $params = $this->getHandler()->sanitizeParamsForBucketing( $params );
+
+ $bucketName = $this->getBucketThumbName( $bucket );
+
+ $tmpFile = $this->makeTransformTmpFile( $bucketPath );
+
+ if ( !$tmpFile ) {
+ return false;
+ }
+
+ $thumb = $this->generateAndSaveThumb( $tmpFile, $params, $flags );
+
+ if ( !$thumb || $thumb->isError() ) {
+ return false;
+ }
+
+ $this->tmpBucketedThumbCache[$bucket] = $tmpFile->getPath();
+ // For the caching to work, we need to make the tmp file survive as long as
+ // this object exists
+ $tmpFile->bind( $this );
+
+ return true;
+ }
+
+ /**
+ * Returns the most appropriate source image for the thumbnail, given a target thumbnail size
+ * @param array $params
+ * @return array Source path and width/height of the source
+ */
+ public function getThumbnailSource( $params ) {
+ if ( $this->repo
+ && $this->getHandler()->supportsBucketing()
+ && isset( $params['physicalWidth'] )
+ && $bucket = $this->getThumbnailBucket( $params['physicalWidth'] )
+ ) {
+ if ( $this->getWidth() != 0 ) {
+ $bucketHeight = round( $this->getHeight() * ( $bucket / $this->getWidth() ) );
+ } else {
+ $bucketHeight = 0;
+ }
+
+ // Try to avoid reading from storage if the file was generated by this script
+ if ( isset( $this->tmpBucketedThumbCache[$bucket] ) ) {
+ $tmpPath = $this->tmpBucketedThumbCache[$bucket];
+
+ if ( file_exists( $tmpPath ) ) {
+ return array(
+ 'path' => $tmpPath,
+ 'width' => $bucket,
+ 'height' => $bucketHeight
+ );
}
- // Give extensions a chance to do something with this thumbnail...
- wfRunHooks( 'FileTransformed', array( $this, $thumb, $tmpThumbPath, $thumbPath ) );
}
- // Purge. Useful in the event of Core -> Squid connection failure or squid
- // purge collisions from elsewhere during failure. Don't keep triggering for
- // "thumbs" which have the main image URL though (bug 13776)
- if ( $wgUseSquid ) {
- if ( !$thumb || $thumb->isError() || $thumb->getUrl() != $this->getURL() ) {
- SquidUpdate::purge( array( $thumbUrl ) );
+ $bucketPath = $this->getBucketThumbPath( $bucket );
+
+ if ( $this->repo->fileExists( $bucketPath ) ) {
+ $fsFile = $this->repo->getLocalReference( $bucketPath );
+
+ if ( $fsFile ) {
+ return array(
+ 'path' => $fsFile->getPath(),
+ 'width' => $bucket,
+ 'height' => $bucketHeight
+ );
}
}
- } while ( false );
+ }
- wfProfileOut( __METHOD__ );
- return is_object( $thumb ) ? $thumb : false;
+ // Thumbnailing a very large file could result in network saturation if
+ // everyone does it at once.
+ if ( $this->getSize() >= 1e7 ) { // 10MB
+ $that = $this;
+ $work = new PoolCounterWorkViaCallback( 'GetLocalFileCopy', sha1( $this->getName() ),
+ array(
+ 'doWork' => function() use ( $that ) {
+ return $that->getLocalRefPath();
+ }
+ )
+ );
+ $srcPath = $work->execute();
+ } else {
+ $srcPath = $this->getLocalRefPath();
+ }
+
+ // Original file
+ return array(
+ 'path' => $srcPath,
+ 'width' => $this->getWidth(),
+ 'height' => $this->getHeight()
+ );
+ }
+
+ /**
+ * Returns the repo path of the thumb for a given bucket
+ * @param int $bucket
+ * @return string
+ */
+ protected function getBucketThumbPath( $bucket ) {
+ $thumbName = $this->getBucketThumbName( $bucket );
+ return $this->getThumbPath( $thumbName );
+ }
+
+ /**
+ * Returns the name of the thumb for a given bucket
+ * @param int $bucket
+ * @return string
+ */
+ protected function getBucketThumbName( $bucket ) {
+ return $this->thumbName( array( 'physicalWidth' => $bucket ) );
+ }
+
+ /**
+ * Creates a temp FS file with the same extension and the thumbnail
+ * @param string $thumbPath Thumbnail path
+ * @return TempFSFile
+ */
+ protected function makeTransformTmpFile( $thumbPath ) {
+ $thumbExt = FileBackend::extensionFromPath( $thumbPath );
+ return TempFSFile::factory( 'transform_', $thumbExt );
}
/**
* @param string $thumbName Thumbnail name
+ * @param string $dispositionType Type of disposition (either "attachment" or "inline")
* @return string Content-Disposition header value
*/
- function getThumbDisposition( $thumbName ) {
+ function getThumbDisposition( $thumbName, $dispositionType = 'inline' ) {
$fileName = $this->name; // file name to suggest
$thumbExt = FileBackend::extensionFromPath( $thumbName );
if ( $thumbExt != '' && $thumbExt !== $this->getExtension() ) {
$fileName .= ".$thumbExt";
}
- return FileBackend::makeContentDisposition( 'inline', $fileName );
+
+ return FileBackend::makeContentDisposition( $dispositionType, $fileName );
}
/**
* Hook into transform() to allow migration of thumbnail files
* STUB
* Overridden by LocalFile
+ * @param string $thumbName
*/
- function migrateThumbFile( $thumbName ) {}
+ function migrateThumbFile( $thumbName ) {
+ }
/**
* Get a MediaHandler instance for this file
*
- * @return MediaHandler|boolean Registered MediaHandler for file's mime type or false if none found
+ * @return MediaHandler|bool Registered MediaHandler for file's MIME type
+ * or false if none found
*/
function getHandler() {
if ( !isset( $this->handler ) ) {
$this->handler = MediaHandler::getHandler( $this->getMimeType() );
}
+
return $this->handler;
}
@@ -1016,23 +1328,26 @@ abstract class File {
* @return ThumbnailImage
*/
function iconThumb() {
- global $wgStylePath, $wgStyleDirectory;
+ global $wgResourceBasePath, $IP;
+ $assetsPath = "$wgResourceBasePath/resources/assets/file-type-icons/";
+ $assetsDirectory = "$IP/resources/assets/file-type-icons/";
$try = array( 'fileicon-' . $this->getExtension() . '.png', 'fileicon.png' );
foreach ( $try as $icon ) {
- $path = '/common/images/icons/' . $icon;
- $filepath = $wgStyleDirectory . $path;
- if ( file_exists( $filepath ) ) { // always FS
+ if ( file_exists( $assetsDirectory . $icon ) ) { // always FS
$params = array( 'width' => 120, 'height' => 120 );
- return new ThumbnailImage( $this, $wgStylePath . $path, false, $params );
+
+ return new ThumbnailImage( $this, $assetsPath . $icon, false, $params );
}
}
+
return null;
}
/**
* Get last thumbnailing error.
* Largely obsolete.
+ * @return string
*/
function getLastError() {
return $this->lastError;
@@ -1053,9 +1368,10 @@ abstract class File {
* STUB
* Overridden by LocalFile
* @param array $options Options, which include:
- * 'forThumbRefresh' : The purging is only to refresh thumbnails
+ * 'forThumbRefresh' : The purging is only to refresh thumbnails
*/
- function purgeCache( $options = array() ) {}
+ function purgeCache( $options = array() ) {
+ }
/**
* Purge the file description page, but don't go after
@@ -1091,9 +1407,9 @@ abstract class File {
* Return a fragment of the history of file.
*
* STUB
- * @param $limit integer Limit of rows to return
- * @param string $start timestamp Only revisions older than $start will be returned
- * @param string $end timestamp Only revisions newer than $end will be returned
+ * @param int $limit Limit of rows to return
+ * @param string $start Only revisions older than $start will be returned
+ * @param string $end Only revisions newer than $end will be returned
* @param bool $inc Include the endpoints of the time range
*
* @return array
@@ -1121,7 +1437,8 @@ abstract class File {
* STUB
* Overridden in LocalFile.
*/
- public function resetHistory() {}
+ public function resetHistory() {
+ }
/**
* Get the filename hash component of the directory including trailing slash,
@@ -1135,6 +1452,7 @@ abstract class File {
$this->assertRepoDefined();
$this->hashPath = $this->repo->getHashPath( $this->getName() );
}
+
return $this->hashPath;
}
@@ -1151,7 +1469,7 @@ abstract class File {
/**
* Get the path of an archived file relative to the public zone root
*
- * @param bool|string $suffix if not false, the name of an archived thumbnail file
+ * @param bool|string $suffix If not false, the name of an archived thumbnail file
*
* @return string
*/
@@ -1162,6 +1480,7 @@ abstract class File {
} else {
$path .= $suffix;
}
+
return $path;
}
@@ -1169,8 +1488,7 @@ abstract class File {
* Get the path, relative to the thumbnail zone root, of the
* thumbnail directory or a particular file if $suffix is specified
*
- * @param bool|string $suffix if not false, the name of a thumbnail file
- *
+ * @param bool|string $suffix If not false, the name of a thumbnail file
* @return string
*/
function getThumbRel( $suffix = false ) {
@@ -1178,6 +1496,7 @@ abstract class File {
if ( $suffix !== false ) {
$path .= '/' . $suffix;
}
+
return $path;
}
@@ -1195,9 +1514,8 @@ abstract class File {
* Get the path, relative to the thumbnail zone root, for an archived file's thumbs directory
* or a specific thumb if the $suffix is given.
*
- * @param string $archiveName the timestamped name of an archived image
- * @param bool|string $suffix if not false, the name of a thumbnail file
- *
+ * @param string $archiveName The timestamped name of an archived image
+ * @param bool|string $suffix If not false, the name of a thumbnail file
* @return string
*/
function getArchiveThumbRel( $archiveName, $suffix = false ) {
@@ -1207,64 +1525,64 @@ abstract class File {
} else {
$path .= $suffix;
}
+
return $path;
}
/**
* Get the path of the archived file.
*
- * @param bool|string $suffix if not false, the name of an archived file.
- *
+ * @param bool|string $suffix If not false, the name of an archived file.
* @return string
*/
function getArchivePath( $suffix = false ) {
$this->assertRepoDefined();
+
return $this->repo->getZonePath( 'public' ) . '/' . $this->getArchiveRel( $suffix );
}
/**
* Get the path of an archived file's thumbs, or a particular thumb if $suffix is specified
*
- * @param string $archiveName the timestamped name of an archived image
- * @param bool|string $suffix if not false, the name of a thumbnail file
- *
+ * @param string $archiveName The timestamped name of an archived image
+ * @param bool|string $suffix If not false, the name of a thumbnail file
* @return string
*/
function getArchiveThumbPath( $archiveName, $suffix = false ) {
$this->assertRepoDefined();
+
return $this->repo->getZonePath( 'thumb' ) . '/' .
- $this->getArchiveThumbRel( $archiveName, $suffix );
+ $this->getArchiveThumbRel( $archiveName, $suffix );
}
/**
* Get the path of the thumbnail directory, or a particular file if $suffix is specified
*
- * @param bool|string $suffix if not false, the name of a thumbnail file
- *
+ * @param bool|string $suffix If not false, the name of a thumbnail file
* @return string
*/
function getThumbPath( $suffix = false ) {
$this->assertRepoDefined();
+
return $this->repo->getZonePath( 'thumb' ) . '/' . $this->getThumbRel( $suffix );
}
/**
* Get the path of the transcoded directory, or a particular file if $suffix is specified
*
- * @param bool|string $suffix if not false, the name of a media file
- *
+ * @param bool|string $suffix If not false, the name of a media file
* @return string
*/
function getTranscodedPath( $suffix = false ) {
$this->assertRepoDefined();
+
return $this->repo->getZonePath( 'transcoded' ) . '/' . $this->getThumbRel( $suffix );
}
/**
* Get the URL of the archive directory, or a particular file if $suffix is specified
*
- * @param bool|string $suffix if not false, the name of an archived file
- *
+ * @param bool|string $suffix If not false, the name of an archived file
* @return string
*/
function getArchiveUrl( $suffix = false ) {
@@ -1276,15 +1594,15 @@ abstract class File {
} else {
$path .= rawurlencode( $suffix );
}
+
return $path;
}
/**
* Get the URL of the archived file's thumbs, or a particular thumb if $suffix is specified
*
- * @param string $archiveName the timestamped name of an archived image
- * @param bool|string $suffix if not false, the name of a thumbnail file
- *
+ * @param string $archiveName The timestamped name of an archived image
+ * @param bool|string $suffix If not false, the name of a thumbnail file
* @return string
*/
function getArchiveThumbUrl( $archiveName, $suffix = false ) {
@@ -1297,16 +1615,16 @@ abstract class File {
} else {
$path .= rawurlencode( $suffix );
}
+
return $path;
}
/**
* Get the URL of the zone directory, or a particular file if $suffix is specified
*
- * @param string $zone name of requested zone
- * @param bool|string $suffix if not false, the name of a file in zone
- *
- * @return string path
+ * @param string $zone Name of requested zone
+ * @param bool|string $suffix If not false, the name of a file in zone
+ * @return string Path
*/
function getZoneUrl( $zone, $suffix = false ) {
$this->assertRepoDefined();
@@ -1315,15 +1633,15 @@ abstract class File {
if ( $suffix !== false ) {
$path .= '/' . rawurlencode( $suffix );
}
+
return $path;
}
/**
* Get the URL of the thumbnail directory, or a particular file if $suffix is specified
*
- * @param bool|string $suffix if not false, the name of a thumbnail file
- *
- * @return string path
+ * @param bool|string $suffix If not false, the name of a thumbnail file
+ * @return string Path
*/
function getThumbUrl( $suffix = false ) {
return $this->getZoneUrl( 'thumb', $suffix );
@@ -1332,9 +1650,8 @@ abstract class File {
/**
* Get the URL of the transcoded directory, or a particular file if $suffix is specified
*
- * @param bool|string $suffix if not false, the name of a media file
- *
- * @return string path
+ * @param bool|string $suffix If not false, the name of a media file
+ * @return string Path
*/
function getTranscodedUrl( $suffix = false ) {
return $this->getZoneUrl( 'transcoded', $suffix );
@@ -1343,8 +1660,7 @@ abstract class File {
/**
* Get the public zone virtual URL for a current version source file
*
- * @param bool|string $suffix if not false, the name of a thumbnail file
- *
+ * @param bool|string $suffix If not false, the name of a thumbnail file
* @return string
*/
function getVirtualUrl( $suffix = false ) {
@@ -1353,14 +1669,14 @@ abstract class File {
if ( $suffix !== false ) {
$path .= '/' . rawurlencode( $suffix );
}
+
return $path;
}
/**
* Get the public zone virtual URL for an archived version source file
*
- * @param bool|string $suffix if not false, the name of a thumbnail file
- *
+ * @param bool|string $suffix If not false, the name of a thumbnail file
* @return string
*/
function getArchiveVirtualUrl( $suffix = false ) {
@@ -1371,14 +1687,14 @@ abstract class File {
} else {
$path .= rawurlencode( $suffix );
}
+
return $path;
}
/**
* Get the virtual URL for a thumbnail file or directory
*
- * @param bool|string $suffix if not false, the name of a thumbnail file
- *
+ * @param bool|string $suffix If not false, the name of a thumbnail file
* @return string
*/
function getThumbVirtualUrl( $suffix = false ) {
@@ -1387,6 +1703,7 @@ abstract class File {
if ( $suffix !== false ) {
$path .= '/' . rawurlencode( $suffix );
}
+
return $path;
}
@@ -1395,6 +1712,7 @@ abstract class File {
*/
function isHashed() {
$this->assertRepoDefined();
+
return (bool)$this->repo->getHashLevels();
}
@@ -1409,18 +1727,20 @@ abstract class File {
* Record a file upload in the upload log and the image table
* STUB
* Overridden by LocalFile
- * @param $oldver
- * @param $desc
- * @param $license string
- * @param $copyStatus string
- * @param $source string
- * @param $watch bool
- * @param $timestamp string|bool
- * @param $user User object or null to use $wgUser
+ * @param string $oldver
+ * @param string $desc
+ * @param string $license
+ * @param string $copyStatus
+ * @param string $source
+ * @param bool $watch
+ * @param string|bool $timestamp
+ * @param null|User $user User object or null to use $wgUser
* @return bool
* @throws MWException
*/
- function recordUpload( $oldver, $desc, $license = '', $copyStatus = '', $source = '', $watch = false, $timestamp = false, User $user = null ) {
+ function recordUpload( $oldver, $desc, $license = '', $copyStatus = '', $source = '',
+ $watch = false, $timestamp = false, User $user = null
+ ) {
$this->readOnlyError();
}
@@ -1435,13 +1755,12 @@ abstract class File {
* Options to $options include:
* - headers : name/value map of HTTP headers to use in response to GET/HEAD requests
*
- * @param string $srcPath local filesystem path to the source image
- * @param $flags Integer: a bitwise combination of:
- * File::DELETE_SOURCE Delete the source file, i.e. move
- * rather than copy
+ * @param string $srcPath Local filesystem path to the source image
+ * @param int $flags A bitwise combination of:
+ * File::DELETE_SOURCE Delete the source file, i.e. move rather than copy
* @param array $options Optional additional parameters
- * @return FileRepoStatus object. On success, the value member contains the
- * archive name, or an empty string if it was a new file.
+ * @return FileRepoStatus On success, the value member contains the
+ * archive name, or an empty string if it was a new file.
*
* STUB
* Overridden by LocalFile
@@ -1457,6 +1776,7 @@ abstract class File {
if ( !$this->getHandler() ) {
return false;
}
+
return $this->getHandler()->formatMetadata( $this, $this->getMetadata() );
}
@@ -1481,7 +1801,7 @@ abstract class File {
/**
* Returns the repository
*
- * @return FileRepo|bool
+ * @return FileRepo|LocalRepo|bool
*/
function getRepo() {
return $this->repo;
@@ -1501,8 +1821,7 @@ abstract class File {
* Is this file a "deleted" file in a private archive?
* STUB
*
- * @param integer $field one of DELETED_* bitfield constants
- *
+ * @param int $field One of DELETED_* bitfield constants
* @return bool
*/
function isDeleted( $field ) {
@@ -1525,6 +1844,7 @@ abstract class File {
*/
function wasDeleted() {
$title = $this->getTitle();
+
return $title && $title->isDeletedQuick();
}
@@ -1537,8 +1857,8 @@ abstract class File {
* Cache purging is done; checks for validity
* and logging are caller's responsibility
*
- * @param $target Title New file name
- * @return FileRepoStatus object.
+ * @param Title $target New file name
+ * @return FileRepoStatus
*/
function move( $target ) {
$this->readOnlyError();
@@ -1552,13 +1872,14 @@ abstract class File {
*
* Cache purging is done; logging is caller's responsibility.
*
- * @param $reason String
- * @param $suppress Boolean: hide content from sysops?
- * @return bool on success, false on some kind of failure
+ * @param string $reason
+ * @param bool $suppress Hide content from sysops?
+ * @param User|null $user
+ * @return bool Boolean on success, false on some kind of failure
* STUB
* Overridden by LocalFile
*/
- function delete( $reason, $suppress = false ) {
+ function delete( $reason, $suppress = false, $user = null ) {
$this->readOnlyError();
}
@@ -1568,11 +1889,11 @@ abstract class File {
*
* May throw database exceptions on error.
*
- * @param array $versions set of record ids of deleted items to restore,
- * or empty to restore all revisions.
- * @param bool $unsuppress remove restrictions on content upon restoration?
- * @return int|bool the number of file revisions restored if successful,
- * or false on failure
+ * @param array $versions Set of record ids of deleted items to restore,
+ * or empty to restore all revisions.
+ * @param bool $unsuppress Remove restrictions on content upon restoration?
+ * @return int|bool The number of file revisions restored if successful,
+ * or false on failure
* STUB
* Overridden by LocalFile
*/
@@ -1585,7 +1906,7 @@ abstract class File {
* e.g. DJVU or PDF. Note that this may be true even if the file in
* question only has a single page.
*
- * @return Bool
+ * @return bool
*/
function isMultipage() {
return $this->getHandler() && $this->handler->isMultiPage( $this );
@@ -1605,15 +1926,16 @@ abstract class File {
$this->pageCount = false;
}
}
+
return $this->pageCount;
}
/**
* Calculate the height of a thumbnail using the source and destination width
*
- * @param $srcWidth
- * @param $srcHeight
- * @param $dstWidth
+ * @param int $srcWidth
+ * @param int $srcHeight
+ * @param int $dstWidth
*
* @return int
*/
@@ -1628,16 +1950,20 @@ abstract class File {
/**
* Get an image size array like that returned by getImageSize(), or false if it
- * can't be determined.
+ * can't be determined. Loads the image size directly from the file ignoring caches.
*
- * @param string $fileName The filename
- * @return Array
+ * @note Use getWidth()/getHeight() instead of this method unless you have a
+ * a good reason. This method skips all caches.
+ *
+ * @param string $filePath The path to the file (e.g. From getLocalPathRef() )
+ * @return array The width, followed by height, with optionally more things after
*/
- function getImageSize( $fileName ) {
+ function getImageSize( $filePath ) {
if ( !$this->getHandler() ) {
return false;
}
- return $this->handler->getImageSize( $this, $fileName );
+
+ return $this->getHandler()->getImageSize( $this, $filePath );
}
/**
@@ -1657,7 +1983,7 @@ abstract class File {
/**
* Get the HTML text of the description page, if available
*
- * @param $lang Language Optional language to fetch description in
+ * @param bool|Language $lang Optional language to fetch description in
* @return string
*/
function getDescriptionText( $lang = false ) {
@@ -1672,11 +1998,16 @@ abstract class File {
if ( $renderUrl ) {
if ( $this->repo->descriptionCacheExpiry > 0 ) {
wfDebug( "Attempting to get the description from cache..." );
- $key = $this->repo->getLocalCacheKey( 'RemoteFileDescription', 'url', $lang->getCode(),
- $this->getName() );
+ $key = $this->repo->getLocalCacheKey(
+ 'RemoteFileDescription',
+ 'url',
+ $lang->getCode(),
+ $this->getName()
+ );
$obj = $wgMemc->get( $key );
if ( $obj ) {
wfDebug( "success!\n" );
+
return $obj;
}
wfDebug( "miss\n" );
@@ -1686,6 +2017,7 @@ abstract class File {
if ( $res && $this->repo->descriptionCacheExpiry > 0 ) {
$wgMemc->set( $key, $res, $this->repo->descriptionCacheExpiry );
}
+
return $res;
} else {
return false;
@@ -1696,12 +2028,12 @@ abstract class File {
* Get description of file revision
* STUB
*
- * @param $audience Integer: one of:
- * File::FOR_PUBLIC to be displayed to all users
- * File::FOR_THIS_USER to be displayed to the given user
- * File::RAW get the description regardless of permissions
- * @param $user User object to check for, only if FOR_THIS_USER is passed
- * to the $audience parameter
+ * @param int $audience One of:
+ * File::FOR_PUBLIC to be displayed to all users
+ * File::FOR_THIS_USER to be displayed to the given user
+ * File::RAW get the description regardless of permissions
+ * @param User $user User object to check for, only if FOR_THIS_USER is
+ * passed to the $audience parameter
* @return string
*/
function getDescription( $audience = self::FOR_PUBLIC, User $user = null ) {
@@ -1715,6 +2047,7 @@ abstract class File {
*/
function getTimestamp() {
$this->assertRepoDefined();
+
return $this->repo->getFileTimestamp( $this->getPath() );
}
@@ -1725,6 +2058,7 @@ abstract class File {
*/
function getSha1() {
$this->assertRepoDefined();
+
return $this->repo->getFileSha1( $this->getPath() );
}
@@ -1740,6 +2074,7 @@ abstract class File {
}
$ext = $this->getExtension();
$dotExt = $ext === '' ? '' : ".$ext";
+
return $hash . $dotExt;
}
@@ -1747,53 +2082,16 @@ abstract class File {
* Determine if the current user is allowed to view a particular
* field of this file, if it's marked as deleted.
* STUB
- * @param $field Integer
- * @param $user User object to check, or null to use $wgUser
- * @return Boolean
+ * @param int $field
+ * @param User $user User object to check, or null to use $wgUser
+ * @return bool
*/
function userCan( $field, User $user = null ) {
return true;
}
/**
- * Get an associative array containing information about a file in the local filesystem.
- *
- * @param string $path absolute local filesystem path
- * @param $ext Mixed: the file extension, or true to extract it from the filename.
- * Set it to false to ignore the extension.
- *
- * @return array
- * @deprecated since 1.19
- */
- static function getPropsFromPath( $path, $ext = true ) {
- wfDebug( __METHOD__ . ": Getting file info for $path\n" );
- wfDeprecated( __METHOD__, '1.19' );
-
- $fsFile = new FSFile( $path );
- return $fsFile->getProps();
- }
-
- /**
- * Get a SHA-1 hash of a file in the local filesystem, in base-36 lower case
- * encoding, zero padded to 31 digits.
- *
- * 160 log 2 / log 36 = 30.95, so the 160-bit hash fills 31 digits in base 36
- * fairly neatly.
- *
- * @param $path string
- *
- * @return bool|string False on failure
- * @deprecated since 1.19
- */
- static function sha1Base36( $path ) {
- wfDeprecated( __METHOD__, '1.19' );
-
- $fsFile = new FSFile( $path );
- return $fsFile->getSha1Base36();
- }
-
- /**
- * @return Array HTTP header name/value map to use for HEAD/GET request responses
+ * @return array HTTP header name/value map to use for HEAD/GET request responses
*/
function getStreamHeaders() {
$handler = $this->getHandler();
@@ -1841,7 +2139,7 @@ abstract class File {
}
/**
- * @return
+ * @return string
*/
function getRedirected() {
return $this->redirected;
@@ -1855,13 +2153,15 @@ abstract class File {
if ( !$this->redirectTitle ) {
$this->redirectTitle = Title::makeTitle( NS_FILE, $this->redirected );
}
+
return $this->redirectTitle;
}
+
return null;
}
/**
- * @param $from
+ * @param string $from
* @return void
*/
function redirectedFrom( $from ) {
@@ -1877,7 +2177,7 @@ abstract class File {
/**
* Check if this file object is small and can be cached
- * @return boolean
+ * @return bool
*/
public function isCacheable() {
return true;
@@ -1902,4 +2202,13 @@ abstract class File {
throw new MWException( "A Title object is not set for this File.\n" );
}
}
+
+ /**
+ * True if creating thumbnails from the file is large or otherwise resource-intensive.
+ * @return bool
+ */
+ public function isExpensiveToThumbnail() {
+ $handler = $this->getHandler();
+ return $handler ? $handler->isExpensiveToThumbnail( $this ) : false;
+ }
}