X-Git-Url: https://git.cyclocoop.org/?a=blobdiff_plain;f=includes%2Ffilerepo%2FLocalFile.php;h=ce70ea2e06b091acaacd74bc977d0d92a3547e34;hb=e5b8b39df157ce7d77538551f24d058835df6fd6;hp=5528c302a7829c59f0ed8f993a5daa827ede8709;hpb=ca4ebe3f7bc1fe721f5c4996af3ad68d8e2cc408;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/filerepo/LocalFile.php b/includes/filerepo/LocalFile.php index 5528c302a7..ce70ea2e06 100644 --- a/includes/filerepo/LocalFile.php +++ b/includes/filerepo/LocalFile.php @@ -5,7 +5,7 @@ /** * Bump this number when serialized cache records may be incompatible. */ -define( 'MW_FILE_VERSION', 7 ); +define( 'MW_FILE_VERSION', 8 ); /** * Class to represent a local file in the wiki's own database @@ -22,7 +22,7 @@ define( 'MW_FILE_VERSION', 7 ); * The convenience functions wfLocalFile() and wfFindFile() should be sufficient * in most cases. * - * @addtogroup FileRepo + * @ingroup FileRepo */ class LocalFile extends File { @@ -49,6 +49,7 @@ class LocalFile extends File $dataLoaded, # Whether or not all this has been loaded from the database (loadFromXxx) $upgraded, # Whether the row was upgraded on load $locked, # True if the image row is locked + $missing, # True if file is not present in file system. Not to be cached in memcached $deleted; # Bitfield akin to rev_deleted /**#@-*/ @@ -68,11 +69,53 @@ class LocalFile extends File * Do not call this except from inside a repo class. */ static function newFromRow( $row, $repo ) { - $title = Title::makeTitle( NS_IMAGE, $row->img_name ); + $title = Title::makeTitle( NS_FILE, $row->img_name ); $file = new self( $title, $repo ); $file->loadFromRow( $row ); return $file; } + + /** + * Create a LocalFile from a SHA-1 key + * Do not call this except from inside a repo class. + */ + static function newFromKey( $sha1, $repo, $timestamp = false ) { + # Polymorphic function name to distinguish foreign and local fetches + $fname = get_class( $this ) . '::' . __FUNCTION__; + + $conds = array( 'img_sha1' => $sha1 ); + if( $timestamp ) { + $conds['img_timestamp'] = $timestamp; + } + $row = $dbr->selectRow( 'image', $this->getCacheFields( 'img_' ), $conds, $fname ); + if( $row ) { + return self::newFromRow( $row, $repo ); + } else { + return false; + } + } + + /** + * Fields in the image table + */ + static function selectFields() { + return array( + 'img_name', + 'img_size', + 'img_width', + 'img_height', + 'img_metadata', + 'img_bits', + 'img_media_type', + 'img_major_mime', + 'img_minor_mime', + 'img_description', + 'img_user', + 'img_user_text', + 'img_timestamp', + 'img_sha1', + ); + } /** * Constructor. @@ -90,11 +133,12 @@ class LocalFile extends File } /** - * Get the memcached key + * Get the memcached key for the main data for this file, or false if + * there is no access to the shared cache. */ function getCacheKey() { $hashedName = md5($this->getName()); - return wfMemcKey( 'file', $hashedName ); + return $this->repo->getSharedCacheKey( 'file', $hashedName ); } /** @@ -106,6 +150,7 @@ class LocalFile extends File $this->dataLoaded = false; $key = $this->getCacheKey(); if ( !$key ) { + wfProfileOut( __METHOD__ ); return false; } $cachedValues = $wgMemc->get( $key ); @@ -290,12 +335,14 @@ class LocalFile extends File # Don't destroy file info of missing files if ( !$this->fileExists ) { wfDebug( __METHOD__.": file does not exist, aborting\n" ); + wfProfileOut( __METHOD__ ); return; } $dbw = $this->repo->getMasterDB(); list( $major, $minor ) = self::splitMime( $this->mime ); if ( wfReadOnly() ) { + wfProfileOut( __METHOD__ ); return; } wfDebug(__METHOD__.': upgrading '.$this->getName()." to the current schema\n"); @@ -349,6 +396,14 @@ class LocalFile extends File /** getPath inherited */ /** isVisible inhereted */ + function isMissing() { + if( $this->missing === null ) { + list( $fileExists ) = $this->repo->fileExistsBatch( array( $this->getVirtualUrl() ), FileRepo::FILES_ONLY ); + $this->missing = !$fileExists; + } + return $this->missing; + } + /** * Return the width of the image * @@ -411,6 +466,11 @@ class LocalFile extends File return $this->metadata; } + function getBitDepth() { + $this->load(); + return $this->bits; + } + /** * Return the size of the image file, in bytes * @public @@ -496,25 +556,21 @@ class LocalFile extends File * Get all thumbnail names previously generated for this file */ function getThumbnails() { - if ( $this->isHashed() ) { - $this->load(); - $files = array(); - $dir = $this->getThumbPath(); - - if ( is_dir( $dir ) ) { - $handle = opendir( $dir ); - - if ( $handle ) { - while ( false !== ( $file = readdir($handle) ) ) { - if ( $file{0} != '.' ) { - $files[] = $file; - } + $this->load(); + $files = array(); + $dir = $this->getThumbPath(); + + if ( is_dir( $dir ) ) { + $handle = opendir( $dir ); + + if ( $handle ) { + while ( false !== ( $file = readdir($handle) ) ) { + if ( $file{0} != '.' ) { + $files[] = $file; } - closedir( $handle ); } + closedir( $handle ); } - } else { - $files = array(); } return $files; @@ -535,8 +591,10 @@ class LocalFile extends File function purgeHistory() { global $wgMemc; $hashedName = md5($this->getName()); - $oldKey = wfMemcKey( 'oldfile', $hashedName ); - $wgMemc->delete( $oldKey ); + $oldKey = $this->repo->getSharedCacheKey( 'oldfile', $hashedName ); + if ( $oldKey ) { + $wgMemc->delete( $oldKey ); + } } /** @@ -581,25 +639,38 @@ class LocalFile extends File /** purgeDescription inherited */ /** purgeEverything inherited */ - function getHistory($limit = null, $start = null, $end = null) { + function getHistory($limit = null, $start = null, $end = null, $inc = true) { $dbr = $this->repo->getSlaveDB(); - $conds = $opts = array(); - $conds[] = "oi_name = " . $dbr->addQuotes( $this->title->getDBKey() ); - if( $start !== null ) { - $conds[] = "oi_timestamp <= " . $dbr->addQuotes( $dbr->timestamp( $start ) ); + $tables = array('oldimage'); + $fields = OldLocalFile::selectFields(); + $conds = $opts = $join_conds = array(); + $eq = $inc ? "=" : ""; + $conds[] = "oi_name = " . $dbr->addQuotes( $this->title->getDBkey() ); + if( $start ) { + $conds[] = "oi_timestamp <$eq " . $dbr->addQuotes( $dbr->timestamp( $start ) ); } - if( $end !== null ) { - $conds[] = "oi_timestamp >= " . $dbr->addQuotes( $dbr->timestamp( $end ) ); + if( $end ) { + $conds[] = "oi_timestamp >$eq " . $dbr->addQuotes( $dbr->timestamp( $end ) ); } if( $limit ) { $opts['LIMIT'] = $limit; } - $opts['ORDER BY'] = 'oi_timestamp DESC'; - $res = $dbr->select('oldimage', '*', $conds, __METHOD__, $opts); + // Search backwards for time > x queries + $order = (!$start && $end !== null) ? "ASC" : "DESC"; + $opts['ORDER BY'] = "oi_timestamp $order"; + $opts['USE INDEX'] = array('oldimage' => 'oi_name_timestamp'); + + wfRunHooks( 'LocalFile::getHistory', array( &$this, &$tables, &$fields, + &$conds, &$opts, &$join_conds ) ); + + $res = $dbr->select( $tables, $fields, $conds, __METHOD__, $opts, $join_conds ); $r = array(); while( $row = $dbr->fetchObject($res) ) { $r[] = OldLocalFile::newFromRow($row, $this->repo); } + if( $order == "ASC" ) { + $r = array_reverse( $r ); // make sure it ends up descending + } return $r; } @@ -688,11 +759,11 @@ class LocalFile extends File * @return FileRepoStatus object. On success, the value member contains the * archive name, or an empty string if it was a new file. */ - function upload( $srcPath, $comment, $pageText, $flags = 0, $props = false, $timestamp = false ) { + function upload( $srcPath, $comment, $pageText, $flags = 0, $props = false, $timestamp = false, $user = null ) { $this->lock(); $status = $this->publish( $srcPath, $flags ); if ( $status->ok ) { - if ( !$this->recordUpload2( $status->value, $comment, $pageText, $props, $timestamp ) ) { + if ( !$this->recordUpload2( $status->value, $comment, $pageText, $props, $timestamp, $user ) ) { $status->fatal( 'filenotfound', $srcPath ); } } @@ -722,18 +793,22 @@ class LocalFile extends File /** * Record a file upload in the upload log and the image table */ - function recordUpload2( $oldver, $comment, $pageText, $props = false, $timestamp = false ) + function recordUpload2( $oldver, $comment, $pageText, $props = false, $timestamp = false, $user = null ) { - global $wgUser; + if( is_null( $user ) ) { + global $wgUser; + $user = $wgUser; + } $dbw = $this->repo->getMasterDB(); + $dbw->begin(); if ( !$props ) { $props = $this->repo->getFileProps( $this->getVirtualUrl() ); } $props['description'] = $comment; - $props['user'] = $wgUser->getID(); - $props['user_text'] = $wgUser->getName(); + $props['user'] = $user->getId(); + $props['user_text'] = $user->getName(); $props['timestamp'] = wfTimestamp( TS_MW ); $this->setProps( $props ); @@ -768,8 +843,8 @@ class LocalFile extends File 'img_minor_mime' => $this->minor_mime, 'img_timestamp' => $timestamp, 'img_description' => $comment, - 'img_user' => $wgUser->getID(), - 'img_user_text' => $wgUser->getName(), + 'img_user' => $user->getId(), + 'img_user_text' => $user->getName(), 'img_metadata' => $this->metadata, 'img_sha1' => $this->sha1 ), @@ -814,8 +889,8 @@ class LocalFile extends File 'img_minor_mime' => $this->minor_mime, 'img_timestamp' => $timestamp, 'img_description' => $comment, - 'img_user' => $wgUser->getID(), - 'img_user_text' => $wgUser->getName(), + 'img_user' => $user->getId(), + 'img_user_text' => $user->getName(), 'img_metadata' => $this->metadata, 'img_sha1' => $this->sha1 ), array( /* WHERE */ @@ -830,17 +905,22 @@ class LocalFile extends File } $descTitle = $this->getTitle(); - $article = new Article( $descTitle ); + $article = new ImagePage( $descTitle ); + $article->setFile( $this ); # Add the log entry $log = new LogPage( 'upload' ); $action = $reupload ? 'overwrite' : 'upload'; - $log->addEntry( $action, $descTitle, $comment ); + $log->addEntry( $action, $descTitle, $comment, array(), $user ); if( $descTitle->exists() ) { # Create a null revision - $nullRevision = Revision::newNullRevision( $dbw, $descTitle->getArticleId(), $log->getRcComment(), false ); + $latest = $descTitle->getLatestRevID(); + $nullRevision = Revision::newNullRevision( $dbw, $descTitle->getArticleId(), + $log->getRcComment(), false ); $nullRevision->insertOn( $dbw ); + + wfRunHooks( 'NewRevisionFromEditComplete', array($article, $nullRevision, $latest, $user) ); $article->updateRevisionOn( $dbw, $nullRevision ); # Invalidate the cache for the description page @@ -922,16 +1002,14 @@ class LocalFile extends File * @return FileRepoStatus object. */ function move( $target ) { + wfDebugLog( 'imagemove', "Got request to move {$this->name} to " . $target->getText() ); $this->lock(); - $dbw = $this->repo->getMasterDB(); - $batch = new LocalFileMoveBatch( $this, $target, $dbw ); + $batch = new LocalFileMoveBatch( $this, $target ); $batch->addCurrent(); $batch->addOlds(); - if( !$this->repo->canTransformVia404() ) { - $batch->addThumbs(); - } $status = $batch->execute(); + wfDebugLog( 'imagemove', "Finished moving {$this->name}" ); $this->purgeEverything(); $this->unlock(); @@ -996,7 +1074,7 @@ class LocalFile extends File * * @param $reason * @param $suppress - * @throws MWException or FSException on database or filestore failure + * @throws MWException or FSException on database or file store failure * @return FileRepoStatus object. */ function deleteOld( $archiveName, $reason, $suppress=false ) { @@ -1065,8 +1143,8 @@ class LocalFile extends File if ( !$revision ) return false; $text = $revision->getText(); if ( !$text ) return false; - $html = $wgParser->parse( $text, new ParserOptions ); - return $html; + $pout = $wgParser->parse( $text, $this->title, new ParserOptions() ); + return $pout->getText(); } function getDescription() { @@ -1084,7 +1162,7 @@ class LocalFile extends File // Initialise now if necessary if ( $this->sha1 == '' && $this->fileExists ) { $this->sha1 = File::sha1Base36( $this->getPath() ); - if ( strval( $this->sha1 ) != '' ) { + if ( !wfReadOnly() && strval( $this->sha1 ) != '' ) { $dbw = $this->repo->getMasterDB(); $dbw->update( 'image', array( 'img_sha1' => $this->sha1 ), @@ -1139,6 +1217,7 @@ class LocalFile extends File /** * Helper class for file deletion + * @ingroup FileRepo */ class LocalFileDeleteBatch { var $file, $reason, $srcRels = array(), $archiveUrls = array(), $deletionBatch, $suppress; @@ -1310,7 +1389,7 @@ class LocalFileDeleteBatch { $dbw->delete( 'oldimage', array( 'oi_name' => $this->file->getName(), - 'oi_archive_name IN (' . $dbw->makeList( array_keys( $oldRels ) ) . ')' + 'oi_archive_name' => array_keys( $oldRels ) ), __METHOD__ ); } if ( $deleteCurrent ) { @@ -1322,7 +1401,7 @@ class LocalFileDeleteBatch { * Run the transaction */ function execute() { - global $wgUser, $wgUseSquid; + global $wgUseSquid; wfProfileIn( __METHOD__ ); $this->file->lock(); @@ -1335,7 +1414,7 @@ class LocalFileDeleteBatch { array( 'oi_archive_name' ), array( 'oi_name' => $this->file->getName(), 'oi_archive_name IN (' . $dbw->makeList( array_keys($oldRels) ) . ')', - 'oi_deleted & ' . File::DELETED_FILE => File::DELETED_FILE ), + $dbw->bitAnd('oi_deleted', File::DELETED_FILE) => File::DELETED_FILE ), __METHOD__ ); while( $row = $dbw->fetchObject( $res ) ) { $privateFiles[$row->oi_archive_name] = 1; @@ -1365,6 +1444,9 @@ class LocalFileDeleteBatch { // them in a separate transaction, then run the file ops, then update the fa_name fields. $this->doDBInserts(); + // Removes non-existent file from the batch, so we don't get errors. + $this->deletionBatch = $this->removeNonexistentFiles( $this->deletionBatch ); + // Execute the file deletion batch $status = $this->file->repo->deleteBatch( $this->deletionBatch ); if ( !$status->isGood() ) { @@ -1376,6 +1458,7 @@ class LocalFileDeleteBatch { // Roll back inserts, release lock and abort // TODO: delete the defunct filearchive rows if we are using a non-transactional DB $this->file->unlockAndRollback(); + wfProfileOut( __METHOD__ ); return $this->status; } @@ -1397,12 +1480,29 @@ class LocalFileDeleteBatch { wfProfileOut( __METHOD__ ); return $this->status; } + + /** + * Removes non-existent files from a deletion batch. + */ + function removeNonexistentFiles( $batch ) { + $files = $newBatch = array(); + foreach( $batch as $batchItem ) { + list( $src, $dest ) = $batchItem; + $files[$src] = $this->file->repo->getVirtualUrl( 'public' ) . '/' . rawurlencode( $src ); + } + $result = $this->file->repo->fileExistsBatch( $files, FSRepo::FILES_ONLY ); + foreach( $batch as $batchItem ) + if( $result[$batchItem[0]] ) + $newBatch[] = $batchItem; + return $newBatch; + } } #------------------------------------------------------------------------------ /** * Helper class for file undeletion + * @ingroup FileRepo */ class LocalFileRestoreBatch { var $file, $cleanupBatch, $ids, $all, $unsuppress = false; @@ -1443,7 +1543,7 @@ class LocalFileRestoreBatch { * So we save the batch and let the caller call cleanup() */ function execute() { - global $wgUser, $wgLang; + global $wgLang; if ( !$this->all && !$this->ids ) { // Do nothing return $this->file->repo->newGood(); @@ -1463,7 +1563,8 @@ class LocalFileRestoreBatch { $result = $dbw->select( 'filearchive', '*', $conditions, __METHOD__, - array( 'ORDER BY' => 'fa_timestamp DESC' ) ); + array( 'ORDER BY' => 'fa_timestamp DESC' ) + ); $idsPresent = array(); $storeBatch = array(); @@ -1508,15 +1609,11 @@ class LocalFileRestoreBatch { 'minor_mime' => $row->fa_minor_mime, 'major_mime' => $row->fa_major_mime, 'media_type' => $row->fa_media_type, - 'metadata' => $row->fa_metadata ); + 'metadata' => $row->fa_metadata + ); } if ( $first && !$exists ) { - // The live (current) version cannot be hidden! - if( !$this->unsuppress && $row->fa_deleted ) { - $this->file->unlock(); - return $status; - } // This revision will be published as the new current version $destRel = $this->file->getRel(); $insertCurrent = array( @@ -1533,7 +1630,13 @@ class LocalFileRestoreBatch { 'img_user' => $row->fa_user, 'img_user_text' => $row->fa_user_text, 'img_timestamp' => $row->fa_timestamp, - 'img_sha1' => $sha1); + 'img_sha1' => $sha1 + ); + // The live (current) version cannot be hidden! + if( !$this->unsuppress && $row->fa_deleted ) { + $storeBatch[] = array( $deletedUrl, 'public', $destRel ); + $this->cleanupBatch[] = $row->fa_storage_key; + } } else { $archiveName = $row->fa_archive_name; if( $archiveName == '' ) { @@ -1570,6 +1673,7 @@ class LocalFileRestoreBatch { $deleteIds[] = $row->fa_id; if( !$this->unsuppress && $row->fa_deleted & File::DELETED_FILE ) { // private files can stay where they are + $status->successCount++; } else { $storeBatch[] = array( $deletedUrl, 'public', $destRel ); $this->cleanupBatch[] = $row->fa_storage_key; @@ -1584,6 +1688,9 @@ class LocalFileRestoreBatch { $status->error( 'undelete-missing-filearchive', $id ); } + // Remove missing files from batch, so we don't get errors when undeleting them + $storeBatch = $this->removeNonexistentFiles( $storeBatch ); + // Run the store batch // Use the OVERWRITE_SAME flag to smooth over a common error $storeStatus = $this->file->repo->storeBatch( $storeBatch, FileRepo::OVERWRITE_SAME ); @@ -1614,7 +1721,7 @@ class LocalFileRestoreBatch { __METHOD__ ); } - if( $status->successCount > 0 ) { + if( $status->successCount > 0 || !$storeBatch ) { // If store batch is empty (all files are missing), deletion is to be considered successful if( !$exists ) { wfDebug( __METHOD__." restored {$status->successCount} items, creating a new current\n" ); @@ -1633,6 +1740,38 @@ class LocalFileRestoreBatch { return $status; } + /** + * Removes non-existent files from a store batch. + */ + function removeNonexistentFiles( $triplets ) { + $files = $filteredTriplets = array(); + foreach( $triplets as $file ) + $files[$file[0]] = $file[0]; + $result = $this->file->repo->fileExistsBatch( $files, FSRepo::FILES_ONLY ); + foreach( $triplets as $file ) + if( $result[$file[0]] ) + $filteredTriplets[] = $file; + return $filteredTriplets; + } + + /** + * Removes non-existent files from a cleanup batch. + */ + function removeNonexistentFromCleanup( $batch ) { + $files = $newBatch = array(); + $repo = $this->file->repo; + foreach( $batch as $file ) { + $files[$file] = $repo->getVirtualUrl( 'deleted' ) . '/' . + rawurlencode( $repo->getDeletedHashPath( $file ) . $file ); + } + + $result = $repo->fileExistsBatch( $files, FSRepo::FILES_ONLY ); + foreach( $batch as $file ) + if( $result[$file] ) + $newBatch[] = $file; + return $newBatch; + } + /** * Delete unused files in the deleted zone. * This should be called from outside the transaction in which execute() was called. @@ -1641,6 +1780,7 @@ class LocalFileRestoreBatch { if ( !$this->cleanupBatch ) { return $this->file->repo->newGood(); } + $this->cleanupBatch = $this->removeNonexistentFromCleanup( $this->cleanupBatch ); $status = $this->file->repo->cleanupDeletedBatch( $this->cleanupBatch ); return $status; } @@ -1650,35 +1790,37 @@ class LocalFileRestoreBatch { /** * Helper class for file movement + * @ingroup FileRepo */ class LocalFileMoveBatch { - var $file, $cur, $olds, $oldcount, $archive, $thumbs, $target, $db; + var $file, $cur, $olds, $oldCount, $archive, $target, $db; - function __construct( File $file, Title $target, Database $db ) { + function __construct( File $file, Title $target ) { $this->file = $file; $this->target = $target; $this->oldHash = $this->file->repo->getHashPath( $this->file->getName() ); - $this->newHash = $this->file->repo->getHashPath( $this->target->getDbKey() ); + $this->newHash = $this->file->repo->getHashPath( $this->target->getDBkey() ); $this->oldName = $this->file->getName(); $this->newName = $this->file->repo->getNameFromTitle( $this->target ); $this->oldRel = $this->oldHash . $this->oldName; $this->newRel = $this->newHash . $this->newName; - $this->db = $db; + $this->db = $file->repo->getMasterDb(); } + /* + * Add the current image to the batch + */ function addCurrent() { $this->cur = array( $this->oldRel, $this->newRel ); } - function addThumbs() { - // Thumbnails are purged, so no need to move them - $this->thumbs = array(); - } - + /* + * Add the old versions of the image to the batch + */ function addOlds() { $archiveBase = 'archive'; $this->olds = array(); - $this->oldcount = 0; + $this->oldCount = 0; $result = $this->db->select( 'oldimage', array( 'oi_archive_name', 'oi_deleted' ), @@ -1686,45 +1828,57 @@ class LocalFileMoveBatch { __METHOD__ ); while( $row = $this->db->fetchObject( $result ) ) { - $oldname = $row->oi_archive_name; - $bits = explode( '!', $oldname, 2 ); + $oldName = $row->oi_archive_name; + $bits = explode( '!', $oldName, 2 ); if( count( $bits ) != 2 ) { - wfDebug( 'Invalid old file name: ' . $oldname ); + wfDebug( "Invalid old file name: $oldName \n" ); continue; } list( $timestamp, $filename ) = $bits; if( $this->oldName != $filename ) { - wfDebug( 'Invalid old file name:' . $oldName ); + wfDebug( "Invalid old file name: $oldName \n" ); continue; } - $this->oldcount++; - // Do we want to add those to oldcount? + $this->oldCount++; + // Do we want to add those to oldCount? if( $row->oi_deleted & File::DELETED_FILE ) { continue; } $this->olds[] = array( - "{$archiveBase}/{$this->oldHash}{$oldname}", - "{$archiveBase}/{$this->oldHash}{$timestamp}!{$this->newName}" + "{$archiveBase}/{$this->oldHash}{$oldName}", + "{$archiveBase}/{$this->newHash}{$timestamp}!{$this->newName}" ); } $this->db->freeResult( $result ); } + /* + * Perform the move. + */ function execute() { $repo = $this->file->repo; $status = $repo->newGood(); $triplets = $this->getMoveTriplets(); + $triplets = $this->removeNonexistentFiles( $triplets ); $statusDb = $this->doDBUpdates(); + wfDebugLog( 'imagemove', "Renamed {$this->file->name} in database: {$statusDb->successCount} successes, {$statusDb->failCount} failures" ); $statusMove = $repo->storeBatch( $triplets, FSRepo::DELETE_SOURCE ); + wfDebugLog( 'imagemove', "Moved files for {$this->file->name}: {$statusMove->successCount} successes, {$statusMove->failCount} failures" ); if( !$statusMove->isOk() ) { + wfDebugLog( 'imagemove', "Error in moving files: " . $statusMove->getWikiText() ); $this->db->rollback(); } + $status->merge( $statusDb ); $status->merge( $statusMove ); return $status; } + /* + * Do the database updates and return a new WikiError indicating how many + * rows where updated. + */ function doDBUpdates() { $repo = $this->file->repo; $status = $repo->newGood(); @@ -1754,22 +1908,43 @@ class LocalFileMoveBatch { __METHOD__ ); $affected = $dbw->affectedRows(); - $total = $this->oldcount; + $total = $this->oldCount; $status->successCount += $affected; $status->failCount += $total - $affected; return $status; } - // Generates triplets for FSRepo::storeBatch() + /* + * Generate triplets for FSRepo::storeBatch(). + */ function getMoveTriplets() { - $moves = array_merge( array( $this->cur ), $this->olds, $this->thumbs ); + $moves = array_merge( array( $this->cur ), $this->olds ); $triplets = array(); // The format is: (srcUrl, destZone, destUrl) foreach( $moves as $move ) { // $move: (oldRelativePath, newRelativePath) $srcUrl = $this->file->repo->getVirtualUrl() . '/public/' . rawurlencode( $move[0] ); $triplets[] = array( $srcUrl, 'public', $move[1] ); + wfDebugLog( 'imagemove', "Generated move triplet for {$this->file->name}: {$srcUrl} :: public :: {$move[1]}" ); } return $triplets; } + + /* + * Removes non-existent files from move batch. + */ + function removeNonexistentFiles( $triplets ) { + $files = array(); + foreach( $triplets as $file ) + $files[$file[0]] = $file[0]; + $result = $this->file->repo->fileExistsBatch( $files, FSRepo::FILES_ONLY ); + $filteredTriplets = array(); + foreach( $triplets as $file ) + if( $result[$file[0]] ) { + $filteredTriplets[] = $file; + } else { + wfDebugLog( 'imagemove', "File {$file[0]} does not exist" ); + } + return $filteredTriplets; + } }