MediaWiki  1.29.1
PageArchive.php
Go to the documentation of this file.
1 <?php
24 
28 class PageArchive {
30  protected $title;
31 
33  protected $fileStatus;
34 
36  protected $revisionStatus;
37 
39  protected $config;
40 
41  public function __construct( $title, Config $config = null ) {
42  if ( is_null( $title ) ) {
43  throw new MWException( __METHOD__ . ' given a null title.' );
44  }
45  $this->title = $title;
46  if ( $config === null ) {
47  wfDebug( __METHOD__ . ' did not have a Config object passed to it' );
48  $config = MediaWikiServices::getInstance()->getMainConfig();
49  }
50  $this->config = $config;
51  }
52 
53  public function doesWrites() {
54  return true;
55  }
56 
64  public static function listAllPages() {
65  $dbr = wfGetDB( DB_REPLICA );
66 
67  return self::listPages( $dbr, '' );
68  }
69 
78  public static function listPagesBySearch( $term ) {
80  if ( $title ) {
81  $ns = $title->getNamespace();
82  $termMain = $title->getText();
83  $termDb = $title->getDBkey();
84  } else {
85  // Prolly won't work too good
86  // @todo handle bare namespace names cleanly?
87  $ns = 0;
88  $termMain = $termDb = $term;
89  }
90 
91  // Try search engine first
92  $engine = MediaWikiServices::getInstance()->newSearchEngine();
93  $engine->setLimitOffset( 100 );
94  $engine->setNamespaces( [ $ns ] );
95  $results = $engine->searchArchiveTitle( $termMain );
96  if ( !$results->isOK() ) {
97  $results = [];
98  } else {
99  $results = $results->getValue();
100  }
101 
102  if ( !$results ) {
103  // Fall back to regular prefix search
104  return self::listPagesByPrefix( $term );
105  }
106 
107  $dbr = wfGetDB( DB_REPLICA );
108  $condTitles = array_unique( array_map( function ( Title $t ) {
109  return $t->getDBkey();
110  }, $results ) );
111  $conds = [
112  'ar_namespace' => $ns,
113  $dbr->makeList( [ 'ar_title' => $condTitles ], LIST_OR ) . " OR ar_title " .
114  $dbr->buildLike( $termDb, $dbr->anyString() )
115  ];
116 
117  return self::listPages( $dbr, $conds );
118  }
119 
128  public static function listPagesByPrefix( $prefix ) {
129  $dbr = wfGetDB( DB_REPLICA );
130 
131  $title = Title::newFromText( $prefix );
132  if ( $title ) {
133  $ns = $title->getNamespace();
134  $prefix = $title->getDBkey();
135  } else {
136  // Prolly won't work too good
137  // @todo handle bare namespace names cleanly?
138  $ns = 0;
139  }
140 
141  $conds = [
142  'ar_namespace' => $ns,
143  'ar_title' . $dbr->buildLike( $prefix, $dbr->anyString() ),
144  ];
145 
146  return self::listPages( $dbr, $conds );
147  }
148 
154  protected static function listPages( $dbr, $condition ) {
155  return $dbr->select(
156  [ 'archive' ],
157  [
158  'ar_namespace',
159  'ar_title',
160  'count' => 'COUNT(*)'
161  ],
162  $condition,
163  __METHOD__,
164  [
165  'GROUP BY' => [ 'ar_namespace', 'ar_title' ],
166  'ORDER BY' => [ 'ar_namespace', 'ar_title' ],
167  'LIMIT' => 100,
168  ]
169  );
170  }
171 
178  public function listRevisions() {
179  $dbr = wfGetDB( DB_REPLICA );
180 
181  $tables = [ 'archive' ];
182 
183  $fields = [
184  'ar_minor_edit', 'ar_timestamp', 'ar_user', 'ar_user_text',
185  'ar_comment', 'ar_len', 'ar_deleted', 'ar_rev_id', 'ar_sha1',
186  'ar_page_id'
187  ];
188 
189  if ( $this->config->get( 'ContentHandlerUseDB' ) ) {
190  $fields[] = 'ar_content_format';
191  $fields[] = 'ar_content_model';
192  }
193 
194  $conds = [ 'ar_namespace' => $this->title->getNamespace(),
195  'ar_title' => $this->title->getDBkey() ];
196 
197  $options = [ 'ORDER BY' => 'ar_timestamp DESC' ];
198 
199  $join_conds = [];
200 
202  $tables,
203  $fields,
204  $conds,
205  $join_conds,
206  $options,
207  ''
208  );
209 
210  return $dbr->select( $tables,
211  $fields,
212  $conds,
213  __METHOD__,
214  $options,
215  $join_conds
216  );
217  }
218 
227  public function listFiles() {
228  if ( $this->title->getNamespace() != NS_FILE ) {
229  return null;
230  }
231 
232  $dbr = wfGetDB( DB_REPLICA );
233  return $dbr->select(
234  'filearchive',
236  [ 'fa_name' => $this->title->getDBkey() ],
237  __METHOD__,
238  [ 'ORDER BY' => 'fa_timestamp DESC' ]
239  );
240  }
241 
249  public function getRevision( $timestamp ) {
250  $dbr = wfGetDB( DB_REPLICA );
251 
252  $fields = [
253  'ar_rev_id',
254  'ar_text',
255  'ar_comment',
256  'ar_user',
257  'ar_user_text',
258  'ar_timestamp',
259  'ar_minor_edit',
260  'ar_flags',
261  'ar_text_id',
262  'ar_deleted',
263  'ar_len',
264  'ar_sha1',
265  ];
266 
267  if ( $this->config->get( 'ContentHandlerUseDB' ) ) {
268  $fields[] = 'ar_content_format';
269  $fields[] = 'ar_content_model';
270  }
271 
272  $row = $dbr->selectRow( 'archive',
273  $fields,
274  [ 'ar_namespace' => $this->title->getNamespace(),
275  'ar_title' => $this->title->getDBkey(),
276  'ar_timestamp' => $dbr->timestamp( $timestamp ) ],
277  __METHOD__ );
278 
279  if ( $row ) {
280  return Revision::newFromArchiveRow( $row, [ 'title' => $this->title ] );
281  }
282 
283  return null;
284  }
285 
296  public function getPreviousRevision( $timestamp ) {
297  $dbr = wfGetDB( DB_REPLICA );
298 
299  // Check the previous deleted revision...
300  $row = $dbr->selectRow( 'archive',
301  'ar_timestamp',
302  [ 'ar_namespace' => $this->title->getNamespace(),
303  'ar_title' => $this->title->getDBkey(),
304  'ar_timestamp < ' .
305  $dbr->addQuotes( $dbr->timestamp( $timestamp ) ) ],
306  __METHOD__,
307  [
308  'ORDER BY' => 'ar_timestamp DESC',
309  'LIMIT' => 1 ] );
310  $prevDeleted = $row ? wfTimestamp( TS_MW, $row->ar_timestamp ) : false;
311 
312  $row = $dbr->selectRow( [ 'page', 'revision' ],
313  [ 'rev_id', 'rev_timestamp' ],
314  [
315  'page_namespace' => $this->title->getNamespace(),
316  'page_title' => $this->title->getDBkey(),
317  'page_id = rev_page',
318  'rev_timestamp < ' .
319  $dbr->addQuotes( $dbr->timestamp( $timestamp ) ) ],
320  __METHOD__,
321  [
322  'ORDER BY' => 'rev_timestamp DESC',
323  'LIMIT' => 1 ] );
324  $prevLive = $row ? wfTimestamp( TS_MW, $row->rev_timestamp ) : false;
325  $prevLiveId = $row ? intval( $row->rev_id ) : null;
326 
327  if ( $prevLive && $prevLive > $prevDeleted ) {
328  // Most prior revision was live
329  return Revision::newFromId( $prevLiveId );
330  } elseif ( $prevDeleted ) {
331  // Most prior revision was deleted
332  return $this->getRevision( $prevDeleted );
333  }
334 
335  // No prior revision on this page.
336  return null;
337  }
338 
345  public function getTextFromRow( $row ) {
346  if ( is_null( $row->ar_text_id ) ) {
347  // An old row from MediaWiki 1.4 or previous.
348  // Text is embedded in this row in classic compression format.
349  return Revision::getRevisionText( $row, 'ar_' );
350  }
351 
352  // New-style: keyed to the text storage backend.
353  $dbr = wfGetDB( DB_REPLICA );
354  $text = $dbr->selectRow( 'text',
355  [ 'old_text', 'old_flags' ],
356  [ 'old_id' => $row->ar_text_id ],
357  __METHOD__ );
358 
359  return Revision::getRevisionText( $text );
360  }
361 
370  public function getLastRevisionText() {
371  $dbr = wfGetDB( DB_REPLICA );
372  $row = $dbr->selectRow( 'archive',
373  [ 'ar_text', 'ar_flags', 'ar_text_id' ],
374  [ 'ar_namespace' => $this->title->getNamespace(),
375  'ar_title' => $this->title->getDBkey() ],
376  __METHOD__,
377  [ 'ORDER BY' => 'ar_timestamp DESC' ] );
378 
379  if ( $row ) {
380  return $this->getTextFromRow( $row );
381  }
382 
383  return null;
384  }
385 
391  public function isDeleted() {
392  $dbr = wfGetDB( DB_REPLICA );
393  $n = $dbr->selectField( 'archive', 'COUNT(ar_title)',
394  [ 'ar_namespace' => $this->title->getNamespace(),
395  'ar_title' => $this->title->getDBkey() ],
396  __METHOD__
397  );
398 
399  return ( $n > 0 );
400  }
401 
421  public function undelete( $timestamps, $comment = '', $fileVersions = [],
422  $unsuppress = false, User $user = null, $tags = null
423  ) {
424  // If both the set of text revisions and file revisions are empty,
425  // restore everything. Otherwise, just restore the requested items.
426  $restoreAll = empty( $timestamps ) && empty( $fileVersions );
427 
428  $restoreText = $restoreAll || !empty( $timestamps );
429  $restoreFiles = $restoreAll || !empty( $fileVersions );
430 
431  if ( $restoreFiles && $this->title->getNamespace() == NS_FILE ) {
432  $img = wfLocalFile( $this->title );
433  $img->load( File::READ_LATEST );
434  $this->fileStatus = $img->restore( $fileVersions, $unsuppress );
435  if ( !$this->fileStatus->isOK() ) {
436  return false;
437  }
438  $filesRestored = $this->fileStatus->successCount;
439  } else {
440  $filesRestored = 0;
441  }
442 
443  if ( $restoreText ) {
444  $this->revisionStatus = $this->undeleteRevisions( $timestamps, $unsuppress, $comment );
445  if ( !$this->revisionStatus->isOK() ) {
446  return false;
447  }
448 
449  $textRestored = $this->revisionStatus->getValue();
450  } else {
451  $textRestored = 0;
452  }
453 
454  // Touch the log!
455 
456  if ( !$textRestored && !$filesRestored ) {
457  wfDebug( "Undelete: nothing undeleted...\n" );
458 
459  return false;
460  }
461 
462  if ( $user === null ) {
463  global $wgUser;
464  $user = $wgUser;
465  }
466 
467  $logEntry = new ManualLogEntry( 'delete', 'restore' );
468  $logEntry->setPerformer( $user );
469  $logEntry->setTarget( $this->title );
470  $logEntry->setComment( $comment );
471  $logEntry->setTags( $tags );
472  $logEntry->setParameters( [
473  ':assoc:count' => [
474  'revisions' => $textRestored,
475  'files' => $filesRestored,
476  ],
477  ] );
478 
479  Hooks::run( 'ArticleUndeleteLogEntry', [ $this, &$logEntry, $user ] );
480 
481  $logid = $logEntry->insert();
482  $logEntry->publish( $logid );
483 
484  return [ $textRestored, $filesRestored, $comment ];
485  }
486 
498  private function undeleteRevisions( $timestamps, $unsuppress = false, $comment = '' ) {
499  if ( wfReadOnly() ) {
500  throw new ReadOnlyError();
501  }
502 
503  $dbw = wfGetDB( DB_MASTER );
504  $dbw->startAtomic( __METHOD__ );
505 
506  $restoreAll = empty( $timestamps );
507 
508  # Does this page already exist? We'll have to update it...
509  $article = WikiPage::factory( $this->title );
510  # Load latest data for the current page (T33179)
511  $article->loadPageData( 'fromdbmaster' );
512  $oldcountable = $article->isCountable();
513 
514  $page = $dbw->selectRow( 'page',
515  [ 'page_id', 'page_latest' ],
516  [ 'page_namespace' => $this->title->getNamespace(),
517  'page_title' => $this->title->getDBkey() ],
518  __METHOD__,
519  [ 'FOR UPDATE' ] // lock page
520  );
521 
522  if ( $page ) {
523  $makepage = false;
524  # Page already exists. Import the history, and if necessary
525  # we'll update the latest revision field in the record.
526 
527  # Get the time span of this page
528  $previousTimestamp = $dbw->selectField( 'revision', 'rev_timestamp',
529  [ 'rev_id' => $page->page_latest ],
530  __METHOD__ );
531 
532  if ( $previousTimestamp === false ) {
533  wfDebug( __METHOD__ . ": existing page refers to a page_latest that does not exist\n" );
534 
535  $status = Status::newGood( 0 );
536  $status->warning( 'undeleterevision-missing' );
537  $dbw->endAtomic( __METHOD__ );
538 
539  return $status;
540  }
541  } else {
542  # Have to create a new article...
543  $makepage = true;
544  $previousTimestamp = 0;
545  }
546 
547  $oldWhere = [
548  'ar_namespace' => $this->title->getNamespace(),
549  'ar_title' => $this->title->getDBkey(),
550  ];
551  if ( !$restoreAll ) {
552  $oldWhere['ar_timestamp'] = array_map( [ &$dbw, 'timestamp' ], $timestamps );
553  }
554 
555  $fields = [
556  'ar_id',
557  'ar_rev_id',
558  'rev_id',
559  'ar_text',
560  'ar_comment',
561  'ar_user',
562  'ar_user_text',
563  'ar_timestamp',
564  'ar_minor_edit',
565  'ar_flags',
566  'ar_text_id',
567  'ar_deleted',
568  'ar_page_id',
569  'ar_len',
570  'ar_sha1'
571  ];
572 
573  if ( $this->config->get( 'ContentHandlerUseDB' ) ) {
574  $fields[] = 'ar_content_format';
575  $fields[] = 'ar_content_model';
576  }
577 
581  $result = $dbw->select(
582  [ 'archive', 'revision' ],
583  $fields,
584  $oldWhere,
585  __METHOD__,
586  /* options */
587  [ 'ORDER BY' => 'ar_timestamp' ],
588  [ 'revision' => [ 'LEFT JOIN', 'ar_rev_id=rev_id' ] ]
589  );
590 
591  $rev_count = $result->numRows();
592  if ( !$rev_count ) {
593  wfDebug( __METHOD__ . ": no revisions to restore\n" );
594 
595  $status = Status::newGood( 0 );
596  $status->warning( "undelete-no-results" );
597  $dbw->endAtomic( __METHOD__ );
598 
599  return $status;
600  }
601 
602  // We use ar_id because there can be duplicate ar_rev_id even for the same
603  // page. In this case, we may be able to restore the first one.
604  $restoreFailedArIds = [];
605 
606  // Map rev_id to the ar_id that is allowed to use it. When checking later,
607  // if it doesn't match, the current ar_id can not be restored.
608 
609  // Value can be an ar_id or -1 (-1 means no ar_id can use it, since the
610  // rev_id is taken before we even start the restore).
611  $allowedRevIdToArIdMap = [];
612 
613  $latestRestorableRow = null;
614 
615  foreach ( $result as $row ) {
616  if ( $row->ar_rev_id ) {
617  // rev_id is taken even before we start restoring.
618  if ( $row->ar_rev_id === $row->rev_id ) {
619  $restoreFailedArIds[] = $row->ar_id;
620  $allowedRevIdToArIdMap[$row->ar_rev_id] = -1;
621  } else {
622  // rev_id is not taken yet in the DB, but it might be taken
623  // by a prior revision in the same restore operation. If
624  // not, we need to reserve it.
625  if ( isset( $allowedRevIdToArIdMap[$row->ar_rev_id] ) ) {
626  $restoreFailedArIds[] = $row->ar_id;
627  } else {
628  $allowedRevIdToArIdMap[$row->ar_rev_id] = $row->ar_id;
629  $latestRestorableRow = $row;
630  }
631  }
632  } else {
633  // If ar_rev_id is null, there can't be a collision, and a
634  // rev_id will be chosen automatically.
635  $latestRestorableRow = $row;
636  }
637  }
638 
639  $result->seek( 0 ); // move back
640 
641  $oldPageId = 0;
642  if ( $latestRestorableRow !== null ) {
643  $oldPageId = (int)$latestRestorableRow->ar_page_id; // pass this to ArticleUndelete hook
644 
645  // grab the content to check consistency with global state before restoring the page.
646  $revision = Revision::newFromArchiveRow( $latestRestorableRow,
647  [
648  'title' => $article->getTitle(), // used to derive default content model
649  ]
650  );
651  $user = User::newFromName( $revision->getUserText( Revision::RAW ), false );
652  $content = $revision->getContent( Revision::RAW );
653 
654  // NOTE: article ID may not be known yet. prepareSave() should not modify the database.
655  $status = $content->prepareSave( $article, 0, -1, $user );
656  if ( !$status->isOK() ) {
657  $dbw->endAtomic( __METHOD__ );
658 
659  return $status;
660  }
661  }
662 
663  $newid = false; // newly created page ID
664  $restored = 0; // number of revisions restored
666  $revision = null;
667  $restoredPages = [];
668  // If there are no restorable revisions, we can skip most of the steps.
669  if ( $latestRestorableRow === null ) {
670  $failedRevisionCount = $rev_count;
671  } else {
672  if ( $makepage ) {
673  // Check the state of the newest to-be version...
674  if ( !$unsuppress
675  && ( $latestRestorableRow->ar_deleted & Revision::DELETED_TEXT )
676  ) {
677  $dbw->endAtomic( __METHOD__ );
678 
679  return Status::newFatal( "undeleterevdel" );
680  }
681  // Safe to insert now...
682  $newid = $article->insertOn( $dbw, $latestRestorableRow->ar_page_id );
683  if ( $newid === false ) {
684  // The old ID is reserved; let's pick another
685  $newid = $article->insertOn( $dbw );
686  }
687  $pageId = $newid;
688  } else {
689  // Check if a deleted revision will become the current revision...
690  if ( $latestRestorableRow->ar_timestamp > $previousTimestamp ) {
691  // Check the state of the newest to-be version...
692  if ( !$unsuppress
693  && ( $latestRestorableRow->ar_deleted & Revision::DELETED_TEXT )
694  ) {
695  $dbw->endAtomic( __METHOD__ );
696 
697  return Status::newFatal( "undeleterevdel" );
698  }
699  }
700 
701  $newid = false;
702  $pageId = $article->getId();
703  }
704 
705  foreach ( $result as $row ) {
706  // Check for key dupes due to needed archive integrity.
707  if ( $row->ar_rev_id && $allowedRevIdToArIdMap[$row->ar_rev_id] !== $row->ar_id ) {
708  continue;
709  }
710  // Insert one revision at a time...maintaining deletion status
711  // unless we are specifically removing all restrictions...
712  $revision = Revision::newFromArchiveRow( $row,
713  [
714  'page' => $pageId,
715  'title' => $this->title,
716  'deleted' => $unsuppress ? 0 : $row->ar_deleted
717  ] );
718 
719  $revision->insertOn( $dbw );
720  $restored++;
721 
722  Hooks::run( 'ArticleRevisionUndeleted',
723  [ &$this->title, $revision, $row->ar_page_id ] );
724  $restoredPages[$row->ar_page_id] = true;
725  }
726 
727  // Now that it's safely stored, take it out of the archive
728  // Don't delete rows that we failed to restore
729  $toDeleteConds = $oldWhere;
730  $failedRevisionCount = count( $restoreFailedArIds );
731  if ( $failedRevisionCount > 0 ) {
732  $toDeleteConds[] = 'ar_id NOT IN ( ' . $dbw->makeList( $restoreFailedArIds ) . ' )';
733  }
734 
735  $dbw->delete( 'archive',
736  $toDeleteConds,
737  __METHOD__ );
738  }
739 
740  $status = Status::newGood( $restored );
741 
742  if ( $failedRevisionCount > 0 ) {
743  $status->warning(
744  wfMessage( 'undeleterevision-duplicate-revid', $failedRevisionCount ) );
745  }
746 
747  // Was anything restored at all?
748  if ( $restored ) {
749  $created = (bool)$newid;
750  // Attach the latest revision to the page...
751  $wasnew = $article->updateIfNewerOn( $dbw, $revision );
752  if ( $created || $wasnew ) {
753  // Update site stats, link tables, etc
754  $article->doEditUpdates(
755  $revision,
756  User::newFromName( $revision->getUserText( Revision::RAW ), false ),
757  [
758  'created' => $created,
759  'oldcountable' => $oldcountable,
760  'restored' => true
761  ]
762  );
763  }
764 
765  Hooks::run( 'ArticleUndelete',
766  [ &$this->title, $created, $comment, $oldPageId, $restoredPages ] );
767  if ( $this->title->getNamespace() == NS_FILE ) {
768  DeferredUpdates::addUpdate( new HTMLCacheUpdate( $this->title, 'imagelinks' ) );
769  }
770  }
771 
772  $dbw->endAtomic( __METHOD__ );
773 
774  return $status;
775  }
776 
780  public function getFileStatus() {
781  return $this->fileStatus;
782  }
783 
787  public function getRevisionStatus() {
788  return $this->revisionStatus;
789  }
790 }
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
Revision\newFromArchiveRow
static newFromArchiveRow( $row, $overrides=[])
Make a fake revision object from an archive table row.
Definition: Revision.php:189
$wgUser
$wgUser
Definition: Setup.php:781
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:265
PageArchive\getFileStatus
getFileStatus()
Definition: PageArchive.php:780
false
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:189
$tables
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist Do not use this to implement individual filters if they are compatible with the ChangesListFilter and ChangesListFilterGroup structure use sub classes of those in conjunction with the ChangesListSpecialPageStructuredFilters hook This hook can be used to implement filters that do not implement that or custom behavior that is not an individual filter e g Watchlist & $tables
Definition: hooks.txt:990
Revision\newFromId
static newFromId( $id, $flags=0)
Load a page revision from a given revision ID number.
Definition: Revision.php:116
PageArchive
Used to show archived pages and eventually restore them.
Definition: PageArchive.php:28
captcha-old.count
count
Definition: captcha-old.py:225
PageArchive\listPages
static listPages( $dbr, $condition)
Definition: PageArchive.php:154
$result
The index of the header message $result[1]=The index of the body text message $result[2 through n]=Parameters passed to body text message. Please note the header message cannot receive/use parameters. 'ImportHandleLogItemXMLTag':When parsing a XML tag in a log item. Return false to stop further processing of the tag $reader:XMLReader object $logInfo:Array of information 'ImportHandlePageXMLTag':When parsing a XML tag in a page. Return false to stop further processing of the tag $reader:XMLReader object & $pageInfo:Array of information 'ImportHandleRevisionXMLTag':When parsing a XML tag in a page revision. Return false to stop further processing of the tag $reader:XMLReader object $pageInfo:Array of page information $revisionInfo:Array of revision information 'ImportHandleToplevelXMLTag':When parsing a top level XML tag. Return false to stop further processing of the tag $reader:XMLReader object 'ImportHandleUploadXMLTag':When parsing a XML tag in a file upload. Return false to stop further processing of the tag $reader:XMLReader object $revisionInfo:Array of information 'ImportLogInterwikiLink':Hook to change the interwiki link used in log entries and edit summaries for transwiki imports. & $fullInterwikiPrefix:Interwiki prefix, may contain colons. & $pageTitle:String that contains page title. 'ImportSources':Called when reading from the $wgImportSources configuration variable. Can be used to lazy-load the import sources list. & $importSources:The value of $wgImportSources. Modify as necessary. See the comment in DefaultSettings.php for the detail of how to structure this array. 'InfoAction':When building information to display on the action=info page. $context:IContextSource object & $pageInfo:Array of information 'InitializeArticleMaybeRedirect':MediaWiki check to see if title is a redirect. & $title:Title object for the current page & $request:WebRequest & $ignoreRedirect:boolean to skip redirect check & $target:Title/string of redirect target & $article:Article object 'InternalParseBeforeLinks':during Parser 's internalParse method before links but after nowiki/noinclude/includeonly/onlyinclude and other processings. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InternalParseBeforeSanitize':during Parser 's internalParse method just before the parser removes unwanted/dangerous HTML tags and after nowiki/noinclude/includeonly/onlyinclude and other processings. Ideal for syntax-extensions after template/parser function execution which respect nowiki and HTML-comments. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InterwikiLoadPrefix':When resolving if a given prefix is an interwiki or not. Return true without providing an interwiki to continue interwiki search. $prefix:interwiki prefix we are looking for. & $iwData:output array describing the interwiki with keys iw_url, iw_local, iw_trans and optionally iw_api and iw_wikiid. 'InvalidateEmailComplete':Called after a user 's email has been invalidated successfully. $user:user(object) whose email is being invalidated 'IRCLineURL':When constructing the URL to use in an IRC notification. Callee may modify $url and $query, URL will be constructed as $url . $query & $url:URL to index.php & $query:Query string $rc:RecentChange object that triggered url generation 'IsFileCacheable':Override the result of Article::isFileCacheable()(if true) & $article:article(object) being checked 'IsTrustedProxy':Override the result of IP::isTrustedProxy() & $ip:IP being check & $result:Change this value to override the result of IP::isTrustedProxy() 'IsUploadAllowedFromUrl':Override the result of UploadFromUrl::isAllowedUrl() $url:URL used to upload from & $allowed:Boolean indicating if uploading is allowed for given URL 'isValidEmailAddr':Override the result of Sanitizer::validateEmail(), for instance to return false if the domain name doesn 't match your organization. $addr:The e-mail address entered by the user & $result:Set this and return false to override the internal checks 'isValidPassword':Override the result of User::isValidPassword() $password:The password entered by the user & $result:Set this and return false to override the internal checks $user:User the password is being validated for 'Language::getMessagesFileName':$code:The language code or the language we 're looking for a messages file for & $file:The messages file path, you can override this to change the location. 'LanguageGetMagic':DEPRECATED! Use $magicWords in a file listed in $wgExtensionMessagesFiles instead. Use this to define synonyms of magic words depending of the language & $magicExtensions:associative array of magic words synonyms $lang:language code(string) 'LanguageGetNamespaces':Provide custom ordering for namespaces or remove namespaces. Do not use this hook to add namespaces. Use CanonicalNamespaces for that. & $namespaces:Array of namespaces indexed by their numbers 'LanguageGetSpecialPageAliases':DEPRECATED! Use $specialPageAliases in a file listed in $wgExtensionMessagesFiles instead. Use to define aliases of special pages names depending of the language & $specialPageAliases:associative array of magic words synonyms $lang:language code(string) 'LanguageGetTranslatedLanguageNames':Provide translated language names. & $names:array of language code=> language name $code:language of the preferred translations 'LanguageLinks':Manipulate a page 's language links. This is called in various places to allow extensions to define the effective language links for a page. $title:The page 's Title. & $links:Array with elements of the form "language:title" in the order that they will be output. & $linkFlags:Associative array mapping prefixed links to arrays of flags. Currently unused, but planned to provide support for marking individual language links in the UI, e.g. for featured articles. 'LanguageSelector':Hook to change the language selector available on a page. $out:The output page. $cssClassName:CSS class name of the language selector. 'LinkBegin':DEPRECATED! Use HtmlPageLinkRendererBegin instead. Used when generating internal and interwiki links in Linker::link(), before processing starts. Return false to skip default processing and return $ret. See documentation for Linker::link() for details on the expected meanings of parameters. $skin:the Skin object $target:the Title that the link is pointing to & $html:the contents that the< a > tag should have(raw HTML) $result
Definition: hooks.txt:1954
wfTimestamp
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Definition: GlobalFunctions.php:1994
PageArchive\$config
Config $config
Definition: PageArchive.php:39
$status
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist Do not use this to implement individual filters if they are compatible with the ChangesListFilter and ChangesListFilterGroup structure use sub classes of those in conjunction with the ChangesListSpecialPageStructuredFilters hook This hook can be used to implement filters that do not implement that or custom behavior that is not an individual filter e g Watchlist and Watchlist you will want to construct new ChangesListBooleanFilter or ChangesListStringOptionsFilter objects When constructing you specify which group they belong to You can reuse existing or create your you must register them with $special registerFilterGroup removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set $status
Definition: hooks.txt:1049
use
as see the revision history and available at free of to any person obtaining a copy of this software and associated documentation to deal in the Software without including without limitation the rights to use
Definition: MIT-LICENSE.txt:10
$user
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a account $user
Definition: hooks.txt:246
DeferredUpdates\addUpdate
static addUpdate(DeferrableUpdate $update, $stage=self::POSTSEND)
Add an update to the deferred list to be run later by execute()
Definition: DeferredUpdates.php:76
StatusValue\newFatal
static newFatal( $message)
Factory function for fatal errors.
Definition: StatusValue.php:63
PageArchive\listPagesByPrefix
static listPagesByPrefix( $prefix)
List deleted pages recorded in the archive table matching the given title prefix.
Definition: PageArchive.php:128
NS_FILE
const NS_FILE
Definition: Defines.php:68
wfReadOnly
wfReadOnly()
Check whether the wiki is in read-only mode.
Definition: GlobalFunctions.php:1277
Revision\getRevisionText
static getRevisionText( $row, $prefix='old_', $wiki=false)
Get revision text associated with an old or archive row.
Definition: Revision.php:1258
User\newFromName
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition: User.php:556
$term
external whereas SearchGetNearMatch runs after $term
Definition: hooks.txt:2759
PageArchive\listFiles
listFiles()
List the deleted file revisions for this page, if it's a file page.
Definition: PageArchive.php:227
Wikimedia\Rdbms\ResultWrapper
Result wrapper for grabbing data queried from an IDatabase object.
Definition: ResultWrapper.php:24
PageArchive\$revisionStatus
Status $revisionStatus
Definition: PageArchive.php:36
PageArchive\listPagesBySearch
static listPagesBySearch( $term)
List deleted pages recorded in the archive matching the given term, using search engine archive.
Definition: PageArchive.php:78
php
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
Wikimedia\Rdbms\IDatabase
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:40
PageArchive\isDeleted
isDeleted()
Quick check if any archived revisions are present for the page.
Definition: PageArchive.php:391
Status
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition: Status.php:40
LIST_OR
const LIST_OR
Definition: Defines.php:44
Title\getDBkey
getDBkey()
Get the main part with underscores.
Definition: Title.php:901
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:120
ChangeTags\modifyDisplayQuery
static modifyDisplayQuery(&$tables, &$fields, &$conds, &$join_conds, &$options, $filter_tag=false)
Applies all tags-related changes to a query.
Definition: ChangeTags.php:632
PageArchive\listAllPages
static listAllPages()
List all deleted pages recorded in the archive table.
Definition: PageArchive.php:64
Title\getNamespace
getNamespace()
Get the namespace index, i.e.
Definition: Title.php:924
$content
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist Do not use this to implement individual filters if they are compatible with the ChangesListFilter and ChangesListFilterGroup structure use sub classes of those in conjunction with the ChangesListSpecialPageStructuredFilters hook This hook can be used to implement filters that do not implement that or custom behavior that is not an individual filter e g Watchlist and Watchlist you will want to construct new ChangesListBooleanFilter or ChangesListStringOptionsFilter objects When constructing you specify which group they belong to You can reuse existing or create your you must register them with $special registerFilterGroup removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content $content
Definition: hooks.txt:1049
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:498
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:3060
$page
do that in ParserLimitReportFormat instead use this to modify the parameters of the image and a DIV can begin in one section and end in another Make sure your code can handle that case gracefully See the EditSectionClearerLink extension for an example zero but section is usually empty its values are the globals values before the output is cached $page
Definition: hooks.txt:2536
PageArchive\listRevisions
listRevisions()
List the revisions of the given page.
Definition: PageArchive.php:178
PageArchive\getRevisionStatus
getRevisionStatus()
Definition: PageArchive.php:787
$engine
the value to return A Title object or null for latest all implement SearchIndexField $engine
Definition: hooks.txt:2782
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:93
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:999
PageArchive\$fileStatus
Status $fileStatus
Definition: PageArchive.php:33
HTMLCacheUpdate
Class to invalidate the HTML cache of all the pages linking to a given title.
Definition: HTMLCacheUpdate.php:29
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:76
title
title
Definition: parserTests.txt:211
Revision\RAW
const RAW
Definition: Revision.php:100
PageArchive\doesWrites
doesWrites()
Definition: PageArchive.php:53
Title
Represents a title within MediaWiki.
Definition: Title.php:39
$dbr
if(! $regexes) $dbr
Definition: cleanup.php:94
PageArchive\getLastRevisionText
getLastRevisionText()
Fetch (and decompress if necessary) the stored text of the most recently edited deleted revision of t...
Definition: PageArchive.php:370
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:421
as
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
$article
Using a hook running we can avoid having all this option specific stuff in our mainline code Using the function array $article
Definition: hooks.txt:78
ManualLogEntry
Class for creating log entries manually, to inject them into the database.
Definition: LogEntry.php:396
PageArchive\getTextFromRow
getTextFromRow( $row)
Get the text from an archive row containing ar_text, ar_flags and ar_text_id.
Definition: PageArchive.php:345
wfMessage
either a unescaped string or a HtmlArmor object after in associative array form externallinks including delete and has completed for all link tables whether this was an auto creation default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a set this to the key of the message First element is the message additional optional elements are parameters for the key that are processed with wfMessage() -> params() ->parseAsBlock() - offset Set to overwrite offset parameter in $wgRequest set to '' to unset offset - wrap String Wrap the message in html(usually something like "&lt
$t
$t
Definition: testCompression.php:67
ArchivedFile\selectFields
static selectFields()
Fields in the filearchive table.
Definition: ArchivedFile.php:220
MediaWikiServices
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency MediaWikiServices
Definition: injection.txt:23
PageArchive\__construct
__construct( $title, Config $config=null)
Definition: PageArchive.php:41
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:50
wfLocalFile
wfLocalFile( $title)
Get an object referring to a locally registered file.
Definition: GlobalFunctions.php:3112
Hooks\run
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:131
PageArchive\$title
Title $title
Definition: PageArchive.php:30
Title\getText
getText()
Get the text form (spaces not underscores) of the main part.
Definition: Title.php:883
$options
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist Do not use this to implement individual filters if they are compatible with the ChangesListFilter and ChangesListFilterGroup structure use sub classes of those in conjunction with the ChangesListSpecialPageStructuredFilters hook This hook can be used to implement filters that do not implement that or custom behavior that is not an individual filter e g Watchlist and Watchlist you will want to construct new ChangesListBooleanFilter or ChangesListStringOptionsFilter objects When constructing you specify which group they belong to You can reuse existing or create your you must register them with $special registerFilterGroup removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context $options
Definition: hooks.txt:1049
Revision\DELETED_TEXT
const DELETED_TEXT
Definition: Revision.php:90
PageArchive\getPreviousRevision
getPreviousRevision( $timestamp)
Return the most-previous revision, either live or deleted, against the deleted revision given by time...
Definition: PageArchive.php:296