86 $lang = RequestContext::getMain()->getLanguage();
88 $repo = $this->file->getRepo();
89 if ( !$this->all && !$this->ids ) {
91 return $repo->newGood();
94 $status = $this->file->acquireFileLock();
95 if ( !$status->isOK() ) {
99 $dbw = $this->file->repo->getPrimaryDB();
101 $ownTrx = !$dbw->trxLevel();
102 $funcName = __METHOD__;
103 $dbw->startAtomic( __METHOD__ );
105 $unlockScope =
new ScopedCallback(
function () use ( $dbw, $funcName ) {
106 $dbw->endAtomic( $funcName );
107 $this->file->releaseFileLock();
112 $status = $this->file->repo->newGood();
119 $queryBuilder = $dbw->newSelectQueryBuilder()
122 ->where( [
'img_name' => $this->file->getName() ] );
127 $queryBuilder->lockInShareMode();
129 $exists = (bool)$queryBuilder->caller( __METHOD__ )->fetchField();
131 $queryBuilder = $dbw->newSelectQueryBuilder()
134 ->where( [
'file_name' => $this->file->getName(),
'file_deleted' => 0 ] );
139 $queryBuilder->lockInShareMode();
141 $exists = (bool)$queryBuilder->caller( __METHOD__ )->fetchField();
147 $arQueryBuilder->where( [
'fa_name' => $this->file->getName() ] )
148 ->orderBy(
'fa_timestamp', SelectQueryBuilder::SORT_DESC );
151 $arQueryBuilder->andWhere( [
'fa_id' => $this->ids ] );
154 $result = $arQueryBuilder->caller( __METHOD__ )->fetchResultSet();
159 $insertCurrent =
false;
160 $insertFileRevisions = [];
165 foreach ( $result as $row ) {
166 $idsPresent[] = $row->fa_id;
168 if ( $row->fa_name != $this->file->getName() ) {
169 $status->error(
'undelete-filename-mismatch', $lang->timeanddate( $row->fa_timestamp ) );
170 $status->failCount++;
174 if ( $row->fa_storage_key ==
'' ) {
176 $status->error(
'undelete-bad-store-key', $lang->timeanddate( $row->fa_timestamp ) );
177 $status->failCount++;
181 $deletedRel = $repo->getDeletedHashPath( $row->fa_storage_key ) .
182 $row->fa_storage_key;
183 $deletedUrl = $repo->getVirtualUrl() .
'/deleted/' . $deletedRel;
185 if ( isset( $row->fa_sha1 ) ) {
186 $sha1 = $row->fa_sha1;
193 if ( strlen( $sha1 ) == 32 && $sha1[0] ==
'0' ) {
194 $sha1 = substr( $sha1, 1 );
197 if ( $row->fa_major_mime ===
null || $row->fa_major_mime ==
'unknown'
198 || $row->fa_minor_mime ===
null || $row->fa_minor_mime ==
'unknown'
199 || $row->fa_media_type ===
null || $row->fa_media_type ==
'UNKNOWN'
200 || $row->fa_metadata ===
null
204 $this->file->loadFromFile( $deletedUrl );
205 $mime = $this->file->getMimeType();
208 'minor_mime' => $minorMime,
209 'major_mime' => $majorMime,
210 'media_type' => $this->file->getMediaType(),
211 'metadata' => $this->file->getMetadataForDb( $dbw )
215 'minor_mime' => $row->fa_minor_mime,
216 'major_mime' => $row->fa_major_mime,
217 'media_type' => $row->fa_media_type,
218 'metadata' => $row->fa_metadata
221 $this->file->setProps( [
222 'media_type' => $mediaInfo[
'media_type'],
223 'major_mime' => $mediaInfo[
'major_mime'],
224 'minor_mime' => $mediaInfo[
'minor_mime'],
226 $comment = $commentStore->getComment(
'fa_description', $row );
228 $commentFieldsNew = $commentStore->insert( $dbw,
'fr_description', $comment );
230 'fr_size' => $row->fa_size,
231 'fr_width' => $row->fa_width,
232 'fr_height' => $row->fa_height,
233 'fr_metadata' => $mediaInfo[
'metadata'],
234 'fr_bits' => $row->fa_bits,
235 'fr_actor' => $row->fa_actor,
236 'fr_timestamp' => $row->fa_timestamp,
238 ] + $commentFieldsNew;
240 if ( $first && !$exists ) {
242 $destRel = $this->file->getRel();
243 $commentFields = $commentStore->insert( $dbw,
'img_description', $comment );
245 'img_name' => $row->fa_name,
246 'img_size' => $row->fa_size,
247 'img_width' => $row->fa_width,
248 'img_height' => $row->fa_height,
249 'img_metadata' => $mediaInfo[
'metadata'],
250 'img_bits' => $row->fa_bits,
251 'img_media_type' => $mediaInfo[
'media_type'],
252 'img_major_mime' => $mediaInfo[
'major_mime'],
253 'img_minor_mime' => $mediaInfo[
'minor_mime'],
254 'img_actor' => $row->fa_actor,
255 'img_timestamp' => $row->fa_timestamp,
260 if ( !$this->unsuppress && $row->fa_deleted ) {
261 $status->fatal(
'undeleterevdel' );
264 $fileRevisionRow[
'fr_archive_name'] =
'';
265 $fileRevisionRow[
'fr_deleted'] = 0;
267 $archiveName = $row->fa_archive_name;
269 if ( $archiveName ===
null ) {
273 $timestamp = (int)
wfTimestamp( TS::UNIX, $row->fa_deleted_timestamp );
276 $archiveName =
wfTimestamp( TS::MW, $timestamp ) .
'!' . $row->fa_name;
278 }
while ( isset( $archiveNames[$archiveName] ) );
281 $archiveNames[$archiveName] =
true;
282 $destRel = $this->file->getArchiveRel( $archiveName );
284 'oi_name' => $row->fa_name,
285 'oi_archive_name' => $archiveName,
286 'oi_size' => $row->fa_size,
287 'oi_width' => $row->fa_width,
288 'oi_height' => $row->fa_height,
289 'oi_bits' => $row->fa_bits,
290 'oi_actor' => $row->fa_actor,
291 'oi_timestamp' => $row->fa_timestamp,
292 'oi_metadata' => $mediaInfo[
'metadata'],
293 'oi_media_type' => $mediaInfo[
'media_type'],
294 'oi_major_mime' => $mediaInfo[
'major_mime'],
295 'oi_minor_mime' => $mediaInfo[
'minor_mime'],
296 'oi_deleted' => $this->unsuppress ? 0 : $row->fa_deleted,
298 ] + $commentStore->insert( $dbw,
'oi_description', $comment );
300 $fileRevisionRow[
'fr_archive_name'] = $archiveName;
301 $fileRevisionRow[
'fr_deleted'] = $this->unsuppress ? 0 : $row->fa_deleted;
303 $insertFileRevisions[] = $fileRevisionRow;
305 $deleteIds[] = $row->fa_id;
309 $status->successCount++;
311 $storeBatch[] = [ $deletedUrl,
'public', $destRel ];
312 $this->cleanupBatch[] = $row->fa_storage_key;
321 $missingIds = array_diff( $this->ids, $idsPresent );
323 foreach ( $missingIds as $id ) {
324 $status->error(
'undelete-missing-filearchive', $id );
327 if ( !$repo->hasSha1Storage() ) {
330 if ( !$checkStatus->isGood() ) {
331 $status->merge( $checkStatus );
334 $storeBatch = $checkStatus->value;
339 $status->merge( $storeStatus );
341 if ( !$status->isGood() ) {
345 $status->setOK(
false );
359 if ( $insertCurrent ) {
361 $dbw->newInsertQueryBuilder()
362 ->insertInto(
'image' )
363 ->row( $insertCurrent )
364 ->caller( __METHOD__ )->execute();
367 $dbw->newUpdateQueryBuilder()
369 ->set( [
'file_deleted' => 0 ] )
370 ->where( [
'file_id' => $this->file->acquireFileIdFromName() ] )
371 ->caller( __METHOD__ )->execute();
376 $dbw->newInsertQueryBuilder()
377 ->insertInto(
'oldimage' )
378 ->rows( $insertBatch )
379 ->caller( __METHOD__ )->execute();
383 $dbw->newDeleteQueryBuilder()
384 ->deleteFrom(
'filearchive' )
385 ->where( [
'fa_id' => $deleteIds ] )
386 ->caller( __METHOD__ )->execute();
391 $insertFileRevisions = array_reverse( $insertFileRevisions );
393 foreach ( $insertFileRevisions as &$row ) {
394 $row[
'fr_file'] = $this->file->getFileIdFromName();
396 $dbw->newInsertQueryBuilder()
397 ->insertInto(
'filerevision' )
398 ->rows( $insertFileRevisions )
399 ->caller( __METHOD__ )->execute();
400 $latestId = $dbw->newSelectQueryBuilder()
402 ->from(
'filerevision' )
403 ->where( [
'fr_file' => $this->file->getFileIdFromName() ] )
404 ->orderBy(
'fr_timestamp',
'DESC' )
405 ->caller( __METHOD__ )->fetchField();
406 $dbw->newUpdateQueryBuilder()
408 ->set( [
'file_latest' => $latestId ] )
409 ->where( [
'file_id' => $this->file->getFileIdFromName() ] )
410 ->caller( __METHOD__ )->execute();
415 if ( $status->successCount > 0 || !$storeBatch || $repo->hasSha1Storage() ) {
417 wfDebug( __METHOD__ .
" restored {$status->successCount} items, creating a new current" );
419 DeferredUpdates::addUpdate( SiteStatsUpdate::factory( [
'images' => 1 ] ) );
421 $this->file->purgeEverything();
423 wfDebug( __METHOD__ .
" restored {$status->successCount} as archived versions" );
424 $this->file->purgeDescription();
428 ScopedCallback::consume( $unlockScope );