MediaWiki  master
PageArchive.php
Go to the documentation of this file.
1 <?php
21 use MediaWiki\HookContainer\ProtectedHookAccessorTrait;
25 use Wikimedia\Assert\Assert;
28 
32 class PageArchive {
33  use ProtectedHookAccessorTrait;
34 
36  protected $title;
37 
39  protected $fileStatus;
40 
42  protected $revisionStatus;
43 
45  protected $config;
46 
47  public function __construct( $title, Config $config = null ) {
48  if ( $title === null ) {
49  throw new MWException( __METHOD__ . ' given a null title.' );
50  }
51  $this->title = $title;
52  if ( $config === null ) {
53  wfDebug( __METHOD__ . ' did not have a Config object passed to it' );
54  $config = MediaWikiServices::getInstance()->getMainConfig();
55  }
56  $this->config = $config;
57  }
58 
62  private function getRevisionStore() {
63  // TODO: Refactor: delete()/undelete() should live in a PageStore service;
64  // Methods in PageArchive and RevisionStore that deal with archive revisions
65  // should move into an ArchiveStore service (but could still be implemented
66  // together with RevisionStore).
67  return MediaWikiServices::getInstance()->getRevisionStore();
68  }
69 
70  public function doesWrites() {
71  return true;
72  }
73 
82  public static function listPagesBySearch( $term ) {
83  $title = Title::newFromText( $term );
84  if ( $title ) {
85  $ns = $title->getNamespace();
86  $termMain = $title->getText();
87  $termDb = $title->getDBkey();
88  } else {
89  // Prolly won't work too good
90  // @todo handle bare namespace names cleanly?
91  $ns = 0;
92  $termMain = $termDb = $term;
93  }
94 
95  // Try search engine first
96  $engine = MediaWikiServices::getInstance()->newSearchEngine();
97  $engine->setLimitOffset( 100 );
98  $engine->setNamespaces( [ $ns ] );
99  $results = $engine->searchArchiveTitle( $termMain );
100  if ( !$results->isOK() ) {
101  $results = [];
102  } else {
103  $results = $results->getValue();
104  }
105 
106  if ( !$results ) {
107  // Fall back to regular prefix search
108  return self::listPagesByPrefix( $term );
109  }
110 
111  $dbr = wfGetDB( DB_REPLICA );
112  $condTitles = array_unique( array_map( function ( Title $t ) {
113  return $t->getDBkey();
114  }, $results ) );
115  $conds = [
116  'ar_namespace' => $ns,
117  $dbr->makeList( [ 'ar_title' => $condTitles ], LIST_OR ) . " OR ar_title " .
118  $dbr->buildLike( $termDb, $dbr->anyString() )
119  ];
120 
121  return self::listPages( $dbr, $conds );
122  }
123 
132  public static function listPagesByPrefix( $prefix ) {
133  $dbr = wfGetDB( DB_REPLICA );
134 
135  $title = Title::newFromText( $prefix );
136  if ( $title ) {
137  $ns = $title->getNamespace();
138  $prefix = $title->getDBkey();
139  } else {
140  // Prolly won't work too good
141  // @todo handle bare namespace names cleanly?
142  $ns = 0;
143  }
144 
145  $conds = [
146  'ar_namespace' => $ns,
147  'ar_title' . $dbr->buildLike( $prefix, $dbr->anyString() ),
148  ];
149 
150  return self::listPages( $dbr, $conds );
151  }
152 
158  protected static function listPages( $dbr, $condition ) {
159  return $dbr->select(
160  [ 'archive' ],
161  [
162  'ar_namespace',
163  'ar_title',
164  'count' => 'COUNT(*)'
165  ],
166  $condition,
167  __METHOD__,
168  [
169  'GROUP BY' => [ 'ar_namespace', 'ar_title' ],
170  'ORDER BY' => [ 'ar_namespace', 'ar_title' ],
171  'LIMIT' => 100,
172  ]
173  );
174  }
175 
182  public function listRevisions() {
183  $revisionStore = $this->getRevisionStore();
184  $queryInfo = $revisionStore->getArchiveQueryInfo();
185 
186  $conds = [
187  'ar_namespace' => $this->title->getNamespace(),
188  'ar_title' => $this->title->getDBkey(),
189  ];
190 
191  // NOTE: ordering by ar_timestamp and ar_id, to remove ambiguity.
192  // XXX: Ideally, we would be ordering by ar_timestamp and ar_rev_id, but since we
193  // don't have an index on ar_rev_id, that causes a file sort.
194  $options = [ 'ORDER BY' => [ 'ar_timestamp DESC', 'ar_id DESC' ] ];
195 
197  $queryInfo['tables'],
198  $queryInfo['fields'],
199  $conds,
200  $queryInfo['joins'],
201  $options,
202  ''
203  );
204 
205  $dbr = wfGetDB( DB_REPLICA );
206  return $dbr->select(
207  $queryInfo['tables'],
208  $queryInfo['fields'],
209  $conds,
210  __METHOD__,
211  $options,
212  $queryInfo['joins']
213  );
214  }
215 
224  public function listFiles() {
225  if ( $this->title->getNamespace() !== NS_FILE ) {
226  return null;
227  }
228 
229  $dbr = wfGetDB( DB_REPLICA );
230  $fileQuery = ArchivedFile::getQueryInfo();
231  return $dbr->select(
232  $fileQuery['tables'],
233  $fileQuery['fields'],
234  [ 'fa_name' => $this->title->getDBkey() ],
235  __METHOD__,
236  [ 'ORDER BY' => 'fa_timestamp DESC' ],
237  $fileQuery['joins']
238  );
239  }
240 
250  public function getRevision( $timestamp ) {
251  wfDeprecated( __METHOD__, '1.32' );
252  $revRecord = $this->getRevisionRecordByTimestamp( $timestamp );
253  return $revRecord ? new Revision( $revRecord ) : null;
254  }
255 
264  public function getRevisionRecordByTimestamp( $timestamp ) {
265  $dbr = wfGetDB( DB_REPLICA );
266  $rec = $this->getRevisionByConditions(
267  [ 'ar_timestamp' => $dbr->timestamp( $timestamp ) ]
268  );
269  return $rec;
270  }
271 
280  public function getArchivedRevision( $revId ) {
281  wfDeprecated( __METHOD__, '1.35' );
282 
283  // Protect against code switching from getRevision() passing in a timestamp.
284  Assert::parameterType( 'integer', $revId, '$revId' );
285 
286  $revRecord = $this->getArchivedRevisionRecord( $revId );
287  return $revRecord ? new Revision( $revRecord ) : null;
288  }
289 
298  public function getArchivedRevisionRecord( int $revId ) {
299  return $this->getRevisionByConditions( [ 'ar_rev_id' => $revId ] );
300  }
301 
308  private function getRevisionByConditions( array $conditions, array $options = [] ) {
309  $dbr = wfGetDB( DB_REPLICA );
310  $arQuery = $this->getRevisionStore()->getArchiveQueryInfo();
311 
312  $conditions += [
313  'ar_namespace' => $this->title->getNamespace(),
314  'ar_title' => $this->title->getDBkey(),
315  ];
316 
317  $row = $dbr->selectRow(
318  $arQuery['tables'],
319  $arQuery['fields'],
320  $conditions,
321  __METHOD__,
322  $options,
323  $arQuery['joins']
324  );
325 
326  if ( $row ) {
327  return $this->getRevisionStore()->newRevisionFromArchiveRow( $row, 0, $this->title );
328  }
329 
330  return null;
331  }
332 
345  public function getPreviousRevision( $timestamp ) {
346  wfDeprecated( __METHOD__, '1.35' );
347 
348  $revRecord = $this->getPreviousRevisionRecord( $timestamp );
349  $rev = $revRecord ? new Revision( $revRecord ) : null;
350  return $rev;
351  }
352 
365  public function getPreviousRevisionRecord( string $timestamp ) {
366  $dbr = wfGetDB( DB_REPLICA );
367 
368  // Check the previous deleted revision...
369  $row = $dbr->selectRow( 'archive',
370  [ 'ar_rev_id', 'ar_timestamp' ],
371  [ 'ar_namespace' => $this->title->getNamespace(),
372  'ar_title' => $this->title->getDBkey(),
373  'ar_timestamp < ' .
374  $dbr->addQuotes( $dbr->timestamp( $timestamp ) ) ],
375  __METHOD__,
376  [
377  'ORDER BY' => 'ar_timestamp DESC',
378  'LIMIT' => 1 ] );
379  $prevDeleted = $row ? wfTimestamp( TS_MW, $row->ar_timestamp ) : false;
380  $prevDeletedId = $row ? intval( $row->ar_rev_id ) : null;
381 
382  $row = $dbr->selectRow( [ 'page', 'revision' ],
383  [ 'rev_id', 'rev_timestamp' ],
384  [
385  'page_namespace' => $this->title->getNamespace(),
386  'page_title' => $this->title->getDBkey(),
387  'page_id = rev_page',
388  'rev_timestamp < ' .
389  $dbr->addQuotes( $dbr->timestamp( $timestamp ) ) ],
390  __METHOD__,
391  [
392  'ORDER BY' => 'rev_timestamp DESC',
393  'LIMIT' => 1 ] );
394  $prevLive = $row ? wfTimestamp( TS_MW, $row->rev_timestamp ) : false;
395  $prevLiveId = $row ? intval( $row->rev_id ) : null;
396 
397  if ( $prevLive && $prevLive > $prevDeleted ) {
398  // Most prior revision was live
399  $rec = $this->getRevisionStore()->getRevisionById( $prevLiveId );
400  } elseif ( $prevDeleted ) {
401  // Most prior revision was deleted
402  $rec = $this->getArchivedRevisionRecord( $prevDeletedId );
403  } else {
404  $rec = null;
405  }
406 
407  return $rec;
408  }
409 
415  public function getLastRevisionId() {
416  $dbr = wfGetDB( DB_REPLICA );
417  $revId = $dbr->selectField(
418  'archive',
419  'ar_rev_id',
420  [ 'ar_namespace' => $this->title->getNamespace(),
421  'ar_title' => $this->title->getDBkey() ],
422  __METHOD__,
423  [ 'ORDER BY' => [ 'ar_timestamp DESC', 'ar_id DESC' ] ]
424  );
425 
426  return $revId ? intval( $revId ) : false;
427  }
428 
435  public function isDeleted() {
436  $dbr = wfGetDB( DB_REPLICA );
437  $row = $dbr->selectRow(
438  [ 'archive' ],
439  '1', // We don't care about the value. Allow the database to optimize.
440  [ 'ar_namespace' => $this->title->getNamespace(),
441  'ar_title' => $this->title->getDBkey() ],
442  __METHOD__
443  );
444 
445  return (bool)$row;
446  }
447 
469  public function undelete( $timestamps, $comment = '', $fileVersions = [],
470  $unsuppress = false, User $user = null, $tags = null
471  ) {
472  wfDeprecated( __METHOD__, '1.35' );
473  if ( $user === null ) {
474  global $wgUser;
475  $user = $wgUser;
476  }
477  $result = $this->undeleteAsUser(
478  $timestamps,
479  $user,
480  $comment,
481  $fileVersions,
482  $unsuppress,
483  $tags
484  );
485  return $result;
486  }
487 
509  public function undeleteAsUser(
510  $timestamps,
511  User $user,
512  $comment = '',
513  $fileVersions = [],
514  $unsuppress = false,
515  $tags = null
516  ) {
517  // If both the set of text revisions and file revisions are empty,
518  // restore everything. Otherwise, just restore the requested items.
519  $restoreAll = empty( $timestamps ) && empty( $fileVersions );
520 
521  $restoreText = $restoreAll || !empty( $timestamps );
522  $restoreFiles = $restoreAll || !empty( $fileVersions );
523 
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() ) {
531  return false;
532  }
533  $filesRestored = $this->fileStatus->successCount;
534  } else {
535  $filesRestored = 0;
536  }
537 
538  if ( $restoreText ) {
539  $this->revisionStatus = $this->undeleteRevisions( $timestamps, $unsuppress, $comment );
540  if ( !$this->revisionStatus->isOK() ) {
541  return false;
542  }
543 
544  $textRestored = $this->revisionStatus->getValue();
545  } else {
546  $textRestored = 0;
547  }
548 
549  // Touch the log!
550 
551  if ( !$textRestored && !$filesRestored ) {
552  wfDebug( "Undelete: nothing undeleted..." );
553 
554  return false;
555  }
556 
557  $logEntry = new ManualLogEntry( 'delete', 'restore' );
558  $logEntry->setPerformer( $user );
559  $logEntry->setTarget( $this->title );
560  $logEntry->setComment( $comment );
561  $logEntry->addTags( $tags );
562  $logEntry->setParameters( [
563  ':assoc:count' => [
564  'revisions' => $textRestored,
565  'files' => $filesRestored,
566  ],
567  ] );
568 
569  $this->getHookRunner()->onArticleUndeleteLogEntry( $this, $logEntry, $user );
570 
571  $logid = $logEntry->insert();
572  $logEntry->publish( $logid );
573 
574  return [ $textRestored, $filesRestored, $comment ];
575  }
576 
588  private function undeleteRevisions( $timestamps, $unsuppress = false, $comment = '' ) {
589  if ( wfReadOnly() ) {
590  throw new ReadOnlyError();
591  }
592 
593  $dbw = wfGetDB( DB_MASTER );
594  $dbw->startAtomic( __METHOD__ );
595 
596  $restoreAll = empty( $timestamps );
597 
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();
603 
604  $page = $dbw->selectRow( 'page',
605  [ 'page_id', 'page_latest' ],
606  [ 'page_namespace' => $this->title->getNamespace(),
607  'page_title' => $this->title->getDBkey() ],
608  __METHOD__,
609  [ 'FOR UPDATE' ] // lock page
610  );
611 
612  if ( $page ) {
613  $makepage = false;
614  # Page already exists. Import the history, and if necessary
615  # we'll update the latest revision field in the record.
616 
617  # Get the time span of this page
618  $previousTimestamp = $dbw->selectField( 'revision', 'rev_timestamp',
619  [ 'rev_id' => $page->page_latest ],
620  __METHOD__ );
621 
622  if ( $previousTimestamp === false ) {
623  wfDebug( __METHOD__ . ": existing page refers to a page_latest that does not exist" );
624 
625  $status = Status::newGood( 0 );
626  $status->warning( 'undeleterevision-missing' );
627  $dbw->endAtomic( __METHOD__ );
628 
629  return $status;
630  }
631  } else {
632  # Have to create a new article...
633  $makepage = true;
634  $previousTimestamp = 0;
635  }
636 
637  $oldWhere = [
638  'ar_namespace' => $this->title->getNamespace(),
639  'ar_title' => $this->title->getDBkey(),
640  ];
641  if ( !$restoreAll ) {
642  $oldWhere['ar_timestamp'] = array_map( [ &$dbw, 'timestamp' ], $timestamps );
643  }
644 
645  $revisionStore = $this->getRevisionStore();
646  $queryInfo = $revisionStore->getArchiveQueryInfo();
647  $queryInfo['tables'][] = 'revision';
648  $queryInfo['fields'][] = 'rev_id';
649  $queryInfo['joins']['revision'] = [ 'LEFT JOIN', 'ar_rev_id=rev_id' ];
650 
654  $result = $dbw->select(
655  $queryInfo['tables'],
656  $queryInfo['fields'],
657  $oldWhere,
658  __METHOD__,
659  /* options */
660  [ 'ORDER BY' => 'ar_timestamp' ],
661  $queryInfo['joins']
662  );
663 
664  $rev_count = $result->numRows();
665  if ( !$rev_count ) {
666  wfDebug( __METHOD__ . ": no revisions to restore" );
667 
668  $status = Status::newGood( 0 );
669  $status->warning( "undelete-no-results" );
670  $dbw->endAtomic( __METHOD__ );
671 
672  return $status;
673  }
674 
675  // We use ar_id because there can be duplicate ar_rev_id even for the same
676  // page. In this case, we may be able to restore the first one.
677  $restoreFailedArIds = [];
678 
679  // Map rev_id to the ar_id that is allowed to use it. When checking later,
680  // if it doesn't match, the current ar_id can not be restored.
681 
682  // Value can be an ar_id or -1 (-1 means no ar_id can use it, since the
683  // rev_id is taken before we even start the restore).
684  $allowedRevIdToArIdMap = [];
685 
686  $latestRestorableRow = null;
687 
688  foreach ( $result as $row ) {
689  if ( $row->ar_rev_id ) {
690  // rev_id is taken even before we start restoring.
691  if ( $row->ar_rev_id === $row->rev_id ) {
692  $restoreFailedArIds[] = $row->ar_id;
693  $allowedRevIdToArIdMap[$row->ar_rev_id] = -1;
694  } else {
695  // rev_id is not taken yet in the DB, but it might be taken
696  // by a prior revision in the same restore operation. If
697  // not, we need to reserve it.
698  if ( isset( $allowedRevIdToArIdMap[$row->ar_rev_id] ) ) {
699  $restoreFailedArIds[] = $row->ar_id;
700  } else {
701  $allowedRevIdToArIdMap[$row->ar_rev_id] = $row->ar_id;
702  $latestRestorableRow = $row;
703  }
704  }
705  } else {
706  // If ar_rev_id is null, there can't be a collision, and a
707  // rev_id will be chosen automatically.
708  $latestRestorableRow = $row;
709  }
710  }
711 
712  $result->seek( 0 ); // move back
713 
714  $oldPageId = 0;
715  if ( $latestRestorableRow !== null ) {
716  $oldPageId = (int)$latestRestorableRow->ar_page_id; // pass this to ArticleUndelete hook
717 
718  // Grab the content to check consistency with global state before restoring the page.
719  // XXX: The only current use case is Wikibase, which tries to enforce uniqueness of
720  // certain things across all pages. There may be a better way to do that.
721  $revision = $revisionStore->newRevisionFromArchiveRow(
722  $latestRestorableRow,
723  0,
724  $this->title
725  );
726 
727  // TODO: use User::newFromUserIdentity from If610c68f4912e
728  // TODO: The User isn't used for anything in prepareSave()! We should drop it.
729  $user = User::newFromName( $revision->getUser( RevisionRecord::RAW )->getName(), false );
730 
731  foreach ( $revision->getSlotRoles() as $role ) {
732  $content = $revision->getContent( $role, RevisionRecord::RAW );
733 
734  // NOTE: article ID may not be known yet. prepareSave() should not modify the database.
735  $status = $content->prepareSave( $article, 0, -1, $user );
736  if ( !$status->isOK() ) {
737  $dbw->endAtomic( __METHOD__ );
738 
739  return $status;
740  }
741  }
742  }
743 
744  $newid = false; // newly created page ID
745  $restored = 0; // number of revisions restored
747  $revision = null;
748  $restoredPages = [];
749  // If there are no restorable revisions, we can skip most of the steps.
750  if ( $latestRestorableRow === null ) {
751  $failedRevisionCount = $rev_count;
752  } else {
753  if ( $makepage ) {
754  // Check the state of the newest to-be version...
755  if ( !$unsuppress
756  && ( $latestRestorableRow->ar_deleted & RevisionRecord::DELETED_TEXT )
757  ) {
758  $dbw->endAtomic( __METHOD__ );
759 
760  return Status::newFatal( "undeleterevdel" );
761  }
762  // Safe to insert now...
763  $newid = $article->insertOn( $dbw, $latestRestorableRow->ar_page_id );
764  if ( $newid === false ) {
765  // The old ID is reserved; let's pick another
766  $newid = $article->insertOn( $dbw );
767  }
768  $pageId = $newid;
769  } else {
770  // Check if a deleted revision will become the current revision...
771  if ( $latestRestorableRow->ar_timestamp > $previousTimestamp ) {
772  // Check the state of the newest to-be version...
773  if ( !$unsuppress
774  && ( $latestRestorableRow->ar_deleted & RevisionRecord::DELETED_TEXT )
775  ) {
776  $dbw->endAtomic( __METHOD__ );
777 
778  return Status::newFatal( "undeleterevdel" );
779  }
780  }
781 
782  $newid = false;
783  $pageId = $article->getId();
784  }
785 
786  foreach ( $result as $row ) {
787  // Check for key dupes due to needed archive integrity.
788  if ( $row->ar_rev_id && $allowedRevIdToArIdMap[$row->ar_rev_id] !== $row->ar_id ) {
789  continue;
790  }
791  // Insert one revision at a time...maintaining deletion status
792  // unless we are specifically removing all restrictions...
793  $revision = $revisionStore->newRevisionFromArchiveRow(
794  $row,
795  0,
796  $this->title,
797  [
798  'page_id' => $pageId,
799  'deleted' => $unsuppress ? 0 : $row->ar_deleted
800  ]
801  );
802 
803  // This will also copy the revision to ip_changes if it was an IP edit.
804  $revisionStore->insertRevisionOn( $revision, $dbw );
805 
806  $restored++;
807 
808  $hookRunner = $this->getHookRunner();
809  $hookRunner->onRevisionUndeleted( $revision, $row->ar_page_id );
810 
811  // Hook is hard deprecated since 1.35
812  if ( $this->getHookContainer()->isRegistered( 'ArticleRevisionUndeleted' ) ) {
813  // Only create the Revision object if it is needed
814  $legacyRevision = new Revision( $revision );
815  $hookRunner->onArticleRevisionUndeleted(
816  $this->title,
817  $legacyRevision,
818  $row->ar_page_id
819  );
820  }
821  $restoredPages[$row->ar_page_id] = true;
822  }
823 
824  // Now that it's safely stored, take it out of the archive
825  // Don't delete rows that we failed to restore
826  $toDeleteConds = $oldWhere;
827  $failedRevisionCount = count( $restoreFailedArIds );
828  if ( $failedRevisionCount > 0 ) {
829  $toDeleteConds[] = 'ar_id NOT IN ( ' . $dbw->makeList( $restoreFailedArIds ) . ' )';
830  }
831 
832  $dbw->delete( 'archive',
833  $toDeleteConds,
834  __METHOD__ );
835  }
836 
837  $status = Status::newGood( $restored );
838 
839  if ( $failedRevisionCount > 0 ) {
840  $status->warning(
841  wfMessage( 'undeleterevision-duplicate-revid', $failedRevisionCount ) );
842  }
843 
844  // Was anything restored at all?
845  if ( $restored ) {
846  $created = (bool)$newid;
847 
848  $latestRevId = $article->getLatest();
849  if ( $latestRevId ) {
850  // If not found (false), cast to 0 so that the page is updated
851  // Just to be on the safe side, even though it should always be found
852  $latestRevTimestamp = (int)$revisionStore->getTimestampFromId(
853  $latestRevId,
854  RevisionStore::READ_LATEST
855  );
856  } else {
857  $latestRevTimestamp = 0;
858  }
859 
860  if ( $revision->getTimestamp() > $latestRevTimestamp ) {
861  // Attach the latest revision to the page...
862  // XXX: updateRevisionOn should probably move into a PageStore service.
863  $wasnew = $article->updateRevisionOn(
864  $dbw,
865  $revision,
866  $latestRevId
867  );
868  } else {
869  $wasnew = false;
870  }
871 
872  if ( $created || $wasnew ) {
873  // Update site stats, link tables, etc
874  // TODO: use DerivedPageDataUpdater from If610c68f4912e!
875  $article->doEditUpdates(
876  $revision,
877  User::newFromName( $revision->getUser( RevisionRecord::RAW )->getName(), false ),
878  [
879  'created' => $created,
880  'oldcountable' => $oldcountable,
881  'restored' => true
882  ]
883  );
884  }
885 
886  $this->getHookRunner()->onArticleUndelete(
887  $this->title, $created, $comment, $oldPageId, $restoredPages );
888 
889  if ( $this->title->getNamespace() === NS_FILE ) {
891  $this->title,
892  'imagelinks',
893  [ 'causeAction' => 'file-restore' ]
894  );
895  JobQueueGroup::singleton()->lazyPush( $job );
896  }
897  }
898 
899  $dbw->endAtomic( __METHOD__ );
900 
901  return $status;
902  }
903 
907  public function getFileStatus() {
908  return $this->fileStatus;
909  }
910 
914  public function getRevisionStatus() {
915  return $this->revisionStatus;
916  }
917 }
ReadOnlyError
Show an error when the wiki is locked/read-only and the user tries to do something that requires writ...
Definition: ReadOnlyError.php:29
PageArchive\getRevisionRecordByTimestamp
getRevisionRecordByTimestamp( $timestamp)
Return a RevisionRecord object containing data for the deleted revision.
Definition: PageArchive.php:264
Title\newFromText
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:329
PageArchive\getFileStatus
getFileStatus()
Definition: PageArchive.php:907
Revision\RevisionRecord
Page revision base class.
Definition: RevisionRecord.php:46
StatusValue\newFatal
static newFatal( $message,... $parameters)
Factory function for fatal errors.
Definition: StatusValue.php:70
PageArchive
Used to show archived pages and eventually restore them.
Definition: PageArchive.php:32
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:154
PageArchive\undeleteAsUser
undeleteAsUser( $timestamps, User $user, $comment='', $fileVersions=[], $unsuppress=false, $tags=null)
Restore the given (or all) text and file revisions for the page.
Definition: PageArchive.php:509
Revision\RevisionStore
Service for looking up page revisions.
Definition: RevisionStore.php:81
PageArchive\listPages
static listPages( $dbr, $condition)
Definition: PageArchive.php:158
wfTimestamp
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Definition: GlobalFunctions.php:1808
PageArchive\$config
Config $config
Definition: PageArchive.php:45
PageArchive\listPagesByPrefix
static listPagesByPrefix( $prefix)
List deleted pages recorded in the archive table matching the given title prefix.
Definition: PageArchive.php:132
NS_FILE
const NS_FILE
Definition: Defines.php:75
wfReadOnly
wfReadOnly()
Check whether the wiki is in read-only mode.
Definition: GlobalFunctions.php:1125
User\newFromName
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition: User.php:538
ArchivedFile\getQueryInfo
static getQueryInfo()
Return the tables, fields, and join conditions to be selected to create a new archivedfile object.
Definition: ArchivedFile.php:234
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1219
PageArchive\listFiles
listFiles()
List the deleted file revisions for this page, if it's a file page.
Definition: PageArchive.php:224
PageArchive\$revisionStatus
Status $revisionStatus
Definition: PageArchive.php:42
PageArchive\listPagesBySearch
static listPagesBySearch( $term)
List deleted pages recorded in the archive matching the given term, using search engine archive.
Definition: PageArchive.php:82
HTMLCacheUpdateJob\newForBacklinks
static newForBacklinks(Title $title, $table, $params=[])
Definition: HTMLCacheUpdateJob.php:59
Wikimedia\Rdbms\IDatabase
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:38
PageArchive\isDeleted
isDeleted()
Quick check if any archived revisions are present for the page.
Definition: PageArchive.php:435
PageArchive\getRevisionStore
getRevisionStore()
Definition: PageArchive.php:62
$dbr
$dbr
Definition: testCompression.php:54
Status
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition: Status.php:44
PageArchive\getArchivedRevisionRecord
getArchivedRevisionRecord(int $revId)
Return the archived revision with the given ID.
Definition: PageArchive.php:298
Revision
Definition: Revision.php:40
ChangeTags\modifyDisplayQuery
static modifyDisplayQuery(&$tables, &$fields, &$conds, &$join_conds, &$options, $filter_tag='')
Applies all tags-related changes to a query.
Definition: ChangeTags.php:797
Config
Interface for configuration instances.
Definition: Config.php:30
LIST_OR
const LIST_OR
Definition: Defines.php:51
Title\getDBkey
getDBkey()
Get the main part with underscores.
Definition: Title.php:1023
MWException
MediaWiki exception.
Definition: MWException.php:29
WikiPage\factory
static factory(Title $title)
Create a WikiPage object of the appropriate class for the given title.
Definition: WikiPage.php:156
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that $function is deprecated.
Definition: GlobalFunctions.php:1026
Wikimedia\Rdbms\IResultWrapper
Result wrapper for grabbing data queried from an IDatabase object.
Definition: IResultWrapper.php:24
Title\getNamespace
getNamespace()
Get the namespace index, i.e.
Definition: Title.php:1032
PageArchive\undeleteRevisions
undeleteRevisions( $timestamps, $unsuppress=false, $comment='')
This is the meaty bit – It restores archived revisions of the given page to the revision table.
Definition: PageArchive.php:588
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2467
PageArchive\listRevisions
listRevisions()
List the revisions of the given page.
Definition: PageArchive.php:182
PageArchive\getPreviousRevisionRecord
getPreviousRevisionRecord(string $timestamp)
Return the most-previous revision, either live or deleted, against the deleted revision given by time...
Definition: PageArchive.php:365
PageArchive\getRevisionStatus
getRevisionStatus()
Definition: PageArchive.php:914
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
DB_MASTER
const DB_MASTER
Definition: defines.php:26
wfDebug
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Definition: GlobalFunctions.php:909
PageArchive\$fileStatus
Status $fileStatus
Definition: PageArchive.php:39
PageArchive\getLastRevisionId
getLastRevisionId()
Returns the ID of the latest deleted revision.
Definition: PageArchive.php:415
$content
$content
Definition: router.php:76
PageArchive\getRevision
getRevision( $timestamp)
Return a Revision object containing data for the deleted revision.
Definition: PageArchive.php:250
StatusValue\newGood
static newGood( $value=null)
Factory function for good results.
Definition: StatusValue.php:82
PageArchive\getArchivedRevision
getArchivedRevision( $revId)
Return the archived revision with the given ID.
Definition: PageArchive.php:280
PageArchive\doesWrites
doesWrites()
Definition: PageArchive.php:70
PageArchive\getRevisionByConditions
getRevisionByConditions(array $conditions, array $options=[])
Definition: PageArchive.php:308
Title
Represents a title within MediaWiki.
Definition: Title.php:42
JobQueueGroup\singleton
static singleton( $domain=false)
Definition: JobQueueGroup.php:70
$job
if(count( $args)< 1) $job
Definition: recompressTracked.php:50
PageArchive\undelete
undelete( $timestamps, $comment='', $fileVersions=[], $unsuppress=false, User $user=null, $tags=null)
Restore the given (or all) text and file revisions for the page.
Definition: PageArchive.php:469
ManualLogEntry
Class for creating new log entries and inserting them into the database.
Definition: ManualLogEntry.php:42
$t
$t
Definition: testCompression.php:74
PageArchive\__construct
__construct( $title, Config $config=null)
Definition: PageArchive.php:47
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:55
PageArchive\$title
Title $title
Definition: PageArchive.php:36
Title\getText
getText()
Get the text form (spaces not underscores) of the main part.
Definition: Title.php:1005
PageArchive\getPreviousRevision
getPreviousRevision( $timestamp)
Return the most-previous revision, either live or deleted, against the deleted revision given by time...
Definition: PageArchive.php:345