<?php
/**
- * Local file in the wiki's own database
+ * Local file in the wiki's own database.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
*
* @file
* @ingroup FileAbstraction
$this->setProps( $props );
}
+ /**
+ * @param $prefix string
+ * @return array
+ */
function getCacheFields( $prefix = 'img_' ) {
static $fields = array( 'size', 'width', 'height', 'bits', 'media_type',
'major_mime', 'minor_mime', 'metadata', 'timestamp', 'sha1', 'user', 'user_text', 'description' );
/**
* Decode a row from the database (either object or array) to an array
* with timestamps and MIME types decoded, and the field prefix removed.
+ * @param $row
+ * @param $prefix string
+ * @throws MWException
* @return array
*/
function decodeRow( $row, $prefix = 'img_' ) {
$dbw->update( 'image',
array(
+ 'img_size' => $this->size, // sanity
'img_width' => $this->width,
'img_height' => $this->height,
'img_bits' => $this->bits,
/** getPath inherited */
/** isVisible inhereted */
+ /**
+ * @return bool
+ */
function isMissing() {
if ( $this->missing === null ) {
list( $fileExists ) = $this->repo->fileExists( $this->getVirtualUrl() );
/**
* Return the width of the image
*
- * Returns false on error
- * @return bool
+ * @param $page int
+ * @return bool|int Returns false on error
*/
public function getWidth( $page = 1 ) {
$this->load();
/**
* Return the height of the image
*
- * Returns false on error
- * @return bool
+ * @param $page int
+ * @return bool|int Returns false on error
*/
public function getHeight( $page = 1 ) {
$this->load();
* Returns ID or name of user who uploaded the file
*
* @param $type string 'text' or 'id'
+ * @return int|string
*/
function getUser( $type = 'text' ) {
$this->load();
return $this->metadata;
}
+ /**
+ * @return int
+ */
function getBitDepth() {
$this->load();
return $this->bits;
/**
* Return the size of the image file, in bytes
+ * @return int
*/
public function getSize() {
$this->load();
/**
* Returns the mime type of the file.
+ * @return string
*/
function getMimeType() {
$this->load();
/**
* Return the type of the media in the file.
* Use the value returned by this function with the MEDIATYPE_xxx constants.
+ * @return string
*/
function getMediaType() {
$this->load();
/**
* Fix thumbnail files from 1.4 or before, with extreme prejudice
+ * @TODO: do we still care about this? Perhaps a maintenance script
+ * can be made instead. Enabling this code results in a serious
+ * RTT regression for wikis without 404 handling.
*/
function migrateThumbFile( $thumbName ) {
$thumbDir = $this->getThumbPath();
}
*/
+ /*
if ( $this->repo->fileExists( $thumbDir ) ) {
// Delete file where directory should be
$this->repo->cleanupBatch( array( $thumbDir ) );
}
+ */
}
/** getHandler inherited */
* @return array first element is the base dir, then files in that base dir.
*/
function getThumbnails( $archiveName = false ) {
- $this->load();
-
if ( $archiveName ) {
$dir = $this->getArchiveThumbPath( $archiveName );
} else {
*/
function purgeOldThumbnails( $archiveName ) {
global $wgUseSquid;
+ wfProfileIn( __METHOD__ );
+
// Get a list of old thumbnails and URLs
$files = $this->getThumbnails( $archiveName );
$dir = array_shift( $files );
}
SquidUpdate::purge( $urls );
}
+
+ wfProfileOut( __METHOD__ );
}
/**
*/
function purgeThumbnails( $options = array() ) {
global $wgUseSquid;
+ wfProfileIn( __METHOD__ );
// Delete thumbnails
$files = $this->getThumbnails();
}
SquidUpdate::purge( $urls );
}
+
+ wfProfileOut( __METHOD__ );
}
/**
/** purgeDescription inherited */
/** purgeEverything inherited */
+ /**
+ * @param $limit null
+ * @param $start null
+ * @param $end null
+ * @param $inc bool
+ * @return array
+ */
function getHistory( $limit = null, $start = null, $end = null, $inc = true ) {
$dbr = $this->repo->getSlaveDB();
$tables = array( 'oldimage' );
* @param $comment String: upload description
* @param $pageText String: text to use for the new description page,
* if a new description page is created
- * @param $flags Integer: flags for publish()
- * @param $props Array: File properties, if known. This can be used to reduce the
+ * @param $flags Integer|bool: flags for publish()
+ * @param $props Array|bool: File properties, if known. This can be used to reduce the
* upload time when uploading virtual URLs for which the file info
* is already known
- * @param $timestamp String: timestamp for img_timestamp, or false to use the current time
- * @param $user Mixed: User object or null to use $wgUser
+ * @param $timestamp String|bool: timestamp for img_timestamp, or false to use the current time
+ * @param $user User|null: User object or null to use $wgUser
*
* @return FileRepoStatus object. On success, the value member contains the
* archive name, or an empty string if it was a new file.
/**
* Record a file upload in the upload log and the image table
+ * @param $oldver
+ * @param $desc string
+ * @param $license string
+ * @param $copyStatus string
+ * @param $source string
+ * @param $watch bool
+ * @param $timestamp string|bool
* @return bool
*/
function recordUpload( $oldver, $desc, $license = '', $copyStatus = '', $source = '',
/**
* Record a file upload in the upload log and the image table
+ * @param $oldver
+ * @param $comment string
+ * @param $pageText string
+ * @param $props bool|array
+ * @param $timestamp bool|string
+ * @param $user null|User
* @return bool
*/
function recordUpload2(
$oldver, $comment, $pageText, $props = false, $timestamp = false, $user = null
) {
+ wfProfileIn( __METHOD__ );
+
if ( is_null( $user ) ) {
global $wgUser;
$user = $wgUser;
$dbw->begin( __METHOD__ );
if ( !$props ) {
+ wfProfileIn( __METHOD__ . '-getProps' );
$props = $this->repo->getFileProps( $this->getVirtualUrl() );
+ wfProfileOut( __METHOD__ . -'getProps' );
}
if ( $timestamp === false ) {
$props['timestamp'] = wfTimestamp( TS_MW, $timestamp ); // DB -> TS_MW
$this->setProps( $props );
- # Delete thumbnails
- $this->purgeThumbnails();
-
- # The file is already on its final location, remove it from the squid cache
- SquidUpdate::purge( array( $this->getURL() ) );
-
# Fail now if the file isn't there
if ( !$this->fileExists ) {
wfDebug( __METHOD__ . ": File " . $this->getRel() . " went missing!\n" );
+ wfProfileOut( __METHOD__ );
return false;
}
__METHOD__,
'IGNORE'
);
-
if ( $dbw->affectedRows() == 0 ) {
# (bug 34993) Note: $oldver can be empty here, if the previous
# version of the file was broken. Allow registration of the new
__METHOD__
);
} else {
- # This is a new file
- # Update the image count
- $dbw->begin( __METHOD__ );
- $dbw->update(
- 'site_stats',
- array( 'ss_images = ss_images+1' ),
- '*',
- __METHOD__
- );
- $dbw->commit( __METHOD__ );
+ # This is a new file, so update the image count
+ DeferredUpdates::addUpdate( SiteStatsUpdate::factory( array( 'images' => 1 ) ) );
}
$descTitle = $this->getTitle();
$action = $reupload ? 'overwrite' : 'upload';
$log->addEntry( $action, $descTitle, $comment, array(), $user );
+ wfProfileIn( __METHOD__ . '-edit' );
if ( $descTitle->exists() ) {
# Create a null revision
$latest = $descTitle->getLatestRevID();
# Squid and file cache for the description page are purged by doEdit.
$wikiPage->doEdit( $pageText, $comment, EDIT_NEW | EDIT_SUPPRESS_RC, false, $user );
}
+ wfProfileOut( __METHOD__ . '-edit' );
# Commit the transaction now, in case something goes wrong later
# The most important thing is that files don't get lost, especially archives
# which in fact doesn't really exist (bug 24978)
$this->saveToCache();
+ if ( $reupload ) {
+ # Delete old thumbnails
+ wfProfileIn( __METHOD__ . '-purge' );
+ $this->purgeThumbnails();
+ wfProfileOut( __METHOD__ . '-purge' );
+
+ # Remove the old file from the squid cache
+ SquidUpdate::purge( array( $this->getURL() ) );
+ }
+
# Hooks, hooks, the magic of hooks...
+ wfProfileIn( __METHOD__ . '-hooks' );
wfRunHooks( 'FileUpload', array( $this, $reupload, $descTitle->exists() ) );
+ wfProfileOut( __METHOD__ . '-hooks' );
# Invalidate cache for all pages using this file
$update = new HTMLCacheUpdate( $this->getTitle(), 'imagelinks' );
$update->doUpdate();
}
+ wfProfileOut( __METHOD__ );
return true;
}
}
wfDebugLog( 'imagemove', "Got request to move {$this->name} to " . $target->getText() );
- $this->lock(); // begin
-
$batch = new LocalFileMoveBatch( $this, $target );
- $batch->addCurrent();
- $batch->addOlds();
+ $this->lock(); // begin
+ $batch->addCurrent();
+ $archiveNames = $batch->addOlds();
$status = $batch->execute();
+ $this->unlock(); // done
+
wfDebugLog( 'imagemove', "Finished moving {$this->name}" );
$this->purgeEverything();
- $this->unlock(); // done
-
- if ( $status->isOk() ) {
+ foreach ( $archiveNames as $archiveName ) {
+ $this->purgeOldThumbnails( $archiveName );
+ }
+ if ( $status->isOK() ) {
// Now switch the object
$this->title = $target;
// Force regeneration of the name and hashpath
return $this->readOnlyFatalStatus();
}
- $this->lock(); // begin
-
$batch = new LocalFileDeleteBatch( $this, $reason, $suppress );
- $batch->addCurrent();
+ $this->lock(); // begin
+ $batch->addCurrent();
# Get old version relative paths
- $dbw = $this->repo->getMasterDB();
- $result = $dbw->select( 'oldimage',
- array( 'oi_archive_name' ),
- array( 'oi_name' => $this->getName() ) );
- foreach ( $result as $row ) {
- $batch->addOld( $row->oi_archive_name );
- $this->purgeOldThumbnails( $row->oi_archive_name );
- }
+ $archiveNames = $batch->addOlds();
$status = $batch->execute();
+ $this->unlock(); // done
if ( $status->isOK() ) {
- // Update site_stats
- $site_stats = $dbw->tableName( 'site_stats' );
- $dbw->query( "UPDATE $site_stats SET ss_images=ss_images-1", __METHOD__ );
- $this->purgeEverything();
+ DeferredUpdates::addUpdate( SiteStatsUpdate::factory( array( 'images' => -1 ) ) );
}
- $this->unlock(); // done
+ $this->purgeEverything();
+ foreach ( $archiveNames as $archiveName ) {
+ $this->purgeOldThumbnails( $archiveName );
+ }
return $status;
}
return $this->readOnlyFatalStatus();
}
- $this->lock(); // begin
-
$batch = new LocalFileDeleteBatch( $this, $reason, $suppress );
+
+ $this->lock(); // begin
$batch->addOld( $archiveName );
- $this->purgeOldThumbnails( $archiveName );
$status = $batch->execute();
-
$this->unlock(); // done
+ $this->purgeOldThumbnails( $archiveName );
if ( $status->isOK() ) {
$this->purgeDescription();
$this->purgeHistory();
return $this->readOnlyFatalStatus();
}
- $this->lock(); // begin
-
$batch = new LocalFileRestoreBatch( $this, $unsuppress );
+ $this->lock(); // begin
if ( !$versions ) {
$batch->addAll();
} else {
$batch->addIds( $versions );
}
-
$status = $batch->execute();
-
if ( $status->isGood() ) {
$cleanupStatus = $batch->cleanup();
$cleanupStatus->successCount = 0;
$cleanupStatus->failCount = 0;
$status->merge( $cleanupStatus );
}
-
$this->unlock(); // done
return $status;
return $pout->getText();
}
+ /**
+ * @return string
+ */
function getDescription() {
$this->load();
return $this->description;
}
+ /**
+ * @return bool|string
+ */
function getTimestamp() {
$this->load();
return $this->timestamp;
}
+ /**
+ * @return string
+ */
function getSha1() {
$this->load();
// Initialise now if necessary
return $this->sha1;
}
+ /**
+ * @return bool
+ */
function isCacheable() {
$this->load();
return strlen( $this->metadata ) <= self::CACHE_FIELD_MAX_LEN; // avoid OOMs
var $reason, $srcRels = array(), $archiveUrls = array(), $deletionBatch, $suppress;
var $status;
+ /**
+ * @param $file File
+ * @param $reason string
+ * @param $suppress bool
+ */
function __construct( File $file, $reason = '', $suppress = false ) {
$this->file = $file;
$this->reason = $reason;
$this->srcRels['.'] = $this->file->getRel();
}
+ /**
+ * @param $oldName string
+ */
function addOld( $oldName ) {
$this->srcRels[$oldName] = $this->file->getArchiveRel( $oldName );
$this->archiveUrls[] = $this->file->getArchiveUrl( $oldName );
}
+ /**
+ * Add the old versions of the image to the batch
+ * @return Array List of archive names from old versions
+ */
+ function addOlds() {
+ $archiveNames = array();
+
+ $dbw = $this->file->repo->getMasterDB();
+ $result = $dbw->select( 'oldimage',
+ array( 'oi_archive_name' ),
+ array( 'oi_name' => $this->file->getName() ),
+ __METHOD__
+ );
+
+ foreach ( $result as $row ) {
+ $this->addOld( $row->oi_archive_name );
+ $archiveNames[] = $row->oi_archive_name;
+ }
+
+ return $archiveNames;
+ }
+
+ /**
+ * @return array
+ */
function getOldRels() {
if ( !isset( $this->srcRels['.'] ) ) {
$oldRels =& $this->srcRels;
return array( $oldRels, $deleteCurrent );
}
+ /**
+ * @return array
+ */
protected function getHashes() {
$hashes = array();
list( $oldRels, $deleteCurrent ) = $this->getOldRels();
* @return FileRepoStatus
*/
function execute() {
- global $wgUseSquid;
wfProfileIn( __METHOD__ );
$this->file->lock();
return $this->status;
}
- // Purge squid
- if ( $wgUseSquid ) {
- $urls = array();
-
- foreach ( $this->srcRels as $srcRel ) {
- $urlRel = str_replace( '%2F', '/', rawurlencode( $srcRel ) );
- $urls[] = $this->file->repo->getZoneUrl( 'public' ) . '/' . $urlRel;
- }
- SquidUpdate::purge( $urls );
- }
-
// Delete image/oldimage rows
$this->doDBDeletes();
/**
* Removes non-existent files from a deletion batch.
+ * @param $batch array
* @return array
*/
function removeNonexistentFiles( $batch ) {
var $cleanupBatch, $ids, $all, $unsuppress = false;
+ /**
+ * @param $file File
+ * @param $unsuppress bool
+ */
function __construct( File $file, $unsuppress = false ) {
$this->file = $file;
$this->cleanupBatch = $this->ids = array();
if ( !$exists ) {
wfDebug( __METHOD__ . " restored {$status->successCount} items, creating a new current\n" );
- // Update site_stats
- $site_stats = $dbw->tableName( 'site_stats' );
- $dbw->query( "UPDATE $site_stats SET ss_images=ss_images+1", __METHOD__ );
+ DeferredUpdates::addUpdate( SiteStatsUpdate::factory( array( 'images' => 1 ) ) );
$this->file->purgeEverything();
} else {
/**
* Removes non-existent files from a store batch.
+ * @param $triplets array
* @return array
*/
function removeNonexistentFiles( $triplets ) {
$files = $filteredTriplets = array();
- foreach ( $triplets as $file )
+ foreach ( $triplets as $file ) {
$files[$file[0]] = $file[0];
+ }
$result = $this->file->repo->fileExistsBatch( $files );
/**
* Removes non-existent files from a cleanup batch.
+ * @param $batch array
* @return array
*/
function removeNonexistentFromCleanup( $batch ) {
class LocalFileMoveBatch {
/**
- * @var File
+ * @var LocalFile
*/
var $file;
*/
var $target;
- var $cur, $olds, $oldCount, $archive, $db;
+ var $cur, $olds, $oldCount, $archive;
+ /**
+ * @var DatabaseBase
+ */
+ var $db;
+
+ /**
+ * @param File $file
+ * @param Title $target
+ */
function __construct( File $file, Title $target ) {
$this->file = $file;
$this->target = $target;
$this->newName = $this->file->repo->getNameFromTitle( $this->target );
$this->oldRel = $this->oldHash . $this->oldName;
$this->newRel = $this->newHash . $this->newName;
- $this->db = $file->repo->getMasterDb();
+ $this->db = $file->getRepo()->getMasterDb();
}
/**
/**
* Add the old versions of the image to the batch
+ * @return Array List of archive names from old versions
*/
function addOlds() {
$archiveBase = 'archive';
$this->olds = array();
$this->oldCount = 0;
+ $archiveNames = array();
$result = $this->db->select( 'oldimage',
array( 'oi_archive_name', 'oi_deleted' ),
);
foreach ( $result as $row ) {
+ $archiveNames[] = $row->oi_archive_name;
$oldName = $row->oi_archive_name;
$bits = explode( '!', $oldName, 2 );
"{$archiveBase}/{$this->newHash}{$timestamp}!{$this->newName}"
);
}
+
+ return $archiveNames;
}
/**
function execute() {
$repo = $this->file->repo;
$status = $repo->newGood();
- $triplets = $this->getMoveTriplets();
+ $triplets = $this->getMoveTriplets();
$triplets = $this->removeNonexistentFiles( $triplets );
// Copy the files into their new location
return $statusMove;
}
- $this->db->begin( __METHOD__ );
+ $this->file->lock(); // begin
$statusDb = $this->doDBUpdates();
wfDebugLog( 'imagemove', "Renamed {$this->file->getName()} in database: {$statusDb->successCount} successes, {$statusDb->failCount} failures" );
if ( !$statusDb->isGood() ) {
- $this->db->rollback( __METHOD__ );
+ $this->file->unlockAndRollback();
// Something went wrong with the DB updates, so remove the target files
$this->cleanupTarget( $triplets );
$statusDb->ok = false;
return $statusDb;
}
- $this->db->commit( __METHOD__ );
+ $this->file->unlock(); // done
// Everything went ok, remove the source files
$this->cleanupSource( $triplets );
/**
* Removes non-existent files from move batch.
+ * @param $triplets array
* @return array
*/
function removeNonexistentFiles( $triplets ) {