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 
249  public function getRevision( $timestamp ) {
250  $dbr = wfGetDB( DB_REPLICA );
251  $rec = $this->getRevisionByConditions(
252  [ 'ar_timestamp' => $dbr->timestamp( $timestamp ) ]
253  );
254  return $rec ? new Revision( $rec ) : null;
255  }
256 
265  public function getArchivedRevision( $revId ) {
266  wfDeprecated( __METHOD__, '1.35' );
267 
268  // Protect against code switching from getRevision() passing in a timestamp.
269  Assert::parameterType( 'integer', $revId, '$revId' );
270 
271  $revRecord = $this->getArchivedRevisionRecord( $revId );
272  return $revRecord ? new Revision( $revRecord ) : null;
273  }
274 
283  public function getArchivedRevisionRecord( int $revId ) {
284  return $this->getRevisionByConditions( [ 'ar_rev_id' => $revId ] );
285  }
286 
293  private function getRevisionByConditions( array $conditions, array $options = [] ) {
294  $dbr = wfGetDB( DB_REPLICA );
295  $arQuery = $this->getRevisionStore()->getArchiveQueryInfo();
296 
297  $conditions = $conditions + [
298  'ar_namespace' => $this->title->getNamespace(),
299  'ar_title' => $this->title->getDBkey(),
300  ];
301 
302  $row = $dbr->selectRow(
303  $arQuery['tables'],
304  $arQuery['fields'],
305  $conditions,
306  __METHOD__,
307  $options,
308  $arQuery['joins']
309  );
310 
311  if ( $row ) {
312  return $this->getRevisionStore()->newRevisionFromArchiveRow( $row, 0, $this->title );
313  }
314 
315  return null;
316  }
317 
330  public function getPreviousRevision( $timestamp ) {
331  wfDeprecated( __METHOD__, '1.35' );
332 
333  $revRecord = $this->getPreviousRevisionRecord( $timestamp );
334  $rev = $revRecord ? new Revision( $revRecord ) : null;
335  return $rev;
336  }
337 
350  public function getPreviousRevisionRecord( string $timestamp ) {
351  $dbr = wfGetDB( DB_REPLICA );
352 
353  // Check the previous deleted revision...
354  $row = $dbr->selectRow( 'archive',
355  [ 'ar_rev_id', 'ar_timestamp' ],
356  [ 'ar_namespace' => $this->title->getNamespace(),
357  'ar_title' => $this->title->getDBkey(),
358  'ar_timestamp < ' .
359  $dbr->addQuotes( $dbr->timestamp( $timestamp ) ) ],
360  __METHOD__,
361  [
362  'ORDER BY' => 'ar_timestamp DESC',
363  'LIMIT' => 1 ] );
364  $prevDeleted = $row ? wfTimestamp( TS_MW, $row->ar_timestamp ) : false;
365  $prevDeletedId = $row ? intval( $row->ar_rev_id ) : null;
366 
367  $row = $dbr->selectRow( [ 'page', 'revision' ],
368  [ 'rev_id', 'rev_timestamp' ],
369  [
370  'page_namespace' => $this->title->getNamespace(),
371  'page_title' => $this->title->getDBkey(),
372  'page_id = rev_page',
373  'rev_timestamp < ' .
374  $dbr->addQuotes( $dbr->timestamp( $timestamp ) ) ],
375  __METHOD__,
376  [
377  'ORDER BY' => 'rev_timestamp DESC',
378  'LIMIT' => 1 ] );
379  $prevLive = $row ? wfTimestamp( TS_MW, $row->rev_timestamp ) : false;
380  $prevLiveId = $row ? intval( $row->rev_id ) : null;
381 
382  if ( $prevLive && $prevLive > $prevDeleted ) {
383  // Most prior revision was live
384  $rec = $this->getRevisionStore()->getRevisionById( $prevLiveId );
385  } elseif ( $prevDeleted ) {
386  // Most prior revision was deleted
387  $rec = $this->getArchivedRevisionRecord( $prevDeletedId );
388  } else {
389  $rec = null;
390  }
391 
392  return $rec;
393  }
394 
400  public function getLastRevisionId() {
401  $dbr = wfGetDB( DB_REPLICA );
402  $revId = $dbr->selectField(
403  'archive',
404  'ar_rev_id',
405  [ 'ar_namespace' => $this->title->getNamespace(),
406  'ar_title' => $this->title->getDBkey() ],
407  __METHOD__,
408  [ 'ORDER BY' => [ 'ar_timestamp DESC', 'ar_id DESC' ] ]
409  );
410 
411  return $revId ? intval( $revId ) : false;
412  }
413 
420  public function isDeleted() {
421  $dbr = wfGetDB( DB_REPLICA );
422  $row = $dbr->selectRow(
423  [ 'archive' ],
424  '1', // We don't care about the value. Allow the database to optimize.
425  [ 'ar_namespace' => $this->title->getNamespace(),
426  'ar_title' => $this->title->getDBkey() ],
427  __METHOD__
428  );
429 
430  return (bool)$row;
431  }
432 
454  public function undelete( $timestamps, $comment = '', $fileVersions = [],
455  $unsuppress = false, User $user = null, $tags = null
456  ) {
457  wfDeprecated( __METHOD__, '1.35' );
458  if ( $user === null ) {
459  global $wgUser;
460  $user = $wgUser;
461  }
462  $result = $this->undeleteAsUser(
463  $timestamps,
464  $user,
465  $comment,
466  $fileVersions,
467  $unsuppress,
468  $tags
469  );
470  return $result;
471  }
472 
494  public function undeleteAsUser(
495  $timestamps,
496  User $user,
497  $comment = '',
498  $fileVersions = [],
499  $unsuppress = false,
500  $tags = null
501  ) {
502  // If both the set of text revisions and file revisions are empty,
503  // restore everything. Otherwise, just restore the requested items.
504  $restoreAll = empty( $timestamps ) && empty( $fileVersions );
505 
506  $restoreText = $restoreAll || !empty( $timestamps );
507  $restoreFiles = $restoreAll || !empty( $fileVersions );
508 
509  if ( $restoreFiles && $this->title->getNamespace() == NS_FILE ) {
511  $img = MediaWikiServices::getInstance()->getRepoGroup()->getLocalRepo()
512  ->newFile( $this->title );
513  $img->load( File::READ_LATEST );
514  $this->fileStatus = $img->restore( $fileVersions, $unsuppress );
515  if ( !$this->fileStatus->isOK() ) {
516  return false;
517  }
518  $filesRestored = $this->fileStatus->successCount;
519  } else {
520  $filesRestored = 0;
521  }
522 
523  if ( $restoreText ) {
524  $this->revisionStatus = $this->undeleteRevisions( $timestamps, $unsuppress, $comment );
525  if ( !$this->revisionStatus->isOK() ) {
526  return false;
527  }
528 
529  $textRestored = $this->revisionStatus->getValue();
530  } else {
531  $textRestored = 0;
532  }
533 
534  // Touch the log!
535 
536  if ( !$textRestored && !$filesRestored ) {
537  wfDebug( "Undelete: nothing undeleted...\n" );
538 
539  return false;
540  }
541 
542  $logEntry = new ManualLogEntry( 'delete', 'restore' );
543  $logEntry->setPerformer( $user );
544  $logEntry->setTarget( $this->title );
545  $logEntry->setComment( $comment );
546  $logEntry->addTags( $tags );
547  $logEntry->setParameters( [
548  ':assoc:count' => [
549  'revisions' => $textRestored,
550  'files' => $filesRestored,
551  ],
552  ] );
553 
554  $this->getHookRunner()->onArticleUndeleteLogEntry( $this, $logEntry, $user );
555 
556  $logid = $logEntry->insert();
557  $logEntry->publish( $logid );
558 
559  return [ $textRestored, $filesRestored, $comment ];
560  }
561 
573  private function undeleteRevisions( $timestamps, $unsuppress = false, $comment = '' ) {
574  if ( wfReadOnly() ) {
575  throw new ReadOnlyError();
576  }
577 
578  $dbw = wfGetDB( DB_MASTER );
579  $dbw->startAtomic( __METHOD__ );
580 
581  $restoreAll = empty( $timestamps );
582 
583  # Does this page already exist? We'll have to update it...
584  $article = WikiPage::factory( $this->title );
585  # Load latest data for the current page (T33179)
586  $article->loadPageData( 'fromdbmaster' );
587  $oldcountable = $article->isCountable();
588 
589  $page = $dbw->selectRow( 'page',
590  [ 'page_id', 'page_latest' ],
591  [ 'page_namespace' => $this->title->getNamespace(),
592  'page_title' => $this->title->getDBkey() ],
593  __METHOD__,
594  [ 'FOR UPDATE' ] // lock page
595  );
596 
597  if ( $page ) {
598  $makepage = false;
599  # Page already exists. Import the history, and if necessary
600  # we'll update the latest revision field in the record.
601 
602  # Get the time span of this page
603  $previousTimestamp = $dbw->selectField( 'revision', 'rev_timestamp',
604  [ 'rev_id' => $page->page_latest ],
605  __METHOD__ );
606 
607  if ( $previousTimestamp === false ) {
608  wfDebug( __METHOD__ . ": existing page refers to a page_latest that does not exist\n" );
609 
610  $status = Status::newGood( 0 );
611  $status->warning( 'undeleterevision-missing' );
612  $dbw->endAtomic( __METHOD__ );
613 
614  return $status;
615  }
616  } else {
617  # Have to create a new article...
618  $makepage = true;
619  $previousTimestamp = 0;
620  }
621 
622  $oldWhere = [
623  'ar_namespace' => $this->title->getNamespace(),
624  'ar_title' => $this->title->getDBkey(),
625  ];
626  if ( !$restoreAll ) {
627  $oldWhere['ar_timestamp'] = array_map( [ &$dbw, 'timestamp' ], $timestamps );
628  }
629 
630  $revisionStore = $this->getRevisionStore();
631  $queryInfo = $revisionStore->getArchiveQueryInfo();
632  $queryInfo['tables'][] = 'revision';
633  $queryInfo['fields'][] = 'rev_id';
634  $queryInfo['joins']['revision'] = [ 'LEFT JOIN', 'ar_rev_id=rev_id' ];
635 
639  $result = $dbw->select(
640  $queryInfo['tables'],
641  $queryInfo['fields'],
642  $oldWhere,
643  __METHOD__,
644  /* options */
645  [ 'ORDER BY' => 'ar_timestamp' ],
646  $queryInfo['joins']
647  );
648 
649  $rev_count = $result->numRows();
650  if ( !$rev_count ) {
651  wfDebug( __METHOD__ . ": no revisions to restore\n" );
652 
653  $status = Status::newGood( 0 );
654  $status->warning( "undelete-no-results" );
655  $dbw->endAtomic( __METHOD__ );
656 
657  return $status;
658  }
659 
660  // We use ar_id because there can be duplicate ar_rev_id even for the same
661  // page. In this case, we may be able to restore the first one.
662  $restoreFailedArIds = [];
663 
664  // Map rev_id to the ar_id that is allowed to use it. When checking later,
665  // if it doesn't match, the current ar_id can not be restored.
666 
667  // Value can be an ar_id or -1 (-1 means no ar_id can use it, since the
668  // rev_id is taken before we even start the restore).
669  $allowedRevIdToArIdMap = [];
670 
671  $latestRestorableRow = null;
672 
673  foreach ( $result as $row ) {
674  if ( $row->ar_rev_id ) {
675  // rev_id is taken even before we start restoring.
676  if ( $row->ar_rev_id === $row->rev_id ) {
677  $restoreFailedArIds[] = $row->ar_id;
678  $allowedRevIdToArIdMap[$row->ar_rev_id] = -1;
679  } else {
680  // rev_id is not taken yet in the DB, but it might be taken
681  // by a prior revision in the same restore operation. If
682  // not, we need to reserve it.
683  if ( isset( $allowedRevIdToArIdMap[$row->ar_rev_id] ) ) {
684  $restoreFailedArIds[] = $row->ar_id;
685  } else {
686  $allowedRevIdToArIdMap[$row->ar_rev_id] = $row->ar_id;
687  $latestRestorableRow = $row;
688  }
689  }
690  } else {
691  // If ar_rev_id is null, there can't be a collision, and a
692  // rev_id will be chosen automatically.
693  $latestRestorableRow = $row;
694  }
695  }
696 
697  $result->seek( 0 ); // move back
698 
699  $oldPageId = 0;
700  if ( $latestRestorableRow !== null ) {
701  $oldPageId = (int)$latestRestorableRow->ar_page_id; // pass this to ArticleUndelete hook
702 
703  // Grab the content to check consistency with global state before restoring the page.
704  // XXX: The only current use case is Wikibase, which tries to enforce uniqueness of
705  // certain things across all pages. There may be a better way to do that.
706  $revision = $revisionStore->newRevisionFromArchiveRow(
707  $latestRestorableRow,
708  0,
709  $this->title
710  );
711 
712  // TODO: use User::newFromUserIdentity from If610c68f4912e
713  // TODO: The User isn't used for anything in prepareSave()! We should drop it.
714  $user = User::newFromName( $revision->getUser( RevisionRecord::RAW )->getName(), false );
715 
716  foreach ( $revision->getSlotRoles() as $role ) {
717  $content = $revision->getContent( $role, RevisionRecord::RAW );
718 
719  // NOTE: article ID may not be known yet. prepareSave() should not modify the database.
720  $status = $content->prepareSave( $article, 0, -1, $user );
721  if ( !$status->isOK() ) {
722  $dbw->endAtomic( __METHOD__ );
723 
724  return $status;
725  }
726  }
727  }
728 
729  $newid = false; // newly created page ID
730  $restored = 0; // number of revisions restored
732  $revision = null;
733  $restoredPages = [];
734  // If there are no restorable revisions, we can skip most of the steps.
735  if ( $latestRestorableRow === null ) {
736  $failedRevisionCount = $rev_count;
737  } else {
738  if ( $makepage ) {
739  // Check the state of the newest to-be version...
740  if ( !$unsuppress
741  && ( $latestRestorableRow->ar_deleted & RevisionRecord::DELETED_TEXT )
742  ) {
743  $dbw->endAtomic( __METHOD__ );
744 
745  return Status::newFatal( "undeleterevdel" );
746  }
747  // Safe to insert now...
748  $newid = $article->insertOn( $dbw, $latestRestorableRow->ar_page_id );
749  if ( $newid === false ) {
750  // The old ID is reserved; let's pick another
751  $newid = $article->insertOn( $dbw );
752  }
753  $pageId = $newid;
754  } else {
755  // Check if a deleted revision will become the current revision...
756  if ( $latestRestorableRow->ar_timestamp > $previousTimestamp ) {
757  // Check the state of the newest to-be version...
758  if ( !$unsuppress
759  && ( $latestRestorableRow->ar_deleted & RevisionRecord::DELETED_TEXT )
760  ) {
761  $dbw->endAtomic( __METHOD__ );
762 
763  return Status::newFatal( "undeleterevdel" );
764  }
765  }
766 
767  $newid = false;
768  $pageId = $article->getId();
769  }
770 
771  foreach ( $result as $row ) {
772  // Check for key dupes due to needed archive integrity.
773  if ( $row->ar_rev_id && $allowedRevIdToArIdMap[$row->ar_rev_id] !== $row->ar_id ) {
774  continue;
775  }
776  // Insert one revision at a time...maintaining deletion status
777  // unless we are specifically removing all restrictions...
778  $revision = $revisionStore->newRevisionFromArchiveRow(
779  $row,
780  0,
781  $this->title,
782  [
783  'page_id' => $pageId,
784  'deleted' => $unsuppress ? 0 : $row->ar_deleted
785  ]
786  );
787 
788  // This will also copy the revision to ip_changes if it was an IP edit.
789  $revisionStore->insertRevisionOn( $revision, $dbw );
790 
791  $restored++;
792 
793  $this->getHookRunner()->onRevisionUndeleted( $revision, $row->ar_page_id );
794 
795  // Deprecated since 1.35
796  $legacyRevision = new Revision( $revision );
797  $this->getHookRunner()->onArticleRevisionUndeleted(
798  $this->title, $legacyRevision, $row->ar_page_id );
799  $restoredPages[$row->ar_page_id] = true;
800  }
801 
802  // Now that it's safely stored, take it out of the archive
803  // Don't delete rows that we failed to restore
804  $toDeleteConds = $oldWhere;
805  $failedRevisionCount = count( $restoreFailedArIds );
806  if ( $failedRevisionCount > 0 ) {
807  $toDeleteConds[] = 'ar_id NOT IN ( ' . $dbw->makeList( $restoreFailedArIds ) . ' )';
808  }
809 
810  $dbw->delete( 'archive',
811  $toDeleteConds,
812  __METHOD__ );
813  }
814 
815  $status = Status::newGood( $restored );
816 
817  if ( $failedRevisionCount > 0 ) {
818  $status->warning(
819  wfMessage( 'undeleterevision-duplicate-revid', $failedRevisionCount ) );
820  }
821 
822  // Was anything restored at all?
823  if ( $restored ) {
824  $created = (bool)$newid;
825  // Attach the latest revision to the page...
826  // XXX: updateRevisionOn should probably move into a PageStore service.
827  $wasnew = $article->updateIfNewerOn( $dbw, $legacyRevision );
828  if ( $created || $wasnew ) {
829  // Update site stats, link tables, etc
830  // TODO: use DerivedPageDataUpdater from If610c68f4912e!
831  $article->doEditUpdates(
832  $legacyRevision,
833  User::newFromName( $revision->getUser( RevisionRecord::RAW )->getName(), false ),
834  [
835  'created' => $created,
836  'oldcountable' => $oldcountable,
837  'restored' => true
838  ]
839  );
840  }
841 
842  $this->getHookRunner()->onArticleUndelete(
843  $this->title, $created, $comment, $oldPageId, $restoredPages );
844 
845  if ( $this->title->getNamespace() == NS_FILE ) {
847  $this->title,
848  'imagelinks',
849  [ 'causeAction' => 'file-restore' ]
850  );
851  JobQueueGroup::singleton()->lazyPush( $job );
852  }
853  }
854 
855  $dbw->endAtomic( __METHOD__ );
856 
857  return $status;
858  }
859 
863  public function getFileStatus() {
864  return $this->fileStatus;
865  }
866 
870  public function getRevisionStatus() {
871  return $this->revisionStatus;
872  }
873 }
ReadOnlyError
Show an error when the wiki is locked/read-only and the user tries to do something that requires writ...
Definition: ReadOnlyError.php:28
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:332
PageArchive\getFileStatus
getFileStatus()
Definition: PageArchive.php:863
Revision\RevisionRecord
Page revision base class.
Definition: RevisionRecord.php:46
StatusValue\newFatal
static newFatal( $message,... $parameters)
Factory function for fatal errors.
Definition: StatusValue.php:69
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:146
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:494
Revision\RevisionStore
Service for looking up page revisions.
Definition: RevisionStore.php:79
true
return true
Definition: router.php:90
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:1806
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:1104
User\newFromName
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition: User.php:535
ArchivedFile\getQueryInfo
static getQueryInfo()
Return the tables, fields, and join conditions to be selected to create a new archivedfile object.
Definition: ArchivedFile.php:229
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1198
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:420
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:42
PageArchive\getArchivedRevisionRecord
getArchivedRevisionRecord(int $revId)
Return the archived revision with the given ID.
Definition: PageArchive.php:283
Revision
Definition: Revision.php:39
ChangeTags\modifyDisplayQuery
static modifyDisplayQuery(&$tables, &$fields, &$conds, &$join_conds, &$options, $filter_tag='')
Applies all tags-related changes to a query.
Definition: ChangeTags.php:782
Config
Interface for configuration instances.
Definition: Config.php:28
LIST_OR
const LIST_OR
Definition: Defines.php:51
Title\getDBkey
getDBkey()
Get the main part with underscores.
Definition: Title.php:1025
MWException
MediaWiki exception.
Definition: MWException.php:26
WikiPage\factory
static factory(Title $title)
Create a WikiPage object of the appropriate class for the given title.
Definition: WikiPage.php:146
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that $function is deprecated.
Definition: GlobalFunctions.php:1030
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:1034
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:573
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2463
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:350
PageArchive\getRevisionStatus
getRevisionStatus()
Definition: PageArchive.php:870
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:913
PageArchive\$fileStatus
Status $fileStatus
Definition: PageArchive.php:39
PageArchive\getLastRevisionId
getLastRevisionId()
Returns the ID of the latest deleted revision.
Definition: PageArchive.php:400
$content
$content
Definition: router.php:76
PageArchive\getRevision
getRevision( $timestamp)
Return a Revision object containing data for the deleted revision.
Definition: PageArchive.php:249
StatusValue\newGood
static newGood( $value=null)
Factory function for good results.
Definition: StatusValue.php:81
PageArchive\getArchivedRevision
getArchivedRevision( $revId)
Return the archived revision with the given ID.
Definition: PageArchive.php:265
PageArchive\doesWrites
doesWrites()
Definition: PageArchive.php:70
PageArchive\getRevisionByConditions
getRevisionByConditions(array $conditions, array $options=[])
Definition: PageArchive.php:293
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:454
ManualLogEntry
Class for creating new log entries and inserting them into the database.
Definition: ManualLogEntry.php:38
$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:1007
PageArchive\getPreviousRevision
getPreviousRevision( $timestamp)
Return the most-previous revision, either live or deleted, against the deleted revision given by time...
Definition: PageArchive.php:330