diff options
author | Pierre Schmitz <pierre@archlinux.de> | 2013-12-08 09:55:49 +0100 |
---|---|---|
committer | Pierre Schmitz <pierre@archlinux.de> | 2013-12-08 09:55:49 +0100 |
commit | 4ac9fa081a7c045f6a9f1cfc529d82423f485b2e (patch) | |
tree | af68743f2f4a47d13f2b0eb05f5c4aaf86d8ea37 /includes/filerepo/file | |
parent | af4da56f1ad4d3ef7b06557bae365da2ea27a897 (diff) |
Update to MediaWiki 1.22.0
Diffstat (limited to 'includes/filerepo/file')
-rw-r--r-- | includes/filerepo/file/ArchivedFile.php | 23 | ||||
-rw-r--r-- | includes/filerepo/file/File.php | 37 | ||||
-rw-r--r-- | includes/filerepo/file/ForeignAPIFile.php | 49 | ||||
-rw-r--r-- | includes/filerepo/file/ForeignDBFile.php | 5 | ||||
-rw-r--r-- | includes/filerepo/file/LocalFile.php | 234 | ||||
-rw-r--r-- | includes/filerepo/file/OldLocalFile.php | 3 |
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; } /** |