369 $row =
$dbr->selectRow(
'archive',
370 [
'ar_rev_id',
'ar_timestamp' ],
371 [
'ar_namespace' => $this->title->getNamespace(),
372 'ar_title' => $this->title->getDBkey(),
374 $dbr->addQuotes(
$dbr->timestamp( $timestamp ) ) ],
377 'ORDER BY' =>
'ar_timestamp DESC',
379 $prevDeleted = $row ?
wfTimestamp( TS_MW, $row->ar_timestamp ) :
false;
380 $prevDeletedId = $row ? intval( $row->ar_rev_id ) :
null;
382 $row =
$dbr->selectRow( [
'page',
'revision' ],
383 [
'rev_id',
'rev_timestamp' ],
385 'page_namespace' => $this->title->getNamespace(),
386 'page_title' => $this->title->getDBkey(),
387 'page_id = rev_page',
389 $dbr->addQuotes(
$dbr->timestamp( $timestamp ) ) ],
392 'ORDER BY' =>
'rev_timestamp DESC',
394 $prevLive = $row ?
wfTimestamp( TS_MW, $row->rev_timestamp ) :
false;
395 $prevLiveId = $row ? intval( $row->rev_id ) :
null;
397 if ( $prevLive && $prevLive > $prevDeleted ) {
400 } elseif ( $prevDeleted ) {
519 $restoreAll = empty( $timestamps ) && empty( $fileVersions );
521 $restoreText = $restoreAll || !empty( $timestamps );
522 $restoreFiles = $restoreAll || !empty( $fileVersions );
524 if ( $restoreFiles && $this->title->getNamespace() ==
NS_FILE ) {
526 $img = MediaWikiServices::getInstance()->getRepoGroup()->getLocalRepo()
527 ->newFile( $this->title );
528 $img->load( File::READ_LATEST );
529 $this->fileStatus = $img->restore( $fileVersions, $unsuppress );
530 if ( !$this->fileStatus->isOK() ) {
533 $filesRestored = $this->fileStatus->successCount;
538 if ( $restoreText ) {
539 $this->revisionStatus = $this->
undeleteRevisions( $timestamps, $unsuppress, $comment );
540 if ( !$this->revisionStatus->isOK() ) {
544 $textRestored = $this->revisionStatus->getValue();
551 if ( !$textRestored && !$filesRestored ) {
552 wfDebug(
"Undelete: nothing undeleted..." );
558 $logEntry->setPerformer( $user );
559 $logEntry->setTarget( $this->title );
560 $logEntry->setComment( $comment );
561 $logEntry->addTags( $tags );
562 $logEntry->setParameters( [
564 'revisions' => $textRestored,
565 'files' => $filesRestored,
569 $this->getHookRunner()->onArticleUndeleteLogEntry( $this, $logEntry, $user );
571 $logid = $logEntry->insert();
572 $logEntry->publish( $logid );
574 return [ $textRestored, $filesRestored, $comment ];
594 $dbw->startAtomic( __METHOD__ );
596 $restoreAll = empty( $timestamps );
598 # Does this page already exist? We'll have to update it...
599 $article = WikiPage::factory( $this->title );
600 # Load latest data for the current page (T33179)
601 $article->loadPageData(
'fromdbmaster' );
602 $oldcountable = $article->isCountable();
604 $page = $dbw->selectRow(
'page',
605 [
'page_id',
'page_latest' ],
606 [
'page_namespace' => $this->title->getNamespace(),
607 'page_title' => $this->title->getDBkey() ],
614 # Page already exists. Import the history, and if necessary
615 # we'll update the latest revision field in the record.
617 # Get the time span of this page
618 $previousTimestamp = $dbw->selectField(
'revision',
'rev_timestamp',
619 [
'rev_id' => $page->page_latest ],
622 if ( $previousTimestamp ===
false ) {
623 wfDebug( __METHOD__ .
": existing page refers to a page_latest that does not exist" );
625 $status = Status::newGood( 0 );
626 $status->warning(
'undeleterevision-missing' );
627 $dbw->endAtomic( __METHOD__ );
632 # Have to create a new article...
634 $previousTimestamp = 0;
638 'ar_namespace' => $this->title->getNamespace(),
639 'ar_title' => $this->title->getDBkey(),
641 if ( !$restoreAll ) {
642 $oldWhere[
'ar_timestamp'] = array_map( [ &$dbw,
'timestamp' ], $timestamps );
646 $queryInfo = $revisionStore->getArchiveQueryInfo();
647 $queryInfo[
'tables'][] =
'revision';
648 $queryInfo[
'fields'][] =
'rev_id';
649 $queryInfo[
'joins'][
'revision'] = [
'LEFT JOIN',
'ar_rev_id=rev_id' ];
654 $result = $dbw->select(
655 $queryInfo[
'tables'],
656 $queryInfo[
'fields'],
660 [
'ORDER BY' =>
'ar_timestamp' ],
664 $rev_count = $result->numRows();
666 wfDebug( __METHOD__ .
": no revisions to restore" );
668 $status = Status::newGood( 0 );
669 $status->warning(
"undelete-no-results" );
670 $dbw->endAtomic( __METHOD__ );
677 $restoreFailedArIds = [];
684 $allowedRevIdToArIdMap = [];
686 $latestRestorableRow =
null;
688 foreach ( $result as $row ) {
689 if ( $row->ar_rev_id ) {
691 if ( $row->ar_rev_id === $row->rev_id ) {
692 $restoreFailedArIds[] = $row->ar_id;
693 $allowedRevIdToArIdMap[$row->ar_rev_id] = -1;
698 if ( isset( $allowedRevIdToArIdMap[$row->ar_rev_id] ) ) {
699 $restoreFailedArIds[] = $row->ar_id;
701 $allowedRevIdToArIdMap[$row->ar_rev_id] = $row->ar_id;
702 $latestRestorableRow = $row;
708 $latestRestorableRow = $row;
715 if ( $latestRestorableRow !==
null ) {
716 $oldPageId = (int)$latestRestorableRow->ar_page_id;
721 $revision = $revisionStore->newRevisionFromArchiveRow(
722 $latestRestorableRow,
729 $user =
User::newFromName( $revision->getUser( RevisionRecord::RAW )->getName(),
false );
731 foreach ( $revision->getSlotRoles() as $role ) {
732 $content = $revision->getContent( $role, RevisionRecord::RAW );
735 $status =
$content->prepareSave( $article, 0, -1, $user );
736 if ( !$status->isOK() ) {
737 $dbw->endAtomic( __METHOD__ );
750 if ( $latestRestorableRow ===
null ) {
751 $failedRevisionCount = $rev_count;
756 && ( $latestRestorableRow->ar_deleted & RevisionRecord::DELETED_TEXT )
758 $dbw->endAtomic( __METHOD__ );
760 return Status::newFatal(
"undeleterevdel" );
763 $newid = $article->insertOn( $dbw, $latestRestorableRow->ar_page_id );
764 if ( $newid ===
false ) {
766 $newid = $article->insertOn( $dbw );
771 if ( $latestRestorableRow->ar_timestamp > $previousTimestamp ) {
774 && ( $latestRestorableRow->ar_deleted & RevisionRecord::DELETED_TEXT )
776 $dbw->endAtomic( __METHOD__ );
778 return Status::newFatal(
"undeleterevdel" );
783 $pageId = $article->getId();
786 foreach ( $result as $row ) {
788 if ( $row->ar_rev_id && $allowedRevIdToArIdMap[$row->ar_rev_id] !== $row->ar_id ) {
793 $revision = $revisionStore->newRevisionFromArchiveRow(
798 'page_id' => $pageId,
799 'deleted' => $unsuppress ? 0 : $row->ar_deleted
804 $revisionStore->insertRevisionOn( $revision, $dbw );
808 $hookRunner = $this->getHookRunner();
809 $hookRunner->onRevisionUndeleted( $revision, $row->ar_page_id );
812 if ( $this->getHookContainer()->isRegistered(
'ArticleRevisionUndeleted' ) ) {
814 $legacyRevision =
new Revision( $revision );
815 $hookRunner->onArticleRevisionUndeleted(
821 $restoredPages[$row->ar_page_id] =
true;
826 $toDeleteConds = $oldWhere;
827 $failedRevisionCount = count( $restoreFailedArIds );
828 if ( $failedRevisionCount > 0 ) {
829 $toDeleteConds[] =
'ar_id NOT IN ( ' . $dbw->makeList( $restoreFailedArIds ) .
' )';
832 $dbw->delete(
'archive',
837 $status = Status::newGood( $restored );
839 if ( $failedRevisionCount > 0 ) {
841 wfMessage(
'undeleterevision-duplicate-revid', $failedRevisionCount ) );
846 $created = (bool)$newid;
848 $latestRevId = $article->getLatest();
849 if ( $latestRevId ) {
852 $latestRevTimestamp = (int)$revisionStore->getTimestampFromId(
854 RevisionStore::READ_LATEST
857 $latestRevTimestamp = 0;
860 if ( $revision->getTimestamp() > $latestRevTimestamp ) {
863 $wasnew = $article->updateRevisionOn(
872 if ( $created || $wasnew ) {
875 $article->doEditUpdates(
877 User::newFromName( $revision->getUser( RevisionRecord::RAW )->getName(), false ),
879 'created' => $created,
880 'oldcountable' => $oldcountable,
886 $this->getHookRunner()->onArticleUndelete(
887 $this->title, $created, $comment, $oldPageId, $restoredPages );
889 if ( $this->title->getNamespace() ==
NS_FILE ) {
893 [
'causeAction' =>
'file-restore' ]
895 JobQueueGroup::singleton()->lazyPush(
$job );
899 $dbw->endAtomic( __METHOD__ );