MediaWiki  master
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();
208  return ContentHandler::getContentText( $content );
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, $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  # Use the greatest of the page's timestamp or the timestamp of any
524  # redirect in the chain (bug 67849)
525  $timestamp = $this->mPage->getTouched();
526  if ( isset( $this->mRedirectedFrom ) ) {
527  $timestamp = max( $timestamp, $this->mRedirectedFrom->getTouched() );
528 
529  # If there can be more than one redirect in the chain, we have
530  # to go through the whole chain too in case an intermediate
531  # redirect was changed.
532  if ( $wgMaxRedirects > 1 ) {
533  $titles = Revision::newFromTitle( $this->mRedirectedFrom )
534  ->getContent( Revision::FOR_THIS_USER, $user )
535  ->getRedirectChain();
536  $thisTitle = $this->getTitle();
537  foreach ( $titles as $title ) {
538  if ( Title::compare( $title, $thisTitle ) === 0 ) {
539  break;
540  }
541  $timestamp = max( $timestamp, $title->getTouched() );
542  }
543  }
544  }
545 
546  # Is it client cached?
547  if ( $outputPage->checkLastModified( $timestamp ) ) {
548  wfDebug( __METHOD__ . ": done 304\n" );
549 
550  return;
551  # Try file cache
552  } elseif ( $wgUseFileCache && $this->tryFileCache() ) {
553  wfDebug( __METHOD__ . ": done file cache\n" );
554  # tell wgOut that output is taken care of
555  $outputPage->disable();
556  $this->mPage->doViewUpdates( $user, $oldid );
557 
558  return;
559  }
560  }
561 
562  # Should the parser cache be used?
563  $useParserCache = $this->mPage->shouldCheckParserCache( $parserOptions, $oldid );
564  wfDebug( 'Article::view using parser cache: ' . ( $useParserCache ? 'yes' : 'no' ) . "\n" );
565  if ( $user->getStubThreshold() ) {
566  $this->getContext()->getStats()->increment( 'pcache_miss_stub' );
567  }
568 
569  $this->showRedirectedFromHeader();
570  $this->showNamespaceHeader();
571 
572  # Iterate through the possible ways of constructing the output text.
573  # Keep going until $outputDone is set, or we run out of things to do.
574  $pass = 0;
575  $outputDone = false;
576  $this->mParserOutput = false;
577 
578  while ( !$outputDone && ++$pass ) {
579  switch ( $pass ) {
580  case 1:
581  Hooks::run( 'ArticleViewHeader', [ &$this, &$outputDone, &$useParserCache ] );
582  break;
583  case 2:
584  # Early abort if the page doesn't exist
585  if ( !$this->mPage->exists() ) {
586  wfDebug( __METHOD__ . ": showing missing article\n" );
587  $this->showMissingArticle();
588  $this->mPage->doViewUpdates( $user );
589  return;
590  }
591 
592  # Try the parser cache
593  if ( $useParserCache ) {
594  $this->mParserOutput = $parserCache->get( $this->mPage, $parserOptions );
595 
596  if ( $this->mParserOutput !== false ) {
597  if ( $oldid ) {
598  wfDebug( __METHOD__ . ": showing parser cache contents for current rev permalink\n" );
599  $this->setOldSubtitle( $oldid );
600  } else {
601  wfDebug( __METHOD__ . ": showing parser cache contents\n" );
602  }
603  $outputPage->addParserOutput( $this->mParserOutput );
604  # Ensure that UI elements requiring revision ID have
605  # the correct version information.
606  $outputPage->setRevisionId( $this->mPage->getLatest() );
607  # Preload timestamp to avoid a DB hit
608  $cachedTimestamp = $this->mParserOutput->getTimestamp();
609  if ( $cachedTimestamp !== null ) {
610  $outputPage->setRevisionTimestamp( $cachedTimestamp );
611  $this->mPage->setTimestamp( $cachedTimestamp );
612  }
613  $outputDone = true;
614  }
615  }
616  break;
617  case 3:
618  # This will set $this->mRevision if needed
619  $this->fetchContentObject();
620 
621  # Are we looking at an old revision
622  if ( $oldid && $this->mRevision ) {
623  $this->setOldSubtitle( $oldid );
624 
625  if ( !$this->showDeletedRevisionHeader() ) {
626  wfDebug( __METHOD__ . ": cannot view deleted revision\n" );
627  return;
628  }
629  }
630 
631  # Ensure that UI elements requiring revision ID have
632  # the correct version information.
633  $outputPage->setRevisionId( $this->getRevIdFetched() );
634  # Preload timestamp to avoid a DB hit
635  $outputPage->setRevisionTimestamp( $this->mPage->getTimestamp() );
636 
637  # Pages containing custom CSS or JavaScript get special treatment
638  if ( $this->getTitle()->isCssOrJsPage() || $this->getTitle()->isCssJsSubpage() ) {
639  wfDebug( __METHOD__ . ": showing CSS/JS source\n" );
640  $this->showCssOrJsPage();
641  $outputDone = true;
642  } elseif ( !Hooks::run( 'ArticleContentViewCustom',
643  [ $this->fetchContentObject(), $this->getTitle(), $outputPage ] ) ) {
644 
645  # Allow extensions do their own custom view for certain pages
646  $outputDone = true;
647  } elseif ( !ContentHandler::runLegacyHooks( 'ArticleViewCustom',
648  [ $this->fetchContentObject(), $this->getTitle(), $outputPage ] ) ) {
649 
650  # Allow extensions do their own custom view for certain pages
651  $outputDone = true;
652  }
653  break;
654  case 4:
655  # Run the parse, protected by a pool counter
656  wfDebug( __METHOD__ . ": doing uncached parse\n" );
657 
658  $content = $this->getContentObject();
659  $poolArticleView = new PoolWorkArticleView( $this->getPage(), $parserOptions,
660  $this->getRevIdFetched(), $useParserCache, $content );
661 
662  if ( !$poolArticleView->execute() ) {
663  $error = $poolArticleView->getError();
664  if ( $error ) {
665  $outputPage->clearHTML(); // for release() errors
666  $outputPage->enableClientCache( false );
667  $outputPage->setRobotPolicy( 'noindex,nofollow' );
668 
669  $errortext = $error->getWikiText( false, 'view-pool-error' );
670  $outputPage->addWikiText( '<div class="errorbox">' . $errortext . '</div>' );
671  }
672  # Connection or timeout error
673  return;
674  }
675 
676  $this->mParserOutput = $poolArticleView->getParserOutput();
677  $outputPage->addParserOutput( $this->mParserOutput );
678  if ( $content->getRedirectTarget() ) {
679  $outputPage->addSubtitle( "<span id=\"redirectsub\">" .
680  $this->getContext()->msg( 'redirectpagesub' )->parse() . "</span>" );
681  }
682 
683  # Don't cache a dirty ParserOutput object
684  if ( $poolArticleView->getIsDirty() ) {
685  $outputPage->setCdnMaxage( 0 );
686  $outputPage->addHTML( "<!-- parser cache is expired, " .
687  "sending anyway due to pool overload-->\n" );
688  }
689 
690  $outputDone = true;
691  break;
692  # Should be unreachable, but just in case...
693  default:
694  break 2;
695  }
696  }
697 
698  # Get the ParserOutput actually *displayed* here.
699  # Note that $this->mParserOutput is the *current*/oldid version output.
700  $pOutput = ( $outputDone instanceof ParserOutput )
701  ? $outputDone // object fetched by hook
702  : $this->mParserOutput;
703 
704  # Adjust title for main page & pages with displaytitle
705  if ( $pOutput ) {
706  $this->adjustDisplayTitle( $pOutput );
707  }
708 
709  # For the main page, overwrite the <title> element with the con-
710  # tents of 'pagetitle-view-mainpage' instead of the default (if
711  # that's not empty).
712  # This message always exists because it is in the i18n files
713  if ( $this->getTitle()->isMainPage() ) {
714  $msg = wfMessage( 'pagetitle-view-mainpage' )->inContentLanguage();
715  if ( !$msg->isDisabled() ) {
716  $outputPage->setHTMLTitle( $msg->title( $this->getTitle() )->text() );
717  }
718  }
719 
720  # Check for any __NOINDEX__ tags on the page using $pOutput
721  $policy = $this->getRobotPolicy( 'view', $pOutput );
722  $outputPage->setIndexPolicy( $policy['index'] );
723  $outputPage->setFollowPolicy( $policy['follow'] );
724 
725  $this->showViewFooter();
726  $this->mPage->doViewUpdates( $user, $oldid );
727 
728  $outputPage->addModules( 'mediawiki.action.view.postEdit' );
729 
730  }
731 
736  public function adjustDisplayTitle( ParserOutput $pOutput ) {
737  # Adjust the title if it was set by displaytitle, -{T|}- or language conversion
738  $titleText = $pOutput->getTitleText();
739  if ( strval( $titleText ) !== '' ) {
740  $this->getContext()->getOutput()->setPageTitle( $titleText );
741  }
742  }
743 
749  protected function showDiffPage() {
750  $request = $this->getContext()->getRequest();
751  $user = $this->getContext()->getUser();
752  $diff = $request->getVal( 'diff' );
753  $rcid = $request->getVal( 'rcid' );
754  $diffOnly = $request->getBool( 'diffonly', $user->getOption( 'diffonly' ) );
755  $purge = $request->getVal( 'action' ) == 'purge';
756  $unhide = $request->getInt( 'unhide' ) == 1;
757  $oldid = $this->getOldID();
758 
759  $rev = $this->getRevisionFetched();
760 
761  if ( !$rev ) {
762  $this->getContext()->getOutput()->setPageTitle( wfMessage( 'errorpagetitle' ) );
763  $msg = $this->getContext()->msg( 'difference-missing-revision' )
764  ->params( $oldid )
765  ->numParams( 1 )
766  ->parseAsBlock();
767  $this->getContext()->getOutput()->addHTML( $msg );
768  return;
769  }
770 
771  $contentHandler = $rev->getContentHandler();
772  $de = $contentHandler->createDifferenceEngine(
773  $this->getContext(),
774  $oldid,
775  $diff,
776  $rcid,
777  $purge,
778  $unhide
779  );
780 
781  // DifferenceEngine directly fetched the revision:
782  $this->mRevIdFetched = $de->mNewid;
783  $de->showDiffPage( $diffOnly );
784 
785  // Run view updates for the newer revision being diffed (and shown
786  // below the diff if not $diffOnly).
787  list( $old, $new ) = $de->mapDiffPrevNext( $oldid, $diff );
788  // New can be false, convert it to 0 - this conveniently means the latest revision
789  $this->mPage->doViewUpdates( $user, (int)$new );
790  }
791 
803  protected function showCssOrJsPage( $showCacheHint = true ) {
804  $outputPage = $this->getContext()->getOutput();
805 
806  if ( $showCacheHint ) {
807  $dir = $this->getContext()->getLanguage()->getDir();
808  $lang = $this->getContext()->getLanguage()->getHtmlCode();
809 
810  $outputPage->wrapWikiMsg(
811  "<div id='mw-clearyourcache' lang='$lang' dir='$dir' class='mw-content-$dir'>\n$1\n</div>",
812  'clearyourcache'
813  );
814  }
815 
816  $this->fetchContentObject();
817 
818  if ( $this->mContentObject ) {
819  // Give hooks a chance to customise the output
820  if ( ContentHandler::runLegacyHooks(
821  'ShowRawCssJs',
822  [ $this->mContentObject, $this->getTitle(), $outputPage ] )
823  ) {
824  // If no legacy hooks ran, display the content of the parser output, including RL modules,
825  // but excluding metadata like categories and language links
826  $po = $this->mContentObject->getParserOutput( $this->getTitle() );
827  $outputPage->addParserOutputContent( $po );
828  }
829  }
830  }
831 
839  public function getRobotPolicy( $action, $pOutput = null ) {
841 
842  $ns = $this->getTitle()->getNamespace();
843 
844  # Don't index user and user talk pages for blocked users (bug 11443)
845  if ( ( $ns == NS_USER || $ns == NS_USER_TALK ) && !$this->getTitle()->isSubpage() ) {
846  $specificTarget = null;
847  $vagueTarget = null;
848  $titleText = $this->getTitle()->getText();
849  if ( IP::isValid( $titleText ) ) {
850  $vagueTarget = $titleText;
851  } else {
852  $specificTarget = $titleText;
853  }
854  if ( Block::newFromTarget( $specificTarget, $vagueTarget ) instanceof Block ) {
855  return [
856  'index' => 'noindex',
857  'follow' => 'nofollow'
858  ];
859  }
860  }
861 
862  if ( $this->mPage->getId() === 0 || $this->getOldID() ) {
863  # Non-articles (special pages etc), and old revisions
864  return [
865  'index' => 'noindex',
866  'follow' => 'nofollow'
867  ];
868  } elseif ( $this->getContext()->getOutput()->isPrintable() ) {
869  # Discourage indexing of printable versions, but encourage following
870  return [
871  'index' => 'noindex',
872  'follow' => 'follow'
873  ];
874  } elseif ( $this->getContext()->getRequest()->getInt( 'curid' ) ) {
875  # For ?curid=x urls, disallow indexing
876  return [
877  'index' => 'noindex',
878  'follow' => 'follow'
879  ];
880  }
881 
882  # Otherwise, construct the policy based on the various config variables.
883  $policy = self::formatRobotPolicy( $wgDefaultRobotPolicy );
884 
885  if ( isset( $wgNamespaceRobotPolicies[$ns] ) ) {
886  # Honour customised robot policies for this namespace
887  $policy = array_merge(
888  $policy,
889  self::formatRobotPolicy( $wgNamespaceRobotPolicies[$ns] )
890  );
891  }
892  if ( $this->getTitle()->canUseNoindex() && is_object( $pOutput ) && $pOutput->getIndexPolicy() ) {
893  # __INDEX__ and __NOINDEX__ magic words, if allowed. Incorporates
894  # a final sanity check that we have really got the parser output.
895  $policy = array_merge(
896  $policy,
897  [ 'index' => $pOutput->getIndexPolicy() ]
898  );
899  }
900 
901  if ( isset( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] ) ) {
902  # (bug 14900) site config can override user-defined __INDEX__ or __NOINDEX__
903  $policy = array_merge(
904  $policy,
905  self::formatRobotPolicy( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] )
906  );
907  }
908 
909  return $policy;
910  }
911 
919  public static function formatRobotPolicy( $policy ) {
920  if ( is_array( $policy ) ) {
921  return $policy;
922  } elseif ( !$policy ) {
923  return [];
924  }
925 
926  $policy = explode( ',', $policy );
927  $policy = array_map( 'trim', $policy );
928 
929  $arr = [];
930  foreach ( $policy as $var ) {
931  if ( in_array( $var, [ 'index', 'noindex' ] ) ) {
932  $arr['index'] = $var;
933  } elseif ( in_array( $var, [ 'follow', 'nofollow' ] ) ) {
934  $arr['follow'] = $var;
935  }
936  }
937 
938  return $arr;
939  }
940 
948  public function showRedirectedFromHeader() {
950 
951  $context = $this->getContext();
952  $outputPage = $context->getOutput();
953  $request = $context->getRequest();
954  $rdfrom = $request->getVal( 'rdfrom' );
955 
956  // Construct a URL for the current page view, but with the target title
957  $query = $request->getValues();
958  unset( $query['rdfrom'] );
959  unset( $query['title'] );
960  if ( $this->getTitle()->isRedirect() ) {
961  // Prevent double redirects
962  $query['redirect'] = 'no';
963  }
964  $redirectTargetUrl = $this->getTitle()->getLinkURL( $query );
965 
966  if ( isset( $this->mRedirectedFrom ) ) {
967  // This is an internally redirected page view.
968  // We'll need a backlink to the source page for navigation.
969  if ( Hooks::run( 'ArticleViewRedirect', [ &$this ] ) ) {
970  $redir = Linker::linkKnown(
971  $this->mRedirectedFrom,
972  null,
973  [],
974  [ 'redirect' => 'no' ]
975  );
976 
977  $outputPage->addSubtitle( "<span class=\"mw-redirectedfrom\">" .
978  $context->msg( 'redirectedfrom' )->rawParams( $redir )->parse()
979  . "</span>" );
980 
981  // Add the script to update the displayed URL and
982  // set the fragment if one was specified in the redirect
983  $outputPage->addJsConfigVars( [
984  'wgInternalRedirectTargetUrl' => $redirectTargetUrl,
985  ] );
986  $outputPage->addModules( 'mediawiki.action.view.redirect' );
987 
988  // Add a <link rel="canonical"> tag
989  $outputPage->setCanonicalUrl( $this->getTitle()->getCanonicalURL() );
990 
991  // Tell the output object that the user arrived at this article through a redirect
992  $outputPage->setRedirectedFrom( $this->mRedirectedFrom );
993 
994  return true;
995  }
996  } elseif ( $rdfrom ) {
997  // This is an externally redirected view, from some other wiki.
998  // If it was reported from a trusted site, supply a backlink.
999  if ( $wgRedirectSources && preg_match( $wgRedirectSources, $rdfrom ) ) {
1000  $redir = Linker::makeExternalLink( $rdfrom, $rdfrom );
1001  $outputPage->addSubtitle( "<span class=\"mw-redirectedfrom\">" .
1002  $context->msg( 'redirectedfrom' )->rawParams( $redir )->parse()
1003  . "</span>" );
1004 
1005  // Add the script to update the displayed URL
1006  $outputPage->addJsConfigVars( [
1007  'wgInternalRedirectTargetUrl' => $redirectTargetUrl,
1008  ] );
1009  $outputPage->addModules( 'mediawiki.action.view.redirect' );
1010 
1011  return true;
1012  }
1013  }
1014 
1015  return false;
1016  }
1017 
1022  public function showNamespaceHeader() {
1023  if ( $this->getTitle()->isTalkPage() ) {
1024  if ( !wfMessage( 'talkpageheader' )->isDisabled() ) {
1025  $this->getContext()->getOutput()->wrapWikiMsg(
1026  "<div class=\"mw-talkpageheader\">\n$1\n</div>",
1027  [ 'talkpageheader' ]
1028  );
1029  }
1030  }
1031  }
1032 
1036  public function showViewFooter() {
1037  # check if we're displaying a [[User talk:x.x.x.x]] anonymous talk page
1038  if ( $this->getTitle()->getNamespace() == NS_USER_TALK
1039  && IP::isValid( $this->getTitle()->getText() )
1040  ) {
1041  $this->getContext()->getOutput()->addWikiMsg( 'anontalkpagetext' );
1042  }
1043 
1044  // Show a footer allowing the user to patrol the shown revision or page if possible
1045  $patrolFooterShown = $this->showPatrolFooter();
1046 
1047  Hooks::run( 'ArticleViewFooter', [ $this, $patrolFooterShown ] );
1048  }
1049 
1059  public function showPatrolFooter() {
1061 
1062  $outputPage = $this->getContext()->getOutput();
1063  $user = $this->getContext()->getUser();
1064  $title = $this->getTitle();
1065  $rc = false;
1066 
1067  if ( !$title->quickUserCan( 'patrol', $user )
1068  || !( $wgUseRCPatrol || $wgUseNPPatrol
1069  || ( $wgUseFilePatrol && $title->inNamespace( NS_FILE ) ) )
1070  ) {
1071  // Patrolling is disabled or the user isn't allowed to
1072  return false;
1073  }
1074 
1075  if ( $this->mRevision
1076  && !RecentChange::isInRCLifespan( $this->mRevision->getTimestamp(), 21600 )
1077  ) {
1078  // The current revision is already older than what could be in the RC table
1079  // 6h tolerance because the RC might not be cleaned out regularly
1080  return false;
1081  }
1082 
1083  // Check for cached results
1084  $key = wfMemcKey( 'unpatrollable-page', $title->getArticleID() );
1086  if ( $cache->get( $key ) ) {
1087  return false;
1088  }
1089 
1090  $dbr = wfGetDB( DB_SLAVE );
1091  $oldestRevisionTimestamp = $dbr->selectField(
1092  'revision',
1093  'MIN( rev_timestamp )',
1094  [ 'rev_page' => $title->getArticleID() ],
1095  __METHOD__
1096  );
1097 
1098  // New page patrol: Get the timestamp of the oldest revison which
1099  // the revision table holds for the given page. Then we look
1100  // whether it's within the RC lifespan and if it is, we try
1101  // to get the recentchanges row belonging to that entry
1102  // (with rc_new = 1).
1103  $recentPageCreation = false;
1104  if ( $oldestRevisionTimestamp
1105  && RecentChange::isInRCLifespan( $oldestRevisionTimestamp, 21600 )
1106  ) {
1107  // 6h tolerance because the RC might not be cleaned out regularly
1108  $recentPageCreation = true;
1110  [
1111  'rc_new' => 1,
1112  'rc_timestamp' => $oldestRevisionTimestamp,
1113  'rc_namespace' => $title->getNamespace(),
1114  'rc_cur_id' => $title->getArticleID()
1115  ],
1116  __METHOD__
1117  );
1118  if ( $rc ) {
1119  // Use generic patrol message for new pages
1120  $markPatrolledMsg = wfMessage( 'markaspatrolledtext' );
1121  }
1122  }
1123 
1124  // File patrol: Get the timestamp of the latest upload for this page,
1125  // check whether it is within the RC lifespan and if it is, we try
1126  // to get the recentchanges row belonging to that entry
1127  // (with rc_type = RC_LOG, rc_log_type = upload).
1128  $recentFileUpload = false;
1129  if ( ( !$rc || $rc->getAttribute( 'rc_patrolled' ) ) && $wgUseFilePatrol
1130  && $title->getNamespace() === NS_FILE ) {
1131  // Retrieve timestamp of most recent upload
1132  $newestUploadTimestamp = $dbr->selectField(
1133  'image',
1134  'MAX( img_timestamp )',
1135  [ 'img_name' => $title->getDBkey() ],
1136  __METHOD__
1137  );
1138  if ( $newestUploadTimestamp
1139  && RecentChange::isInRCLifespan( $newestUploadTimestamp, 21600 )
1140  ) {
1141  // 6h tolerance because the RC might not be cleaned out regularly
1142  $recentFileUpload = true;
1144  [
1145  'rc_type' => RC_LOG,
1146  'rc_log_type' => 'upload',
1147  'rc_timestamp' => $newestUploadTimestamp,
1148  'rc_namespace' => NS_FILE,
1149  'rc_cur_id' => $title->getArticleID()
1150  ],
1151  __METHOD__,
1152  [ 'USE INDEX' => 'rc_timestamp' ]
1153  );
1154  if ( $rc ) {
1155  // Use patrol message specific to files
1156  $markPatrolledMsg = wfMessage( 'markaspatrolledtext-file' );
1157  }
1158  }
1159  }
1160 
1161  if ( !$recentPageCreation && !$recentFileUpload ) {
1162  // Page creation and latest upload (for files) is too old to be in RC
1163 
1164  // We definitely can't patrol so cache the information
1165  // When a new file version is uploaded, the cache is cleared
1166  $cache->set( $key, '1' );
1167 
1168  return false;
1169  }
1170 
1171  if ( !$rc ) {
1172  // Don't cache: This can be hit if the page gets accessed very fast after
1173  // its creation / latest upload or in case we have high slave lag. In case
1174  // the revision is too old, we will already return above.
1175  return false;
1176  }
1177 
1178  if ( $rc->getAttribute( 'rc_patrolled' ) ) {
1179  // Patrolled RC entry around
1180 
1181  // Cache the information we gathered above in case we can't patrol
1182  // Don't cache in case we can patrol as this could change
1183  $cache->set( $key, '1' );
1184 
1185  return false;
1186  }
1187 
1188  if ( $rc->getPerformer()->equals( $user ) ) {
1189  // Don't show a patrol link for own creations/uploads. If the user could
1190  // patrol them, they already would be patrolled
1191  return false;
1192  }
1193 
1194  $rcid = $rc->getAttribute( 'rc_id' );
1195 
1196  $token = $user->getEditToken( $rcid );
1197 
1198  $outputPage->preventClickjacking();
1199  if ( $wgEnableAPI && $wgEnableWriteAPI && $user->isAllowed( 'writeapi' ) ) {
1200  $outputPage->addModules( 'mediawiki.page.patrol.ajax' );
1201  }
1202 
1204  $title,
1205  $markPatrolledMsg->escaped(),
1206  [],
1207  [
1208  'action' => 'markpatrolled',
1209  'rcid' => $rcid,
1210  'token' => $token,
1211  ]
1212  );
1213 
1214  $outputPage->addHTML(
1215  "<div class='patrollink' data-mw='interface'>" .
1216  wfMessage( 'markaspatrolledlink' )->rawParams( $link )->escaped() .
1217  '</div>'
1218  );
1219 
1220  return true;
1221  }
1222 
1229  public static function purgePatrolFooterCache( $articleID ) {
1231  $cache->delete( wfMemcKey( 'unpatrollable-page', $articleID ) );
1232  }
1233 
1238  public function showMissingArticle() {
1240 
1241  $outputPage = $this->getContext()->getOutput();
1242  // Whether the page is a root user page of an existing user (but not a subpage)
1243  $validUserPage = false;
1244 
1245  $title = $this->getTitle();
1246 
1247  # Show info in user (talk) namespace. Does the user exist? Is he blocked?
1248  if ( $title->getNamespace() == NS_USER
1249  || $title->getNamespace() == NS_USER_TALK
1250  ) {
1251  $rootPart = explode( '/', $title->getText() )[0];
1252  $user = User::newFromName( $rootPart, false /* allow IP users*/ );
1253  $ip = User::isIP( $rootPart );
1254  $block = Block::newFromTarget( $user, $user );
1255 
1256  if ( !( $user && $user->isLoggedIn() ) && !$ip ) { # User does not exist
1257  $outputPage->wrapWikiMsg( "<div class=\"mw-userpage-userdoesnotexist error\">\n\$1\n</div>",
1258  [ 'userpage-userdoesnotexist-view', wfEscapeWikiText( $rootPart ) ] );
1259  } elseif ( !is_null( $block ) && $block->getType() != Block::TYPE_AUTO ) {
1260  # Show log extract if the user is currently blocked
1262  $outputPage,
1263  'block',
1264  MWNamespace::getCanonicalName( NS_USER ) . ':' . $block->getTarget(),
1265  '',
1266  [
1267  'lim' => 1,
1268  'showIfEmpty' => false,
1269  'msgKey' => [
1270  'blocked-notice-logextract',
1271  $user->getName() # Support GENDER in notice
1272  ]
1273  ]
1274  );
1275  $validUserPage = !$title->isSubpage();
1276  } else {
1277  $validUserPage = !$title->isSubpage();
1278  }
1279  }
1280 
1281  Hooks::run( 'ShowMissingArticle', [ $this ] );
1282 
1283  # Show delete and move logs if there were any such events.
1284  # The logging query can DOS the site when bots/crawlers cause 404 floods,
1285  # so be careful showing this. 404 pages must be cheap as they are hard to cache.
1287  $key = wfMemcKey( 'page-recent-delete', md5( $title->getPrefixedText() ) );
1288  $loggedIn = $this->getContext()->getUser()->isLoggedIn();
1289  if ( $loggedIn || $cache->get( $key ) ) {
1290  $logTypes = [ 'delete', 'move' ];
1291  $conds = [ "log_action != 'revision'" ];
1292  // Give extensions a chance to hide their (unrelated) log entries
1293  Hooks::run( 'Article::MissingArticleConditions', [ &$conds, $logTypes ] );
1295  $outputPage,
1296  $logTypes,
1297  $title,
1298  '',
1299  [
1300  'lim' => 10,
1301  'conds' => $conds,
1302  'showIfEmpty' => false,
1303  'msgKey' => [ $loggedIn
1304  ? 'moveddeleted-notice'
1305  : 'moveddeleted-notice-recent'
1306  ]
1307  ]
1308  );
1309  }
1310 
1311  if ( !$this->mPage->hasViewableContent() && $wgSend404Code && !$validUserPage ) {
1312  // If there's no backing content, send a 404 Not Found
1313  // for better machine handling of broken links.
1314  $this->getContext()->getRequest()->response()->statusHeader( 404 );
1315  }
1316 
1317  // Also apply the robot policy for nonexisting pages (even if a 404 was used for sanity)
1318  $policy = $this->getRobotPolicy( 'view' );
1319  $outputPage->setIndexPolicy( $policy['index'] );
1320  $outputPage->setFollowPolicy( $policy['follow'] );
1321 
1322  $hookResult = Hooks::run( 'BeforeDisplayNoArticleText', [ $this ] );
1323 
1324  if ( !$hookResult ) {
1325  return;
1326  }
1327 
1328  # Show error message
1329  $oldid = $this->getOldID();
1330  if ( !$oldid && $title->getNamespace() === NS_MEDIAWIKI && $title->hasSourceText() ) {
1331  $outputPage->addParserOutput( $this->getContentObject()->getParserOutput( $title ) );
1332  } else {
1333  if ( $oldid ) {
1334  $text = wfMessage( 'missing-revision', $oldid )->plain();
1335  } elseif ( $title->quickUserCan( 'create', $this->getContext()->getUser() )
1336  && $title->quickUserCan( 'edit', $this->getContext()->getUser() )
1337  ) {
1338  $message = $this->getContext()->getUser()->isLoggedIn() ? 'noarticletext' : 'noarticletextanon';
1339  $text = wfMessage( $message )->plain();
1340  } else {
1341  $text = wfMessage( 'noarticletext-nopermission' )->plain();
1342  }
1343 
1344  $dir = $this->getContext()->getLanguage()->getDir();
1345  $lang = $this->getContext()->getLanguage()->getCode();
1346  $outputPage->addWikiText( Xml::openElement( 'div', [
1347  'class' => "noarticletext mw-content-$dir",
1348  'dir' => $dir,
1349  'lang' => $lang,
1350  ] ) . "\n$text\n</div>" );
1351  }
1352  }
1353 
1360  public function showDeletedRevisionHeader() {
1361  if ( !$this->mRevision->isDeleted( Revision::DELETED_TEXT ) ) {
1362  // Not deleted
1363  return true;
1364  }
1365 
1366  $outputPage = $this->getContext()->getOutput();
1367  $user = $this->getContext()->getUser();
1368  // If the user is not allowed to see it...
1369  if ( !$this->mRevision->userCan( Revision::DELETED_TEXT, $user ) ) {
1370  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1371  'rev-deleted-text-permission' );
1372 
1373  return false;
1374  // If the user needs to confirm that they want to see it...
1375  } elseif ( $this->getContext()->getRequest()->getInt( 'unhide' ) != 1 ) {
1376  # Give explanation and add a link to view the revision...
1377  $oldid = intval( $this->getOldID() );
1378  $link = $this->getTitle()->getFullURL( "oldid={$oldid}&unhide=1" );
1379  $msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ?
1380  'rev-suppressed-text-unhide' : 'rev-deleted-text-unhide';
1381  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1382  [ $msg, $link ] );
1383 
1384  return false;
1385  // We are allowed to see...
1386  } else {
1387  $msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ?
1388  'rev-suppressed-text-view' : 'rev-deleted-text-view';
1389  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n", $msg );
1390 
1391  return true;
1392  }
1393  }
1394 
1403  public function setOldSubtitle( $oldid = 0 ) {
1404  if ( !Hooks::run( 'DisplayOldSubtitle', [ &$this, &$oldid ] ) ) {
1405  return;
1406  }
1407 
1408  $context = $this->getContext();
1409  $unhide = $context->getRequest()->getInt( 'unhide' ) == 1;
1410 
1411  # Cascade unhide param in links for easy deletion browsing
1412  $extraParams = [];
1413  if ( $unhide ) {
1414  $extraParams['unhide'] = 1;
1415  }
1416 
1417  if ( $this->mRevision && $this->mRevision->getId() === $oldid ) {
1418  $revision = $this->mRevision;
1419  } else {
1420  $revision = Revision::newFromId( $oldid );
1421  }
1422 
1423  $timestamp = $revision->getTimestamp();
1424 
1425  $current = ( $oldid == $this->mPage->getLatest() );
1426  $language = $context->getLanguage();
1427  $user = $context->getUser();
1428 
1429  $td = $language->userTimeAndDate( $timestamp, $user );
1430  $tddate = $language->userDate( $timestamp, $user );
1431  $tdtime = $language->userTime( $timestamp, $user );
1432 
1433  # Show user links if allowed to see them. If hidden, then show them only if requested...
1434  $userlinks = Linker::revUserTools( $revision, !$unhide );
1435 
1436  $infomsg = $current && !$context->msg( 'revision-info-current' )->isDisabled()
1437  ? 'revision-info-current'
1438  : 'revision-info';
1439 
1440  $outputPage = $context->getOutput();
1441  $outputPage->addSubtitle( "<div id=\"mw-{$infomsg}\">" .
1442  $context->msg( $infomsg, $td )
1443  ->rawParams( $userlinks )
1444  ->params( $revision->getId(), $tddate, $tdtime, $revision->getUserText() )
1445  ->rawParams( Linker::revComment( $revision, true, true ) )
1446  ->parse() .
1447  "</div>"
1448  );
1449 
1450  $lnk = $current
1451  ? $context->msg( 'currentrevisionlink' )->escaped()
1453  $this->getTitle(),
1454  $context->msg( 'currentrevisionlink' )->escaped(),
1455  [],
1456  $extraParams
1457  );
1458  $curdiff = $current
1459  ? $context->msg( 'diff' )->escaped()
1461  $this->getTitle(),
1462  $context->msg( 'diff' )->escaped(),
1463  [],
1464  [
1465  'diff' => 'cur',
1466  'oldid' => $oldid
1467  ] + $extraParams
1468  );
1469  $prev = $this->getTitle()->getPreviousRevisionID( $oldid );
1470  $prevlink = $prev
1472  $this->getTitle(),
1473  $context->msg( 'previousrevision' )->escaped(),
1474  [],
1475  [
1476  'direction' => 'prev',
1477  'oldid' => $oldid
1478  ] + $extraParams
1479  )
1480  : $context->msg( 'previousrevision' )->escaped();
1481  $prevdiff = $prev
1483  $this->getTitle(),
1484  $context->msg( 'diff' )->escaped(),
1485  [],
1486  [
1487  'diff' => 'prev',
1488  'oldid' => $oldid
1489  ] + $extraParams
1490  )
1491  : $context->msg( 'diff' )->escaped();
1492  $nextlink = $current
1493  ? $context->msg( 'nextrevision' )->escaped()
1495  $this->getTitle(),
1496  $context->msg( 'nextrevision' )->escaped(),
1497  [],
1498  [
1499  'direction' => 'next',
1500  'oldid' => $oldid
1501  ] + $extraParams
1502  );
1503  $nextdiff = $current
1504  ? $context->msg( 'diff' )->escaped()
1506  $this->getTitle(),
1507  $context->msg( 'diff' )->escaped(),
1508  [],
1509  [
1510  'diff' => 'next',
1511  'oldid' => $oldid
1512  ] + $extraParams
1513  );
1514 
1515  $cdel = Linker::getRevDeleteLink( $user, $revision, $this->getTitle() );
1516  if ( $cdel !== '' ) {
1517  $cdel .= ' ';
1518  }
1519 
1520  $outputPage->addSubtitle( "<div id=\"mw-revision-nav\">" . $cdel .
1521  $context->msg( 'revision-nav' )->rawParams(
1522  $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff
1523  )->escaped() . "</div>" );
1524  }
1525 
1537  public function viewRedirect( $target, $appendSubtitle = true, $forceKnown = false ) {
1538  $lang = $this->getTitle()->getPageLanguage();
1539  $out = $this->getContext()->getOutput();
1540  if ( $appendSubtitle ) {
1541  $out->addSubtitle( wfMessage( 'redirectpagesub' ) );
1542  }
1543  $out->addModuleStyles( 'mediawiki.action.view.redirectPage' );
1544  return static::getRedirectHeaderHtml( $lang, $target, $forceKnown );
1545  }
1546 
1559  public static function getRedirectHeaderHtml( Language $lang, $target, $forceKnown = false ) {
1560  if ( !is_array( $target ) ) {
1561  $target = [ $target ];
1562  }
1563 
1564  $html = '<ul class="redirectText">';
1566  foreach ( $target as $title ) {
1567  $html .= '<li>' . Linker::link(
1568  $title,
1569  htmlspecialchars( $title->getFullText() ),
1570  [],
1571  // Make sure wiki page redirects are not followed
1572  $title->isRedirect() ? [ 'redirect' => 'no' ] : [],
1573  ( $forceKnown ? [ 'known', 'noclasses' ] : [] )
1574  ) . '</li>';
1575  }
1576  $html .= '</ul>';
1577 
1578  $redirectToText = wfMessage( 'redirectto' )->inLanguage( $lang )->escaped();
1579 
1580  return '<div class="redirectMsg">' .
1581  '<p>' . $redirectToText . '</p>' .
1582  $html .
1583  '</div>';
1584  }
1585 
1594  public function addHelpLink( $to, $overrideBaseUrl = false ) {
1595  $msg = wfMessage(
1596  'namespace-' . $this->getTitle()->getNamespace() . '-helppage'
1597  );
1598 
1599  $out = $this->getContext()->getOutput();
1600  if ( !$msg->isDisabled() ) {
1601  $helpUrl = Skin::makeUrl( $msg->plain() );
1602  $out->addHelpLink( $helpUrl, true );
1603  } else {
1604  $out->addHelpLink( $to, $overrideBaseUrl );
1605  }
1606  }
1607 
1611  public function render() {
1612  $this->getContext()->getRequest()->response()->header( 'X-Robots-Tag: noindex' );
1613  $this->getContext()->getOutput()->setArticleBodyOnly( true );
1614  $this->getContext()->getOutput()->enableSectionEditLinks( false );
1615  $this->view();
1616  }
1617 
1621  public function protect() {
1622  $form = new ProtectionForm( $this );
1623  $form->execute();
1624  }
1625 
1629  public function unprotect() {
1630  $this->protect();
1631  }
1632 
1636  public function delete() {
1637  # This code desperately needs to be totally rewritten
1638 
1639  $title = $this->getTitle();
1640  $context = $this->getContext();
1641  $user = $context->getUser();
1642  $request = $context->getRequest();
1643 
1644  # Check permissions
1645  $permissionErrors = $title->getUserPermissionsErrors( 'delete', $user );
1646  if ( count( $permissionErrors ) ) {
1647  throw new PermissionsError( 'delete', $permissionErrors );
1648  }
1649 
1650  # Read-only check...
1651  if ( wfReadOnly() ) {
1652  throw new ReadOnlyError;
1653  }
1654 
1655  # Better double-check that it hasn't been deleted yet!
1656  $this->mPage->loadPageData(
1658  );
1659  if ( !$this->mPage->exists() ) {
1660  $deleteLogPage = new LogPage( 'delete' );
1661  $outputPage = $context->getOutput();
1662  $outputPage->setPageTitle( $context->msg( 'cannotdelete-title', $title->getPrefixedText() ) );
1663  $outputPage->wrapWikiMsg( "<div class=\"error mw-error-cannotdelete\">\n$1\n</div>",
1664  [ 'cannotdelete', wfEscapeWikiText( $title->getPrefixedText() ) ]
1665  );
1666  $outputPage->addHTML(
1667  Xml::element( 'h2', null, $deleteLogPage->getName()->text() )
1668  );
1670  $outputPage,
1671  'delete',
1672  $title
1673  );
1674 
1675  return;
1676  }
1677 
1678  $deleteReasonList = $request->getText( 'wpDeleteReasonList', 'other' );
1679  $deleteReason = $request->getText( 'wpReason' );
1680 
1681  if ( $deleteReasonList == 'other' ) {
1682  $reason = $deleteReason;
1683  } elseif ( $deleteReason != '' ) {
1684  // Entry from drop down menu + additional comment
1685  $colonseparator = wfMessage( 'colon-separator' )->inContentLanguage()->text();
1686  $reason = $deleteReasonList . $colonseparator . $deleteReason;
1687  } else {
1688  $reason = $deleteReasonList;
1689  }
1690 
1691  if ( $request->wasPosted() && $user->matchEditToken( $request->getVal( 'wpEditToken' ),
1692  [ 'delete', $this->getTitle()->getPrefixedText() ] )
1693  ) {
1694  # Flag to hide all contents of the archived revisions
1695  $suppress = $request->getVal( 'wpSuppress' ) && $user->isAllowed( 'suppressrevision' );
1696 
1697  $this->doDelete( $reason, $suppress );
1698 
1699  WatchAction::doWatchOrUnwatch( $request->getCheck( 'wpWatch' ), $title, $user );
1700 
1701  return;
1702  }
1703 
1704  // Generate deletion reason
1705  $hasHistory = false;
1706  if ( !$reason ) {
1707  try {
1708  $reason = $this->generateReason( $hasHistory );
1709  } catch ( Exception $e ) {
1710  # if a page is horribly broken, we still want to be able to
1711  # delete it. So be lenient about errors here.
1712  wfDebug( "Error while building auto delete summary: $e" );
1713  $reason = '';
1714  }
1715  }
1716 
1717  // If the page has a history, insert a warning
1718  if ( $hasHistory ) {
1719  $title = $this->getTitle();
1720 
1721  // The following can use the real revision count as this is only being shown for users
1722  // that can delete this page.
1723  // This, as a side-effect, also makes sure that the following query isn't being run for
1724  // pages with a larger history, unless the user has the 'bigdelete' right
1725  // (and is about to delete this page).
1726  $dbr = wfGetDB( DB_SLAVE );
1727  $revisions = $edits = (int)$dbr->selectField(
1728  'revision',
1729  'COUNT(rev_page)',
1730  [ 'rev_page' => $title->getArticleID() ],
1731  __METHOD__
1732  );
1733 
1734  // @todo FIXME: i18n issue/patchwork message
1735  $context->getOutput()->addHTML(
1736  '<strong class="mw-delete-warning-revisions">' .
1737  $context->msg( 'historywarning' )->numParams( $revisions )->parse() .
1738  $context->msg( 'word-separator' )->escaped() . Linker::linkKnown( $title,
1739  $context->msg( 'history' )->escaped(),
1740  [],
1741  [ 'action' => 'history' ] ) .
1742  '</strong>'
1743  );
1744 
1745  if ( $title->isBigDeletion() ) {
1747  $context->getOutput()->wrapWikiMsg( "<div class='error'>\n$1\n</div>\n",
1748  [
1749  'delete-warning-toobig',
1750  $context->getLanguage()->formatNum( $wgDeleteRevisionsLimit )
1751  ]
1752  );
1753  }
1754  }
1755 
1756  $this->confirmDelete( $reason );
1757  }
1758 
1764  public function confirmDelete( $reason ) {
1765  wfDebug( "Article::confirmDelete\n" );
1766 
1767  $title = $this->getTitle();
1768  $ctx = $this->getContext();
1769  $outputPage = $ctx->getOutput();
1770  $useMediaWikiUIEverywhere = $ctx->getConfig()->get( 'UseMediaWikiUIEverywhere' );
1771  $outputPage->setPageTitle( wfMessage( 'delete-confirm', $title->getPrefixedText() ) );
1772  $outputPage->addBacklinkSubtitle( $title );
1773  $outputPage->setRobotPolicy( 'noindex,nofollow' );
1774  $backlinkCache = $title->getBacklinkCache();
1775  if ( $backlinkCache->hasLinks( 'pagelinks' ) || $backlinkCache->hasLinks( 'templatelinks' ) ) {
1776  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1777  'deleting-backlinks-warning' );
1778  }
1779  $outputPage->addWikiMsg( 'confirmdeletetext' );
1780 
1781  Hooks::run( 'ArticleConfirmDelete', [ $this, $outputPage, &$reason ] );
1782 
1783  $user = $this->getContext()->getUser();
1784 
1785  if ( $user->isAllowed( 'suppressrevision' ) ) {
1786  $suppress = Html::openElement( 'div', [ 'id' => 'wpDeleteSuppressRow' ] ) .
1787  Xml::checkLabel( wfMessage( 'revdelete-suppress' )->text(),
1788  'wpSuppress', 'wpSuppress', false, [ 'tabindex' => '4' ] ) .
1789  Html::closeElement( 'div' );
1790  } else {
1791  $suppress = '';
1792  }
1793  $checkWatch = $user->getBoolOption( 'watchdeletion' ) || $user->isWatched( $title );
1794 
1795  $form = Html::openElement( 'form', [ 'method' => 'post',
1796  'action' => $title->getLocalURL( 'action=delete' ), 'id' => 'deleteconfirm' ] ) .
1797  Html::openElement( 'fieldset', [ 'id' => 'mw-delete-table' ] ) .
1798  Html::element( 'legend', null, wfMessage( 'delete-legend' )->text() ) .
1799  Html::openElement( 'div', [ 'id' => 'mw-deleteconfirm-table' ] ) .
1800  Html::openElement( 'div', [ 'id' => 'wpDeleteReasonListRow' ] ) .
1801  Html::label( wfMessage( 'deletecomment' )->text(), 'wpDeleteReasonList' ) .
1802  '&nbsp;' .
1804  'wpDeleteReasonList',
1805  wfMessage( 'deletereason-dropdown' )->inContentLanguage()->text(),
1806  wfMessage( 'deletereasonotherlist' )->inContentLanguage()->text(),
1807  '',
1808  'wpReasonDropDown',
1809  1
1810  ) .
1811  Html::closeElement( 'div' ) .
1812  Html::openElement( 'div', [ 'id' => 'wpDeleteReasonRow' ] ) .
1813  Html::label( wfMessage( 'deleteotherreason' )->text(), 'wpReason' ) .
1814  '&nbsp;' .
1815  Html::input( 'wpReason', $reason, 'text', [
1816  'size' => '60',
1817  'maxlength' => '255',
1818  'tabindex' => '2',
1819  'id' => 'wpReason',
1820  'class' => 'mw-ui-input-inline',
1821  'autofocus'
1822  ] ) .
1823  Html::closeElement( 'div' );
1824 
1825  # Disallow watching if user is not logged in
1826  if ( $user->isLoggedIn() ) {
1827  $form .=
1828  Xml::checkLabel( wfMessage( 'watchthis' )->text(),
1829  'wpWatch', 'wpWatch', $checkWatch, [ 'tabindex' => '3' ] );
1830  }
1831 
1832  $form .=
1833  Html::openElement( 'div' ) .
1834  $suppress .
1835  Xml::submitButton( wfMessage( 'deletepage' )->text(),
1836  [
1837  'name' => 'wpConfirmB',
1838  'id' => 'wpConfirmB',
1839  'tabindex' => '5',
1840  'class' => $useMediaWikiUIEverywhere ? 'mw-ui-button mw-ui-destructive' : '',
1841  ]
1842  ) .
1843  Html::closeElement( 'div' ) .
1844  Html::closeElement( 'div' ) .
1845  Xml::closeElement( 'fieldset' ) .
1846  Html::hidden(
1847  'wpEditToken',
1848  $user->getEditToken( [ 'delete', $title->getPrefixedText() ] )
1849  ) .
1850  Xml::closeElement( 'form' );
1851 
1852  if ( $user->isAllowed( 'editinterface' ) ) {
1854  $ctx->msg( 'deletereason-dropdown' )->inContentLanguage()->getTitle(),
1855  wfMessage( 'delete-edit-reasonlist' )->escaped(),
1856  [],
1857  [ 'action' => 'edit' ]
1858  );
1859  $form .= '<p class="mw-delete-editreasons">' . $link . '</p>';
1860  }
1861 
1862  $outputPage->addHTML( $form );
1863 
1864  $deleteLogPage = new LogPage( 'delete' );
1865  $outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) );
1866  LogEventsList::showLogExtract( $outputPage, 'delete', $title );
1867  }
1868 
1874  public function doDelete( $reason, $suppress = false ) {
1875  $error = '';
1876  $context = $this->getContext();
1877  $outputPage = $context->getOutput();
1878  $user = $context->getUser();
1879  $status = $this->mPage->doDeleteArticleReal( $reason, $suppress, 0, true, $error, $user );
1880 
1881  if ( $status->isGood() ) {
1882  $deleted = $this->getTitle()->getPrefixedText();
1883 
1884  $outputPage->setPageTitle( wfMessage( 'actioncomplete' ) );
1885  $outputPage->setRobotPolicy( 'noindex,nofollow' );
1886 
1887  $loglink = '[[Special:Log/delete|' . wfMessage( 'deletionlog' )->text() . ']]';
1888 
1889  $outputPage->addWikiMsg( 'deletedtext', wfEscapeWikiText( $deleted ), $loglink );
1890 
1891  Hooks::run( 'ArticleDeleteAfterSuccess', [ $this->getTitle(), $outputPage ] );
1892 
1893  $outputPage->returnToMain( false );
1894  } else {
1895  $outputPage->setPageTitle(
1896  wfMessage( 'cannotdelete-title',
1897  $this->getTitle()->getPrefixedText() )
1898  );
1899 
1900  if ( $error == '' ) {
1901  $outputPage->addWikiText(
1902  "<div class=\"error mw-error-cannotdelete\">\n" . $status->getWikiText() . "\n</div>"
1903  );
1904  $deleteLogPage = new LogPage( 'delete' );
1905  $outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) );
1906 
1908  $outputPage,
1909  'delete',
1910  $this->getTitle()
1911  );
1912  } else {
1913  $outputPage->addHTML( $error );
1914  }
1915  }
1916  }
1917 
1918  /* Caching functions */
1919 
1927  protected function tryFileCache() {
1928  static $called = false;
1929 
1930  if ( $called ) {
1931  wfDebug( "Article::tryFileCache(): called twice!?\n" );
1932  return false;
1933  }
1934 
1935  $called = true;
1936  if ( $this->isFileCacheable() ) {
1937  $cache = new HTMLFileCache( $this->getTitle(), 'view' );
1938  if ( $cache->isCacheGood( $this->mPage->getTouched() ) ) {
1939  wfDebug( "Article::tryFileCache(): about to load file\n" );
1940  $cache->loadFromFileCache( $this->getContext() );
1941  return true;
1942  } else {
1943  wfDebug( "Article::tryFileCache(): starting buffer\n" );
1944  ob_start( [ &$cache, 'saveToFileCache' ] );
1945  }
1946  } else {
1947  wfDebug( "Article::tryFileCache(): not cacheable\n" );
1948  }
1949 
1950  return false;
1951  }
1952 
1957  public function isFileCacheable() {
1958  $cacheable = false;
1959 
1960  if ( HTMLFileCache::useFileCache( $this->getContext() ) ) {
1961  $cacheable = $this->mPage->getId()
1962  && !$this->mRedirectedFrom && !$this->getTitle()->isRedirect();
1963  // Extension may have reason to disable file caching on some pages.
1964  if ( $cacheable ) {
1965  $cacheable = Hooks::run( 'IsFileCacheable', [ &$this ] );
1966  }
1967  }
1968 
1969  return $cacheable;
1970  }
1971 
1985  public function getParserOutput( $oldid = null, User $user = null ) {
1986  // XXX: bypasses mParserOptions and thus setParserOptions()
1987 
1988  if ( $user === null ) {
1989  $parserOptions = $this->getParserOptions();
1990  } else {
1991  $parserOptions = $this->mPage->makeParserOptions( $user );
1992  }
1993 
1994  return $this->mPage->getParserOutput( $parserOptions, $oldid );
1995  }
1996 
2004  if ( $this->mParserOptions ) {
2005  throw new MWException( "can't change parser options after they have already been set" );
2006  }
2007 
2008  // clone, so if $options is modified later, it doesn't confuse the parser cache.
2009  $this->mParserOptions = clone $options;
2010  }
2011 
2016  public function getParserOptions() {
2017  if ( !$this->mParserOptions ) {
2018  $this->mParserOptions = $this->mPage->makeParserOptions( $this->getContext() );
2019  }
2020  // Clone to allow modifications of the return value without affecting cache
2021  return clone $this->mParserOptions;
2022  }
2023 
2030  public function setContext( $context ) {
2031  $this->mContext = $context;
2032  }
2033 
2040  public function getContext() {
2041  if ( $this->mContext instanceof IContextSource ) {
2042  return $this->mContext;
2043  } else {
2044  wfDebug( __METHOD__ . " called and \$mContext is null. " .
2045  "Return RequestContext::getMain(); for sanity\n" );
2046  return RequestContext::getMain();
2047  }
2048  }
2049 
2057  public function __get( $fname ) {
2058  if ( property_exists( $this->mPage, $fname ) ) {
2059  # wfWarn( "Access to raw $fname field " . __CLASS__ );
2060  return $this->mPage->$fname;
2061  }
2062  trigger_error( 'Inaccessible property via __get(): ' . $fname, E_USER_NOTICE );
2063  }
2064 
2072  public function __set( $fname, $fvalue ) {
2073  if ( property_exists( $this->mPage, $fname ) ) {
2074  # wfWarn( "Access to raw $fname field of " . __CLASS__ );
2075  $this->mPage->$fname = $fvalue;
2076  // Note: extensions may want to toss on new fields
2077  } elseif ( !in_array( $fname, [ 'mContext', 'mPage' ] ) ) {
2078  $this->mPage->$fname = $fvalue;
2079  } else {
2080  trigger_error( 'Inaccessible property via __set(): ' . $fname, E_USER_NOTICE );
2081  }
2082  }
2083 
2088  public function checkFlags( $flags ) {
2089  return $this->mPage->checkFlags( $flags );
2090  }
2091 
2096  public function checkTouched() {
2097  return $this->mPage->checkTouched();
2098  }
2099 
2104  public function clearPreparedEdit() {
2105  $this->mPage->clearPreparedEdit();
2106  }
2107 
2112  public function doDeleteArticleReal(
2113  $reason, $suppress = false, $u1 = null, $u2 = null, &$error = '', User $user = null
2114  ) {
2115  return $this->mPage->doDeleteArticleReal(
2116  $reason, $suppress, $u1, $u2, $error, $user
2117  );
2118  }
2119 
2124  public function doDeleteUpdates( $id, Content $content = null ) {
2125  return $this->mPage->doDeleteUpdates( $id, $content );
2126  }
2127 
2132  public function doEdit( $text, $summary, $flags = 0, $baseRevId = false, $user = null ) {
2133  ContentHandler::deprecated( __METHOD__, '1.21' );
2134  return $this->mPage->doEdit( $text, $summary, $flags, $baseRevId, $user );
2135  }
2136 
2141  public function doEditContent( Content $content, $summary, $flags = 0, $baseRevId = false,
2142  User $user = null, $serialFormat = null
2143  ) {
2144  return $this->mPage->doEditContent( $content, $summary, $flags, $baseRevId,
2145  $user, $serialFormat
2146  );
2147  }
2148 
2153  public function doEditUpdates( Revision $revision, User $user, array $options = [] ) {
2154  return $this->mPage->doEditUpdates( $revision, $user, $options );
2155  }
2156 
2161  public function doPurge() {
2162  return $this->mPage->doPurge();
2163  }
2164 
2169  public function doQuickEditContent(
2170  Content $content, User $user, $comment = '', $minor = false, $serialFormat = null
2171  ) {
2172  return $this->mPage->doQuickEditContent(
2173  $content, $user, $comment, $minor, $serialFormat
2174  );
2175  }
2176 
2181  public function doViewUpdates( User $user, $oldid = 0 ) {
2182  $this->mPage->doViewUpdates( $user, $oldid );
2183  }
2184 
2189  public function exists() {
2190  return $this->mPage->exists();
2191  }
2192 
2197  public function followRedirect() {
2198  return $this->mPage->followRedirect();
2199  }
2200 
2205  public function getActionOverrides() {
2206  return $this->mPage->getActionOverrides();
2207  }
2208 
2213  public function getAutoDeleteReason( &$hasHistory ) {
2214  return $this->mPage->getAutoDeleteReason( $hasHistory );
2215  }
2216 
2221  public function getCategories() {
2222  return $this->mPage->getCategories();
2223  }
2224 
2229  public function getComment( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2230  return $this->mPage->getComment( $audience, $user );
2231  }
2232 
2237  public function getContentHandler() {
2238  return $this->mPage->getContentHandler();
2239  }
2240 
2245  public function getContentModel() {
2246  return $this->mPage->getContentModel();
2247  }
2248 
2253  public function getContributors() {
2254  return $this->mPage->getContributors();
2255  }
2256 
2261  public function getCreator( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2262  return $this->mPage->getCreator( $audience, $user );
2263  }
2264 
2269  public function getDeletionUpdates( Content $content = null ) {
2270  return $this->mPage->getDeletionUpdates( $content );
2271  }
2272 
2277  public function getHiddenCategories() {
2278  return $this->mPage->getHiddenCategories();
2279  }
2280 
2285  public function getId() {
2286  return $this->mPage->getId();
2287  }
2288 
2293  public function getLatest() {
2294  return $this->mPage->getLatest();
2295  }
2296 
2301  public function getLinksTimestamp() {
2302  return $this->mPage->getLinksTimestamp();
2303  }
2304 
2309  public function getMinorEdit() {
2310  return $this->mPage->getMinorEdit();
2311  }
2312 
2317  public function getOldestRevision() {
2318  return $this->mPage->getOldestRevision();
2319  }
2320 
2325  public function getRedirectTarget() {
2326  return $this->mPage->getRedirectTarget();
2327  }
2328 
2333  public function getRedirectURL( $rt ) {
2334  return $this->mPage->getRedirectURL( $rt );
2335  }
2336 
2341  public function getRevision() {
2342  return $this->mPage->getRevision();
2343  }
2344 
2349  public function getText( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2350  ContentHandler::deprecated( __METHOD__, '1.21' );
2351  return $this->mPage->getText( $audience, $user );
2352  }
2353 
2358  public function getTimestamp() {
2359  return $this->mPage->getTimestamp();
2360  }
2361 
2366  public function getTouched() {
2367  return $this->mPage->getTouched();
2368  }
2369 
2374  public function getUndoContent( Revision $undo, Revision $undoafter = null ) {
2375  return $this->mPage->getUndoContent( $undo, $undoafter );
2376  }
2377 
2382  public function getUser( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2383  return $this->mPage->getUser( $audience, $user );
2384  }
2385 
2390  public function getUserText( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2391  return $this->mPage->getUserText( $audience, $user );
2392  }
2393 
2398  public function hasViewableContent() {
2399  return $this->mPage->hasViewableContent();
2400  }
2401 
2406  public function insertOn( $dbw, $pageId = null ) {
2407  return $this->mPage->insertOn( $dbw, $pageId );
2408  }
2409 
2414  public function insertProtectNullRevision( $revCommentMsg, array $limit,
2415  array $expiry, $cascade, $reason, $user = null
2416  ) {
2417  return $this->mPage->insertProtectNullRevision( $revCommentMsg, $limit,
2418  $expiry, $cascade, $reason, $user
2419  );
2420  }
2421 
2426  public function insertRedirect() {
2427  return $this->mPage->insertRedirect();
2428  }
2429 
2434  public function insertRedirectEntry( Title $rt, $oldLatest = null ) {
2435  return $this->mPage->insertRedirectEntry( $rt, $oldLatest );
2436  }
2437 
2442  public function isCountable( $editInfo = false ) {
2443  return $this->mPage->isCountable( $editInfo );
2444  }
2445 
2450  public function isRedirect() {
2451  return $this->mPage->isRedirect();
2452  }
2453 
2458  public function loadFromRow( $data, $from ) {
2459  return $this->mPage->loadFromRow( $data, $from );
2460  }
2461 
2466  public function loadPageData( $from = 'fromdb' ) {
2467  $this->mPage->loadPageData( $from );
2468  }
2469 
2474  public function lockAndGetLatest() {
2475  return $this->mPage->lockAndGetLatest();
2476  }
2477 
2482  public function makeParserOptions( $context ) {
2483  return $this->mPage->makeParserOptions( $context );
2484  }
2485 
2490  public function pageDataFromId( $dbr, $id, $options = [] ) {
2491  return $this->mPage->pageDataFromId( $dbr, $id, $options );
2492  }
2493 
2498  public function pageDataFromTitle( $dbr, $title, $options = [] ) {
2499  return $this->mPage->pageDataFromTitle( $dbr, $title, $options );
2500  }
2501 
2506  public function prepareContentForEdit(
2507  Content $content, $revision = null, User $user = null,
2508  $serialFormat = null, $useCache = true
2509  ) {
2510  return $this->mPage->prepareContentForEdit(
2511  $content, $revision, $user,
2512  $serialFormat, $useCache
2513  );
2514  }
2515 
2520  public function prepareTextForEdit( $text, $revid = null, User $user = null ) {
2521  return $this->mPage->prepareTextForEdit( $text, $revid, $user );
2522  }
2523 
2528  public function protectDescription( array $limit, array $expiry ) {
2529  return $this->mPage->protectDescription( $limit, $expiry );
2530  }
2531 
2536  public function protectDescriptionLog( array $limit, array $expiry ) {
2537  return $this->mPage->protectDescriptionLog( $limit, $expiry );
2538  }
2539 
2544  public function replaceSectionAtRev( $sectionId, Content $sectionContent,
2545  $sectionTitle = '', $baseRevId = null
2546  ) {
2547  return $this->mPage->replaceSectionAtRev( $sectionId, $sectionContent,
2548  $sectionTitle, $baseRevId
2549  );
2550  }
2551 
2556  public function replaceSectionContent(
2557  $sectionId, Content $sectionContent, $sectionTitle = '', $edittime = null
2558  ) {
2559  return $this->mPage->replaceSectionContent(
2560  $sectionId, $sectionContent, $sectionTitle, $edittime
2561  );
2562  }
2563 
2568  public function setTimestamp( $ts ) {
2569  return $this->mPage->setTimestamp( $ts );
2570  }
2571 
2576  public function shouldCheckParserCache( ParserOptions $parserOptions, $oldId ) {
2577  return $this->mPage->shouldCheckParserCache( $parserOptions, $oldId );
2578  }
2579 
2584  public function supportsSections() {
2585  return $this->mPage->supportsSections();
2586  }
2587 
2593  return $this->mPage->triggerOpportunisticLinksUpdate( $parserOutput );
2594  }
2595 
2600  public function updateCategoryCounts( array $added, array $deleted, $id = 0 ) {
2601  return $this->mPage->updateCategoryCounts( $added, $deleted, $id );
2602  }
2603 
2608  public function updateIfNewerOn( $dbw, $revision ) {
2609  return $this->mPage->updateIfNewerOn( $dbw, $revision );
2610  }
2611 
2616  public function updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect = null ) {
2617  return $this->mPage->updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect = null );
2618  }
2619 
2624  public function updateRevisionOn( $dbw, $revision, $lastRevision = null,
2625  $lastRevIsRedirect = null
2626  ) {
2627  return $this->mPage->updateRevisionOn( $dbw, $revision, $lastRevision,
2628  $lastRevIsRedirect
2629  );
2630  }
2631 
2640  public function doUpdateRestrictions( array $limit, array $expiry, &$cascade,
2641  $reason, User $user
2642  ) {
2643  return $this->mPage->doUpdateRestrictions( $limit, $expiry, $cascade, $reason, $user );
2644  }
2645 
2653  public function updateRestrictions( $limit = [], $reason = '',
2654  &$cascade = 0, $expiry = []
2655  ) {
2656  return $this->mPage->doUpdateRestrictions(
2657  $limit,
2658  $expiry,
2659  $cascade,
2660  $reason,
2661  $this->getContext()->getUser()
2662  );
2663  }
2664 
2673  public function doDeleteArticle(
2674  $reason, $suppress = false, $u1 = null, $u2 = null, &$error = ''
2675  ) {
2676  return $this->mPage->doDeleteArticle( $reason, $suppress, $u1, $u2, $error );
2677  }
2678 
2688  public function doRollback( $fromP, $summary, $token, $bot, &$resultDetails, User $user = null ) {
2689  $user = is_null( $user ) ? $this->getContext()->getUser() : $user;
2690  return $this->mPage->doRollback( $fromP, $summary, $token, $bot, $resultDetails, $user );
2691  }
2692 
2701  public function commitRollback( $fromP, $summary, $bot, &$resultDetails, User $guser = null ) {
2702  $guser = is_null( $guser ) ? $this->getContext()->getUser() : $guser;
2703  return $this->mPage->commitRollback( $fromP, $summary, $bot, $resultDetails, $guser );
2704  }
2705 
2710  public function generateReason( &$hasHistory ) {
2711  $title = $this->mPage->getTitle();
2712  $handler = ContentHandler::getForTitle( $title );
2713  return $handler->getAutoDeleteReason( $title, $hasHistory );
2714  }
2715 
2721  public static function selectFields() {
2722  wfDeprecated( __METHOD__, '1.24' );
2723  return WikiPage::selectFields();
2724  }
2725 
2731  public static function onArticleCreate( $title ) {
2732  wfDeprecated( __METHOD__, '1.24' );
2734  }
2735 
2741  public static function onArticleDelete( $title ) {
2742  wfDeprecated( __METHOD__, '1.24' );
2744  }
2745 
2751  public static function onArticleEdit( $title ) {
2752  wfDeprecated( __METHOD__, '1.24' );
2754  }
2755 
2763  public static function getAutosummary( $oldtext, $newtext, $flags ) {
2764  return WikiPage::getAutosummary( $oldtext, $newtext, $flags );
2765  }
2766  // ******
2767 }
pageDataFromId($dbr, $id, $options=[])
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2490
__set($fname, $fvalue)
Use PHP's magic __set handler to handle setting of raw WikiPage fields for backwards compatibility...
Definition: Article.php:2072
static newFromName($name, $validate= 'valid')
Static factory method for creation from username.
Definition: User.php:522
getRedirectTarget()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2325
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:1229
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:1537
static newFromID($id, $flags=0)
Create a new Title from an article ID.
Definition: Title.php:396
static closeElement($element)
Returns "</$element>".
Definition: Html.php:306
lockAndGetLatest()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2474
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:3270
static onArticleCreate($title)
Definition: Article.php:2731
getUndoContent(Revision $undo, Revision $undoafter=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2374
doDeleteArticleReal($reason, $suppress=false, $u1=null, $u2=null, &$error= '', User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2112
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:1816
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:1551
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:776
getRobotPolicy($action, $pOutput=null)
Get the robot policy to be used for the current view.
Definition: Article.php:839
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:1435
updateRestrictions($limit=[], $reason= '', &$cascade=0, $expiry=[])
Definition: Article.php:2653
doEditContent(Content $content, $summary, $flags=0, $baseRevId=false, User $user=null, $serialFormat=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2141
getText($audience=Revision::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2349
getLatest()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2293
setParserOptions(ParserOptions $options)
Override the ParserOptions used to render the primary article wikitext.
Definition: Article.php:2003
doEditUpdates(Revision $revision, User $user, array $options=[])
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2153
$context
Definition: load.php:43
$wgEnableAPI
Enable the MediaWiki API for convenient access to machine-readable data via api.php.
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:2159
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:2189
supportsSections()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2584
static makeUrl($name, $urlaction= '')
Definition: Skin.php:1085
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:2688
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:2673
protect()
action=protect handler
Definition: Article.php:1621
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:1435
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException'returning false will NOT prevent logging $e
Definition: hooks.txt:1980
$wgNamespaceRobotPolicies
Robot policies per namespaces.
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:2442
Wrapper allowing us to handle a system message as a Content object.
$wgDebugToolbar
Display the new debugging toolbar.
getPage()
Get the WikiPage object of this instance.
Definition: Article.php:176
getParserOutput($oldid=null, User $user=null)
#@-
Definition: Article.php:1985
updateCategoryCounts(array $added, array $deleted, $id=0)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2600
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:2466
if(!isset($args[0])) $lang
ParserOptions $mParserOptions
ParserOptions object for $wgUser articles.
Definition: Article.php:42
$wgEnableWriteAPI
Allow the API to be used to perform write operations (page edits, rollback, etc.) when an authorised ...
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:2124
adjustDisplayTitle(ParserOutput $pOutput)
Adjust title for pages with displaytitle, -{T|}- or language conversion.
Definition: Article.php:736
static hidden($name, $value, array $attribs=[])
Convenience function to produce an input element with type=hidden.
Definition: Html.php:749
shouldCheckParserCache(ParserOptions $parserOptions, $oldId)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2576
doUpdateRestrictions(array $limit, array $expiry, &$cascade, $reason, User $user)
Definition: Article.php:2640
$comment
Class for viewing MediaWiki article and history.
Definition: Article.php:34
null for the local wiki Added in
Definition: hooks.txt:1435
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:2197
confirmDelete($reason)
Output deletion confirmation dialog.
Definition: Article.php:1764
Class for viewing MediaWiki file description pages.
Definition: ImagePage.php:28
triggerOpportunisticLinksUpdate(ParserOutput $parserOutput)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2592
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:2057
it s the revision text itself In either if gzip is the revision text is gzipped $flags
Definition: hooks.txt:2588
getDeletionUpdates(Content $content=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2269
updateIfNewerOn($dbw, $revision)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2608
clearPreparedEdit()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2104
static getMainStashInstance()
Get the cache object for the main stash.
Represents a title within MediaWiki.
Definition: Title.php:36
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
$wgUseNPPatrol
Use new page patrolling to check new pages on Special:Newpages.
getContributors()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2253
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:2701
showMissingArticle()
Show the error text for a missing article.
Definition: Article.php:1238
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:2528
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:2621
Class to simplify the use of log pages.
Definition: LogPage.php:32
The User object encapsulates all of the user-specific settings (user_id, name, rights, email address, options, last login time).
Definition: User.php:47
getContext()
Gets the context this Article is executed in.
Definition: Article.php:2040
static closeElement($element)
Shortcut to close an XML element.
Definition: Xml.php:118
isRedirect()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2450
__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:2568
protectDescriptionLog(array $limit, array $expiry)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2536
static onArticleDelete($title)
Definition: Article.php:2741
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:2482
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.
$wgDefaultRobotPolicy
Default robot policy.
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:2506
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...
showViewFooter()
Show the footer section of an ordinary page view.
Definition: Article.php:1036
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:1057
$wgUseFilePatrol
Use file patrolling to check new files on Special:Newfiles.
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:1020
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:1020
insertProtectNullRevision($revCommentMsg, array $limit, array $expiry, $cascade, $reason, $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2414
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:948
ParserOutput $mParserOutput
Definition: Article.php:75
$summary
getRevision()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2341
it s the revision text itself In either if gzip is set
Definition: hooks.txt:2588
generateReason(&$hasHistory)
Definition: Article.php:2710
static getRedirectHeaderHtml(Language $lang, $target, $forceKnown=false)
Return the HTML for the top of a redirect page.
Definition: Article.php:1559
insertRedirect()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2426
int null $mOldId
The oldid of the article that is to be shown, 0 for the current revision.
Definition: Article.php:60
MediaWiki exception.
Definition: MWException.php:26
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:2398
getComment($audience=Revision::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2229
Base interface for content objects.
Definition: Content.php:34
Internationalisation code.
Definition: Language.php:39
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:2458
$cache
Definition: mcc.php:33
getActionOverrides()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2205
doViewUpdates(User $user, $oldid=0)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2181
getTitle()
Get the title object of the article.
Definition: WikiPage.php:213
const NS_CATEGORY
Definition: Defines.php:83
doPurge()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2161
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:1611
static isIP($name)
Does the string match an anonymous IP address?
Definition: User.php:824
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:2376
const DB_SLAVE
Definition: Defines.php:46
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 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 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
getCategories()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2221
Allows to change the fields on the form that will be generated are created Can be used to omit specific feeds from being outputted You must not use this hook to add use OutputPage::addFeedLink() instead.&$feedLinks conditions will AND in the final query as a Content object as a Content object $title
Definition: hooks.txt:312
static linkKnown($target, $html=null, $customAttribs=[], $query=[], $options=[ 'known'])
Identical to link(), except $options defaults to 'known'.
Definition: Linker.php:255
replaceSectionAtRev($sectionId, Content $sectionContent, $sectionTitle= '', $baseRevId=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2544
static run($event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:131
$wgUseRCPatrol
Use RC Patrolling to check for vandalism (from recent changes and watchlists) New pages and new files...
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:2088
const NS_FILE
Definition: Defines.php:75
$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:1601
$wgArticleRobotPolicies
Robot policies per article.
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:2237
showCssOrJsPage($showCacheHint=true)
Show a page view for a page formatted as CSS or JavaScript.
Definition: Article.php:803
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:203
static onArticleEdit(Title $title, Revision $revision=null)
Purge caches on page update etc.
Definition: WikiPage.php:3341
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:1957
getCreator($audience=Revision::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2261
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:1594
newPage(Title $title)
Definition: Article.php:91
Class representing a MediaWiki article and history.
Definition: WikiPage.php:31
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:2317
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:936
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:2213
checkTouched()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2096
updateRevisionOn($dbw, $revision, $lastRevision=null, $lastRevIsRedirect=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2624
static selectFields()
Definition: Article.php:2721
prepareTextForEdit($text, $revid=null, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2520
error also a ContextSource you ll probably need to make sure the header is varied on $request
Definition: hooks.txt:2458
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:36
getId()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2285
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:2358
static input($name, $value= '', $type= 'text', array $attribs=[])
Convenience function to produce an "<input>" element.
Definition: Html.php:666
pageDataFromTitle($dbr, $title, $options=[])
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2498
getMinorEdit()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2309
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:1020
static onArticleDelete(Title $title)
Clears caches when article is deleted.
Definition: WikiPage.php:3294
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:1874
showNamespaceHeader()
Show a header specific to the namespace currently being viewed, like [[MediaWiki:Talkpagetext]].
Definition: Article.php:1022
Show an error when a user tries to do something they do not have the necessary permissions for...
$wgDeleteRevisionsLimit
Optional to restrict deletion of pages with higher revision counts to users with the 'bigdelete' perm...
updateRedirectOn($dbw, $redirectTitle, $lastRevIsRedirect=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2616
tryFileCache()
checkLastModified returns true if it has taken care of all output to the client that is necessary for...
Definition: Article.php:1927
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:1020
insertRedirectEntry(Title $rt, $oldLatest=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2434
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:1020
doEdit($text, $summary, $flags=0, $baseRevId=false, $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2132
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:1403
static compare($a, $b)
Callback for usort() to do title sorts by (namespace, title)
Definition: Title.php:759
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:1629
Handles the page protection UI and backend.
getTouched()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2366
static onArticleEdit($title)
Definition: Article.php:2751
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:776
Allows to change the fields on the form that will be generated are created Can be used to omit specific feeds from being outputted You must not use this hook to add use OutputPage::addFeedLink() instead.&$feedLinks conditions will AND in the final query $logTypes
Definition: hooks.txt:312
getUser($audience=Revision::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2382
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:262
static getAutosummary($oldtext, $newtext, $flags)
Return an applicable autosummary if one exists for the given edit.
Definition: WikiPage.php:3423
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:2556
setContext($context)
Sets the context this Article is executed in.
Definition: Article.php:2030
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:1142
showDiffPage()
Show a diff page according to current request variables.
Definition: Article.php:749
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:2333
Definition: Block.php:22
static getAutosummary($oldtext, $newtext, $flags)
Definition: Article.php:2763
showDeletedRevisionHeader()
If the revision requested for view is deleted, check permissions.
Definition: Article.php:1360
static makeTitle($ns, $title, $fragment= '', $interwiki= '')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:503
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.
getContentModel()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2245
doQuickEditContent(Content $content, User $user, $comment= '', $minor=false, $serialFormat=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2169
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:2376
getLinksTimestamp()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2301
insertOn($dbw, $pageId=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2406
getParserOptions()
Get parser options suitable for rendering the primary article wikitext.
Definition: Article.php:2016
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:2390
showPatrolFooter()
If patrol is possible, output a patrol UI box.
Definition: Article.php:1059
static label($label, $id, array $attribs=[])
Convenience function for generating a label for inputs.
Definition: Html.php:733
static formatRobotPolicy($policy)
Converts a String robot policy into an associative array, to allow merging of several policies using ...
Definition: Article.php:919
getHiddenCategories()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2277