97 $repo = $this->file->getRepo();
98 if ( !$this->all && !$this->ids ) {
100 return $repo->newGood();
103 $status = $this->file->acquireFileLock();
104 if ( !$status->isOK() ) {
108 $dbw = $this->file->repo->getPrimaryDB();
110 $ownTrx = !$dbw->trxLevel();
111 $funcName = __METHOD__;
112 $dbw->startAtomic( __METHOD__ );
114 $unlockScope =
new ScopedCallback(
function () use ( $dbw, $funcName ) {
115 $dbw->endAtomic( $funcName );
116 $this->file->releaseFileLock();
119 $commentStore = MediaWikiServices::getInstance()->getCommentStore();
121 $status = $this->file->repo->newGood();
123 $queryBuilder = $dbw->newSelectQueryBuilder()
126 ->where( [
'img_name' => $this->file->getName() ] );
131 $queryBuilder->lockInShareMode();
133 $exists = (bool)$queryBuilder->caller( __METHOD__ )->fetchField();
137 $arQueryBuilder = FileSelectQueryBuilder::newForArchivedFile( $dbw );
138 $arQueryBuilder->where( [
'fa_name' => $this->file->getName() ] )
139 ->orderBy(
'fa_timestamp', SelectQueryBuilder::SORT_DESC );
142 $arQueryBuilder->andWhere( [
'fa_id' => $this->ids ] );
145 $result = $arQueryBuilder->caller( __METHOD__ )->fetchResultSet();
150 $insertCurrent =
false;
155 foreach ( $result as $row ) {
156 $idsPresent[] = $row->fa_id;
158 if ( $row->fa_name != $this->file->getName() ) {
159 $status->error(
'undelete-filename-mismatch',
$wgLang->timeanddate( $row->fa_timestamp ) );
160 $status->failCount++;
164 if ( $row->fa_storage_key ==
'' ) {
166 $status->error(
'undelete-bad-store-key',
$wgLang->timeanddate( $row->fa_timestamp ) );
167 $status->failCount++;
171 $deletedRel = $repo->getDeletedHashPath( $row->fa_storage_key ) .
172 $row->fa_storage_key;
173 $deletedUrl = $repo->getVirtualUrl() .
'/deleted/' . $deletedRel;
175 if ( isset( $row->fa_sha1 ) ) {
176 $sha1 = $row->fa_sha1;
179 $sha1 = LocalRepo::getHashFromKey( $row->fa_storage_key );
183 if ( strlen( $sha1 ) == 32 && $sha1[0] ==
'0' ) {
184 $sha1 = substr( $sha1, 1 );
187 if ( $row->fa_major_mime ===
null || $row->fa_major_mime ==
'unknown'
188 || $row->fa_minor_mime ===
null || $row->fa_minor_mime ==
'unknown'
189 || $row->fa_media_type ===
null || $row->fa_media_type ==
'UNKNOWN'
190 || $row->fa_metadata ===
null
194 $this->file->loadFromFile( $deletedUrl );
195 $mime = $this->file->getMimeType();
196 [ $majorMime, $minorMime ] = File::splitMime( $mime );
198 'minor_mime' => $minorMime,
199 'major_mime' => $majorMime,
200 'media_type' => $this->file->getMediaType(),
201 'metadata' => $this->file->getMetadataForDb( $dbw )
205 'minor_mime' => $row->fa_minor_mime,
206 'major_mime' => $row->fa_major_mime,
207 'media_type' => $row->fa_media_type,
208 'metadata' => $row->fa_metadata
212 $comment = $commentStore->getComment(
'fa_description', $row );
213 if ( $first && !$exists ) {
215 $destRel = $this->file->getRel();
216 $commentFields = $commentStore->insert( $dbw,
'img_description', $comment );
218 'img_name' => $row->fa_name,
219 'img_size' => $row->fa_size,
220 'img_width' => $row->fa_width,
221 'img_height' => $row->fa_height,
222 'img_metadata' => $mediaInfo[
'metadata'],
223 'img_bits' => $row->fa_bits,
224 'img_media_type' => $mediaInfo[
'media_type'],
225 'img_major_mime' => $mediaInfo[
'major_mime'],
226 'img_minor_mime' => $mediaInfo[
'minor_mime'],
227 'img_actor' => $row->fa_actor,
228 'img_timestamp' => $row->fa_timestamp,
233 if ( !$this->unsuppress && $row->fa_deleted ) {
234 $status->fatal(
'undeleterevdel' );
238 $archiveName = $row->fa_archive_name;
240 if ( $archiveName ===
null ) {
244 $timestamp = (int)
wfTimestamp( TS_UNIX, $row->fa_deleted_timestamp );
247 $archiveName =
wfTimestamp( TS_MW, $timestamp ) .
'!' . $row->fa_name;
249 }
while ( isset( $archiveNames[$archiveName] ) );
252 $archiveNames[$archiveName] =
true;
253 $destRel = $this->file->getArchiveRel( $archiveName );
255 'oi_name' => $row->fa_name,
256 'oi_archive_name' => $archiveName,
257 'oi_size' => $row->fa_size,
258 'oi_width' => $row->fa_width,
259 'oi_height' => $row->fa_height,
260 'oi_bits' => $row->fa_bits,
261 'oi_actor' => $row->fa_actor,
262 'oi_timestamp' => $row->fa_timestamp,
263 'oi_metadata' => $mediaInfo[
'metadata'],
264 'oi_media_type' => $mediaInfo[
'media_type'],
265 'oi_major_mime' => $mediaInfo[
'major_mime'],
266 'oi_minor_mime' => $mediaInfo[
'minor_mime'],
267 'oi_deleted' => $this->unsuppress ? 0 : $row->fa_deleted,
269 ] + $commentStore->insert( $dbw,
'oi_description', $comment );
272 $deleteIds[] = $row->fa_id;
274 if ( !$this->unsuppress && $row->fa_deleted & File::DELETED_FILE ) {
276 $status->successCount++;
278 $storeBatch[] = [ $deletedUrl,
'public', $destRel ];
279 $this->cleanupBatch[] = $row->fa_storage_key;
288 $missingIds = array_diff( $this->ids, $idsPresent );
290 foreach ( $missingIds as $id ) {
291 $status->error(
'undelete-missing-filearchive', $id );
294 if ( !$repo->hasSha1Storage() ) {
297 if ( !$checkStatus->isGood() ) {
298 $status->merge( $checkStatus );
301 $storeBatch = $checkStatus->value;
306 $status->merge( $storeStatus );
308 if ( !$status->isGood() ) {
312 $status->setOK(
false );
323 if ( $insertCurrent ) {
324 $dbw->newInsertQueryBuilder()
325 ->insertInto(
'image' )
326 ->row( $insertCurrent )
327 ->caller( __METHOD__ )->execute();
330 if ( $insertBatch ) {
331 $dbw->newInsertQueryBuilder()
332 ->insertInto(
'oldimage' )
333 ->rows( $insertBatch )
334 ->caller( __METHOD__ )->execute();
338 $dbw->newDeleteQueryBuilder()
339 ->deleteFrom(
'filearchive' )
340 ->where( [
'fa_id' => $deleteIds ] )
341 ->caller( __METHOD__ )->execute();
345 if ( $status->successCount > 0 || !$storeBatch || $repo->hasSha1Storage() ) {
347 wfDebug( __METHOD__ .
" restored {$status->successCount} items, creating a new current" );
349 DeferredUpdates::addUpdate( SiteStatsUpdate::factory( [
'images' => 1 ] ) );
351 $this->file->purgeEverything();
353 wfDebug( __METHOD__ .
" restored {$status->successCount} as archived versions" );
354 $this->file->purgeDescription();
358 ScopedCallback::consume( $unlockScope );