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 $title, Config $config = null ) {
48  $this->title = $title;
49  if ( $config === null ) {
50  wfDebug( __METHOD__ . ' did not have a Config object passed to it' );
51  $config = MediaWikiServices::getInstance()->getMainConfig();
52  }
53  $this->config = $config;
54  }
55 
59  private function getRevisionStore() {
60  // TODO: Refactor: delete()/undeleteAsUser() should live in a PageStore service;
61  // Methods in PageArchive and RevisionStore that deal with archive revisions
62  // should move into an ArchiveStore service (but could still be implemented
63  // together with RevisionStore).
64  return MediaWikiServices::getInstance()->getRevisionStore();
65  }
66 
67  public function doesWrites() {
68  return true;
69  }
70 
79  public static function listPagesBySearch( $term ) {
80  $title = Title::newFromText( $term );
81  if ( $title ) {
82  $ns = $title->getNamespace();
83  $termMain = $title->getText();
84  $termDb = $title->getDBkey();
85  } else {
86  // Prolly won't work too good
87  // @todo handle bare namespace names cleanly?
88  $ns = 0;
89  $termMain = $termDb = $term;
90  }
91 
92  // Try search engine first
93  $engine = MediaWikiServices::getInstance()->newSearchEngine();
94  $engine->setLimitOffset( 100 );
95  $engine->setNamespaces( [ $ns ] );
96  $results = $engine->searchArchiveTitle( $termMain );
97  if ( !$results->isOK() ) {
98  $results = [];
99  } else {
100  $results = $results->getValue();
101  }
102 
103  if ( !$results ) {
104  // Fall back to regular prefix search
105  return self::listPagesByPrefix( $term );
106  }
107 
108  $dbr = wfGetDB( DB_REPLICA );
109  $condTitles = array_unique( array_map( static function ( Title $t ) {
110  return $t->getDBkey();
111  }, $results ) );
112  $conds = [
113  'ar_namespace' => $ns,
114  $dbr->makeList( [ 'ar_title' => $condTitles ], LIST_OR ) . " OR ar_title " .
115  $dbr->buildLike( $termDb, $dbr->anyString() )
116  ];
117 
118  return self::listPages( $dbr, $conds );
119  }
120 
129  public static function listPagesByPrefix( $prefix ) {
130  $dbr = wfGetDB( DB_REPLICA );
131 
132  $title = Title::newFromText( $prefix );
133  if ( $title ) {
134  $ns = $title->getNamespace();
135  $prefix = $title->getDBkey();
136  } else {
137  // Prolly won't work too good
138  // @todo handle bare namespace names cleanly?
139  $ns = 0;
140  }
141 
142  $conds = [
143  'ar_namespace' => $ns,
144  'ar_title' . $dbr->buildLike( $prefix, $dbr->anyString() ),
145  ];
146 
147  return self::listPages( $dbr, $conds );
148  }
149 
155  protected static function listPages( $dbr, $condition ) {
156  return $dbr->select(
157  [ 'archive' ],
158  [
159  'ar_namespace',
160  'ar_title',
161  'count' => 'COUNT(*)'
162  ],
163  $condition,
164  __METHOD__,
165  [
166  'GROUP BY' => [ 'ar_namespace', 'ar_title' ],
167  'ORDER BY' => [ 'ar_namespace', 'ar_title' ],
168  'LIMIT' => 100,
169  ]
170  );
171  }
172 
179  public function listRevisions() {
180  $revisionStore = $this->getRevisionStore();
181  $queryInfo = $revisionStore->getArchiveQueryInfo();
182 
183  $conds = [
184  'ar_namespace' => $this->title->getNamespace(),
185  'ar_title' => $this->title->getDBkey(),
186  ];
187 
188  // NOTE: ordering by ar_timestamp and ar_id, to remove ambiguity.
189  // XXX: Ideally, we would be ordering by ar_timestamp and ar_rev_id, but since we
190  // don't have an index on ar_rev_id, that causes a file sort.
191  $options = [ 'ORDER BY' => [ 'ar_timestamp DESC', 'ar_id DESC' ] ];
192 
194  $queryInfo['tables'],
195  $queryInfo['fields'],
196  $conds,
197  $queryInfo['joins'],
198  $options,
199  ''
200  );
201 
202  $dbr = wfGetDB( DB_REPLICA );
203  return $dbr->select(
204  $queryInfo['tables'],
205  $queryInfo['fields'],
206  $conds,
207  __METHOD__,
208  $options,
209  $queryInfo['joins']
210  );
211  }
212 
221  public function listFiles() {
222  if ( $this->title->getNamespace() !== NS_FILE ) {
223  return null;
224  }
225 
226  $dbr = wfGetDB( DB_REPLICA );
227  $fileQuery = ArchivedFile::getQueryInfo();
228  return $dbr->select(
229  $fileQuery['tables'],
230  $fileQuery['fields'],
231  [ 'fa_name' => $this->title->getDBkey() ],
232  __METHOD__,
233  [ 'ORDER BY' => 'fa_timestamp DESC' ],
234  $fileQuery['joins']
235  );
236  }
237 
247  public function getRevision( $timestamp ) {
248  wfDeprecated( __METHOD__, '1.32' );
249  $revRecord = $this->getRevisionRecordByTimestamp( $timestamp );
250  return $revRecord ? new Revision( $revRecord ) : null;
251  }
252 
261  public function getRevisionRecordByTimestamp( $timestamp ) {
262  $dbr = wfGetDB( DB_REPLICA );
263  $rec = $this->getRevisionByConditions(
264  [ 'ar_timestamp' => $dbr->timestamp( $timestamp ) ]
265  );
266  return $rec;
267  }
268 
277  public function getArchivedRevision( $revId ) {
278  wfDeprecated( __METHOD__, '1.35' );
279 
280  // Protect against code switching from getRevision() passing in a timestamp.
281  Assert::parameterType( 'integer', $revId, '$revId' );
282 
283  $revRecord = $this->getArchivedRevisionRecord( $revId );
284  return $revRecord ? new Revision( $revRecord ) : null;
285  }
286 
295  public function getArchivedRevisionRecord( int $revId ) {
296  return $this->getRevisionByConditions( [ 'ar_rev_id' => $revId ] );
297  }
298 
305  private function getRevisionByConditions( array $conditions, array $options = [] ) {
306  $dbr = wfGetDB( DB_REPLICA );
307  $arQuery = $this->getRevisionStore()->getArchiveQueryInfo();
308 
309  $conditions += [
310  'ar_namespace' => $this->title->getNamespace(),
311  'ar_title' => $this->title->getDBkey(),
312  ];
313 
314  $row = $dbr->selectRow(
315  $arQuery['tables'],
316  $arQuery['fields'],
317  $conditions,
318  __METHOD__,
319  $options,
320  $arQuery['joins']
321  );
322 
323  if ( $row ) {
324  return $this->getRevisionStore()->newRevisionFromArchiveRow( $row, 0, $this->title );
325  }
326 
327  return null;
328  }
329 
342  public function getPreviousRevision( $timestamp ) {
343  wfDeprecated( __METHOD__, '1.35' );
344 
345  $revRecord = $this->getPreviousRevisionRecord( $timestamp );
346  $rev = $revRecord ? new Revision( $revRecord ) : null;
347  return $rev;
348  }
349 
362  public function getPreviousRevisionRecord( string $timestamp ) {
363  $dbr = wfGetDB( DB_REPLICA );
364 
365  // Check the previous deleted revision...
366  $row = $dbr->selectRow( 'archive',
367  [ 'ar_rev_id', 'ar_timestamp' ],
368  [ 'ar_namespace' => $this->title->getNamespace(),
369  'ar_title' => $this->title->getDBkey(),
370  'ar_timestamp < ' .
371  $dbr->addQuotes( $dbr->timestamp( $timestamp ) ) ],
372  __METHOD__,
373  [
374  'ORDER BY' => 'ar_timestamp DESC',
375  'LIMIT' => 1 ] );
376  $prevDeleted = $row ? wfTimestamp( TS_MW, $row->ar_timestamp ) : false;
377  $prevDeletedId = $row ? intval( $row->ar_rev_id ) : null;
378 
379  $row = $dbr->selectRow( [ 'page', 'revision' ],
380  [ 'rev_id', 'rev_timestamp' ],
381  [
382  'page_namespace' => $this->title->getNamespace(),
383  'page_title' => $this->title->getDBkey(),
384  'page_id = rev_page',
385  'rev_timestamp < ' .
386  $dbr->addQuotes( $dbr->timestamp( $timestamp ) ) ],
387  __METHOD__,
388  [
389  'ORDER BY' => 'rev_timestamp DESC',
390  'LIMIT' => 1 ] );
391  $prevLive = $row ? wfTimestamp( TS_MW, $row->rev_timestamp ) : false;
392  $prevLiveId = $row ? intval( $row->rev_id ) : null;
393 
394  if ( $prevLive && $prevLive > $prevDeleted ) {
395  // Most prior revision was live
396  $rec = $this->getRevisionStore()->getRevisionById( $prevLiveId );
397  } elseif ( $prevDeleted ) {
398  // Most prior revision was deleted
399  $rec = $this->getArchivedRevisionRecord( $prevDeletedId );
400  } else {
401  $rec = null;
402  }
403 
404  return $rec;
405  }
406 
412  public function getLastRevisionId() {
413  $dbr = wfGetDB( DB_REPLICA );
414  $revId = $dbr->selectField(
415  'archive',
416  'ar_rev_id',
417  [ 'ar_namespace' => $this->title->getNamespace(),
418  'ar_title' => $this->title->getDBkey() ],
419  __METHOD__,
420  [ 'ORDER BY' => [ 'ar_timestamp DESC', 'ar_id DESC' ] ]
421  );
422 
423  return $revId ? intval( $revId ) : false;
424  }
425 
432  public function isDeleted() {
433  $dbr = wfGetDB( DB_REPLICA );
434  $row = $dbr->selectRow(
435  [ 'archive' ],
436  '1', // We don't care about the value. Allow the database to optimize.
437  [ 'ar_namespace' => $this->title->getNamespace(),
438  'ar_title' => $this->title->getDBkey() ],
439  __METHOD__
440  );
441 
442  return (bool)$row;
443  }
444 
466  public function undeleteAsUser(
467  $timestamps,
468  User $user,
469  $comment = '',
470  $fileVersions = [],
471  $unsuppress = false,
472  $tags = null
473  ) {
474  // If both the set of text revisions and file revisions are empty,
475  // restore everything. Otherwise, just restore the requested items.
476  $restoreAll = empty( $timestamps ) && empty( $fileVersions );
477 
478  $restoreText = $restoreAll || !empty( $timestamps );
479  $restoreFiles = $restoreAll || !empty( $fileVersions );
480 
481  if ( $restoreFiles && $this->title->getNamespace() === NS_FILE ) {
483  $img = MediaWikiServices::getInstance()->getRepoGroup()->getLocalRepo()
484  ->newFile( $this->title );
485  $img->load( File::READ_LATEST );
486  $this->fileStatus = $img->restore( $fileVersions, $unsuppress );
487  if ( !$this->fileStatus->isOK() ) {
488  return false;
489  }
490  $filesRestored = $this->fileStatus->successCount;
491  } else {
492  $filesRestored = 0;
493  }
494 
495  if ( $restoreText ) {
496  $this->revisionStatus = $this->undeleteRevisions( $timestamps, $unsuppress, $comment );
497  if ( !$this->revisionStatus->isOK() ) {
498  return false;
499  }
500 
501  $textRestored = $this->revisionStatus->getValue();
502  } else {
503  $textRestored = 0;
504  }
505 
506  // Touch the log!
507 
508  if ( !$textRestored && !$filesRestored ) {
509  wfDebug( "Undelete: nothing undeleted..." );
510 
511  return false;
512  }
513 
514  $logEntry = new ManualLogEntry( 'delete', 'restore' );
515  $logEntry->setPerformer( $user );
516  $logEntry->setTarget( $this->title );
517  $logEntry->setComment( $comment );
518  $logEntry->addTags( $tags );
519  $logEntry->setParameters( [
520  ':assoc:count' => [
521  'revisions' => $textRestored,
522  'files' => $filesRestored,
523  ],
524  ] );
525 
526  $this->getHookRunner()->onArticleUndeleteLogEntry( $this, $logEntry, $user );
527 
528  $logid = $logEntry->insert();
529  $logEntry->publish( $logid );
530 
531  return [ $textRestored, $filesRestored, $comment ];
532  }
533 
545  private function undeleteRevisions( $timestamps, $unsuppress = false, $comment = '' ) {
546  if ( wfReadOnly() ) {
547  throw new ReadOnlyError();
548  }
549 
550  $dbw = wfGetDB( DB_MASTER );
551  $dbw->startAtomic( __METHOD__ );
552 
553  $restoreAll = empty( $timestamps );
554 
555  # Does this page already exist? We'll have to update it...
556  $article = MediaWikiServices::getInstance()->getWikiPageFactory()->newFromTitle( $this->title );
557  # Load latest data for the current page (T33179)
558  $article->loadPageData( 'fromdbmaster' );
559  $oldcountable = $article->isCountable();
560 
561  if ( $article->exists() ) {
562  # Page already exists. Import the history, and if necessary
563  # we'll update the latest revision field in the record.
564  $makepage = false;
565 
566  $page = $dbw->selectRow( 'page',
567  [ 'page_id', 'page_latest' ],
568  [ 'page_namespace' => $this->title->getNamespace(),
569  'page_title' => $this->title->getDBkey() ],
570  __METHOD__,
571  [ 'FOR UPDATE' ] // lock page for WikiPage::updateRevisionOn call
572  );
573 
574  # Get the time span of this page
575  $previousTimestamp = $dbw->selectField( 'revision', 'rev_timestamp',
576  [ 'rev_id' => $page->page_latest ],
577  __METHOD__ );
578 
579  if ( $previousTimestamp === false ) {
580  wfDebug( __METHOD__ . ": existing page refers to a page_latest that does not exist" );
581 
582  $status = Status::newGood( 0 );
583  $status->warning( 'undeleterevision-missing' );
584  $dbw->endAtomic( __METHOD__ );
585 
586  return $status;
587  }
588  } else {
589  # Have to create a new article...
590  $makepage = true;
591  $previousTimestamp = 0;
592  }
593 
594  $oldWhere = [
595  'ar_namespace' => $this->title->getNamespace(),
596  'ar_title' => $this->title->getDBkey(),
597  ];
598  if ( !$restoreAll ) {
599  $oldWhere['ar_timestamp'] = array_map( [ &$dbw, 'timestamp' ], $timestamps );
600  }
601 
602  $revisionStore = $this->getRevisionStore();
603  $queryInfo = $revisionStore->getArchiveQueryInfo();
604  $queryInfo['tables'][] = 'revision';
605  $queryInfo['fields'][] = 'rev_id';
606  $queryInfo['joins']['revision'] = [ 'LEFT JOIN', 'ar_rev_id=rev_id' ];
607 
611  $result = $dbw->select(
612  $queryInfo['tables'],
613  $queryInfo['fields'],
614  $oldWhere,
615  __METHOD__,
616  /* options */
617  [ 'ORDER BY' => 'ar_timestamp' ],
618  $queryInfo['joins']
619  );
620 
621  $rev_count = $result->numRows();
622  if ( !$rev_count ) {
623  wfDebug( __METHOD__ . ": no revisions to restore" );
624 
625  $status = Status::newGood( 0 );
626  $status->warning( "undelete-no-results" );
627  $dbw->endAtomic( __METHOD__ );
628 
629  return $status;
630  }
631 
632  // We use ar_id because there can be duplicate ar_rev_id even for the same
633  // page. In this case, we may be able to restore the first one.
634  $restoreFailedArIds = [];
635 
636  // Map rev_id to the ar_id that is allowed to use it. When checking later,
637  // if it doesn't match, the current ar_id can not be restored.
638 
639  // Value can be an ar_id or -1 (-1 means no ar_id can use it, since the
640  // rev_id is taken before we even start the restore).
641  $allowedRevIdToArIdMap = [];
642 
643  $latestRestorableRow = null;
644 
645  foreach ( $result as $row ) {
646  if ( $row->ar_rev_id ) {
647  // rev_id is taken even before we start restoring.
648  if ( $row->ar_rev_id === $row->rev_id ) {
649  $restoreFailedArIds[] = $row->ar_id;
650  $allowedRevIdToArIdMap[$row->ar_rev_id] = -1;
651  } else {
652  // rev_id is not taken yet in the DB, but it might be taken
653  // by a prior revision in the same restore operation. If
654  // not, we need to reserve it.
655  if ( isset( $allowedRevIdToArIdMap[$row->ar_rev_id] ) ) {
656  $restoreFailedArIds[] = $row->ar_id;
657  } else {
658  $allowedRevIdToArIdMap[$row->ar_rev_id] = $row->ar_id;
659  $latestRestorableRow = $row;
660  }
661  }
662  } else {
663  // If ar_rev_id is null, there can't be a collision, and a
664  // rev_id will be chosen automatically.
665  $latestRestorableRow = $row;
666  }
667  }
668 
669  $result->seek( 0 ); // move back
670 
671  $oldPageId = 0;
672  if ( $latestRestorableRow !== null ) {
673  $oldPageId = (int)$latestRestorableRow->ar_page_id; // pass this to ArticleUndelete hook
674 
675  // Grab the content to check consistency with global state before restoring the page.
676  // XXX: The only current use case is Wikibase, which tries to enforce uniqueness of
677  // certain things across all pages. There may be a better way to do that.
678  $revision = $revisionStore->newRevisionFromArchiveRow(
679  $latestRestorableRow,
680  0,
681  $this->title
682  );
683 
684  // TODO: use User::newFromUserIdentity from If610c68f4912e
685  // TODO: The User isn't used for anything in prepareSave()! We should drop it.
686  $user = User::newFromName( $revision->getUser( RevisionRecord::RAW )->getName(), false );
687 
688  foreach ( $revision->getSlotRoles() as $role ) {
689  $content = $revision->getContent( $role, RevisionRecord::RAW );
690 
691  // NOTE: article ID may not be known yet. prepareSave() should not modify the database.
692  $status = $content->prepareSave( $article, 0, -1, $user );
693  if ( !$status->isOK() ) {
694  $dbw->endAtomic( __METHOD__ );
695 
696  return $status;
697  }
698  }
699  }
700 
701  $newid = false; // newly created page ID
702  $restored = 0; // number of revisions restored
704  $revision = null;
705  $restoredPages = [];
706  // If there are no restorable revisions, we can skip most of the steps.
707  if ( $latestRestorableRow === null ) {
708  $failedRevisionCount = $rev_count;
709  } else {
710  if ( $makepage ) {
711  // Check the state of the newest to-be version...
712  if ( !$unsuppress
713  && ( $latestRestorableRow->ar_deleted & RevisionRecord::DELETED_TEXT )
714  ) {
715  $dbw->endAtomic( __METHOD__ );
716 
717  return Status::newFatal( "undeleterevdel" );
718  }
719  // Safe to insert now...
720  $newid = $article->insertOn( $dbw, $latestRestorableRow->ar_page_id );
721  if ( $newid === false ) {
722  // The old ID is reserved; let's pick another
723  $newid = $article->insertOn( $dbw );
724  }
725  $pageId = $newid;
726  } else {
727  // Check if a deleted revision will become the current revision...
728  if ( $latestRestorableRow->ar_timestamp > $previousTimestamp ) {
729  // Check the state of the newest to-be version...
730  if ( !$unsuppress
731  && ( $latestRestorableRow->ar_deleted & RevisionRecord::DELETED_TEXT )
732  ) {
733  $dbw->endAtomic( __METHOD__ );
734 
735  return Status::newFatal( "undeleterevdel" );
736  }
737  }
738 
739  $newid = false;
740  $pageId = $article->getId();
741  }
742 
743  foreach ( $result as $row ) {
744  // Check for key dupes due to needed archive integrity.
745  if ( $row->ar_rev_id && $allowedRevIdToArIdMap[$row->ar_rev_id] !== $row->ar_id ) {
746  continue;
747  }
748  // Insert one revision at a time...maintaining deletion status
749  // unless we are specifically removing all restrictions...
750  $revision = $revisionStore->newRevisionFromArchiveRow(
751  $row,
752  0,
753  $this->title,
754  [
755  'page_id' => $pageId,
756  'deleted' => $unsuppress ? 0 : $row->ar_deleted
757  ]
758  );
759 
760  // This will also copy the revision to ip_changes if it was an IP edit.
761  $revisionStore->insertRevisionOn( $revision, $dbw );
762 
763  $restored++;
764 
765  $hookRunner = $this->getHookRunner();
766  $hookRunner->onRevisionUndeleted( $revision, $row->ar_page_id );
767 
768  // Hook is hard deprecated since 1.35
769  if ( $this->getHookContainer()->isRegistered( 'ArticleRevisionUndeleted' ) ) {
770  // Only create the Revision object if it is needed
771  $legacyRevision = new Revision( $revision );
772  $hookRunner->onArticleRevisionUndeleted(
773  $this->title,
774  $legacyRevision,
775  $row->ar_page_id
776  );
777  }
778  $restoredPages[$row->ar_page_id] = true;
779  }
780 
781  // Now that it's safely stored, take it out of the archive
782  // Don't delete rows that we failed to restore
783  $toDeleteConds = $oldWhere;
784  $failedRevisionCount = count( $restoreFailedArIds );
785  if ( $failedRevisionCount > 0 ) {
786  $toDeleteConds[] = 'ar_id NOT IN ( ' . $dbw->makeList( $restoreFailedArIds ) . ' )';
787  }
788 
789  $dbw->delete( 'archive',
790  $toDeleteConds,
791  __METHOD__ );
792  }
793 
794  $status = Status::newGood( $restored );
795 
796  if ( $failedRevisionCount > 0 ) {
797  $status->warning(
798  wfMessage( 'undeleterevision-duplicate-revid', $failedRevisionCount ) );
799  }
800 
801  // Was anything restored at all?
802  if ( $restored ) {
803  $created = (bool)$newid;
804 
805  $latestRevId = $article->getLatest();
806  if ( $latestRevId ) {
807  // If not found (false), cast to 0 so that the page is updated
808  // Just to be on the safe side, even though it should always be found
809  $latestRevTimestamp = (int)$revisionStore->getTimestampFromId(
810  $latestRevId,
811  RevisionStore::READ_LATEST
812  );
813  } else {
814  $latestRevTimestamp = 0;
815  }
816 
817  if ( $revision->getTimestamp() > $latestRevTimestamp ) {
818  // Attach the latest revision to the page...
819  // XXX: updateRevisionOn should probably move into a PageStore service.
820  $wasnew = $article->updateRevisionOn(
821  $dbw,
822  $revision,
823  $latestRevId
824  );
825  } else {
826  $wasnew = false;
827  }
828 
829  if ( $created || $wasnew ) {
830  // Update site stats, link tables, etc
831  // TODO: use DerivedPageDataUpdater from If610c68f4912e!
832  $article->doEditUpdates(
833  $revision,
834  User::newFromName( $revision->getUser( RevisionRecord::RAW )->getName(), false ),
835  [
836  'created' => $created,
837  'oldcountable' => $oldcountable,
838  'restored' => true
839  ]
840  );
841  }
842 
843  $this->getHookRunner()->onArticleUndelete(
844  $this->title, $created, $comment, $oldPageId, $restoredPages );
845 
846  if ( $this->title->getNamespace() === NS_FILE ) {
848  $this->title,
849  'imagelinks',
850  [ 'causeAction' => 'file-restore' ]
851  );
852  JobQueueGroup::singleton()->lazyPush( $job );
853  }
854  }
855 
856  $dbw->endAtomic( __METHOD__ );
857 
858  return $status;
859  }
860 
864  public function getFileStatus() {
865  return $this->fileStatus;
866  }
867 
871  public function getRevisionStatus() {
872  return $this->revisionStatus;
873  }
874 }
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:261
LIST_OR
const LIST_OR
Definition: Defines.php:46
PageArchive\$fileStatus
Status null $fileStatus
Definition: PageArchive.php:39
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:363
PageArchive\getFileStatus
getFileStatus()
Definition: PageArchive.php:864
Revision\RevisionRecord
Page revision base class.
Definition: RevisionRecord.php:47
StatusValue\newFatal
static newFatal( $message,... $parameters)
Factory function for fatal errors.
Definition: StatusValue.php:70
PageArchive\$revisionStatus
Status null $revisionStatus
Definition: PageArchive.php:42
PageArchive\__construct
__construct(Title $title, Config $config=null)
Definition: PageArchive.php:47
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:173
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:466
Revision\RevisionStore
Service for looking up page revisions.
Definition: RevisionStore.php:89
PageArchive\listPages
static listPages( $dbr, $condition)
Definition: PageArchive.php:155
wfTimestamp
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Definition: GlobalFunctions.php:1832
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:129
wfReadOnly
wfReadOnly()
Check whether the wiki is in read-only mode.
Definition: GlobalFunctions.php:1135
User\newFromName
static newFromName( $name, $validate='valid')
Definition: User.php:586
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:1231
PageArchive\listFiles
listFiles()
List the deleted file revisions for this page, if it's a file page.
Definition: PageArchive.php:221
PageArchive\listPagesBySearch
static listPagesBySearch( $term)
List deleted pages recorded in the archive matching the given term, using search engine archive.
Definition: PageArchive.php:79
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:432
PageArchive\getRevisionStore
getRevisionStore()
Definition: PageArchive.php:59
$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:295
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:860
Config
Interface for configuration instances.
Definition: Config.php:30
Title\getDBkey
getDBkey()
Get the main part with underscores.
Definition: Title.php:1059
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that $function is deprecated.
Definition: GlobalFunctions.php:1034
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:1068
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:545
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:179
PageArchive\getPreviousRevisionRecord
getPreviousRevisionRecord(string $timestamp)
Return the most-previous revision, either live or deleted, against the deleted revision given by time...
Definition: PageArchive.php:362
PageArchive\getRevisionStatus
getRevisionStatus()
Definition: PageArchive.php:871
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:915
PageArchive\getLastRevisionId
getLastRevisionId()
Returns the ID of the latest deleted revision.
Definition: PageArchive.php:412
$content
$content
Definition: router.php:76
PageArchive\getRevision
getRevision( $timestamp)
Return a Revision object containing data for the deleted revision.
Definition: PageArchive.php:247
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:277
PageArchive\doesWrites
doesWrites()
Definition: PageArchive.php:67
PageArchive\getRevisionByConditions
getRevisionByConditions(array $conditions, array $options=[])
Definition: PageArchive.php:305
Title
Represents a title within MediaWiki.
Definition: Title.php:46
JobQueueGroup\singleton
static singleton( $domain=false)
Definition: JobQueueGroup.php:70
$job
if(count( $args)< 1) $job
Definition: recompressTracked.php:50
ManualLogEntry
Class for creating new log entries and inserting them into the database.
Definition: ManualLogEntry.php:43
$t
$t
Definition: testCompression.php:74
NS_FILE
const NS_FILE
Definition: Defines.php:70
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:66
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:1041
PageArchive\getPreviousRevision
getPreviousRevision( $timestamp)
Return the most-previous revision, either live or deleted, against the deleted revision given by time...
Definition: PageArchive.php:342