MediaWiki  1.27.2
Article.php
Go to the documentation of this file.
1 <?php
34 class Article implements Page {
36  protected $mContext;
37 
39  protected $mPage;
40 
43 
48  public $mContent;
49 
55 
57  public $mContentLoaded = false;
58 
60  public $mOldId;
61 
63  public $mRedirectedFrom = null;
64 
66  public $mRedirectUrl = false;
67 
69  public $mRevIdFetched = 0;
70 
72  public $mRevision = null;
73 
76 
82  public function __construct( Title $title, $oldId = null ) {
83  $this->mOldId = $oldId;
84  $this->mPage = $this->newPage( $title );
85  }
86 
91  protected function newPage( Title $title ) {
92  return new WikiPage( $title );
93  }
94 
100  public static function newFromID( $id ) {
101  $t = Title::newFromID( $id );
102  # @todo FIXME: Doesn't inherit right
103  return $t == null ? null : new self( $t );
104  # return $t == null ? null : new static( $t ); // PHP 5.3
105  }
106 
114  public static function newFromTitle( $title, IContextSource $context ) {
115  if ( NS_MEDIA == $title->getNamespace() ) {
116  // FIXME: where should this go?
117  $title = Title::makeTitle( NS_FILE, $title->getDBkey() );
118  }
119 
120  $page = null;
121  Hooks::run( 'ArticleFromTitle', [ &$title, &$page, $context ] );
122  if ( !$page ) {
123  switch ( $title->getNamespace() ) {
124  case NS_FILE:
125  $page = new ImagePage( $title );
126  break;
127  case NS_CATEGORY:
128  $page = new CategoryPage( $title );
129  break;
130  default:
131  $page = new Article( $title );
132  }
133  }
134  $page->setContext( $context );
135 
136  return $page;
137  }
138 
146  public static function newFromWikiPage( WikiPage $page, IContextSource $context ) {
147  $article = self::newFromTitle( $page->getTitle(), $context );
148  $article->mPage = $page; // override to keep process cached vars
149  return $article;
150  }
151 
157  public function setRedirectedFrom( Title $from ) {
158  $this->mRedirectedFrom = $from;
159  }
160 
166  public function getTitle() {
167  return $this->mPage->getTitle();
168  }
169 
176  public function getPage() {
177  return $this->mPage;
178  }
179 
183  public function clear() {
184  $this->mContentLoaded = false;
185 
186  $this->mRedirectedFrom = null; # Title object if set
187  $this->mRevIdFetched = 0;
188  $this->mRedirectUrl = false;
189 
190  $this->mPage->clear();
191  }
192 
205  public function getContent() {
206  ContentHandler::deprecated( __METHOD__, '1.21' );
207  $content = $this->getContentObject();
209  }
210 
226  protected function getContentObject() {
227 
228  if ( $this->mPage->getId() === 0 ) {
229  # If this is a MediaWiki:x message, then load the messages
230  # and return the message value for x.
231  if ( $this->getTitle()->getNamespace() == NS_MEDIAWIKI ) {
232  $text = $this->getTitle()->getDefaultMessageText();
233  if ( $text === false ) {
234  $text = '';
235  }
236 
237  $content = ContentHandler::makeContent( $text, $this->getTitle() );
238  } else {
239  $message = $this->getContext()->getUser()->isLoggedIn() ? 'noarticletext' : 'noarticletextanon';
240  $content = new MessageContent( $message, null, 'parsemag' );
241  }
242  } else {
243  $this->fetchContentObject();
245  }
246 
247  return $content;
248  }
249 
253  public function getOldID() {
254  if ( is_null( $this->mOldId ) ) {
255  $this->mOldId = $this->getOldIDFromRequest();
256  }
257 
258  return $this->mOldId;
259  }
260 
266  public function getOldIDFromRequest() {
267  $this->mRedirectUrl = false;
268 
269  $request = $this->getContext()->getRequest();
270  $oldid = $request->getIntOrNull( 'oldid' );
271 
272  if ( $oldid === null ) {
273  return 0;
274  }
275 
276  if ( $oldid !== 0 ) {
277  # Load the given revision and check whether the page is another one.
278  # In that case, update this instance to reflect the change.
279  if ( $oldid === $this->mPage->getLatest() ) {
280  $this->mRevision = $this->mPage->getRevision();
281  } else {
282  $this->mRevision = Revision::newFromId( $oldid );
283  if ( $this->mRevision !== null ) {
284  // Revision title doesn't match the page title given?
285  if ( $this->mPage->getId() != $this->mRevision->getPage() ) {
286  $function = [ get_class( $this->mPage ), 'newFromID' ];
287  $this->mPage = call_user_func( $function, $this->mRevision->getPage() );
288  }
289  }
290  }
291  }
292 
293  if ( $request->getVal( 'direction' ) == 'next' ) {
294  $nextid = $this->getTitle()->getNextRevisionID( $oldid );
295  if ( $nextid ) {
296  $oldid = $nextid;
297  $this->mRevision = null;
298  } else {
299  $this->mRedirectUrl = $this->getTitle()->getFullURL( 'redirect=no' );
300  }
301  } elseif ( $request->getVal( 'direction' ) == 'prev' ) {
302  $previd = $this->getTitle()->getPreviousRevisionID( $oldid );
303  if ( $previd ) {
304  $oldid = $previd;
305  $this->mRevision = null;
306  }
307  }
308 
309  return $oldid;
310  }
311 
326  function fetchContent() {
327  // BC cruft!
328 
329  ContentHandler::deprecated( __METHOD__, '1.21' );
330 
331  if ( $this->mContentLoaded && $this->mContent ) {
332  return $this->mContent;
333  }
334 
335  $content = $this->fetchContentObject();
336 
337  if ( !$content ) {
338  return false;
339  }
340 
341  // @todo Get rid of mContent everywhere!
342  $this->mContent = ContentHandler::getContentText( $content );
343  ContentHandler::runLegacyHooks( 'ArticleAfterFetchContent', [ &$this, &$this->mContent ] );
344 
345  return $this->mContent;
346  }
347 
360  protected function fetchContentObject() {
361  if ( $this->mContentLoaded ) {
362  return $this->mContentObject;
363  }
364 
365  $this->mContentLoaded = true;
366  $this->mContent = null;
367 
368  $oldid = $this->getOldID();
369 
370  # Pre-fill content with error message so that if something
371  # fails we'll have something telling us what we intended.
372  // XXX: this isn't page content but a UI message. horrible.
373  $this->mContentObject = new MessageContent( 'missing-revision', [ $oldid ] );
374 
375  if ( $oldid ) {
376  # $this->mRevision might already be fetched by getOldIDFromRequest()
377  if ( !$this->mRevision ) {
378  $this->mRevision = Revision::newFromId( $oldid );
379  if ( !$this->mRevision ) {
380  wfDebug( __METHOD__ . " failed to retrieve specified revision, id $oldid\n" );
381  return false;
382  }
383  }
384  } else {
385  $oldid = $this->mPage->getLatest();
386  if ( !$oldid ) {
387  wfDebug( __METHOD__ . " failed to find page data for title " .
388  $this->getTitle()->getPrefixedText() . "\n" );
389  return false;
390  }
391 
392  # Update error message with correct oldid
393  $this->mContentObject = new MessageContent( 'missing-revision', [ $oldid ] );
394 
395  $this->mRevision = $this->mPage->getRevision();
396 
397  if ( !$this->mRevision ) {
398  wfDebug( __METHOD__ . " failed to retrieve current page, rev_id $oldid\n" );
399  return false;
400  }
401  }
402 
403  // @todo FIXME: Horrible, horrible! This content-loading interface just plain sucks.
404  // We should instead work with the Revision object when we need it...
405  // Loads if user is allowed
406  $content = $this->mRevision->getContent(
408  $this->getContext()->getUser()
409  );
410 
411  if ( !$content ) {
412  wfDebug( __METHOD__ . " failed to retrieve content of revision " .
413  $this->mRevision->getId() . "\n" );
414  return false;
415  }
416 
417  $this->mContentObject = $content;
418  $this->mRevIdFetched = $this->mRevision->getId();
419 
420  Hooks::run( 'ArticleAfterFetchContentObject', [ &$this, &$this->mContentObject ] );
421 
422  return $this->mContentObject;
423  }
424 
430  public function isCurrent() {
431  # If no oldid, this is the current version.
432  if ( $this->getOldID() == 0 ) {
433  return true;
434  }
435 
436  return $this->mPage->exists() && $this->mRevision && $this->mRevision->isCurrent();
437  }
438 
446  public function getRevisionFetched() {
447  $this->fetchContentObject();
448 
449  return $this->mRevision;
450  }
451 
457  public function getRevIdFetched() {
458  if ( $this->mRevIdFetched ) {
459  return $this->mRevIdFetched;
460  } else {
461  return $this->mPage->getLatest();
462  }
463  }
464 
469  public function view() {
470  global $wgUseFileCache, $wgUseETag, $wgDebugToolbar, $wgMaxRedirects;
471 
472  # Get variables from query string
473  # As side effect this will load the revision and update the title
474  # in a revision ID is passed in the request, so this should remain
475  # the first call of this method even if $oldid is used way below.
476  $oldid = $this->getOldID();
477 
478  $user = $this->getContext()->getUser();
479  # Another whitelist check in case getOldID() is altering the title
480  $permErrors = $this->getTitle()->getUserPermissionsErrors( 'read', $user );
481  if ( count( $permErrors ) ) {
482  wfDebug( __METHOD__ . ": denied on secondary read check\n" );
483  throw new PermissionsError( 'read', $permErrors );
484  }
485 
486  $outputPage = $this->getContext()->getOutput();
487  # getOldID() may as well want us to redirect somewhere else
488  if ( $this->mRedirectUrl ) {
489  $outputPage->redirect( $this->mRedirectUrl );
490  wfDebug( __METHOD__ . ": redirecting due to oldid\n" );
491 
492  return;
493  }
494 
495  # If we got diff in the query, we want to see a diff page instead of the article.
496  if ( $this->getContext()->getRequest()->getCheck( 'diff' ) ) {
497  wfDebug( __METHOD__ . ": showing diff page\n" );
498  $this->showDiffPage();
499 
500  return;
501  }
502 
503  # Set page title (may be overridden by DISPLAYTITLE)
504  $outputPage->setPageTitle( $this->getTitle()->getPrefixedText() );
505 
506  $outputPage->setArticleFlag( true );
507  # Allow frames by default
508  $outputPage->allowClickjacking();
509 
510  $parserCache = ParserCache::singleton();
511 
512  $parserOptions = $this->getParserOptions();
513  # Render printable version, use printable version cache
514  if ( $outputPage->isPrintable() ) {
515  $parserOptions->setIsPrintable( true );
516  $parserOptions->setEditSection( false );
517  } elseif ( !$this->isCurrent() || !$this->getTitle()->quickUserCan( 'edit', $user ) ) {
518  $parserOptions->setEditSection( false );
519  }
520 
521  # Try client and file cache
522  if ( !$wgDebugToolbar && $oldid === 0 && $this->mPage->checkTouched() ) {
523  if ( $wgUseETag ) {
524  $outputPage->setETag( $parserCache->getETag( $this->mPage, $parserOptions ) );
525  }
526 
527  # Use the greatest of the page's timestamp or the timestamp of any
528  # redirect in the chain (bug 67849)
529  $timestamp = $this->mPage->getTouched();
530  if ( isset( $this->mRedirectedFrom ) ) {
531  $timestamp = max( $timestamp, $this->mRedirectedFrom->getTouched() );
532 
533  # If there can be more than one redirect in the chain, we have
534  # to go through the whole chain too in case an intermediate
535  # redirect was changed.
536  if ( $wgMaxRedirects > 1 ) {
537  $titles = Revision::newFromTitle( $this->mRedirectedFrom )
538  ->getContent( Revision::FOR_THIS_USER, $user )
539  ->getRedirectChain();
540  $thisTitle = $this->getTitle();
541  foreach ( $titles as $title ) {
542  if ( Title::compare( $title, $thisTitle ) === 0 ) {
543  break;
544  }
545  $timestamp = max( $timestamp, $title->getTouched() );
546  }
547  }
548  }
549 
550  # Is it client cached?
551  if ( $outputPage->checkLastModified( $timestamp ) ) {
552  wfDebug( __METHOD__ . ": done 304\n" );
553 
554  return;
555  # Try file cache
556  } elseif ( $wgUseFileCache && $this->tryFileCache() ) {
557  wfDebug( __METHOD__ . ": done file cache\n" );
558  # tell wgOut that output is taken care of
559  $outputPage->disable();
560  $this->mPage->doViewUpdates( $user, $oldid );
561 
562  return;
563  }
564  }
565 
566  # Should the parser cache be used?
567  $useParserCache = $this->mPage->shouldCheckParserCache( $parserOptions, $oldid );
568  wfDebug( 'Article::view using parser cache: ' . ( $useParserCache ? 'yes' : 'no' ) . "\n" );
569  if ( $user->getStubThreshold() ) {
570  $this->getContext()->getStats()->increment( 'pcache_miss_stub' );
571  }
572 
573  $this->showRedirectedFromHeader();
574  $this->showNamespaceHeader();
575 
576  # Iterate through the possible ways of constructing the output text.
577  # Keep going until $outputDone is set, or we run out of things to do.
578  $pass = 0;
579  $outputDone = false;
580  $this->mParserOutput = false;
581 
582  while ( !$outputDone && ++$pass ) {
583  switch ( $pass ) {
584  case 1:
585  Hooks::run( 'ArticleViewHeader', [ &$this, &$outputDone, &$useParserCache ] );
586  break;
587  case 2:
588  # Early abort if the page doesn't exist
589  if ( !$this->mPage->exists() ) {
590  wfDebug( __METHOD__ . ": showing missing article\n" );
591  $this->showMissingArticle();
592  $this->mPage->doViewUpdates( $user );
593  return;
594  }
595 
596  # Try the parser cache
597  if ( $useParserCache ) {
598  $this->mParserOutput = $parserCache->get( $this->mPage, $parserOptions );
599 
600  if ( $this->mParserOutput !== false ) {
601  if ( $oldid ) {
602  wfDebug( __METHOD__ . ": showing parser cache contents for current rev permalink\n" );
603  $this->setOldSubtitle( $oldid );
604  } else {
605  wfDebug( __METHOD__ . ": showing parser cache contents\n" );
606  }
607  $outputPage->addParserOutput( $this->mParserOutput );
608  # Ensure that UI elements requiring revision ID have
609  # the correct version information.
610  $outputPage->setRevisionId( $this->mPage->getLatest() );
611  # Preload timestamp to avoid a DB hit
612  $cachedTimestamp = $this->mParserOutput->getTimestamp();
613  if ( $cachedTimestamp !== null ) {
614  $outputPage->setRevisionTimestamp( $cachedTimestamp );
615  $this->mPage->setTimestamp( $cachedTimestamp );
616  }
617  $outputDone = true;
618  }
619  }
620  break;
621  case 3:
622  # This will set $this->mRevision if needed
623  $this->fetchContentObject();
624 
625  # Are we looking at an old revision
626  if ( $oldid && $this->mRevision ) {
627  $this->setOldSubtitle( $oldid );
628 
629  if ( !$this->showDeletedRevisionHeader() ) {
630  wfDebug( __METHOD__ . ": cannot view deleted revision\n" );
631  return;
632  }
633  }
634 
635  # Ensure that UI elements requiring revision ID have
636  # the correct version information.
637  $outputPage->setRevisionId( $this->getRevIdFetched() );
638  # Preload timestamp to avoid a DB hit
639  $outputPage->setRevisionTimestamp( $this->mPage->getTimestamp() );
640 
641  # Pages containing custom CSS or JavaScript get special treatment
642  if ( $this->getTitle()->isCssOrJsPage() || $this->getTitle()->isCssJsSubpage() ) {
643  wfDebug( __METHOD__ . ": showing CSS/JS source\n" );
644  $this->showCssOrJsPage();
645  $outputDone = true;
646  } elseif ( !Hooks::run( 'ArticleContentViewCustom',
647  [ $this->fetchContentObject(), $this->getTitle(), $outputPage ] ) ) {
648 
649  # Allow extensions do their own custom view for certain pages
650  $outputDone = true;
651  } elseif ( !ContentHandler::runLegacyHooks( 'ArticleViewCustom',
652  [ $this->fetchContentObject(), $this->getTitle(), $outputPage ] ) ) {
653 
654  # Allow extensions do their own custom view for certain pages
655  $outputDone = true;
656  }
657  break;
658  case 4:
659  # Run the parse, protected by a pool counter
660  wfDebug( __METHOD__ . ": doing uncached parse\n" );
661 
662  $content = $this->getContentObject();
663  $poolArticleView = new PoolWorkArticleView( $this->getPage(), $parserOptions,
664  $this->getRevIdFetched(), $useParserCache, $content );
665 
666  if ( !$poolArticleView->execute() ) {
667  $error = $poolArticleView->getError();
668  if ( $error ) {
669  $outputPage->clearHTML(); // for release() errors
670  $outputPage->enableClientCache( false );
671  $outputPage->setRobotPolicy( 'noindex,nofollow' );
672 
673  $errortext = $error->getWikiText( false, 'view-pool-error' );
674  $outputPage->addWikiText( '<div class="errorbox">' . $errortext . '</div>' );
675  }
676  # Connection or timeout error
677  return;
678  }
679 
680  $this->mParserOutput = $poolArticleView->getParserOutput();
681  $outputPage->addParserOutput( $this->mParserOutput );
682  if ( $content->getRedirectTarget() ) {
683  $outputPage->addSubtitle( "<span id=\"redirectsub\">" .
684  $this->getContext()->msg( 'redirectpagesub' )->parse() . "</span>" );
685  }
686 
687  # Don't cache a dirty ParserOutput object
688  if ( $poolArticleView->getIsDirty() ) {
689  $outputPage->setCdnMaxage( 0 );
690  $outputPage->addHTML( "<!-- parser cache is expired, " .
691  "sending anyway due to pool overload-->\n" );
692  }
693 
694  $outputDone = true;
695  break;
696  # Should be unreachable, but just in case...
697  default:
698  break 2;
699  }
700  }
701 
702  # Get the ParserOutput actually *displayed* here.
703  # Note that $this->mParserOutput is the *current*/oldid version output.
704  $pOutput = ( $outputDone instanceof ParserOutput )
705  ? $outputDone // object fetched by hook
706  : $this->mParserOutput;
707 
708  # Adjust title for main page & pages with displaytitle
709  if ( $pOutput ) {
710  $this->adjustDisplayTitle( $pOutput );
711  }
712 
713  # For the main page, overwrite the <title> element with the con-
714  # tents of 'pagetitle-view-mainpage' instead of the default (if
715  # that's not empty).
716  # This message always exists because it is in the i18n files
717  if ( $this->getTitle()->isMainPage() ) {
718  $msg = wfMessage( 'pagetitle-view-mainpage' )->inContentLanguage();
719  if ( !$msg->isDisabled() ) {
720  $outputPage->setHTMLTitle( $msg->title( $this->getTitle() )->text() );
721  }
722  }
723 
724  # Check for any __NOINDEX__ tags on the page using $pOutput
725  $policy = $this->getRobotPolicy( 'view', $pOutput );
726  $outputPage->setIndexPolicy( $policy['index'] );
727  $outputPage->setFollowPolicy( $policy['follow'] );
728 
729  $this->showViewFooter();
730  $this->mPage->doViewUpdates( $user, $oldid );
731 
732  $outputPage->addModules( 'mediawiki.action.view.postEdit' );
733 
734  }
735 
740  public function adjustDisplayTitle( ParserOutput $pOutput ) {
741  # Adjust the title if it was set by displaytitle, -{T|}- or language conversion
742  $titleText = $pOutput->getTitleText();
743  if ( strval( $titleText ) !== '' ) {
744  $this->getContext()->getOutput()->setPageTitle( $titleText );
745  }
746  }
747 
753  protected function showDiffPage() {
754  $request = $this->getContext()->getRequest();
755  $user = $this->getContext()->getUser();
756  $diff = $request->getVal( 'diff' );
757  $rcid = $request->getVal( 'rcid' );
758  $diffOnly = $request->getBool( 'diffonly', $user->getOption( 'diffonly' ) );
759  $purge = $request->getVal( 'action' ) == 'purge';
760  $unhide = $request->getInt( 'unhide' ) == 1;
761  $oldid = $this->getOldID();
762 
763  $rev = $this->getRevisionFetched();
764 
765  if ( !$rev ) {
766  $this->getContext()->getOutput()->setPageTitle( wfMessage( 'errorpagetitle' ) );
767  $msg = $this->getContext()->msg( 'difference-missing-revision' )
768  ->params( $oldid )
769  ->numParams( 1 )
770  ->parseAsBlock();
771  $this->getContext()->getOutput()->addHTML( $msg );
772  return;
773  }
774 
775  $contentHandler = $rev->getContentHandler();
776  $de = $contentHandler->createDifferenceEngine(
777  $this->getContext(),
778  $oldid,
779  $diff,
780  $rcid,
781  $purge,
782  $unhide
783  );
784 
785  // DifferenceEngine directly fetched the revision:
786  $this->mRevIdFetched = $de->mNewid;
787  $de->showDiffPage( $diffOnly );
788 
789  // Run view updates for the newer revision being diffed (and shown
790  // below the diff if not $diffOnly).
791  list( $old, $new ) = $de->mapDiffPrevNext( $oldid, $diff );
792  // New can be false, convert it to 0 - this conveniently means the latest revision
793  $this->mPage->doViewUpdates( $user, (int)$new );
794  }
795 
807  protected function showCssOrJsPage( $showCacheHint = true ) {
808  $outputPage = $this->getContext()->getOutput();
809 
810  if ( $showCacheHint ) {
811  $dir = $this->getContext()->getLanguage()->getDir();
812  $lang = $this->getContext()->getLanguage()->getHtmlCode();
813 
814  $outputPage->wrapWikiMsg(
815  "<div id='mw-clearyourcache' lang='$lang' dir='$dir' class='mw-content-$dir'>\n$1\n</div>",
816  'clearyourcache'
817  );
818  }
819 
820  $this->fetchContentObject();
821 
822  if ( $this->mContentObject ) {
823  // Give hooks a chance to customise the output
825  'ShowRawCssJs',
826  [ $this->mContentObject, $this->getTitle(), $outputPage ] )
827  ) {
828  // If no legacy hooks ran, display the content of the parser output, including RL modules,
829  // but excluding metadata like categories and language links
830  $po = $this->mContentObject->getParserOutput( $this->getTitle() );
831  $outputPage->addParserOutputContent( $po );
832  }
833  }
834  }
835 
843  public function getRobotPolicy( $action, $pOutput = null ) {
844  global $wgArticleRobotPolicies, $wgNamespaceRobotPolicies, $wgDefaultRobotPolicy;
845 
846  $ns = $this->getTitle()->getNamespace();
847 
848  # Don't index user and user talk pages for blocked users (bug 11443)
849  if ( ( $ns == NS_USER || $ns == NS_USER_TALK ) && !$this->getTitle()->isSubpage() ) {
850  $specificTarget = null;
851  $vagueTarget = null;
852  $titleText = $this->getTitle()->getText();
853  if ( IP::isValid( $titleText ) ) {
854  $vagueTarget = $titleText;
855  } else {
856  $specificTarget = $titleText;
857  }
858  if ( Block::newFromTarget( $specificTarget, $vagueTarget ) instanceof Block ) {
859  return [
860  'index' => 'noindex',
861  'follow' => 'nofollow'
862  ];
863  }
864  }
865 
866  if ( $this->mPage->getId() === 0 || $this->getOldID() ) {
867  # Non-articles (special pages etc), and old revisions
868  return [
869  'index' => 'noindex',
870  'follow' => 'nofollow'
871  ];
872  } elseif ( $this->getContext()->getOutput()->isPrintable() ) {
873  # Discourage indexing of printable versions, but encourage following
874  return [
875  'index' => 'noindex',
876  'follow' => 'follow'
877  ];
878  } elseif ( $this->getContext()->getRequest()->getInt( 'curid' ) ) {
879  # For ?curid=x urls, disallow indexing
880  return [
881  'index' => 'noindex',
882  'follow' => 'follow'
883  ];
884  }
885 
886  # Otherwise, construct the policy based on the various config variables.
887  $policy = self::formatRobotPolicy( $wgDefaultRobotPolicy );
888 
889  if ( isset( $wgNamespaceRobotPolicies[$ns] ) ) {
890  # Honour customised robot policies for this namespace
891  $policy = array_merge(
892  $policy,
893  self::formatRobotPolicy( $wgNamespaceRobotPolicies[$ns] )
894  );
895  }
896  if ( $this->getTitle()->canUseNoindex() && is_object( $pOutput ) && $pOutput->getIndexPolicy() ) {
897  # __INDEX__ and __NOINDEX__ magic words, if allowed. Incorporates
898  # a final sanity check that we have really got the parser output.
899  $policy = array_merge(
900  $policy,
901  [ 'index' => $pOutput->getIndexPolicy() ]
902  );
903  }
904 
905  if ( isset( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] ) ) {
906  # (bug 14900) site config can override user-defined __INDEX__ or __NOINDEX__
907  $policy = array_merge(
908  $policy,
909  self::formatRobotPolicy( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] )
910  );
911  }
912 
913  return $policy;
914  }
915 
923  public static function formatRobotPolicy( $policy ) {
924  if ( is_array( $policy ) ) {
925  return $policy;
926  } elseif ( !$policy ) {
927  return [];
928  }
929 
930  $policy = explode( ',', $policy );
931  $policy = array_map( 'trim', $policy );
932 
933  $arr = [];
934  foreach ( $policy as $var ) {
935  if ( in_array( $var, [ 'index', 'noindex' ] ) ) {
936  $arr['index'] = $var;
937  } elseif ( in_array( $var, [ 'follow', 'nofollow' ] ) ) {
938  $arr['follow'] = $var;
939  }
940  }
941 
942  return $arr;
943  }
944 
952  public function showRedirectedFromHeader() {
954 
955  $context = $this->getContext();
956  $outputPage = $context->getOutput();
957  $request = $context->getRequest();
958  $rdfrom = $request->getVal( 'rdfrom' );
959 
960  // Construct a URL for the current page view, but with the target title
961  $query = $request->getValues();
962  unset( $query['rdfrom'] );
963  unset( $query['title'] );
964  if ( $this->getTitle()->isRedirect() ) {
965  // Prevent double redirects
966  $query['redirect'] = 'no';
967  }
968  $redirectTargetUrl = $this->getTitle()->getLinkURL( $query );
969 
970  if ( isset( $this->mRedirectedFrom ) ) {
971  // This is an internally redirected page view.
972  // We'll need a backlink to the source page for navigation.
973  if ( Hooks::run( 'ArticleViewRedirect', [ &$this ] ) ) {
974  $redir = Linker::linkKnown(
975  $this->mRedirectedFrom,
976  null,
977  [],
978  [ 'redirect' => 'no' ]
979  );
980 
981  $outputPage->addSubtitle( "<span class=\"mw-redirectedfrom\">" .
982  $context->msg( 'redirectedfrom' )->rawParams( $redir )->parse()
983  . "</span>" );
984 
985  // Add the script to update the displayed URL and
986  // set the fragment if one was specified in the redirect
987  $outputPage->addJsConfigVars( [
988  'wgInternalRedirectTargetUrl' => $redirectTargetUrl,
989  ] );
990  $outputPage->addModules( 'mediawiki.action.view.redirect' );
991 
992  // Add a <link rel="canonical"> tag
993  $outputPage->setCanonicalUrl( $this->getTitle()->getCanonicalURL() );
994 
995  // Tell the output object that the user arrived at this article through a redirect
996  $outputPage->setRedirectedFrom( $this->mRedirectedFrom );
997 
998  return true;
999  }
1000  } elseif ( $rdfrom ) {
1001  // This is an externally redirected view, from some other wiki.
1002  // If it was reported from a trusted site, supply a backlink.
1003  if ( $wgRedirectSources && preg_match( $wgRedirectSources, $rdfrom ) ) {
1004  $redir = Linker::makeExternalLink( $rdfrom, $rdfrom );
1005  $outputPage->addSubtitle( "<span class=\"mw-redirectedfrom\">" .
1006  $context->msg( 'redirectedfrom' )->rawParams( $redir )->parse()
1007  . "</span>" );
1008 
1009  // Add the script to update the displayed URL
1010  $outputPage->addJsConfigVars( [
1011  'wgInternalRedirectTargetUrl' => $redirectTargetUrl,
1012  ] );
1013  $outputPage->addModules( 'mediawiki.action.view.redirect' );
1014 
1015  return true;
1016  }
1017  }
1018 
1019  return false;
1020  }
1021 
1026  public function showNamespaceHeader() {
1027  if ( $this->getTitle()->isTalkPage() ) {
1028  if ( !wfMessage( 'talkpageheader' )->isDisabled() ) {
1029  $this->getContext()->getOutput()->wrapWikiMsg(
1030  "<div class=\"mw-talkpageheader\">\n$1\n</div>",
1031  [ 'talkpageheader' ]
1032  );
1033  }
1034  }
1035  }
1036 
1040  public function showViewFooter() {
1041  # check if we're displaying a [[User talk:x.x.x.x]] anonymous talk page
1042  if ( $this->getTitle()->getNamespace() == NS_USER_TALK
1043  && IP::isValid( $this->getTitle()->getText() )
1044  ) {
1045  $this->getContext()->getOutput()->addWikiMsg( 'anontalkpagetext' );
1046  }
1047 
1048  // Show a footer allowing the user to patrol the shown revision or page if possible
1049  $patrolFooterShown = $this->showPatrolFooter();
1050 
1051  Hooks::run( 'ArticleViewFooter', [ $this, $patrolFooterShown ] );
1052  }
1053 
1063  public function showPatrolFooter() {
1064  global $wgUseNPPatrol, $wgUseRCPatrol, $wgUseFilePatrol, $wgEnableAPI, $wgEnableWriteAPI;
1065 
1066  $outputPage = $this->getContext()->getOutput();
1067  $user = $this->getContext()->getUser();
1068  $title = $this->getTitle();
1069  $rc = false;
1070 
1071  if ( !$title->quickUserCan( 'patrol', $user )
1072  || !( $wgUseRCPatrol || $wgUseNPPatrol
1073  || ( $wgUseFilePatrol && $title->inNamespace( NS_FILE ) ) )
1074  ) {
1075  // Patrolling is disabled or the user isn't allowed to
1076  return false;
1077  }
1078 
1079  if ( $this->mRevision
1080  && !RecentChange::isInRCLifespan( $this->mRevision->getTimestamp(), 21600 )
1081  ) {
1082  // The current revision is already older than what could be in the RC table
1083  // 6h tolerance because the RC might not be cleaned out regularly
1084  return false;
1085  }
1086 
1087  // Check for cached results
1088  $key = wfMemcKey( 'unpatrollable-page', $title->getArticleID() );
1090  if ( $cache->get( $key ) ) {
1091  return false;
1092  }
1093 
1094  $dbr = wfGetDB( DB_SLAVE );
1095  $oldestRevisionTimestamp = $dbr->selectField(
1096  'revision',
1097  'MIN( rev_timestamp )',
1098  [ 'rev_page' => $title->getArticleID() ],
1099  __METHOD__
1100  );
1101 
1102  // New page patrol: Get the timestamp of the oldest revison which
1103  // the revision table holds for the given page. Then we look
1104  // whether it's within the RC lifespan and if it is, we try
1105  // to get the recentchanges row belonging to that entry
1106  // (with rc_new = 1).
1107  $recentPageCreation = false;
1108  if ( $oldestRevisionTimestamp
1109  && RecentChange::isInRCLifespan( $oldestRevisionTimestamp, 21600 )
1110  ) {
1111  // 6h tolerance because the RC might not be cleaned out regularly
1112  $recentPageCreation = true;
1114  [
1115  'rc_new' => 1,
1116  'rc_timestamp' => $oldestRevisionTimestamp,
1117  'rc_namespace' => $title->getNamespace(),
1118  'rc_cur_id' => $title->getArticleID()
1119  ],
1120  __METHOD__
1121  );
1122  if ( $rc ) {
1123  // Use generic patrol message for new pages
1124  $markPatrolledMsg = wfMessage( 'markaspatrolledtext' );
1125  }
1126  }
1127 
1128  // File patrol: Get the timestamp of the latest upload for this page,
1129  // check whether it is within the RC lifespan and if it is, we try
1130  // to get the recentchanges row belonging to that entry
1131  // (with rc_type = RC_LOG, rc_log_type = upload).
1132  $recentFileUpload = false;
1133  if ( ( !$rc || $rc->getAttribute( 'rc_patrolled' ) ) && $wgUseFilePatrol
1134  && $title->getNamespace() === NS_FILE ) {
1135  // Retrieve timestamp of most recent upload
1136  $newestUploadTimestamp = $dbr->selectField(
1137  'image',
1138  'MAX( img_timestamp )',
1139  [ 'img_name' => $title->getDBkey() ],
1140  __METHOD__
1141  );
1142  if ( $newestUploadTimestamp
1143  && RecentChange::isInRCLifespan( $newestUploadTimestamp, 21600 )
1144  ) {
1145  // 6h tolerance because the RC might not be cleaned out regularly
1146  $recentFileUpload = true;
1148  [
1149  'rc_type' => RC_LOG,
1150  'rc_log_type' => 'upload',
1151  'rc_timestamp' => $newestUploadTimestamp,
1152  'rc_namespace' => NS_FILE,
1153  'rc_cur_id' => $title->getArticleID()
1154  ],
1155  __METHOD__,
1156  [ 'USE INDEX' => 'rc_timestamp' ]
1157  );
1158  if ( $rc ) {
1159  // Use patrol message specific to files
1160  $markPatrolledMsg = wfMessage( 'markaspatrolledtext-file' );
1161  }
1162  }
1163  }
1164 
1165  if ( !$recentPageCreation && !$recentFileUpload ) {
1166  // Page creation and latest upload (for files) is too old to be in RC
1167 
1168  // We definitely can't patrol so cache the information
1169  // When a new file version is uploaded, the cache is cleared
1170  $cache->set( $key, '1' );
1171 
1172  return false;
1173  }
1174 
1175  if ( !$rc ) {
1176  // Don't cache: This can be hit if the page gets accessed very fast after
1177  // its creation / latest upload or in case we have high slave lag. In case
1178  // the revision is too old, we will already return above.
1179  return false;
1180  }
1181 
1182  if ( $rc->getAttribute( 'rc_patrolled' ) ) {
1183  // Patrolled RC entry around
1184 
1185  // Cache the information we gathered above in case we can't patrol
1186  // Don't cache in case we can patrol as this could change
1187  $cache->set( $key, '1' );
1188 
1189  return false;
1190  }
1191 
1192  if ( $rc->getPerformer()->equals( $user ) ) {
1193  // Don't show a patrol link for own creations/uploads. If the user could
1194  // patrol them, they already would be patrolled
1195  return false;
1196  }
1197 
1198  $rcid = $rc->getAttribute( 'rc_id' );
1199 
1200  $token = $user->getEditToken( $rcid );
1201 
1202  $outputPage->preventClickjacking();
1203  if ( $wgEnableAPI && $wgEnableWriteAPI && $user->isAllowed( 'writeapi' ) ) {
1204  $outputPage->addModules( 'mediawiki.page.patrol.ajax' );
1205  }
1206 
1208  $title,
1209  $markPatrolledMsg->escaped(),
1210  [],
1211  [
1212  'action' => 'markpatrolled',
1213  'rcid' => $rcid,
1214  'token' => $token,
1215  ]
1216  );
1217 
1218  $outputPage->addHTML(
1219  "<div class='patrollink' data-mw='interface'>" .
1220  wfMessage( 'markaspatrolledlink' )->rawParams( $link )->escaped() .
1221  '</div>'
1222  );
1223 
1224  return true;
1225  }
1226 
1233  public static function purgePatrolFooterCache( $articleID ) {
1235  $cache->delete( wfMemcKey( 'unpatrollable-page', $articleID ) );
1236  }
1237 
1242  public function showMissingArticle() {
1244 
1245  $outputPage = $this->getContext()->getOutput();
1246  // Whether the page is a root user page of an existing user (but not a subpage)
1247  $validUserPage = false;
1248 
1249  $title = $this->getTitle();
1250 
1251  # Show info in user (talk) namespace. Does the user exist? Is he blocked?
1252  if ( $title->getNamespace() == NS_USER
1253  || $title->getNamespace() == NS_USER_TALK
1254  ) {
1255  $rootPart = explode( '/', $title->getText() )[0];
1256  $user = User::newFromName( $rootPart, false /* allow IP users*/ );
1257  $ip = User::isIP( $rootPart );
1258  $block = Block::newFromTarget( $user, $user );
1259 
1260  if ( !( $user && $user->isLoggedIn() ) && !$ip ) { # User does not exist
1261  $outputPage->wrapWikiMsg( "<div class=\"mw-userpage-userdoesnotexist error\">\n\$1\n</div>",
1262  [ 'userpage-userdoesnotexist-view', wfEscapeWikiText( $rootPart ) ] );
1263  } elseif ( !is_null( $block ) && $block->getType() != Block::TYPE_AUTO ) {
1264  # Show log extract if the user is currently blocked
1266  $outputPage,
1267  'block',
1268  MWNamespace::getCanonicalName( NS_USER ) . ':' . $block->getTarget(),
1269  '',
1270  [
1271  'lim' => 1,
1272  'showIfEmpty' => false,
1273  'msgKey' => [
1274  'blocked-notice-logextract',
1275  $user->getName() # Support GENDER in notice
1276  ]
1277  ]
1278  );
1279  $validUserPage = !$title->isSubpage();
1280  } else {
1281  $validUserPage = !$title->isSubpage();
1282  }
1283  }
1284 
1285  Hooks::run( 'ShowMissingArticle', [ $this ] );
1286 
1287  # Show delete and move logs if there were any such events.
1288  # The logging query can DOS the site when bots/crawlers cause 404 floods,
1289  # so be careful showing this. 404 pages must be cheap as they are hard to cache.
1291  $key = wfMemcKey( 'page-recent-delete', md5( $title->getPrefixedText() ) );
1292  $loggedIn = $this->getContext()->getUser()->isLoggedIn();
1293  if ( $loggedIn || $cache->get( $key ) ) {
1294  $logTypes = [ 'delete', 'move' ];
1295  $conds = [ "log_action != 'revision'" ];
1296  // Give extensions a chance to hide their (unrelated) log entries
1297  Hooks::run( 'Article::MissingArticleConditions', [ &$conds, $logTypes ] );
1299  $outputPage,
1300  $logTypes,
1301  $title,
1302  '',
1303  [
1304  'lim' => 10,
1305  'conds' => $conds,
1306  'showIfEmpty' => false,
1307  'msgKey' => [ $loggedIn
1308  ? 'moveddeleted-notice'
1309  : 'moveddeleted-notice-recent'
1310  ]
1311  ]
1312  );
1313  }
1314 
1315  if ( !$this->mPage->hasViewableContent() && $wgSend404Code && !$validUserPage ) {
1316  // If there's no backing content, send a 404 Not Found
1317  // for better machine handling of broken links.
1318  $this->getContext()->getRequest()->response()->statusHeader( 404 );
1319  }
1320 
1321  // Also apply the robot policy for nonexisting pages (even if a 404 was used for sanity)
1322  $policy = $this->getRobotPolicy( 'view' );
1323  $outputPage->setIndexPolicy( $policy['index'] );
1324  $outputPage->setFollowPolicy( $policy['follow'] );
1325 
1326  $hookResult = Hooks::run( 'BeforeDisplayNoArticleText', [ $this ] );
1327 
1328  if ( !$hookResult ) {
1329  return;
1330  }
1331 
1332  # Show error message
1333  $oldid = $this->getOldID();
1334  if ( !$oldid && $title->getNamespace() === NS_MEDIAWIKI && $title->hasSourceText() ) {
1335  $outputPage->addParserOutput( $this->getContentObject()->getParserOutput( $title ) );
1336  } else {
1337  if ( $oldid ) {
1338  $text = wfMessage( 'missing-revision', $oldid )->plain();
1339  } elseif ( $title->quickUserCan( 'create', $this->getContext()->getUser() )
1340  && $title->quickUserCan( 'edit', $this->getContext()->getUser() )
1341  ) {
1342  $message = $this->getContext()->getUser()->isLoggedIn() ? 'noarticletext' : 'noarticletextanon';
1343  $text = wfMessage( $message )->plain();
1344  } else {
1345  $text = wfMessage( 'noarticletext-nopermission' )->plain();
1346  }
1347 
1348  $dir = $this->getContext()->getLanguage()->getDir();
1349  $lang = $this->getContext()->getLanguage()->getCode();
1350  $outputPage->addWikiText( Xml::openElement( 'div', [
1351  'class' => "noarticletext mw-content-$dir",
1352  'dir' => $dir,
1353  'lang' => $lang,
1354  ] ) . "\n$text\n</div>" );
1355  }
1356  }
1357 
1364  public function showDeletedRevisionHeader() {
1365  if ( !$this->mRevision->isDeleted( Revision::DELETED_TEXT ) ) {
1366  // Not deleted
1367  return true;
1368  }
1369 
1370  $outputPage = $this->getContext()->getOutput();
1371  $user = $this->getContext()->getUser();
1372  // If the user is not allowed to see it...
1373  if ( !$this->mRevision->userCan( Revision::DELETED_TEXT, $user ) ) {
1374  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1375  'rev-deleted-text-permission' );
1376 
1377  return false;
1378  // If the user needs to confirm that they want to see it...
1379  } elseif ( $this->getContext()->getRequest()->getInt( 'unhide' ) != 1 ) {
1380  # Give explanation and add a link to view the revision...
1381  $oldid = intval( $this->getOldID() );
1382  $link = $this->getTitle()->getFullURL( "oldid={$oldid}&unhide=1" );
1383  $msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ?
1384  'rev-suppressed-text-unhide' : 'rev-deleted-text-unhide';
1385  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1386  [ $msg, $link ] );
1387 
1388  return false;
1389  // We are allowed to see...
1390  } else {
1391  $msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ?
1392  'rev-suppressed-text-view' : 'rev-deleted-text-view';
1393  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n", $msg );
1394 
1395  return true;
1396  }
1397  }
1398 
1407  public function setOldSubtitle( $oldid = 0 ) {
1408  if ( !Hooks::run( 'DisplayOldSubtitle', [ &$this, &$oldid ] ) ) {
1409  return;
1410  }
1411 
1412  $context = $this->getContext();
1413  $unhide = $context->getRequest()->getInt( 'unhide' ) == 1;
1414 
1415  # Cascade unhide param in links for easy deletion browsing
1416  $extraParams = [];
1417  if ( $unhide ) {
1418  $extraParams['unhide'] = 1;
1419  }
1420 
1421  if ( $this->mRevision && $this->mRevision->getId() === $oldid ) {
1422  $revision = $this->mRevision;
1423  } else {
1424  $revision = Revision::newFromId( $oldid );
1425  }
1426 
1427  $timestamp = $revision->getTimestamp();
1428 
1429  $current = ( $oldid == $this->mPage->getLatest() );
1430  $language = $context->getLanguage();
1431  $user = $context->getUser();
1432 
1433  $td = $language->userTimeAndDate( $timestamp, $user );
1434  $tddate = $language->userDate( $timestamp, $user );
1435  $tdtime = $language->userTime( $timestamp, $user );
1436 
1437  # Show user links if allowed to see them. If hidden, then show them only if requested...
1438  $userlinks = Linker::revUserTools( $revision, !$unhide );
1439 
1440  $infomsg = $current && !$context->msg( 'revision-info-current' )->isDisabled()
1441  ? 'revision-info-current'
1442  : 'revision-info';
1443 
1444  $outputPage = $context->getOutput();
1445  $outputPage->addSubtitle( "<div id=\"mw-{$infomsg}\">" .
1446  $context->msg( $infomsg, $td )
1447  ->rawParams( $userlinks )
1448  ->params( $revision->getId(), $tddate, $tdtime, $revision->getUserText() )
1449  ->rawParams( Linker::revComment( $revision, true, true ) )
1450  ->parse() .
1451  "</div>"
1452  );
1453 
1454  $lnk = $current
1455  ? $context->msg( 'currentrevisionlink' )->escaped()
1457  $this->getTitle(),
1458  $context->msg( 'currentrevisionlink' )->escaped(),
1459  [],
1460  $extraParams
1461  );
1462  $curdiff = $current
1463  ? $context->msg( 'diff' )->escaped()
1465  $this->getTitle(),
1466  $context->msg( 'diff' )->escaped(),
1467  [],
1468  [
1469  'diff' => 'cur',
1470  'oldid' => $oldid
1471  ] + $extraParams
1472  );
1473  $prev = $this->getTitle()->getPreviousRevisionID( $oldid );
1474  $prevlink = $prev
1476  $this->getTitle(),
1477  $context->msg( 'previousrevision' )->escaped(),
1478  [],
1479  [
1480  'direction' => 'prev',
1481  'oldid' => $oldid
1482  ] + $extraParams
1483  )
1484  : $context->msg( 'previousrevision' )->escaped();
1485  $prevdiff = $prev
1487  $this->getTitle(),
1488  $context->msg( 'diff' )->escaped(),
1489  [],
1490  [
1491  'diff' => 'prev',
1492  'oldid' => $oldid
1493  ] + $extraParams
1494  )
1495  : $context->msg( 'diff' )->escaped();
1496  $nextlink = $current
1497  ? $context->msg( 'nextrevision' )->escaped()
1499  $this->getTitle(),
1500  $context->msg( 'nextrevision' )->escaped(),
1501  [],
1502  [
1503  'direction' => 'next',
1504  'oldid' => $oldid
1505  ] + $extraParams
1506  );
1507  $nextdiff = $current
1508  ? $context->msg( 'diff' )->escaped()
1510  $this->getTitle(),
1511  $context->msg( 'diff' )->escaped(),
1512  [],
1513  [
1514  'diff' => 'next',
1515  'oldid' => $oldid
1516  ] + $extraParams
1517  );
1518 
1519  $cdel = Linker::getRevDeleteLink( $user, $revision, $this->getTitle() );
1520  if ( $cdel !== '' ) {
1521  $cdel .= ' ';
1522  }
1523 
1524  $outputPage->addSubtitle( "<div id=\"mw-revision-nav\">" . $cdel .
1525  $context->msg( 'revision-nav' )->rawParams(
1526  $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff
1527  )->escaped() . "</div>" );
1528  }
1529 
1541  public function viewRedirect( $target, $appendSubtitle = true, $forceKnown = false ) {
1542  $lang = $this->getTitle()->getPageLanguage();
1543  $out = $this->getContext()->getOutput();
1544  if ( $appendSubtitle ) {
1545  $out->addSubtitle( wfMessage( 'redirectpagesub' ) );
1546  }
1547  $out->addModuleStyles( 'mediawiki.action.view.redirectPage' );
1548  return static::getRedirectHeaderHtml( $lang, $target, $forceKnown );
1549  }
1550 
1563  public static function getRedirectHeaderHtml( Language $lang, $target, $forceKnown = false ) {
1564  if ( !is_array( $target ) ) {
1565  $target = [ $target ];
1566  }
1567 
1568  $html = '<ul class="redirectText">';
1570  foreach ( $target as $title ) {
1571  $html .= '<li>' . Linker::link(
1572  $title,
1573  htmlspecialchars( $title->getFullText() ),
1574  [],
1575  // Make sure wiki page redirects are not followed
1576  $title->isRedirect() ? [ 'redirect' => 'no' ] : [],
1577  ( $forceKnown ? [ 'known', 'noclasses' ] : [] )
1578  ) . '</li>';
1579  }
1580  $html .= '</ul>';
1581 
1582  $redirectToText = wfMessage( 'redirectto' )->inLanguage( $lang )->escaped();
1583 
1584  return '<div class="redirectMsg">' .
1585  '<p>' . $redirectToText . '</p>' .
1586  $html .
1587  '</div>';
1588  }
1589 
1598  public function addHelpLink( $to, $overrideBaseUrl = false ) {
1599  $msg = wfMessage(
1600  'namespace-' . $this->getTitle()->getNamespace() . '-helppage'
1601  );
1602 
1603  $out = $this->getContext()->getOutput();
1604  if ( !$msg->isDisabled() ) {
1605  $helpUrl = Skin::makeUrl( $msg->plain() );
1606  $out->addHelpLink( $helpUrl, true );
1607  } else {
1608  $out->addHelpLink( $to, $overrideBaseUrl );
1609  }
1610  }
1611 
1615  public function render() {
1616  $this->getContext()->getRequest()->response()->header( 'X-Robots-Tag: noindex' );
1617  $this->getContext()->getOutput()->setArticleBodyOnly( true );
1618  $this->getContext()->getOutput()->enableSectionEditLinks( false );
1619  $this->view();
1620  }
1621 
1625  public function protect() {
1626  $form = new ProtectionForm( $this );
1627  $form->execute();
1628  }
1629 
1633  public function unprotect() {
1634  $this->protect();
1635  }
1636 
1640  public function delete() {
1641  # This code desperately needs to be totally rewritten
1642 
1643  $title = $this->getTitle();
1644  $context = $this->getContext();
1645  $user = $context->getUser();
1646  $request = $context->getRequest();
1647 
1648  # Check permissions
1649  $permissionErrors = $title->getUserPermissionsErrors( 'delete', $user );
1650  if ( count( $permissionErrors ) ) {
1651  throw new PermissionsError( 'delete', $permissionErrors );
1652  }
1653 
1654  # Read-only check...
1655  if ( wfReadOnly() ) {
1656  throw new ReadOnlyError;
1657  }
1658 
1659  # Better double-check that it hasn't been deleted yet!
1660  $this->mPage->loadPageData(
1662  );
1663  if ( !$this->mPage->exists() ) {
1664  $deleteLogPage = new LogPage( 'delete' );
1665  $outputPage = $context->getOutput();
1666  $outputPage->setPageTitle( $context->msg( 'cannotdelete-title', $title->getPrefixedText() ) );
1667  $outputPage->wrapWikiMsg( "<div class=\"error mw-error-cannotdelete\">\n$1\n</div>",
1668  [ 'cannotdelete', wfEscapeWikiText( $title->getPrefixedText() ) ]
1669  );
1670  $outputPage->addHTML(
1671  Xml::element( 'h2', null, $deleteLogPage->getName()->text() )
1672  );
1674  $outputPage,
1675  'delete',
1676  $title
1677  );
1678 
1679  return;
1680  }
1681 
1682  $deleteReasonList = $request->getText( 'wpDeleteReasonList', 'other' );
1683  $deleteReason = $request->getText( 'wpReason' );
1684 
1685  if ( $deleteReasonList == 'other' ) {
1686  $reason = $deleteReason;
1687  } elseif ( $deleteReason != '' ) {
1688  // Entry from drop down menu + additional comment
1689  $colonseparator = wfMessage( 'colon-separator' )->inContentLanguage()->text();
1690  $reason = $deleteReasonList . $colonseparator . $deleteReason;
1691  } else {
1692  $reason = $deleteReasonList;
1693  }
1694 
1695  if ( $request->wasPosted() && $user->matchEditToken( $request->getVal( 'wpEditToken' ),
1696  [ 'delete', $this->getTitle()->getPrefixedText() ] )
1697  ) {
1698  # Flag to hide all contents of the archived revisions
1699  $suppress = $request->getVal( 'wpSuppress' ) && $user->isAllowed( 'suppressrevision' );
1700 
1701  $this->doDelete( $reason, $suppress );
1702 
1703  WatchAction::doWatchOrUnwatch( $request->getCheck( 'wpWatch' ), $title, $user );
1704 
1705  return;
1706  }
1707 
1708  // Generate deletion reason
1709  $hasHistory = false;
1710  if ( !$reason ) {
1711  try {
1712  $reason = $this->generateReason( $hasHistory );
1713  } catch ( Exception $e ) {
1714  # if a page is horribly broken, we still want to be able to
1715  # delete it. So be lenient about errors here.
1716  wfDebug( "Error while building auto delete summary: $e" );
1717  $reason = '';
1718  }
1719  }
1720 
1721  // If the page has a history, insert a warning
1722  if ( $hasHistory ) {
1723  $title = $this->getTitle();
1724 
1725  // The following can use the real revision count as this is only being shown for users
1726  // that can delete this page.
1727  // This, as a side-effect, also makes sure that the following query isn't being run for
1728  // pages with a larger history, unless the user has the 'bigdelete' right
1729  // (and is about to delete this page).
1730  $dbr = wfGetDB( DB_SLAVE );
1731  $revisions = $edits = (int)$dbr->selectField(
1732  'revision',
1733  'COUNT(rev_page)',
1734  [ 'rev_page' => $title->getArticleID() ],
1735  __METHOD__
1736  );
1737 
1738  // @todo FIXME: i18n issue/patchwork message
1739  $context->getOutput()->addHTML(
1740  '<strong class="mw-delete-warning-revisions">' .
1741  $context->msg( 'historywarning' )->numParams( $revisions )->parse() .
1742  $context->msg( 'word-separator' )->escaped() . Linker::linkKnown( $title,
1743  $context->msg( 'history' )->escaped(),
1744  [],
1745  [ 'action' => 'history' ] ) .
1746  '</strong>'
1747  );
1748 
1749  if ( $title->isBigDeletion() ) {
1750  global $wgDeleteRevisionsLimit;
1751  $context->getOutput()->wrapWikiMsg( "<div class='error'>\n$1\n</div>\n",
1752  [
1753  'delete-warning-toobig',
1754  $context->getLanguage()->formatNum( $wgDeleteRevisionsLimit )
1755  ]
1756  );
1757  }
1758  }
1759 
1760  $this->confirmDelete( $reason );
1761  }
1762 
1768  public function confirmDelete( $reason ) {
1769  wfDebug( "Article::confirmDelete\n" );
1770 
1771  $title = $this->getTitle();
1772  $ctx = $this->getContext();
1773  $outputPage = $ctx->getOutput();
1774  $useMediaWikiUIEverywhere = $ctx->getConfig()->get( 'UseMediaWikiUIEverywhere' );
1775  $outputPage->setPageTitle( wfMessage( 'delete-confirm', $title->getPrefixedText() ) );
1776  $outputPage->addBacklinkSubtitle( $title );
1777  $outputPage->setRobotPolicy( 'noindex,nofollow' );
1778  $backlinkCache = $title->getBacklinkCache();
1779  if ( $backlinkCache->hasLinks( 'pagelinks' ) || $backlinkCache->hasLinks( 'templatelinks' ) ) {
1780  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1781  'deleting-backlinks-warning' );
1782  }
1783  $outputPage->addWikiMsg( 'confirmdeletetext' );
1784 
1785  Hooks::run( 'ArticleConfirmDelete', [ $this, $outputPage, &$reason ] );
1786 
1787  $user = $this->getContext()->getUser();
1788 
1789  if ( $user->isAllowed( 'suppressrevision' ) ) {
1790  $suppress = Html::openElement( 'div', [ 'id' => 'wpDeleteSuppressRow' ] ) .
1791  Xml::checkLabel( wfMessage( 'revdelete-suppress' )->text(),
1792  'wpSuppress', 'wpSuppress', false, [ 'tabindex' => '4' ] ) .
1793  Html::closeElement( 'div' );
1794  } else {
1795  $suppress = '';
1796  }
1797  $checkWatch = $user->getBoolOption( 'watchdeletion' ) || $user->isWatched( $title );
1798 
1799  $form = Html::openElement( 'form', [ 'method' => 'post',
1800  'action' => $title->getLocalURL( 'action=delete' ), 'id' => 'deleteconfirm' ] ) .
1801  Html::openElement( 'fieldset', [ 'id' => 'mw-delete-table' ] ) .
1802  Html::element( 'legend', null, wfMessage( 'delete-legend' )->text() ) .
1803  Html::openElement( 'div', [ 'id' => 'mw-deleteconfirm-table' ] ) .
1804  Html::openElement( 'div', [ 'id' => 'wpDeleteReasonListRow' ] ) .
1805  Html::label( wfMessage( 'deletecomment' )->text(), 'wpDeleteReasonList' ) .
1806  '&nbsp;' .
1808  'wpDeleteReasonList',
1809  wfMessage( 'deletereason-dropdown' )->inContentLanguage()->text(),
1810  wfMessage( 'deletereasonotherlist' )->inContentLanguage()->text(),
1811  '',
1812  'wpReasonDropDown',
1813  1
1814  ) .
1815  Html::closeElement( 'div' ) .
1816  Html::openElement( 'div', [ 'id' => 'wpDeleteReasonRow' ] ) .
1817  Html::label( wfMessage( 'deleteotherreason' )->text(), 'wpReason' ) .
1818  '&nbsp;' .
1819  Html::input( 'wpReason', $reason, 'text', [
1820  'size' => '60',
1821  'maxlength' => '255',
1822  'tabindex' => '2',
1823  'id' => 'wpReason',
1824  'class' => 'mw-ui-input-inline',
1825  'autofocus'
1826  ] ) .
1827  Html::closeElement( 'div' );
1828 
1829  # Disallow watching if user is not logged in
1830  if ( $user->isLoggedIn() ) {
1831  $form .=
1832  Xml::checkLabel( wfMessage( 'watchthis' )->text(),
1833  'wpWatch', 'wpWatch', $checkWatch, [ 'tabindex' => '3' ] );
1834  }
1835 
1836  $form .=
1837  Html::openElement( 'div' ) .
1838  $suppress .
1839  Xml::submitButton( wfMessage( 'deletepage' )->text(),
1840  [
1841  'name' => 'wpConfirmB',
1842  'id' => 'wpConfirmB',
1843  'tabindex' => '5',
1844  'class' => $useMediaWikiUIEverywhere ? 'mw-ui-button mw-ui-destructive' : '',
1845  ]
1846  ) .
1847  Html::closeElement( 'div' ) .
1848  Html::closeElement( 'div' ) .
1849  Xml::closeElement( 'fieldset' ) .
1850  Html::hidden(
1851  'wpEditToken',
1852  $user->getEditToken( [ 'delete', $title->getPrefixedText() ] )
1853  ) .
1854  Xml::closeElement( 'form' );
1855 
1856  if ( $user->isAllowed( 'editinterface' ) ) {
1858  $ctx->msg( 'deletereason-dropdown' )->inContentLanguage()->getTitle(),
1859  wfMessage( 'delete-edit-reasonlist' )->escaped(),
1860  [],
1861  [ 'action' => 'edit' ]
1862  );
1863  $form .= '<p class="mw-delete-editreasons">' . $link . '</p>';
1864  }
1865 
1866  $outputPage->addHTML( $form );
1867 
1868  $deleteLogPage = new LogPage( 'delete' );
1869  $outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) );
1870  LogEventsList::showLogExtract( $outputPage, 'delete', $title );
1871  }
1872 
1878  public function doDelete( $reason, $suppress = false ) {
1879  $error = '';
1880  $context = $this->getContext();
1881  $outputPage = $context->getOutput();
1882  $user = $context->getUser();
1883  $status = $this->mPage->doDeleteArticleReal( $reason, $suppress, 0, true, $error, $user );
1884 
1885  if ( $status->isGood() ) {
1886  $deleted = $this->getTitle()->getPrefixedText();
1887 
1888  $outputPage->setPageTitle( wfMessage( 'actioncomplete' ) );
1889  $outputPage->setRobotPolicy( 'noindex,nofollow' );
1890 
1891  $loglink = '[[Special:Log/delete|' . wfMessage( 'deletionlog' )->text() . ']]';
1892 
1893  $outputPage->addWikiMsg( 'deletedtext', wfEscapeWikiText( $deleted ), $loglink );
1894 
1895  Hooks::run( 'ArticleDeleteAfterSuccess', [ $this->getTitle(), $outputPage ] );
1896 
1897  $outputPage->returnToMain( false );
1898  } else {
1899  $outputPage->setPageTitle(
1900  wfMessage( 'cannotdelete-title',
1901  $this->getTitle()->getPrefixedText() )
1902  );
1903 
1904  if ( $error == '' ) {
1905  $outputPage->addWikiText(
1906  "<div class=\"error mw-error-cannotdelete\">\n" . $status->getWikiText() . "\n</div>"
1907  );
1908  $deleteLogPage = new LogPage( 'delete' );
1909  $outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) );
1910 
1912  $outputPage,
1913  'delete',
1914  $this->getTitle()
1915  );
1916  } else {
1917  $outputPage->addHTML( $error );
1918  }
1919  }
1920  }
1921 
1922  /* Caching functions */
1923 
1931  protected function tryFileCache() {
1932  static $called = false;
1933 
1934  if ( $called ) {
1935  wfDebug( "Article::tryFileCache(): called twice!?\n" );
1936  return false;
1937  }
1938 
1939  $called = true;
1940  if ( $this->isFileCacheable() ) {
1941  $cache = new HTMLFileCache( $this->getTitle(), 'view' );
1942  if ( $cache->isCacheGood( $this->mPage->getTouched() ) ) {
1943  wfDebug( "Article::tryFileCache(): about to load file\n" );
1944  $cache->loadFromFileCache( $this->getContext() );
1945  return true;
1946  } else {
1947  wfDebug( "Article::tryFileCache(): starting buffer\n" );
1948  ob_start( [ &$cache, 'saveToFileCache' ] );
1949  }
1950  } else {
1951  wfDebug( "Article::tryFileCache(): not cacheable\n" );
1952  }
1953 
1954  return false;
1955  }
1956 
1961  public function isFileCacheable() {
1962  $cacheable = false;
1963 
1964  if ( HTMLFileCache::useFileCache( $this->getContext() ) ) {
1965  $cacheable = $this->mPage->getId()
1966  && !$this->mRedirectedFrom && !$this->getTitle()->isRedirect();
1967  // Extension may have reason to disable file caching on some pages.
1968  if ( $cacheable ) {
1969  $cacheable = Hooks::run( 'IsFileCacheable', [ &$this ] );
1970  }
1971  }
1972 
1973  return $cacheable;
1974  }
1975 
1989  public function getParserOutput( $oldid = null, User $user = null ) {
1990  // XXX: bypasses mParserOptions and thus setParserOptions()
1991 
1992  if ( $user === null ) {
1993  $parserOptions = $this->getParserOptions();
1994  } else {
1995  $parserOptions = $this->mPage->makeParserOptions( $user );
1996  }
1997 
1998  return $this->mPage->getParserOutput( $parserOptions, $oldid );
1999  }
2000 
2008  if ( $this->mParserOptions ) {
2009  throw new MWException( "can't change parser options after they have already been set" );
2010  }
2011 
2012  // clone, so if $options is modified later, it doesn't confuse the parser cache.
2013  $this->mParserOptions = clone $options;
2014  }
2015 
2020  public function getParserOptions() {
2021  if ( !$this->mParserOptions ) {
2022  $this->mParserOptions = $this->mPage->makeParserOptions( $this->getContext() );
2023  }
2024  // Clone to allow modifications of the return value without affecting cache
2025  return clone $this->mParserOptions;
2026  }
2027 
2034  public function setContext( $context ) {
2035  $this->mContext = $context;
2036  }
2037 
2044  public function getContext() {
2045  if ( $this->mContext instanceof IContextSource ) {
2046  return $this->mContext;
2047  } else {
2048  wfDebug( __METHOD__ . " called and \$mContext is null. " .
2049  "Return RequestContext::getMain(); for sanity\n" );
2050  return RequestContext::getMain();
2051  }
2052  }
2053 
2061  public function __get( $fname ) {
2062  if ( property_exists( $this->mPage, $fname ) ) {
2063  # wfWarn( "Access to raw $fname field " . __CLASS__ );
2064  return $this->mPage->$fname;
2065  }
2066  trigger_error( 'Inaccessible property via __get(): ' . $fname, E_USER_NOTICE );
2067  }
2068 
2076  public function __set( $fname, $fvalue ) {
2077  if ( property_exists( $this->mPage, $fname ) ) {
2078  # wfWarn( "Access to raw $fname field of " . __CLASS__ );
2079  $this->mPage->$fname = $fvalue;
2080  // Note: extensions may want to toss on new fields
2081  } elseif ( !in_array( $fname, [ 'mContext', 'mPage' ] ) ) {
2082  $this->mPage->$fname = $fvalue;
2083  } else {
2084  trigger_error( 'Inaccessible property via __set(): ' . $fname, E_USER_NOTICE );
2085  }
2086  }
2087 
2092  public function checkFlags( $flags ) {
2093  return $this->mPage->checkFlags( $flags );
2094  }
2095 
2100  public function checkTouched() {
2101  return $this->mPage->checkTouched();
2102  }
2103 
2108  public function clearPreparedEdit() {
2109  $this->mPage->clearPreparedEdit();
2110  }
2111 
2116  public function doDeleteArticleReal(
2117  $reason, $suppress = false, $u1 = null, $u2 = null, &$error = '', User $user = null
2118  ) {
2119  return $this->mPage->doDeleteArticleReal(
2120  $reason, $suppress, $u1, $u2, $error, $user
2121  );
2122  }
2123 
2128  public function doDeleteUpdates( $id, Content $content = null ) {
2129  return $this->mPage->doDeleteUpdates( $id, $content );
2130  }
2131 
2136  public function doEdit( $text, $summary, $flags = 0, $baseRevId = false, $user = null ) {
2137  ContentHandler::deprecated( __METHOD__, '1.21' );
2138  return $this->mPage->doEdit( $text, $summary, $flags, $baseRevId, $user );
2139  }
2140 
2145  public function doEditContent( Content $content, $summary, $flags = 0, $baseRevId = false,
2146  User $user = null, $serialFormat = null
2147  ) {
2148  return $this->mPage->doEditContent( $content, $summary, $flags, $baseRevId,
2149  $user, $serialFormat
2150  );
2151  }
2152 
2157  public function doEditUpdates( Revision $revision, User $user, array $options = [] ) {
2158  return $this->mPage->doEditUpdates( $revision, $user, $options );
2159  }
2160 
2165  public function doPurge() {
2166  return $this->mPage->doPurge();
2167  }
2168 
2173  public function doQuickEditContent(
2174  Content $content, User $user, $comment = '', $minor = false, $serialFormat = null
2175  ) {
2176  return $this->mPage->doQuickEditContent(
2177  $content, $user, $comment, $minor, $serialFormat
2178  );
2179  }
2180 
2185  public function doViewUpdates( User $user, $oldid = 0 ) {
2186  $this->mPage->doViewUpdates( $user, $oldid );
2187  }
2188 
2193  public function exists() {
2194  return $this->mPage->exists();
2195  }
2196 
2201  public function followRedirect() {
2202  return $this->mPage->followRedirect();
2203  }
2204 
2209  public function getActionOverrides() {
2210  return $this->mPage->getActionOverrides();
2211  }
2212 
2217  public function getAutoDeleteReason( &$hasHistory ) {
2218  return $this->mPage->getAutoDeleteReason( $hasHistory );
2219  }
2220 
2225  public function getCategories() {
2226  return $this->mPage->getCategories();
2227  }
2228 
2233  public function getComment( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2234  return $this->mPage->getComment( $audience, $user );
2235  }
2236 
2241  public function getContentHandler() {
2242  return $this->mPage->getContentHandler();
2243  }
2244 
2249  public function getContentModel() {
2250  return $this->mPage->getContentModel();
2251  }
2252 
2257  public function getContributors() {
2258  return $this->mPage->getContributors();
2259  }
2260 
2265  public function getCreator( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2266  return $this->mPage->getCreator( $audience, $user );
2267  }
2268 
2273  public function getDeletionUpdates( Content $content = null ) {
2274  return $this->mPage->getDeletionUpdates( $content );
2275  }
2276 
2281  public function getHiddenCategories() {
2282  return $this->mPage->getHiddenCategories();
2283  }
2284 
2289  public function getId() {
2290  return $this->mPage->getId();
2291  }
2292 
2297  public function getLatest() {
2298  return $this->mPage->getLatest();
2299  }
2300 
2305  public function getLinksTimestamp() {
2306  return $this->mPage->getLinksTimestamp();
2307  }
2308 
2313  public function getMinorEdit() {
2314  return $this->mPage->getMinorEdit();
2315  }
2316 
2321  public function getOldestRevision() {
2322  return $this->mPage->getOldestRevision();
2323  }
2324 
2329  public function getRedirectTarget() {
2330  return $this->mPage->getRedirectTarget();
2331  }
2332 
2337  public function getRedirectURL( $rt ) {
2338  return $this->mPage->getRedirectURL( $rt );
2339  }
2340 
2345  public function getRevision() {
2346  return $this->mPage->getRevision();
2347  }
2348 
2353  public function getText( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2354  ContentHandler::deprecated( __METHOD__, '1.21' );
2355  return $this->mPage->getText( $audience, $user );
2356  }
2357 
2362  public function getTimestamp() {
2363  return $this->mPage->getTimestamp();
2364  }
2365 
2370  public function getTouched() {
2371  return $this->mPage->getTouched();
2372  }
2373 
2378  public function getUndoContent( Revision $undo, Revision $undoafter = null ) {
2379  return $this->mPage->getUndoContent( $undo, $undoafter );
2380  }
2381 
2386  public function getUser( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2387  return $this->mPage->getUser( $audience, $user );
2388  }
2389 
2394  public function getUserText( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2395  return $this->mPage->getUserText( $audience, $user );
2396  }
2397 
2402  public function hasViewableContent() {
2403  return $this->mPage->hasViewableContent();
2404  }
2405 
2410  public function insertOn( $dbw, $pageId = null ) {
2411  return $this->mPage->insertOn( $dbw, $pageId );
2412  }
2413 
2418  public function insertProtectNullRevision( $revCommentMsg, array $limit,
2419  array $expiry, $cascade, $reason, $user = null
2420  ) {
2421  return $this->mPage->insertProtectNullRevision( $revCommentMsg, $limit,
2422  $expiry, $cascade, $reason, $user
2423  );
2424  }
2425 
2430  public function insertRedirect() {
2431  return $this->mPage->insertRedirect();
2432  }
2433 
2438  public function insertRedirectEntry( Title $rt, $oldLatest = null ) {
2439  return $this->mPage->insertRedirectEntry( $rt, $oldLatest );
2440  }
2441 
2446  public function isCountable( $editInfo = false ) {
2447  return $this->mPage->isCountable( $editInfo );
2448  }
2449 
2454  public function isRedirect() {
2455  return $this->mPage->isRedirect();
2456  }
2457 
2462  public function loadFromRow( $data, $from ) {
2463  return $this->mPage->loadFromRow( $data, $from );
2464  }
2465 
2470  public function loadPageData( $from = 'fromdb' ) {
2471  $this->mPage->loadPageData( $from );
2472  }
2473 
2478  public function lockAndGetLatest() {
2479  return $this->mPage->lockAndGetLatest();
2480  }
2481 
2486  public function makeParserOptions( $context ) {
2487  return $this->mPage->makeParserOptions( $context );
2488  }
2489 
2494  public function pageDataFromId( $dbr, $id, $options = [] ) {
2495  return $this->mPage->pageDataFromId( $dbr, $id, $options );
2496  }
2497 
2502  public function pageDataFromTitle( $dbr, $title, $options = [] ) {
2503  return $this->mPage->pageDataFromTitle( $dbr, $title, $options );
2504  }
2505 
2510  public function prepareContentForEdit(
2511  Content $content, $revision = null, User $user = null,
2512  $serialFormat = null, $useCache = true
2513  ) {
2514  return $this->mPage->prepareContentForEdit(
2515  $content, $revision, $user,
2516  $serialFormat, $useCache
2517  );
2518  }
2519 
2524  public function prepareTextForEdit( $text, $revid = null, User $user = null ) {
2525  return $this->mPage->prepareTextForEdit( $text, $revid, $user );
2526  }
2527 
2532  public function protectDescription( array $limit, array $expiry ) {
2533  return $this->mPage->protectDescription( $limit, $expiry );
2534  }
2535 
2540  public function protectDescriptionLog( array $limit, array $expiry ) {
2541  return $this->mPage->protectDescriptionLog( $limit, $expiry );
2542  }
2543 
2548  public function replaceSectionAtRev( $sectionId, Content $sectionContent,
2549  $sectionTitle = '', $baseRevId = null
2550  ) {
2551  return $this->mPage->replaceSectionAtRev( $sectionId, $sectionContent,
2552  $sectionTitle, $baseRevId
2553  );
2554  }
2555 
2560  public function replaceSectionContent(
2561  $sectionId, Content $sectionContent, $sectionTitle = '', $edittime = null
2562  ) {
2563  return $this->mPage->replaceSectionContent(
2564  $sectionId, $sectionContent, $sectionTitle, $edittime
2565  );
2566  }
2567 
2572  public function setTimestamp( $ts ) {
2573  return $this->mPage->setTimestamp( $ts );
2574  }
2575 
2580  public function shouldCheckParserCache( ParserOptions $parserOptions, $oldId ) {
2581  return $this->mPage->shouldCheckParserCache( $parserOptions, $oldId );
2582  }
2583 
2588  public function supportsSections() {
2589  return $this->mPage->supportsSections();
2590  }
2591 
2597  return $this->mPage->triggerOpportunisticLinksUpdate( $parserOutput );
2598  }
2599 
2604  public function updateCategoryCounts( array $added, array $deleted ) {
2605  return $this->mPage->updateCategoryCounts( $added, $deleted );
2606  }
2607 
2612  public function updateIfNewerOn( $dbw, $revision ) {
2613  return $this->mPage->updateIfNewerOn( $dbw, $revision );
2614  }
2615 
2620  public function updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect = null ) {
2621  return $this->mPage->updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect = null );
2622  }
2623 
2628  public function updateRevisionOn( $dbw, $revision, $lastRevision = null,
2629  $lastRevIsRedirect = null
2630  ) {
2631  return $this->mPage->updateRevisionOn( $dbw, $revision, $lastRevision,
2632  $lastRevIsRedirect
2633  );
2634  }
2635 
2644  public function doUpdateRestrictions( array $limit, array $expiry, &$cascade,
2645  $reason, User $user
2646  ) {
2647  return $this->mPage->doUpdateRestrictions( $limit, $expiry, $cascade, $reason, $user );
2648  }
2649 
2657  public function updateRestrictions( $limit = [], $reason = '',
2658  &$cascade = 0, $expiry = []
2659  ) {
2660  return $this->mPage->doUpdateRestrictions(
2661  $limit,
2662  $expiry,
2663  $cascade,
2664  $reason,
2665  $this->getContext()->getUser()
2666  );
2667  }
2668 
2677  public function doDeleteArticle(
2678  $reason, $suppress = false, $u1 = null, $u2 = null, &$error = ''
2679  ) {
2680  return $this->mPage->doDeleteArticle( $reason, $suppress, $u1, $u2, $error );
2681  }
2682 
2692  public function doRollback( $fromP, $summary, $token, $bot, &$resultDetails, User $user = null ) {
2693  $user = is_null( $user ) ? $this->getContext()->getUser() : $user;
2694  return $this->mPage->doRollback( $fromP, $summary, $token, $bot, $resultDetails, $user );
2695  }
2696 
2705  public function commitRollback( $fromP, $summary, $bot, &$resultDetails, User $guser = null ) {
2706  $guser = is_null( $guser ) ? $this->getContext()->getUser() : $guser;
2707  return $this->mPage->commitRollback( $fromP, $summary, $bot, $resultDetails, $guser );
2708  }
2709 
2714  public function generateReason( &$hasHistory ) {
2715  $title = $this->mPage->getTitle();
2717  return $handler->getAutoDeleteReason( $title, $hasHistory );
2718  }
2719 
2725  public static function selectFields() {
2726  wfDeprecated( __METHOD__, '1.24' );
2727  return WikiPage::selectFields();
2728  }
2729 
2735  public static function onArticleCreate( $title ) {
2736  wfDeprecated( __METHOD__, '1.24' );
2738  }
2739 
2745  public static function onArticleDelete( $title ) {
2746  wfDeprecated( __METHOD__, '1.24' );
2748  }
2749 
2755  public static function onArticleEdit( $title ) {
2756  wfDeprecated( __METHOD__, '1.24' );
2758  }
2759 
2767  public static function getAutosummary( $oldtext, $newtext, $flags ) {
2768  return WikiPage::getAutosummary( $oldtext, $newtext, $flags );
2769  }
2770  // ******
2771 }
pageDataFromId($dbr, $id, $options=[])
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2494
__set($fname, $fvalue)
Use PHP's magic __set handler to handle setting of raw WikiPage fields for backwards compatibility...
Definition: Article.php:2076
static newFromName($name, $validate= 'valid')
Static factory method for creation from username.
Definition: User.php:568
getRedirectTarget()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2329
static purgePatrolFooterCache($articleID)
Purge the cache used to check if it is worth showing the patrol footer For example, it is done during re-uploads when file patrol is used.
Definition: Article.php:1233
const FOR_THIS_USER
Definition: Revision.php:84
viewRedirect($target, $appendSubtitle=true, $forceKnown=false)
Return the HTML for the top of a redirect page.
Definition: Article.php:1541
static newFromID($id, $flags=0)
Create a new Title from an article ID.
Definition: Title.php:417
static closeElement($element)
Returns "".
Definition: Html.php:306
lockAndGetLatest()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2478
static onArticleCreate(Title $title)
The onArticle*() functions are supposed to be a kind of hooks which should be called whenever any of ...
Definition: WikiPage.php:3236
static onArticleCreate($title)
Definition: Article.php:2735
getUndoContent(Revision $undo, Revision $undoafter=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2378
doDeleteArticleReal($reason, $suppress=false, $u1=null, $u2=null, &$error= '', User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2116
static getMainWANInstance()
Get the main WAN cache object.
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return an< a > element with HTML attributes $attribs and contents $html will be returned If you return $ret will be returned and may include noclasses & $html
Definition: hooks.txt:1798
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
Definition: deferred.txt:11
Interface for objects which can provide a MediaWiki context on request.
static isInRCLifespan($timestamp, $tolerance=0)
Check whether the given timestamp is new enough to have a RC row with a given tolerance as the recent...
static revComment(Revision $rev, $local=false, $isPublic=false)
Wrap and format the given revision's comment block, if the current user is allowed to view it...
Definition: Linker.php:1656
WikiPage $mPage
The WikiPage object of this instance.
Definition: Article.php:39
wfGetDB($db, $groups=[], $wiki=false)
Get a Database object.
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output $out
Definition: hooks.txt:762
getRobotPolicy($action, $pOutput=null)
Get the robot policy to be used for the current view.
Definition: Article.php:843
the array() calling protocol came about after MediaWiki 1.4rc1.
null for the local wiki Added should default to null in handler for backwards compatibility add a value to it if you want to add a cookie that have to vary cache options can modify $query
Definition: hooks.txt:1418
updateRestrictions($limit=[], $reason= '', &$cascade=0, $expiry=[])
Definition: Article.php:2657
doEditContent(Content $content, $summary, $flags=0, $baseRevId=false, User $user=null, $serialFormat=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2145
getText($audience=Revision::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2353
getLatest()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2297
setParserOptions(ParserOptions $options)
Override the ParserOptions used to render the primary article wikitext.
Definition: Article.php:2007
static linkKnown($target, $html=null, $customAttribs=[], $query=[], $options=[ 'known', 'noclasses'])
Identical to link(), except $options defaults to 'known'.
Definition: Linker.php:264
magic word the default is to use $key to get the and $key value or $key value text $key value html to format the value $key
Definition: hooks.txt:2321
doEditUpdates(Revision $revision, User $user, array $options=[])
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2157
$context
Definition: load.php:44
static getRevDeleteLink(User $user, Revision $rev, Title $title)
Get a revision-deletion link, or disabled link, or nothing, depending on user permissions & the setti...
Definition: Linker.php:2256
if(count($args)==0) $dir
static element($element, $attribs=null, $contents= '', $allowShortTag=true)
Format an XML element with given attributes and, optionally, text content.
Definition: Xml.php:39
exists()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2193
supportsSections()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2588
static makeUrl($name, $urlaction= '')
Definition: Skin.php:1084
getContent()
Note that getContent does not follow redirects anymore.
Definition: Article.php:205
doRollback($fromP, $summary, $token, $bot, &$resultDetails, User $user=null)
Definition: Article.php:2692
Show an error when the wiki is locked/read-only and the user tries to do something that requires writ...
doDeleteArticle($reason, $suppress=false, $u1=null, $u2=null, &$error= '')
Definition: Article.php:2677
protect()
action=protect handler
Definition: Article.php:1625
null for the local wiki Added should default to null in handler for backwards compatibility add a value to it if you want to add a cookie that have to vary cache options can modify prev or next refreshes the diff cache $unhide
Definition: hooks.txt:1418
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException'returning false will NOT prevent logging $e
Definition: hooks.txt:1932
IContextSource $mContext
The context this Article is executed in.
Definition: Article.php:36
Set options of the Parser.
isCountable($editInfo=false)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2446
Wrapper allowing us to handle a system message as a Content object.
getPage()
Get the WikiPage object of this instance.
Definition: Article.php:176
getParserOutput($oldid=null, User $user=null)
#@-
Definition: Article.php:1989
string bool $mRedirectUrl
URL to redirect to or false if none.
Definition: Article.php:66
loadPageData($from= 'fromdb')
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2470
if(!isset($args[0])) $lang
ParserOptions $mParserOptions
ParserOptions object for $wgUser articles.
Definition: Article.php:42
Content $mContentObject
Content of the revision we are working on.
Definition: Article.php:54
Special handling for category description pages, showing pages, subcategories and file that belong to...
doDeleteUpdates($id, Content $content=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2128
adjustDisplayTitle(ParserOutput $pOutput)
Adjust title for pages with displaytitle, -{T|}- or language conversion.
Definition: Article.php:740
static hidden($name, $value, array $attribs=[])
Convenience function to produce an input element with type=hidden.
Definition: Html.php:759
shouldCheckParserCache(ParserOptions $parserOptions, $oldId)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2580
doUpdateRestrictions(array $limit, array $expiry, &$cascade, $reason, User $user)
Definition: Article.php:2644
$comment
Class for viewing MediaWiki article and history.
Definition: Article.php:34
null for the local wiki Added in
Definition: hooks.txt:1418
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
Page view caching in the file system.
followRedirect()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2201
confirmDelete($reason)
Output deletion confirmation dialog.
Definition: Article.php:1768
Class for viewing MediaWiki file description pages.
Definition: ImagePage.php:28
triggerOpportunisticLinksUpdate(ParserOutput $parserOutput)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2596
getOldIDFromRequest()
Sets $this->mRedirectUrl to a correct URL if the query parameters are incorrect.
Definition: Article.php:266
__get($fname)
Use PHP's magic __get handler to handle accessing of raw WikiPage fields for backwards compatibility...
Definition: Article.php:2061
it s the revision text itself In either if gzip is the revision text is gzipped $flags
Definition: hooks.txt:2548
getDeletionUpdates(Content $content=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2273
updateIfNewerOn($dbw, $revision)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2612
clearPreparedEdit()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2108
static getMainStashInstance()
Get the cache object for the main stash.
Represents a title within MediaWiki.
Definition: Title.php:34
when a variable name is used in a it is silently declared as a new local masking the global
Definition: design.txt:93
static useFileCache(IContextSource $context)
Check if pages can be cached for this request/user.
static newFromTitle(LinkTarget $linkTarget, $id=0, $flags=0)
Load either the current, or a specified, revision that's attached to a given link target...
Definition: Revision.php:117
getContributors()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2257
static submitButton($value, $attribs=[])
Convenience function to build an HTML submit button When $wgUseMediaWikiUIEverywhere is true it will ...
Definition: Xml.php:460
commitRollback($fromP, $summary, $bot, &$resultDetails, User $guser=null)
Definition: Article.php:2705
showMissingArticle()
Show the error text for a missing article.
Definition: Article.php:1242
wfDebug($text, $dest= 'all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
static showLogExtract(&$out, $types=[], $page= '', $user= '', $param=[])
Show log extract.
protectDescription(array $limit, array $expiry)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2532
the value to return A Title object or null for latest to be modified or replaced by the hook handler or if authentication is not possible after cache objects are set for highlighting & $link
Definition: hooks.txt:2581
Class to simplify the use of log pages.
Definition: LogPage.php:32
getContext()
Gets the context this Article is executed in.
Definition: Article.php:2044
static closeElement($element)
Shortcut to close an XML element.
Definition: Xml.php:118
isRedirect()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2454
__construct(Title $title, $oldId=null)
Constructor and clear the article.
Definition: Article.php:82
setTimestamp($ts)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2572
protectDescriptionLog(array $limit, array $expiry)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2540
static onArticleDelete($title)
Definition: Article.php:2745
static openElement($element, $attribs=[])
Identical to rawElement(), but has no third parameter and omits the end tag (and the self-closing '/'...
Definition: Html.php:248
Interface for type hinting (accepts WikiPage, Article, ImagePage, CategoryPage)
Definition: Page.php:24
makeParserOptions($context)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2486
wfEscapeWikiText($text)
Escapes the given text so that it may be output using addWikiText() without any linking, formatting, etc.
wfReadOnly()
Check whether the wiki is in read-only mode.
static getMain()
Static methods.
prepareContentForEdit(Content $content, $revision=null, User $user=null, $serialFormat=null, $useCache=true)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2510
const FOR_PUBLIC
Definition: Revision.php:83
static getCanonicalName($index)
Returns the canonical (English) name for a given index.
$wgUseFileCache
This will cache static pages for non-logged-in users to reduce database traffic on public sites...
static deprecated($func, $version, $component=false)
Logs a deprecation warning, visible if $wgDevelopmentWarnings, but only if self::$enableDeprecationWa...
showViewFooter()
Show the footer section of an ordinary page view.
Definition: Article.php:1040
if($limit) $timestamp
static newFromTarget($specificTarget, $vagueTarget=null, $fromMaster=false)
Given a target and the target's type, get an existing Block object if possible.
Definition: Block.php:1077
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist 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 $parserOutput
Definition: hooks.txt:1004
getRevisionFetched()
Get the fetched Revision object depending on request parameters or null on failure.
Definition: Article.php:446
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist 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:1004
static runLegacyHooks($event, $args=[], $warn=null)
Call a legacy hook that uses text instead of Content objects.
insertProtectNullRevision($revCommentMsg, array $limit, array $expiry, $cascade, $reason, $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2418
const NS_MEDIA
Definition: Defines.php:57
static isValid($ip)
Validate an IP address.
Definition: IP.php:113
showRedirectedFromHeader()
If this request is a redirect view, send "redirected from" subtitle to the output.
Definition: Article.php:952
static getContentText(Content $content=null)
Convenience function for getting flat text from a Content object.
ParserOutput $mParserOutput
Definition: Article.php:75
$summary
getRevision()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2345
it s the revision text itself In either if gzip is set
Definition: hooks.txt:2548
generateReason(&$hasHistory)
Definition: Article.php:2714
static getRedirectHeaderHtml(Language $lang, $target, $forceKnown=false)
Return the HTML for the top of a redirect page.
Definition: Article.php:1563
insertRedirect()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2430
int null $mOldId
The oldid of the article that is to be shown, 0 for the current revision.
Definition: Article.php:60
static openElement($element, $attribs=null)
This opens an XML element.
Definition: Xml.php:109
hasViewableContent()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2402
getComment($audience=Revision::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2233
Base interface for content objects.
Definition: Content.php:34
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return an< a > element with HTML attributes $attribs and contents $html will be returned If you return $ret will be returned and may include noclasses after processing 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 unsetoffset-wrap String Wrap the message in html(usually something like"&lt
getTitle()
Get the title object of the article.
Definition: Article.php:166
loadFromRow($data, $from)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2462
$cache
Definition: mcc.php:33
getActionOverrides()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2209
doViewUpdates(User $user, $oldid=0)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2185
getTitle()
Get the title object of the article.
Definition: WikiPage.php:217
const NS_CATEGORY
Definition: Defines.php:83
doPurge()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2165
static newFromTitle($title, IContextSource $context)
Create an Article object of the appropriate class for the given page.
Definition: Article.php:114
wfDeprecated($function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
render()
Handle action=render.
Definition: Article.php:1615
static isIP($name)
Does the string match an anonymous IPv4 address?
Definition: User.php:830
const DELETED_RESTRICTED
Definition: Revision.php:79
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 & $sectionContent
Definition: hooks.txt:2338
const DB_SLAVE
Definition: Defines.php:46
getCategories()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2225
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:912
replaceSectionAtRev($sectionId, Content $sectionContent, $sectionTitle= '', $baseRevId=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2548
static run($event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:131
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at etc Handles the details of getting and saving to the user table of the and dealing with sessions and cookies OutputPage Encapsulates the entire HTML page that will be sent in response to any server request It is used by calling its functions to add text
Definition: design.txt:12
checkFlags($flags)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2092
const NS_FILE
Definition: Defines.php:75
static makeContent($text, Title $title=null, $modelId=null, $format=null)
Convenience function for creating a Content object from a given textual representation.
$wgRedirectSources
If local interwikis are set up which allow redirects, set this regexp to restrict URLs which will be ...
const TYPE_AUTO
Definition: Block.php:78
presenting them properly to the user as errors is done by the caller return true use this to change the list i e etc $rev
Definition: hooks.txt:1584
static getForTitle(Title $title)
Returns the appropriate ContentHandler singleton for the given title.
updateCategoryCounts(array $added, array $deleted)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2604
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
getContentHandler()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2241
showCssOrJsPage($showCacheHint=true)
Show a page view for a page formatted as CSS or JavaScript.
Definition: Article.php:807
const NS_MEDIAWIKI
Definition: Defines.php:77
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 local account $user
Definition: hooks.txt:242
static link($target, $html=null, $customAttribs=[], $query=[], $options=[])
This function returns an HTML link to the given target.
Definition: Linker.php:195
static onArticleEdit(Title $title, Revision $revision=null)
Purge caches on page update etc.
Definition: WikiPage.php:3299
Title $mRedirectedFrom
Title from which we were redirected here.
Definition: Article.php:63
const DELETED_TEXT
Definition: Revision.php:76
isFileCacheable()
Check if the page can be cached.
Definition: Article.php:1961
getCreator($audience=Revision::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2265
static singleton()
Get an instance of this object.
Definition: ParserCache.php:36
addHelpLink($to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
Definition: Article.php:1598
newPage(Title $title)
Definition: Article.php:91
Class representing a MediaWiki article and history.
Definition: WikiPage.php:29
static newFromId($id, $flags=0)
Load a page revision from a given revision ID number.
Definition: Revision.php:99
getOldestRevision()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2321
string $mContent
Text of the revision we are working on.
Definition: Article.php:48
static makeExternalLink($url, $text, $escape=true, $linktype= '', $attribs=[], $title=null)
Make an external link.
Definition: Linker.php:1052
clear()
Clear the object.
Definition: Article.php:183
$from
bool $mContentLoaded
Is the content ($mContent) already loaded?
Definition: Article.php:57
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
getAutoDeleteReason(&$hasHistory)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2217
checkTouched()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2100
updateRevisionOn($dbw, $revision, $lastRevision=null, $lastRevIsRedirect=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2628
static selectFields()
Definition: Article.php:2725
prepareTextForEdit($text, $revid=null, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2524
error also a ContextSource you ll probably need to make sure the header is varied on $request
Definition: hooks.txt:2418
static newFromID($id)
Constructor from a page id.
Definition: Article.php:100
if(!defined( 'MEDIAWIKI')) $fname
This file is not a valid entry point, perform no further processing unless MEDIAWIKI is defined...
Definition: Setup.php:35
getId()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2289
linkcache txt The LinkCache class maintains a list of article titles and the information about whether or not the article exists in the database This is used to mark up links when displaying a page If the same link appears more than once on any page then it only has to be looked up once In most cases link lookups are done in batches with the LinkBatch class or the equivalent in so the link cache is mostly useful for short snippets of parsed and for links in the navigation areas of the skin The link cache was formerly used to track links used in a document for the purposes of updating the link tables This application is now deprecated To create a you can use the following $titles
Definition: linkcache.txt:17
getTimestamp()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2362
static input($name, $value= '', $type= 'text', array $attribs=[])
Convenience function to produce an "" element.
Definition: Html.php:676
pageDataFromTitle($dbr, $title, $options=[])
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2502
getMinorEdit()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2313
view()
This is the default action of the index.php entry point: just view the page of the given title...
Definition: Article.php:469
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist 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:1004
static onArticleDelete(Title $title)
Clears caches when article is deleted.
Definition: WikiPage.php:3252
getContentObject()
Returns a Content object representing the pages effective display content, not necessarily the revisi...
Definition: Article.php:226
getOldID()
Definition: Article.php:253
doDelete($reason, $suppress=false)
Perform a deletion and output success or failure messages.
Definition: Article.php:1878
showNamespaceHeader()
Show a header specific to the namespace currently being viewed, like [[MediaWiki:Talkpagetext]].
Definition: Article.php:1026
Show an error when a user tries to do something they do not have the necessary permissions for...
updateRedirectOn($dbw, $redirectTitle, $lastRevIsRedirect=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2620
tryFileCache()
checkLastModified returns true if it has taken care of all output to the client that is necessary for...
Definition: Article.php:1931
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist 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 the output can only depend on parameters provided to this hook not on global state indicating whether full HTML should be generated If generation of HTML may be but other information should still be present in the ParserOutput object to manipulate or replace but no entry for that model exists in $wgContentHandlers if desired whether it is OK to use $contentModel on $title Handler functions that modify $ok should generally return false to prevent further hooks from further modifying $ok inclusive $limit
Definition: hooks.txt:1004
insertRedirectEntry(Title $rt, $oldLatest=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2438
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist 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:1004
doEdit($text, $summary, $flags=0, $baseRevId=false, $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2136
static checkLabel($label, $name, $id, $checked=false, $attribs=[])
Convenience function to build an HTML checkbox with a label.
Definition: Xml.php:420
int $mRevIdFetched
Revision ID of revision we are working on.
Definition: Article.php:69
wfMemcKey()
Make a cache key for the local wiki.
fetchContentObject()
Get text content object Does NOT follow redirects.
Definition: Article.php:360
setOldSubtitle($oldid=0)
Generate the navigation links when browsing through an article revisions It shows the information as:...
Definition: Article.php:1407
static compare($a, $b)
Callback for usort() to do title sorts by (namespace, title)
Definition: Title.php:780
isCurrent()
Returns true if the currently-referenced revision is the current edit to this page (and it exists)...
Definition: Article.php:430
unprotect()
action=unprotect handler (alias)
Definition: Article.php:1633
Handles the page protection UI and backend.
getTouched()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2370
static onArticleEdit($title)
Definition: Article.php:2755
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output modifiable modifiable after all normalizations have been except for the $wgMaxImageArea check set to true or false to override the $wgMaxImageArea check result gives extension the possibility to transform it themselves $handler
Definition: hooks.txt:762
getUser($audience=Revision::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2386
static newFromWikiPage(WikiPage $page, IContextSource $context)
Create an Article object of the appropriate class for the given page.
Definition: Article.php:146
fetchContent()
Get text of an article from database Does NOT follow redirects.
Definition: Article.php:326
static selectFields()
Return the list of revision fields that should be selected to create a new page.
Definition: WikiPage.php:266
static getAutosummary($oldtext, $newtext, $flags)
Return an applicable autosummary if one exists for the given edit.
Definition: WikiPage.php:3381
static doWatchOrUnwatch($watch, Title $title, User $user)
Watch or unwatch a page.
Definition: WatchAction.php:83
$wgSend404Code
Some web hosts attempt to rewrite all responses with a 404 (not found) status code, mangling or hiding MediaWiki's output.
replaceSectionContent($sectionId, Content $sectionContent, $sectionTitle= '', $edittime=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2560
setContext($context)
Sets the context this Article is executed in.
Definition: Article.php:2034
static listDropDown($name= '', $list= '', $other= '', $selected= '', $class= '', $tabindex=null)
Build a drop-down box from a textual list.
Definition: Xml.php:508
getRevIdFetched()
Use this to fetch the rev ID used on page views.
Definition: Article.php:457
const NS_USER_TALK
Definition: Defines.php:72
static revUserTools($rev, $isPublic=false)
Generate a user tool link cluster if the current user is allowed to view it.
Definition: Linker.php:1252
showDiffPage()
Show a diff page according to current request variables.
Definition: Article.php:753
static element($element, $attribs=[], $contents= '')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
Definition: Html.php:230
getRedirectURL($rt)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2337
Definition: Block.php:22
static getAutosummary($oldtext, $newtext, $flags)
Definition: Article.php:2767
showDeletedRevisionHeader()
If the revision requested for view is deleted, check permissions.
Definition: Article.php:1364
$wgUseETag
Whether MediaWiki should send an ETag header.
setRedirectedFrom(Title $from)
Tell the page view functions that this view was redirected from another page on the wiki...
Definition: Article.php:157
static newFromConds($conds, $fname=__METHOD__, $dbType=DB_SLAVE)
Find the first recent change matching some specific conditions.
static & makeTitle($ns, $title, $fragment= '', $interwiki= '')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:524
getContentModel()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2249
doQuickEditContent(Content $content, User $user, $comment= '', $minor=false, $serialFormat=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2173
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:2338
getLinksTimestamp()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2305
insertOn($dbw, $pageId=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2410
getParserOptions()
Get parser options suitable for rendering the primary article wikitext.
Definition: Article.php:2020
const RC_LOG
Definition: Defines.php:171
Revision $mRevision
Revision we are working on.
Definition: Article.php:72
getUserText($audience=Revision::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2394
showPatrolFooter()
If patrol is possible, output a patrol UI box.
Definition: Article.php:1063
static label($label, $id, array $attribs=[])
Convenience function for generating a label for inputs.
Definition: Html.php:743
static formatRobotPolicy($policy)
Converts a String robot policy into an associative array, to allow merging of several policies using ...
Definition: Article.php:923
getHiddenCategories()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2281