MediaWiki REL1_35
PageArchive.php
Go to the documentation of this file.
1<?php
21use MediaWiki\HookContainer\ProtectedHookAccessorTrait;
25use Wikimedia\Assert\Assert;
28
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
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 ) {
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
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
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 ) {
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 = [] ) {
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 ) {
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() {
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() {
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}
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfReadOnly()
Check whether the wiki is in read-only mode.
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that $function is deprecated.
static getQueryInfo()
Return the tables, fields, and join conditions to be selected to create a new archivedfile object.
static modifyDisplayQuery(&$tables, &$fields, &$conds, &$join_conds, &$options, $filter_tag='')
Applies all tags-related changes to a query.
static newForBacklinks(Title $title, $table, $params=[])
MediaWiki exception.
Class for creating new log entries and inserting them into the database.
MediaWikiServices is the service locator for the application scope of MediaWiki.
Page revision base class.
Service for looking up page revisions.
Used to show archived pages and eventually restore them.
listFiles()
List the deleted file revisions for this page, if it's a file page.
undeleteAsUser( $timestamps, User $user, $comment='', $fileVersions=[], $unsuppress=false, $tags=null)
Restore the given (or all) text and file revisions for the page.
getPreviousRevisionRecord(string $timestamp)
Return the most-previous revision, either live or deleted, against the deleted revision given by time...
Config $config
Status $fileStatus
static listPages( $dbr, $condition)
static listPagesBySearch( $term)
List deleted pages recorded in the archive matching the given term, using search engine archive.
getArchivedRevisionRecord(int $revId)
Return the archived revision with the given ID.
getRevisionRecordByTimestamp( $timestamp)
Return a RevisionRecord object containing data for the deleted revision.
getLastRevisionId()
Returns the ID of the latest deleted revision.
getArchivedRevision( $revId)
Return the archived revision with the given ID.
listRevisions()
List the revisions of the given page.
undeleteRevisions( $timestamps, $unsuppress=false, $comment='')
This is the meaty bit – It restores archived revisions of the given page to the revision table.
undelete( $timestamps, $comment='', $fileVersions=[], $unsuppress=false, User $user=null, $tags=null)
Restore the given (or all) text and file revisions for the page.
static listPagesByPrefix( $prefix)
List deleted pages recorded in the archive table matching the given title prefix.
getRevision( $timestamp)
Return a Revision object containing data for the deleted revision.
Status $revisionStatus
__construct( $title, Config $config=null)
isDeleted()
Quick check if any archived revisions are present for the page.
getPreviousRevision( $timestamp)
Return the most-previous revision, either live or deleted, against the deleted revision given by time...
getRevisionByConditions(array $conditions, array $options=[])
Show an error when the wiki is locked/read-only and the user tries to do something that requires writ...
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition Status.php:44
Represents a title within MediaWiki.
Definition Title.php:42
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition User.php:60
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition User.php:541
const NS_FILE
Definition Defines.php:76
const LIST_OR
Definition Defines.php:52
Interface for configuration instances.
Definition Config.php:30
Basic database interface for live and lazy-loaded relation database handles.
Definition IDatabase.php:38
Result wrapper for grabbing data queried from an IDatabase object.
const DB_REPLICA
Definition defines.php:25
const DB_MASTER
Definition defines.php:29
if(count( $args)< 1) $job
$content
Definition router.php:76