summaryrefslogtreecommitdiff
path: root/includes/filerepo/file
diff options
context:
space:
mode:
Diffstat (limited to 'includes/filerepo/file')
-rw-r--r--includes/filerepo/file/ArchivedFile.php23
-rw-r--r--includes/filerepo/file/File.php37
-rw-r--r--includes/filerepo/file/ForeignAPIFile.php49
-rw-r--r--includes/filerepo/file/ForeignDBFile.php5
-rw-r--r--includes/filerepo/file/LocalFile.php234
-rw-r--r--includes/filerepo/file/OldLocalFile.php3
6 files changed, 231 insertions, 120 deletions
diff --git a/includes/filerepo/file/ArchivedFile.php b/includes/filerepo/file/ArchivedFile.php
index 3f786197..749f11a5 100644
--- a/includes/filerepo/file/ArchivedFile.php
+++ b/includes/filerepo/file/ArchivedFile.php
@@ -90,7 +90,7 @@ class ArchivedFile {
$this->exists = false;
$this->sha1 = '';
- if( $title instanceof Title ) {
+ if ( $title instanceof Title ) {
$this->title = File::normalizeTitle( $title, 'exception' );
$this->name = $title->getDBkey();
}
@@ -119,22 +119,22 @@ class ArchivedFile {
}
$conds = array();
- if( $this->id > 0 ) {
+ if ( $this->id > 0 ) {
$conds['fa_id'] = $this->id;
}
- if( $this->key ) {
+ if ( $this->key ) {
$conds['fa_storage_group'] = $this->group;
$conds['fa_storage_key'] = $this->key;
}
- if( $this->title ) {
+ if ( $this->title ) {
$conds['fa_name'] = $this->title->getDBkey();
}
- if( !count( $conds ) ) {
+ if ( !count( $conds ) ) {
throw new MWException( "No specific information for retrieving archived file" );
}
- if( !$this->title || $this->title->getNamespace() == NS_FILE ) {
+ if ( !$this->title || $this->title->getNamespace() == NS_FILE ) {
$this->dataLoaded = true; // set it here, to have also true on miss
$dbr = wfGetDB( DB_SLAVE );
$row = $dbr->selectRow(
@@ -196,6 +196,7 @@ class ArchivedFile {
'fa_user_text',
'fa_timestamp',
'fa_deleted',
+ 'fa_deleted_timestamp', /* Used by LocalFileRestoreBatch */
'fa_sha1',
);
}
@@ -224,7 +225,7 @@ class ArchivedFile {
$this->user_text = $row->fa_user_text;
$this->timestamp = $row->fa_timestamp;
$this->deleted = $row->fa_deleted;
- if( isset( $row->fa_sha1 ) ) {
+ if ( isset( $row->fa_sha1 ) ) {
$this->sha1 = $row->fa_sha1;
} else {
// old row, populate from key
@@ -409,7 +410,7 @@ class ArchivedFile {
*/
public function getUser() {
$this->load();
- if( $this->isDeleted( File::DELETED_USER ) ) {
+ if ( $this->isDeleted( File::DELETED_USER ) ) {
return 0;
} else {
return $this->user;
@@ -423,7 +424,7 @@ class ArchivedFile {
*/
public function getUserText() {
$this->load();
- if( $this->isDeleted( File::DELETED_USER ) ) {
+ if ( $this->isDeleted( File::DELETED_USER ) ) {
return 0;
} else {
return $this->user_text;
@@ -437,7 +438,7 @@ class ArchivedFile {
*/
public function getDescription() {
$this->load();
- if( $this->isDeleted( File::DELETED_COMMENT ) ) {
+ if ( $this->isDeleted( File::DELETED_COMMENT ) ) {
return 0;
} else {
return $this->description;
@@ -491,7 +492,7 @@ class ArchivedFile {
*/
public function isDeleted( $field ) {
$this->load();
- return ($this->deleted & $field) == $field;
+ return ( $this->deleted & $field ) == $field;
}
/**
diff --git a/includes/filerepo/file/File.php b/includes/filerepo/file/File.php
index cecd0aee..ec5f927b 100644
--- a/includes/filerepo/file/File.php
+++ b/includes/filerepo/file/File.php
@@ -48,6 +48,7 @@
* @ingroup FileAbstraction
*/
abstract class File {
+ // Bitfield values akin to the Revision deletion constants
const DELETED_FILE = 1;
const DELETED_COMMENT = 2;
const DELETED_USER = 4;
@@ -201,9 +202,9 @@ abstract class File {
'mpeg' => 'mpg',
'tiff' => 'tif',
'ogv' => 'ogg' );
- if( isset( $squish[$lower] ) ) {
+ if ( isset( $squish[$lower] ) ) {
return $squish[$lower];
- } elseif( preg_match( '/^[0-9a-z]+$/', $lower ) ) {
+ } elseif ( preg_match( '/^[0-9a-z]+$/', $lower ) ) {
return $lower;
} else {
return '';
@@ -241,7 +242,7 @@ abstract class File {
* @return array ("text", "html") etc
*/
public static function splitMime( $mime ) {
- if( strpos( $mime, '/' ) !== false ) {
+ if ( strpos( $mime, '/' ) !== false ) {
return explode( '/', $mime, 2 );
} else {
return array( $mime, 'unknown' );
@@ -518,7 +519,7 @@ abstract class File {
* @param $version integer version number.
* @return Array containing metadata, or what was passed to it on fail (unserializing if not array)
*/
- public function convertMetadataVersion($metadata, $version) {
+ public function convertMetadataVersion( $metadata, $version ) {
$handler = $this->getHandler();
if ( !is_array( $metadata ) ) {
// Just to make the return type consistent
@@ -681,7 +682,7 @@ abstract class File {
if ( $mime === "unknown/unknown" ) {
return false; #unknown type, not trusted
}
- if ( in_array( $mime, $wgTrustedMediaFormats) ) {
+ if ( in_array( $mime, $wgTrustedMediaFormats ) ) {
return true;
}
@@ -737,7 +738,7 @@ abstract class File {
if ( $this->repo ) {
$script = $this->repo->getThumbScriptUrl();
if ( $script ) {
- $this->transformScript = "$script?f=" . urlencode( $this->getName() );
+ $this->transformScript = wfAppendQuery( $script, array( 'f' => $this->getName() ) );
}
}
}
@@ -841,8 +842,9 @@ abstract class File {
protected function transformErrorOutput( $thumbPath, $thumbUrl, $params, $flags ) {
global $wgIgnoreImageErrors;
- if ( $wgIgnoreImageErrors && !( $flags & self::RENDER_NOW ) ) {
- return $this->getHandler()->getTransform( $this, $thumbPath, $thumbUrl, $params );
+ $handler = $this->getHandler();
+ if ( $handler && $wgIgnoreImageErrors && !( $flags & self::RENDER_NOW ) ) {
+ return $handler->getTransform( $this, $thumbPath, $thumbUrl, $params );
} else {
return new MediaTransformError( 'thumbnail_error',
$params['width'], 0, wfMessage( 'thumbnail-dest-create' )->text() );
@@ -910,8 +912,7 @@ abstract class File {
// 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 );
+ $thumb = $handler->getTransform( $this, $thumbPath, $thumbUrl, $params );
$thumb->setStoragePath( $thumbPath );
break;
}
@@ -1000,7 +1001,7 @@ abstract class File {
/**
* Get a MediaHandler instance for this file
*
- * @return MediaHandler
+ * @return MediaHandler|boolean Registered MediaHandler for file's mime type or false if none found
*/
function getHandler() {
if ( !isset( $this->handler ) ) {
@@ -1097,7 +1098,7 @@ abstract class File {
*
* @return array
*/
- function getHistory( $limit = null, $start = null, $end = null, $inc=true ) {
+ function getHistory( $limit = null, $start = null, $end = null, $inc = true ) {
return array();
}
@@ -1500,7 +1501,7 @@ abstract class File {
* Is this file a "deleted" file in a private archive?
* STUB
*
- * @param $field
+ * @param integer $field one of DELETED_* bitfield constants
*
* @return bool
*/
@@ -1656,18 +1657,22 @@ abstract class File {
/**
* Get the HTML text of the description page, if available
*
+ * @param $lang Language Optional language to fetch description in
* @return string
*/
- function getDescriptionText() {
+ function getDescriptionText( $lang = false ) {
global $wgMemc, $wgLang;
if ( !$this->repo || !$this->repo->fetchDescription ) {
return false;
}
- $renderUrl = $this->repo->getDescriptionRenderUrl( $this->getName(), $wgLang->getCode() );
+ if ( !$lang ) {
+ $lang = $wgLang;
+ }
+ $renderUrl = $this->repo->getDescriptionRenderUrl( $this->getName(), $lang->getCode() );
if ( $renderUrl ) {
if ( $this->repo->descriptionCacheExpiry > 0 ) {
wfDebug( "Attempting to get the description from cache..." );
- $key = $this->repo->getLocalCacheKey( 'RemoteFileDescription', 'url', $wgLang->getCode(),
+ $key = $this->repo->getLocalCacheKey( 'RemoteFileDescription', 'url', $lang->getCode(),
$this->getName() );
$obj = $wgMemc->get( $key );
if ( $obj ) {
diff --git a/includes/filerepo/file/ForeignAPIFile.php b/includes/filerepo/file/ForeignAPIFile.php
index a96c1f3f..ed96d446 100644
--- a/includes/filerepo/file/ForeignAPIFile.php
+++ b/includes/filerepo/file/ForeignAPIFile.php
@@ -54,22 +54,22 @@ class ForeignAPIFile extends File {
*/
static function newFromTitle( Title $title, $repo ) {
$data = $repo->fetchImageQuery( array(
- 'titles' => 'File:' . $title->getDBKey(),
- 'iiprop' => self::getProps(),
- 'prop' => 'imageinfo',
+ 'titles' => 'File:' . $title->getDBkey(),
+ 'iiprop' => self::getProps(),
+ 'prop' => 'imageinfo',
'iimetadataversion' => MediaHandler::getMetadataVersion()
) );
$info = $repo->getImageInfo( $data );
- if( $info ) {
+ if ( $info ) {
$lastRedirect = isset( $data['query']['redirects'] )
? count( $data['query']['redirects'] ) - 1
: -1;
- if( $lastRedirect >= 0 ) {
- $newtitle = Title::newFromText( $data['query']['redirects'][$lastRedirect]['to']);
+ if ( $lastRedirect >= 0 ) {
+ $newtitle = Title::newFromText( $data['query']['redirects'][$lastRedirect]['to'] );
$img = new self( $newtitle, $repo, $info, true );
- if( $img ) {
+ if ( $img ) {
$img->redirectedFrom( $title->getDBkey() );
}
} else {
@@ -86,7 +86,7 @@ class ForeignAPIFile extends File {
* @return string
*/
static function getProps() {
- return 'timestamp|user|comment|url|size|sha1|metadata|mime';
+ return 'timestamp|user|comment|url|size|sha1|metadata|mime|mediatype';
}
// Dummy functions...
@@ -111,7 +111,7 @@ class ForeignAPIFile extends File {
* @return bool|MediaTransformOutput
*/
function transform( $params, $flags = 0 ) {
- if( !$this->canRender() ) {
+ if ( !$this->canRender() ) {
// show icon
return parent::transform( $params, $flags );
}
@@ -119,12 +119,25 @@ class ForeignAPIFile extends File {
// Note, the this->canRender() check above implies
// that we have a handler, and it can do makeParamString.
$otherParams = $this->handler->makeParamString( $params );
+ $width = isset( $params['width'] ) ? $params['width'] : -1;
+ $height = isset( $params['height'] ) ? $params['height'] : -1;
$thumbUrl = $this->repo->getThumbUrlFromCache(
$this->getName(),
- isset( $params['width'] ) ? $params['width'] : -1,
- isset( $params['height'] ) ? $params['height'] : -1,
- $otherParams );
+ $width,
+ $height,
+ $otherParams
+ );
+ if ( $thumbUrl === false ) {
+ global $wgLang;
+ return $this->repo->getThumbError(
+ $this->getName(),
+ $width,
+ $height,
+ $otherParams,
+ $wgLang->getCode()
+ );
+ }
return $this->handler->getTransform( $this, 'bogus', $thumbUrl, $params );
}
@@ -161,12 +174,12 @@ class ForeignAPIFile extends File {
* @return array
*/
public static function parseMetadata( $metadata ) {
- if( !is_array( $metadata ) ) {
+ if ( !is_array( $metadata ) ) {
return $metadata;
}
$ret = array();
- foreach( $metadata as $meta ) {
- $ret[ $meta['name'] ] = self::parseMetadata( $meta['value'] );
+ foreach ( $metadata as $meta ) {
+ $ret[$meta['name']] = self::parseMetadata( $meta['value'] );
}
return $ret;
}
@@ -224,7 +237,7 @@ class ForeignAPIFile extends File {
* @return string
*/
function getMimeType() {
- if( !isset( $this->mInfo['mime'] ) ) {
+ if ( !isset( $this->mInfo['mime'] ) ) {
$magic = MimeMagic::singleton();
$this->mInfo['mime'] = $magic->guessTypesForExtension( $this->getExtension() );
}
@@ -232,10 +245,12 @@ class ForeignAPIFile extends File {
}
/**
- * @todo FIXME: May guess wrong on file types that can be eg audio or video
* @return int|string
*/
function getMediaType() {
+ if ( isset( $this->mInfo['mediatype'] ) ) {
+ return $this->mInfo['mediatype'];
+ }
$magic = MimeMagic::singleton();
return $magic->getMediaType( null, $this->getMimeType() );
}
diff --git a/includes/filerepo/file/ForeignDBFile.php b/includes/filerepo/file/ForeignDBFile.php
index ee5883c4..01d6b0f5 100644
--- a/includes/filerepo/file/ForeignDBFile.php
+++ b/includes/filerepo/file/ForeignDBFile.php
@@ -120,10 +120,11 @@ class ForeignDBFile extends LocalFile {
}
/**
+ * @param $lang Language Optional language to fetch description in.
* @return string
*/
- function getDescriptionText() {
+ function getDescriptionText( $lang = false ) {
// Restore remote behavior
- return File::getDescriptionText();
+ return File::getDescriptionText( $lang );
}
}
diff --git a/includes/filerepo/file/LocalFile.php b/includes/filerepo/file/LocalFile.php
index 4f50bfaa..fe769be2 100644
--- a/includes/filerepo/file/LocalFile.php
+++ b/includes/filerepo/file/LocalFile.php
@@ -382,6 +382,7 @@ class LocalFile extends File {
$this->$name = $value;
}
} else {
+ wfProfileOut( $fname );
throw new MWException( "Could not find data for image '{$this->getName()}'." );
}
@@ -531,15 +532,15 @@ class LocalFile extends File {
$dbw->update( 'image',
array(
- 'img_size' => $this->size, // sanity
- 'img_width' => $this->width,
- 'img_height' => $this->height,
- 'img_bits' => $this->bits,
+ 'img_size' => $this->size, // sanity
+ 'img_width' => $this->width,
+ 'img_height' => $this->height,
+ 'img_bits' => $this->bits,
'img_media_type' => $this->media_type,
'img_major_mime' => $major,
'img_minor_mime' => $minor,
- 'img_metadata' => $this->metadata,
- 'img_sha1' => $this->sha1,
+ 'img_metadata' => $dbw->encodeBlob($this->metadata),
+ 'img_sha1' => $this->sha1,
),
array( 'img_name' => $this->getName() ),
__METHOD__
@@ -603,17 +604,23 @@ class LocalFile extends File {
* Return the width of the image
*
* @param $page int
- * @return bool|int Returns false on error
+ * @return int
*/
public function getWidth( $page = 1 ) {
$this->load();
if ( $this->isMultipage() ) {
- $dim = $this->getHandler()->getPageDimensions( $this, $page );
+ $handler = $this->getHandler();
+ if ( !$handler ) {
+ return 0;
+ }
+ $dim = $handler->getPageDimensions( $this, $page );
if ( $dim ) {
return $dim['width'];
} else {
- return false;
+ // For non-paged media, the false goes through an
+ // intval, turning failure into 0, so do same here.
+ return 0;
}
} else {
return $this->width;
@@ -624,17 +631,23 @@ class LocalFile extends File {
* Return the height of the image
*
* @param $page int
- * @return bool|int Returns false on error
+ * @return int
*/
public function getHeight( $page = 1 ) {
$this->load();
if ( $this->isMultipage() ) {
- $dim = $this->getHandler()->getPageDimensions( $this, $page );
+ $handler = $this->getHandler();
+ if ( !$handler ) {
+ return 0;
+ }
+ $dim = $handler->getPageDimensions( $this, $page );
if ( $dim ) {
return $dim['height'];
} else {
- return false;
+ // For non-paged media, the false goes through an
+ // intval, turning failure into 0, so do same here.
+ return 0;
}
} else {
return $this->height;
@@ -775,10 +788,12 @@ class LocalFile extends File {
$backend = $this->repo->getBackend();
$files = array( $dir );
- $iterator = $backend->getFileList( array( 'dir' => $dir ) );
- foreach ( $iterator as $file ) {
- $files[] = $file;
- }
+ try {
+ $iterator = $backend->getFileList( array( 'dir' => $dir ) );
+ foreach ( $iterator as $file ) {
+ $files[] = $file;
+ }
+ } catch ( FileBackendError $e ) {} // suppress (bug 54674)
return $files;
}
@@ -793,7 +808,9 @@ class LocalFile extends File {
}
/**
- * Purge the shared history (OldLocalFile) cache
+ * Purge the shared history (OldLocalFile) cache.
+ *
+ * @note This used to purge old thumbnails as well.
*/
function purgeHistory() {
global $wgMemc;
@@ -801,20 +818,20 @@ class LocalFile extends File {
$hashedName = md5( $this->getName() );
$oldKey = $this->repo->getSharedCacheKey( 'oldfile', $hashedName );
- // Must purge thumbnails for old versions too! bug 30192
- foreach( $this->getHistory() as $oldFile ) {
- $oldFile->purgeThumbnails();
- }
-
if ( $oldKey ) {
$wgMemc->delete( $oldKey );
}
}
/**
- * Delete all previously generated thumbnails, refresh metadata in memcached and purge the squid
+ * Delete all previously generated thumbnails, refresh metadata in memcached and purge the squid.
+ *
+ * @param Array $options An array potentially with the key forThumbRefresh.
+ *
+ * @note This used to purge old thumbnails by default as well, but doesn't anymore.
*/
function purgeCache( $options = array() ) {
+ wfProfileIn( __METHOD__ );
// Refresh metadata cache
$this->purgeMetadataCache();
@@ -823,6 +840,7 @@ class LocalFile extends File {
// Purge squid cache for this file
SquidUpdate::purge( array( $this->getURL() ) );
+ wfProfileOut( __METHOD__ );
}
/**
@@ -844,7 +862,7 @@ class LocalFile extends File {
// Purge the squid
if ( $wgUseSquid ) {
$urls = array();
- foreach( $files as $file ) {
+ foreach ( $files as $file ) {
$urls[] = $this->getArchiveThumbUrl( $archiveName, $file );
}
SquidUpdate::purge( $urls );
@@ -865,7 +883,7 @@ class LocalFile extends File {
// Always purge all files from squid regardless of handler filters
if ( $wgUseSquid ) {
$urls = array();
- foreach( $files as $file ) {
+ foreach ( $files as $file ) {
$urls[] = $this->getThumbUrl( $file );
}
array_shift( $urls ); // don't purge directory
@@ -1195,20 +1213,20 @@ class LocalFile extends File {
# doesn't deadlock. SELECT FOR UPDATE causes a deadlock for every race condition.
$dbw->insert( 'image',
array(
- 'img_name' => $this->getName(),
- 'img_size' => $this->size,
- 'img_width' => intval( $this->width ),
- 'img_height' => intval( $this->height ),
- 'img_bits' => $this->bits,
- 'img_media_type' => $this->media_type,
- 'img_major_mime' => $this->major_mime,
- 'img_minor_mime' => $this->minor_mime,
- 'img_timestamp' => $timestamp,
+ 'img_name' => $this->getName(),
+ 'img_size' => $this->size,
+ 'img_width' => intval( $this->width ),
+ 'img_height' => intval( $this->height ),
+ 'img_bits' => $this->bits,
+ 'img_media_type' => $this->media_type,
+ 'img_major_mime' => $this->major_mime,
+ 'img_minor_mime' => $this->minor_mime,
+ 'img_timestamp' => $timestamp,
'img_description' => $comment,
- 'img_user' => $user->getId(),
- 'img_user_text' => $user->getName(),
- 'img_metadata' => $this->metadata,
- 'img_sha1' => $this->sha1
+ 'img_user' => $user->getId(),
+ 'img_user_text' => $user->getName(),
+ 'img_metadata' => $dbw->encodeBlob($this->metadata),
+ 'img_sha1' => $this->sha1
),
__METHOD__,
'IGNORE'
@@ -1258,7 +1276,7 @@ class LocalFile extends File {
'img_description' => $comment,
'img_user' => $user->getId(),
'img_user_text' => $user->getName(),
- 'img_metadata' => $this->metadata,
+ 'img_metadata' => $dbw->encodeBlob($this->metadata),
'img_sha1' => $this->sha1
),
array( 'img_name' => $this->getName() ),
@@ -1274,20 +1292,45 @@ class LocalFile extends File {
$wikiPage->setFile( $this );
# Add the log entry
- $log = new LogPage( 'upload' );
$action = $reupload ? 'overwrite' : 'upload';
- $logId = $log->addEntry( $action, $descTitle, $comment, array(), $user );
- wfProfileIn( __METHOD__ . '-edit' );
+ $logEntry = new ManualLogEntry( 'upload', $action );
+ $logEntry->setPerformer( $user );
+ $logEntry->setComment( $comment );
+ $logEntry->setTarget( $descTitle );
+
+ // Allow people using the api to associate log entries with the upload.
+ // Log has a timestamp, but sometimes different from upload timestamp.
+ $logEntry->setParameters(
+ array(
+ 'img_sha1' => $this->sha1,
+ 'img_timestamp' => $timestamp,
+ )
+ );
+ // Note we keep $logId around since during new image
+ // creation, page doesn't exist yet, so log_page = 0
+ // but we want it to point to the page we're making,
+ // so we later modify the log entry.
+ // For a similar reason, we avoid making an RC entry
+ // now and wait until the page exists.
+ $logId = $logEntry->insert();
+
$exists = $descTitle->exists();
+ if ( $exists ) {
+ // Page exists, do RC entry now (otherwise we wait for later).
+ $logEntry->publish( $logId );
+ }
+ wfProfileIn( __METHOD__ . '-edit' );
if ( $exists ) {
# Create a null revision
$latest = $descTitle->getLatestRevID();
+ $editSummary = LogFormatter::newFromEntry( $logEntry )->getPlainActionText();
+
$nullRevision = Revision::newNullRevision(
$dbw,
$descTitle->getArticleID(),
- $log->getRcComment(),
+ $editSummary,
false
);
if ( !is_null( $nullRevision ) ) {
@@ -1315,16 +1358,20 @@ class LocalFile extends File {
$content = ContentHandler::makeContent( $pageText, $descTitle );
$status = $wikiPage->doEditContent( $content, $comment, EDIT_NEW | EDIT_SUPPRESS_RC, false, $user );
- if ( isset( $status->value['revision'] ) ) { // XXX; doEdit() uses a transaction
- $dbw->begin( __METHOD__ );
+ $dbw->begin( __METHOD__ ); // XXX; doEdit() uses a transaction
+ // Now that the page exists, make an RC entry.
+ $logEntry->publish( $logId );
+ if ( isset( $status->value['revision'] ) ) {
$dbw->update( 'logging',
array( 'log_page' => $status->value['revision']->getPage() ),
array( 'log_id' => $logId ),
__METHOD__
);
- $dbw->commit( __METHOD__ ); // commit before anything bad can happen
}
+ $dbw->commit( __METHOD__ ); // commit before anything bad can happen
}
+
+
wfProfileOut( __METHOD__ . '-edit' );
# Save to cache and purge the squid
@@ -1351,11 +1398,17 @@ class LocalFile extends File {
# Invalidate cache for all pages using this file
$update = new HTMLCacheUpdate( $this->getTitle(), 'imagelinks' );
$update->doUpdate();
+ if ( !$reupload ) {
+ LinksUpdate::queueRecursiveJobsForTable( $this->getTitle(), 'imagelinks' );
+ }
# Invalidate cache for all pages that redirects on this page
$redirs = $this->getTitle()->getRedirectsHere();
foreach ( $redirs as $redir ) {
+ if ( !$reupload && $redir->getNamespace() === NS_FILE ) {
+ LinksUpdate::queueRecursiveJobsForTable( $redir, 'imagelinks' );
+ }
$update = new HTMLCacheUpdate( $redir, 'imagelinks' );
$update->doUpdate();
}
@@ -1405,7 +1458,7 @@ class LocalFile extends File {
$this->lock(); // begin
- $archiveName = wfTimestamp( TS_MW ) . '!'. $this->getName();
+ $archiveName = wfTimestamp( TS_MW ) . '!' . $this->getName();
$archiveRel = 'archive/' . $this->getHashPath() . $archiveName;
$flags = $flags & File::DELETE_SOURCE ? LocalRepo::DELETE_SOURCE : 0;
$status = $this->repo->publish( $srcPath, $dstRel, $archiveRel, $flags, $options );
@@ -1454,18 +1507,27 @@ class LocalFile extends File {
wfDebugLog( 'imagemove', "Finished moving {$this->name}" );
- $this->purgeEverything();
- foreach ( $archiveNames as $archiveName ) {
- $this->purgeOldThumbnails( $archiveName );
- }
+ // Purge the source and target files...
+ $oldTitleFile = wfLocalFile( $this->title );
+ $newTitleFile = wfLocalFile( $target );
+ // Hack: the lock()/unlock() pair is nested in a transaction so the locking is not
+ // tied to BEGIN/COMMIT. To avoid slow purges in the transaction, move them outside.
+ $this->getRepo()->getMasterDB()->onTransactionIdle(
+ function() use ( $oldTitleFile, $newTitleFile, $archiveNames ) {
+ $oldTitleFile->purgeEverything();
+ foreach ( $archiveNames as $archiveName ) {
+ $oldTitleFile->purgeOldThumbnails( $archiveName );
+ }
+ $newTitleFile->purgeEverything();
+ }
+ );
+
if ( $status->isOK() ) {
// Now switch the object
$this->title = $target;
// Force regeneration of the name and hashpath
unset( $this->name );
unset( $this->hashPath );
- // Purge the new image
- $this->purgeEverything();
}
return $status;
@@ -1484,7 +1546,6 @@ class LocalFile extends File {
* @return FileRepoStatus object.
*/
function delete( $reason, $suppress = false ) {
- global $wgUseSquid;
if ( $this->getRepo()->getReadOnlyReason() !== false ) {
return $this->readOnlyFatalStatus();
}
@@ -1502,19 +1563,28 @@ class LocalFile extends File {
DeferredUpdates::addUpdate( SiteStatsUpdate::factory( array( 'images' => -1 ) ) );
}
- $this->purgeEverything();
- foreach ( $archiveNames as $archiveName ) {
- $this->purgeOldThumbnails( $archiveName );
- }
+ // Hack: the lock()/unlock() pair is nested in a transaction so the locking is not
+ // tied to BEGIN/COMMIT. To avoid slow purges in the transaction, move them outside.
+ $file = $this;
+ $this->getRepo()->getMasterDB()->onTransactionIdle(
+ function() use ( $file, $archiveNames ) {
+ global $wgUseSquid;
- if ( $wgUseSquid ) {
- // Purge the squid
- $purgeUrls = array();
- foreach ($archiveNames as $archiveName ) {
- $purgeUrls[] = $this->getArchiveUrl( $archiveName );
+ $file->purgeEverything();
+ foreach ( $archiveNames as $archiveName ) {
+ $file->purgeOldThumbnails( $archiveName );
+ }
+
+ if ( $wgUseSquid ) {
+ // Purge the squid
+ $purgeUrls = array();
+ foreach ( $archiveNames as $archiveName ) {
+ $purgeUrls[] = $file->getArchiveUrl( $archiveName );
+ }
+ SquidUpdate::purge( $purgeUrls );
+ }
}
- SquidUpdate::purge( $purgeUrls );
- }
+ );
return $status;
}
@@ -1606,21 +1676,27 @@ class LocalFile extends File {
* @return String
*/
function getDescriptionUrl() {
- return $this->title->getLocalUrl();
+ return $this->title->getLocalURL();
}
/**
* Get the HTML text of the description page
* This is not used by ImagePage for local files, since (among other things)
* it skips the parser cache.
+ *
+ * @param $lang Language What language to get description in (Optional)
* @return bool|mixed
*/
- function getDescriptionText() {
+ function getDescriptionText( $lang = null ) {
$revision = Revision::newFromTitle( $this->title, false, Revision::READ_NORMAL );
- if ( !$revision ) return false;
+ if ( !$revision ) {
+ return false;
+ }
$content = $revision->getContent();
- if ( !$content ) return false;
- $pout = $content->getParserOutput( $this->title, null, new ParserOptions() );
+ if ( !$content ) {
+ return false;
+ }
+ $pout = $content->getParserOutput( $this->title, null, new ParserOptions( null, $lang ) );
return $pout->getText();
}
@@ -1674,11 +1750,13 @@ class LocalFile extends File {
}
/**
- * @return bool
+ * @return bool Whether to cache in RepoGroup (this avoids OOMs)
*/
function isCacheable() {
$this->load();
- return strlen( $this->metadata ) <= self::CACHE_FIELD_MAX_LEN; // avoid OOMs
+ // If extra data (metadata) was not loaded then it must have been large
+ return $this->extraDataLoaded
+ && strlen( serialize( $this->metadata ) ) <= self::CACHE_FIELD_MAX_LEN;
}
/**
@@ -1695,6 +1773,16 @@ class LocalFile extends File {
$this->lockedOwnTrx = true;
}
$this->locked++;
+ // Bug 54736: use simple lock to handle when the file does not exist.
+ // SELECT FOR UPDATE only locks records not the gaps where there are none.
+ $cache = wfGetMainCache();
+ $key = $this->getCacheKey();
+ if ( !$cache->lock( $key, 60 ) ) {
+ throw new MWException( "Could not acquire lock for '{$this->getName()}.'" );
+ }
+ $dbw->onTransactionIdle( function() use ( $cache, $key ) {
+ $cache->unlock( $key ); // release on commit
+ } );
}
return $dbw->selectField( 'image', '1',
@@ -2189,7 +2277,7 @@ class LocalFileRestoreBatch {
$deletedRel = $this->file->repo->getDeletedHashPath( $row->fa_storage_key ) . $row->fa_storage_key;
$deletedUrl = $this->file->repo->getVirtualUrl() . '/deleted/' . $deletedRel;
- if( isset( $row->fa_sha1 ) ) {
+ if ( isset( $row->fa_sha1 ) ) {
$sha1 = $row->fa_sha1;
} else {
// old row, populate from key
diff --git a/includes/filerepo/file/OldLocalFile.php b/includes/filerepo/file/OldLocalFile.php
index 5c505928..2c545963 100644
--- a/includes/filerepo/file/OldLocalFile.php
+++ b/includes/filerepo/file/OldLocalFile.php
@@ -218,6 +218,7 @@ class OldLocalFile extends LocalFile {
$this->$name = $value;
}
} else {
+ wfProfileOut( __METHOD__ );
throw new MWException( "Could not find data for image '{$this->archive_name}'." );
}
@@ -290,7 +291,7 @@ class OldLocalFile extends LocalFile {
*/
function isDeleted( $field ) {
$this->load();
- return ($this->deleted & $field) == $field;
+ return ( $this->deleted & $field ) == $field;
}
/**