26use Psr\Log\LoggerInterface;
29use Wikimedia\ScopedCallback;
77 private $haveSourceLock =
false;
80 private $haveTargetLock =
false;
88 $this->oldHash = $this->file->repo->getHashPath( $this->file->getName() );
89 $this->newHash = $this->file->repo->getHashPath( $this->target->getDBkey() );
90 $this->oldName = $this->file->getName();
91 $this->newName = $this->file->repo->getNameFromTitle( $this->target );
96 $this->logger = LoggerFactory::getInstance(
'imagemove' );
106 if ( $status->isOK() ) {
117 $archiveBase =
'archive';
122 $result = $this->db->newSelectQueryBuilder()
123 ->select( [
'oi_archive_name',
'oi_deleted' ] )
126 ->where( [
'oi_name' => $this->oldName ] )
127 ->caller( __METHOD__ )->fetchResultSet();
129 foreach ( $result as $row ) {
130 $archiveNames[] = $row->oi_archive_name;
132 $bits = explode(
'!',
$oldName, 2 );
134 if ( count( $bits ) != 2 ) {
135 $this->logger->debug(
136 'Old file name missing !: {oldName}',
142 [ $timestamp, $filename ] = $bits;
144 if ( $this->oldName != $filename ) {
145 $this->logger->debug(
146 'Old file name does not match: {oldName}',
155 if ( $row->oi_deleted & File::DELETED_FILE ) {
160 "{$archiveBase}/{$this->oldHash}{$oldName}",
161 "{$archiveBase}/{$this->newHash}{$timestamp}!{$this->newName}"
165 return $archiveNames;
174 if ( $this->haveSourceLock ) {
175 return Status::newGood();
177 $status = $this->file->acquireFileLock();
178 if ( $status->isOK() ) {
179 $this->haveSourceLock =
true;
190 if ( $this->haveTargetLock ) {
191 return Status::newGood();
194 if ( $status->isOK() ) {
195 $this->haveTargetLock =
true;
204 if ( $this->haveSourceLock ) {
205 $this->file->releaseFileLock();
206 $this->haveSourceLock =
false;
208 if ( $this->haveTargetLock ) {
210 $this->haveTargetLock =
false;
220 if ( $this->targetFile ===
null ) {
221 $this->targetFile = MediaWikiServices::getInstance()->getRepoGroup()->getLocalRepo()
222 ->newFile( $this->target );
224 return $this->targetFile;
232 $repo = $this->file->repo;
233 $status = $repo->newGood();
236 if ( !$status->isOK() ) {
240 if ( !$status->isOK() ) {
244 $unlockScope =
new ScopedCallback(
function () {
250 if ( !$checkStatus->isGood() ) {
251 $status->merge( $checkStatus );
254 $triplets = $checkStatus->value;
258 if ( !$statusDb->isGood() ) {
259 $statusDb->setOK(
false );
264 if ( !$repo->hasSha1Storage() ) {
270 $this->logger->debug(
271 'Moved files for {fileName}: {successCount} successes, {failCount} failures',
273 'fileName' => $this->file->getName(),
274 'successCount' => $statusMove->successCount,
275 'failCount' => $statusMove->failCount,
279 if ( !$statusMove->isGood() ) {
283 $this->logger->debug(
284 'Error in moving files: {error}',
285 [
'error' => $statusMove->getWikiText(
false,
false,
'en' ) ]
288 $statusMove->setOK(
false );
292 $status->merge( $statusMove );
298 $this->logger->debug(
299 'Renamed {fileName} in database: {successCount} successes, {failCount} failures',
301 'fileName' => $this->file->getName(),
302 'successCount' => $statusDb->successCount,
303 'failCount' => $statusDb->failCount,
311 if ( $this->db->trxLevel() ) {
312 ScopedCallback::cancel( $unlockScope );
313 $this->db->onTransactionResolution(
function () {
317 ScopedCallback::consume( $unlockScope );
320 $status->merge( $statusDb );
332 $repo = $this->file->repo;
333 $status = $repo->newGood();
339 ->where( [
'img_name' => $this->oldName ] )
341 ->caller( __METHOD__ )
345 $oldRowCount = $dbw->newSelectQueryBuilder()
347 ->where( [
'oi_name' => $this->oldName ] )
349 ->caller( __METHOD__ )
353 $status->successCount++;
355 $status->failCount++;
357 $status->successCount += $oldRowCount;
361 $status->failCount += max( 0, $this->oldCount - $oldRowCount );
362 if ( $status->failCount ) {
363 $status->error(
'imageinvalidfilename' );
376 $migrationStage = MediaWikiServices::getInstance()->getMainConfig()->get(
377 MainConfigNames::FileSchemaMigrationStage
382 ->set( [
'file_name' => $this->newName ] )
383 ->where( [
'file_id' => $this->file->getFileIdFromName() ] )
384 ->caller( __METHOD__ )->execute();
387 $dbw->newUpdateQueryBuilder()
389 ->set( [
'img_name' => $this->newName ] )
390 ->where( [
'img_name' => $this->oldName ] )
391 ->caller( __METHOD__ )->execute();
394 $dbw->newUpdateQueryBuilder()
395 ->update(
'oldimage' )
397 'oi_name' => $this->newName,
398 'oi_archive_name' =>
new RawSQLValue( $dbw->strreplace(
400 $dbw->addQuotes( $this->oldName ),
401 $dbw->addQuotes( $this->newName )
404 ->where( [
'oi_name' => $this->oldName ] )
405 ->caller( __METHOD__ )->execute();
413 $moves = array_merge( [ $this->cur ], $this->olds );
416 foreach ( $moves as $move ) {
418 $srcUrl = $this->file->repo->getVirtualUrl() .
'/public/' . rawurlencode( $move[0] );
419 $triplets[] = [ $srcUrl,
'public', $move[1] ];
421 $this->logger->debug(
422 'Generated move triplet for {fileName}: {srcUrl} :: public :: {move1}',
424 'fileName' => $this->file->getName(),
442 foreach ( $triplets as
$file ) {
446 $result = $this->file->repo->fileExistsBatch( $files );
447 if ( in_array(
null, $result,
true ) ) {
448 return Status::newFatal(
'backend-fail-internal',
449 $this->file->repo->getBackend()->getName() );
452 $filteredTriplets = [];
453 foreach ( $triplets as
$file ) {
454 if ( $result[
$file[0]] ) {
455 $filteredTriplets[] =
$file;
457 $this->logger->debug(
458 'File {file} does not exist',
459 [
'file' =>
$file[0] ]
464 return Status::newGood( $filteredTriplets );
475 foreach ( $triplets as $triplet ) {
477 $pairs[] = [ $triplet[1], $triplet[2] ];
480 $this->file->repo->cleanupBatch( $pairs );
491 foreach ( $triplets as $triplet ) {
492 $files[] = $triplet[0];
495 $this->file->repo->cleanupBatch( $files );
const SCHEMA_COMPAT_WRITE_NEW
Helper class for file movement.
getTargetFile()
Get the target file.
releaseLocks()
Release both file locks.
cleanupTarget( $triplets)
Cleanup a partially moved array of triplets by deleting the target files.
addOlds()
Add the old versions of the image to the batch.
doDBUpdates()
Do the database updates and return a new Status indicating how many rows where updated.
acquireSourceLock()
Acquire the source file lock, if it has not been acquired already.
getMoveTriplets()
Generate triplets for FileRepo::storeBatch().
execute()
Perform the move.
verifyDBUpdates()
Verify the database updates and return a new Status indicating how many rows would be updated.
removeNonexistentFiles( $triplets)
Removes non-existent files from move batch.
__construct(LocalFile $file, Title $target)
acquireTargetLock()
Acquire the target file lock, if it has not been acquired already.
addCurrent()
Add the current image to the batch.
cleanupSource( $triplets)
Cleanup a fully moved array of triplets by deleting the source files.
Local file in the wiki's own database.
A class containing constants representing the names of configuration variables.