MediaWiki  1.27.4
Article.php
Go to the documentation of this file.
1 <?php
34 class Article implements Page {
36  protected $mContext;
37 
39  protected $mPage;
40 
43 
48  public $mContent;
49 
55 
57  public $mContentLoaded = false;
58 
60  public $mOldId;
61 
63  public $mRedirectedFrom = null;
64 
66  public $mRedirectUrl = false;
67 
69  public $mRevIdFetched = 0;
70 
72  public $mRevision = null;
73 
76 
82  public function __construct( Title $title, $oldId = null ) {
83  $this->mOldId = $oldId;
84  $this->mPage = $this->newPage( $title );
85  }
86 
91  protected function newPage( Title $title ) {
92  return new WikiPage( $title );
93  }
94 
100  public static function newFromID( $id ) {
101  $t = Title::newFromID( $id );
102  # @todo FIXME: Doesn't inherit right
103  return $t == null ? null : new self( $t );
104  # return $t == null ? null : new static( $t ); // PHP 5.3
105  }
106 
114  public static function newFromTitle( $title, IContextSource $context ) {
115  if ( NS_MEDIA == $title->getNamespace() ) {
116  // FIXME: where should this go?
117  $title = Title::makeTitle( NS_FILE, $title->getDBkey() );
118  }
119 
120  $page = null;
121  Hooks::run( 'ArticleFromTitle', [ &$title, &$page, $context ] );
122  if ( !$page ) {
123  switch ( $title->getNamespace() ) {
124  case NS_FILE:
125  $page = new ImagePage( $title );
126  break;
127  case NS_CATEGORY:
128  $page = new CategoryPage( $title );
129  break;
130  default:
131  $page = new Article( $title );
132  }
133  }
134  $page->setContext( $context );
135 
136  return $page;
137  }
138 
146  public static function newFromWikiPage( WikiPage $page, IContextSource $context ) {
147  $article = self::newFromTitle( $page->getTitle(), $context );
148  $article->mPage = $page; // override to keep process cached vars
149  return $article;
150  }
151 
157  public function setRedirectedFrom( Title $from ) {
158  $this->mRedirectedFrom = $from;
159  }
160 
166  public function getTitle() {
167  return $this->mPage->getTitle();
168  }
169 
176  public function getPage() {
177  return $this->mPage;
178  }
179 
183  public function clear() {
184  $this->mContentLoaded = false;
185 
186  $this->mRedirectedFrom = null; # Title object if set
187  $this->mRevIdFetched = 0;
188  $this->mRedirectUrl = false;
189 
190  $this->mPage->clear();
191  }
192 
205  public function getContent() {
206  ContentHandler::deprecated( __METHOD__, '1.21' );
207  $content = $this->getContentObject();
209  }
210 
226  protected function getContentObject() {
227 
228  if ( $this->mPage->getId() === 0 ) {
229  # If this is a MediaWiki:x message, then load the messages
230  # and return the message value for x.
231  if ( $this->getTitle()->getNamespace() == NS_MEDIAWIKI ) {
232  $text = $this->getTitle()->getDefaultMessageText();
233  if ( $text === false ) {
234  $text = '';
235  }
236 
237  $content = ContentHandler::makeContent( $text, $this->getTitle() );
238  } else {
239  $message = $this->getContext()->getUser()->isLoggedIn() ? 'noarticletext' : 'noarticletextanon';
240  $content = new MessageContent( $message, null, 'parsemag' );
241  }
242  } else {
243  $this->fetchContentObject();
245  }
246 
247  return $content;
248  }
249 
253  public function getOldID() {
254  if ( is_null( $this->mOldId ) ) {
255  $this->mOldId = $this->getOldIDFromRequest();
256  }
257 
258  return $this->mOldId;
259  }
260 
266  public function getOldIDFromRequest() {
267  $this->mRedirectUrl = false;
268 
269  $request = $this->getContext()->getRequest();
270  $oldid = $request->getIntOrNull( 'oldid' );
271 
272  if ( $oldid === null ) {
273  return 0;
274  }
275 
276  if ( $oldid !== 0 ) {
277  # Load the given revision and check whether the page is another one.
278  # In that case, update this instance to reflect the change.
279  if ( $oldid === $this->mPage->getLatest() ) {
280  $this->mRevision = $this->mPage->getRevision();
281  } else {
282  $this->mRevision = Revision::newFromId( $oldid );
283  if ( $this->mRevision !== null ) {
284  // Revision title doesn't match the page title given?
285  if ( $this->mPage->getId() != $this->mRevision->getPage() ) {
286  $function = [ get_class( $this->mPage ), 'newFromID' ];
287  $this->mPage = call_user_func( $function, $this->mRevision->getPage() );
288  }
289  }
290  }
291  }
292 
293  if ( $request->getVal( 'direction' ) == 'next' ) {
294  $nextid = $this->getTitle()->getNextRevisionID( $oldid );
295  if ( $nextid ) {
296  $oldid = $nextid;
297  $this->mRevision = null;
298  } else {
299  $this->mRedirectUrl = $this->getTitle()->getFullURL( 'redirect=no' );
300  }
301  } elseif ( $request->getVal( 'direction' ) == 'prev' ) {
302  $previd = $this->getTitle()->getPreviousRevisionID( $oldid );
303  if ( $previd ) {
304  $oldid = $previd;
305  $this->mRevision = null;
306  }
307  }
308 
309  return $oldid;
310  }
311 
326  function fetchContent() {
327  // BC cruft!
328 
329  ContentHandler::deprecated( __METHOD__, '1.21' );
330 
331  if ( $this->mContentLoaded && $this->mContent ) {
332  return $this->mContent;
333  }
334 
335  $content = $this->fetchContentObject();
336 
337  if ( !$content ) {
338  return false;
339  }
340 
341  // @todo Get rid of mContent everywhere!
342  $this->mContent = ContentHandler::getContentText( $content );
343 
344  // Avoid PHP 7.1 warning of passing $this by reference
345  $articlePage = $this;
346  ContentHandler::runLegacyHooks( 'ArticleAfterFetchContent', [ &$articlePage, &$this->mContent ] );
347 
348  return $this->mContent;
349  }
350 
363  protected function fetchContentObject() {
364  if ( $this->mContentLoaded ) {
365  return $this->mContentObject;
366  }
367 
368  $this->mContentLoaded = true;
369  $this->mContent = null;
370 
371  $oldid = $this->getOldID();
372 
373  # Pre-fill content with error message so that if something
374  # fails we'll have something telling us what we intended.
375  // XXX: this isn't page content but a UI message. horrible.
376  $this->mContentObject = new MessageContent( 'missing-revision', [ $oldid ] );
377 
378  if ( $oldid ) {
379  # $this->mRevision might already be fetched by getOldIDFromRequest()
380  if ( !$this->mRevision ) {
381  $this->mRevision = Revision::newFromId( $oldid );
382  if ( !$this->mRevision ) {
383  wfDebug( __METHOD__ . " failed to retrieve specified revision, id $oldid\n" );
384  return false;
385  }
386  }
387  } else {
388  $oldid = $this->mPage->getLatest();
389  if ( !$oldid ) {
390  wfDebug( __METHOD__ . " failed to find page data for title " .
391  $this->getTitle()->getPrefixedText() . "\n" );
392  return false;
393  }
394 
395  # Update error message with correct oldid
396  $this->mContentObject = new MessageContent( 'missing-revision', [ $oldid ] );
397 
398  $this->mRevision = $this->mPage->getRevision();
399 
400  if ( !$this->mRevision ) {
401  wfDebug( __METHOD__ . " failed to retrieve current page, rev_id $oldid\n" );
402  return false;
403  }
404  }
405 
406  // @todo FIXME: Horrible, horrible! This content-loading interface just plain sucks.
407  // We should instead work with the Revision object when we need it...
408  // Loads if user is allowed
409  $content = $this->mRevision->getContent(
411  $this->getContext()->getUser()
412  );
413 
414  if ( !$content ) {
415  wfDebug( __METHOD__ . " failed to retrieve content of revision " .
416  $this->mRevision->getId() . "\n" );
417  return false;
418  }
419 
420  $this->mContentObject = $content;
421  $this->mRevIdFetched = $this->mRevision->getId();
422 
423  // Avoid PHP 7.1 warning of passing $this by reference
424  $articlePage = $this;
425  Hooks::run( 'ArticleAfterFetchContentObject', [ &$articlePage, &$this->mContentObject ] );
426 
427  return $this->mContentObject;
428  }
429 
435  public function isCurrent() {
436  # If no oldid, this is the current version.
437  if ( $this->getOldID() == 0 ) {
438  return true;
439  }
440 
441  return $this->mPage->exists() && $this->mRevision && $this->mRevision->isCurrent();
442  }
443 
451  public function getRevisionFetched() {
452  $this->fetchContentObject();
453 
454  return $this->mRevision;
455  }
456 
462  public function getRevIdFetched() {
463  if ( $this->mRevIdFetched ) {
464  return $this->mRevIdFetched;
465  } else {
466  return $this->mPage->getLatest();
467  }
468  }
469 
474  public function view() {
475  global $wgUseFileCache, $wgUseETag, $wgDebugToolbar, $wgMaxRedirects;
476 
477  # Get variables from query string
478  # As side effect this will load the revision and update the title
479  # in a revision ID is passed in the request, so this should remain
480  # the first call of this method even if $oldid is used way below.
481  $oldid = $this->getOldID();
482 
483  $user = $this->getContext()->getUser();
484  # Another whitelist check in case getOldID() is altering the title
485  $permErrors = $this->getTitle()->getUserPermissionsErrors( 'read', $user );
486  if ( count( $permErrors ) ) {
487  wfDebug( __METHOD__ . ": denied on secondary read check\n" );
488  throw new PermissionsError( 'read', $permErrors );
489  }
490 
491  $outputPage = $this->getContext()->getOutput();
492  # getOldID() may as well want us to redirect somewhere else
493  if ( $this->mRedirectUrl ) {
494  $outputPage->redirect( $this->mRedirectUrl );
495  wfDebug( __METHOD__ . ": redirecting due to oldid\n" );
496 
497  return;
498  }
499 
500  # If we got diff in the query, we want to see a diff page instead of the article.
501  if ( $this->getContext()->getRequest()->getCheck( 'diff' ) ) {
502  wfDebug( __METHOD__ . ": showing diff page\n" );
503  $this->showDiffPage();
504 
505  return;
506  }
507 
508  # Set page title (may be overridden by DISPLAYTITLE)
509  $outputPage->setPageTitle( $this->getTitle()->getPrefixedText() );
510 
511  $outputPage->setArticleFlag( true );
512  # Allow frames by default
513  $outputPage->allowClickjacking();
514 
515  $parserCache = ParserCache::singleton();
516 
517  $parserOptions = $this->getParserOptions();
518  # Render printable version, use printable version cache
519  if ( $outputPage->isPrintable() ) {
520  $parserOptions->setIsPrintable( true );
521  $parserOptions->setEditSection( false );
522  } elseif ( !$this->isCurrent() || !$this->getTitle()->quickUserCan( 'edit', $user ) ) {
523  $parserOptions->setEditSection( false );
524  }
525 
526  # Try client and file cache
527  if ( !$wgDebugToolbar && $oldid === 0 && $this->mPage->checkTouched() ) {
528  if ( $wgUseETag ) {
529  $outputPage->setETag( $parserCache->getETag( $this->mPage, $parserOptions ) );
530  }
531 
532  # Use the greatest of the page's timestamp or the timestamp of any
533  # redirect in the chain (bug 67849)
534  $timestamp = $this->mPage->getTouched();
535  if ( isset( $this->mRedirectedFrom ) ) {
536  $timestamp = max( $timestamp, $this->mRedirectedFrom->getTouched() );
537 
538  # If there can be more than one redirect in the chain, we have
539  # to go through the whole chain too in case an intermediate
540  # redirect was changed.
541  if ( $wgMaxRedirects > 1 ) {
542  $titles = Revision::newFromTitle( $this->mRedirectedFrom )
543  ->getContent( Revision::FOR_THIS_USER, $user )
544  ->getRedirectChain();
545  $thisTitle = $this->getTitle();
546  foreach ( $titles as $title ) {
547  if ( Title::compare( $title, $thisTitle ) === 0 ) {
548  break;
549  }
550  $timestamp = max( $timestamp, $title->getTouched() );
551  }
552  }
553  }
554 
555  # Is it client cached?
556  if ( $outputPage->checkLastModified( $timestamp ) ) {
557  wfDebug( __METHOD__ . ": done 304\n" );
558 
559  return;
560  # Try file cache
561  } elseif ( $wgUseFileCache && $this->tryFileCache() ) {
562  wfDebug( __METHOD__ . ": done file cache\n" );
563  # tell wgOut that output is taken care of
564  $outputPage->disable();
565  $this->mPage->doViewUpdates( $user, $oldid );
566 
567  return;
568  }
569  }
570 
571  # Should the parser cache be used?
572  $useParserCache = $this->mPage->shouldCheckParserCache( $parserOptions, $oldid );
573  wfDebug( 'Article::view using parser cache: ' . ( $useParserCache ? 'yes' : 'no' ) . "\n" );
574  if ( $user->getStubThreshold() ) {
575  $this->getContext()->getStats()->increment( 'pcache_miss_stub' );
576  }
577 
578  $this->showRedirectedFromHeader();
579  $this->showNamespaceHeader();
580 
581  # Iterate through the possible ways of constructing the output text.
582  # Keep going until $outputDone is set, or we run out of things to do.
583  $pass = 0;
584  $outputDone = false;
585  $this->mParserOutput = false;
586 
587  while ( !$outputDone && ++$pass ) {
588  switch ( $pass ) {
589  case 1:
590  // Avoid PHP 7.1 warning of passing $this by reference
591  $articlePage = $this;
592  Hooks::run( 'ArticleViewHeader', [ &$articlePage, &$outputDone, &$useParserCache ] );
593  break;
594  case 2:
595  # Early abort if the page doesn't exist
596  if ( !$this->mPage->exists() ) {
597  wfDebug( __METHOD__ . ": showing missing article\n" );
598  $this->showMissingArticle();
599  $this->mPage->doViewUpdates( $user );
600  return;
601  }
602 
603  # Try the parser cache
604  if ( $useParserCache ) {
605  $this->mParserOutput = $parserCache->get( $this->mPage, $parserOptions );
606 
607  if ( $this->mParserOutput !== false ) {
608  if ( $oldid ) {
609  wfDebug( __METHOD__ . ": showing parser cache contents for current rev permalink\n" );
610  $this->setOldSubtitle( $oldid );
611  } else {
612  wfDebug( __METHOD__ . ": showing parser cache contents\n" );
613  }
614  $outputPage->addParserOutput( $this->mParserOutput );
615  # Ensure that UI elements requiring revision ID have
616  # the correct version information.
617  $outputPage->setRevisionId( $this->mPage->getLatest() );
618  # Preload timestamp to avoid a DB hit
619  $cachedTimestamp = $this->mParserOutput->getTimestamp();
620  if ( $cachedTimestamp !== null ) {
621  $outputPage->setRevisionTimestamp( $cachedTimestamp );
622  $this->mPage->setTimestamp( $cachedTimestamp );
623  }
624  $outputDone = true;
625  }
626  }
627  break;
628  case 3:
629  # This will set $this->mRevision if needed
630  $this->fetchContentObject();
631 
632  # Are we looking at an old revision
633  if ( $oldid && $this->mRevision ) {
634  $this->setOldSubtitle( $oldid );
635 
636  if ( !$this->showDeletedRevisionHeader() ) {
637  wfDebug( __METHOD__ . ": cannot view deleted revision\n" );
638  return;
639  }
640  }
641 
642  # Ensure that UI elements requiring revision ID have
643  # the correct version information.
644  $outputPage->setRevisionId( $this->getRevIdFetched() );
645  # Preload timestamp to avoid a DB hit
646  $outputPage->setRevisionTimestamp( $this->mPage->getTimestamp() );
647 
648  # Pages containing custom CSS or JavaScript get special treatment
649  if ( $this->getTitle()->isCssOrJsPage() || $this->getTitle()->isCssJsSubpage() ) {
650  wfDebug( __METHOD__ . ": showing CSS/JS source\n" );
651  $this->showCssOrJsPage();
652  $outputDone = true;
653  } elseif ( !Hooks::run( 'ArticleContentViewCustom',
654  [ $this->fetchContentObject(), $this->getTitle(), $outputPage ] ) ) {
655 
656  # Allow extensions do their own custom view for certain pages
657  $outputDone = true;
658  } elseif ( !ContentHandler::runLegacyHooks( 'ArticleViewCustom',
659  [ $this->fetchContentObject(), $this->getTitle(), $outputPage ] ) ) {
660 
661  # Allow extensions do their own custom view for certain pages
662  $outputDone = true;
663  }
664  break;
665  case 4:
666  # Run the parse, protected by a pool counter
667  wfDebug( __METHOD__ . ": doing uncached parse\n" );
668 
669  $content = $this->getContentObject();
670  $poolArticleView = new PoolWorkArticleView( $this->getPage(), $parserOptions,
671  $this->getRevIdFetched(), $useParserCache, $content );
672 
673  if ( !$poolArticleView->execute() ) {
674  $error = $poolArticleView->getError();
675  if ( $error ) {
676  $outputPage->clearHTML(); // for release() errors
677  $outputPage->enableClientCache( false );
678  $outputPage->setRobotPolicy( 'noindex,nofollow' );
679 
680  $errortext = $error->getWikiText( false, 'view-pool-error' );
681  $outputPage->addWikiText( '<div class="errorbox">' . $errortext . '</div>' );
682  }
683  # Connection or timeout error
684  return;
685  }
686 
687  $this->mParserOutput = $poolArticleView->getParserOutput();
688  $outputPage->addParserOutput( $this->mParserOutput );
689  if ( $content->getRedirectTarget() ) {
690  $outputPage->addSubtitle( "<span id=\"redirectsub\">" .
691  $this->getContext()->msg( 'redirectpagesub' )->parse() . "</span>" );
692  }
693 
694  # Don't cache a dirty ParserOutput object
695  if ( $poolArticleView->getIsDirty() ) {
696  $outputPage->setCdnMaxage( 0 );
697  $outputPage->addHTML( "<!-- parser cache is expired, " .
698  "sending anyway due to pool overload-->\n" );
699  }
700 
701  $outputDone = true;
702  break;
703  # Should be unreachable, but just in case...
704  default:
705  break 2;
706  }
707  }
708 
709  # Get the ParserOutput actually *displayed* here.
710  # Note that $this->mParserOutput is the *current*/oldid version output.
711  $pOutput = ( $outputDone instanceof ParserOutput )
712  ? $outputDone // object fetched by hook
713  : $this->mParserOutput;
714 
715  # Adjust title for main page & pages with displaytitle
716  if ( $pOutput ) {
717  $this->adjustDisplayTitle( $pOutput );
718  }
719 
720  # For the main page, overwrite the <title> element with the con-
721  # tents of 'pagetitle-view-mainpage' instead of the default (if
722  # that's not empty).
723  # This message always exists because it is in the i18n files
724  if ( $this->getTitle()->isMainPage() ) {
725  $msg = wfMessage( 'pagetitle-view-mainpage' )->inContentLanguage();
726  if ( !$msg->isDisabled() ) {
727  $outputPage->setHTMLTitle( $msg->title( $this->getTitle() )->text() );
728  }
729  }
730 
731  # Check for any __NOINDEX__ tags on the page using $pOutput
732  $policy = $this->getRobotPolicy( 'view', $pOutput );
733  $outputPage->setIndexPolicy( $policy['index'] );
734  $outputPage->setFollowPolicy( $policy['follow'] );
735 
736  $this->showViewFooter();
737  $this->mPage->doViewUpdates( $user, $oldid );
738 
739  $outputPage->addModules( 'mediawiki.action.view.postEdit' );
740 
741  }
742 
747  public function adjustDisplayTitle( ParserOutput $pOutput ) {
748  # Adjust the title if it was set by displaytitle, -{T|}- or language conversion
749  $titleText = $pOutput->getTitleText();
750  if ( strval( $titleText ) !== '' ) {
751  $this->getContext()->getOutput()->setPageTitle( $titleText );
752  }
753  }
754 
760  protected function showDiffPage() {
761  $request = $this->getContext()->getRequest();
762  $user = $this->getContext()->getUser();
763  $diff = $request->getVal( 'diff' );
764  $rcid = $request->getVal( 'rcid' );
765  $diffOnly = $request->getBool( 'diffonly', $user->getOption( 'diffonly' ) );
766  $purge = $request->getVal( 'action' ) == 'purge';
767  $unhide = $request->getInt( 'unhide' ) == 1;
768  $oldid = $this->getOldID();
769 
770  $rev = $this->getRevisionFetched();
771 
772  if ( !$rev ) {
773  $this->getContext()->getOutput()->setPageTitle( wfMessage( 'errorpagetitle' ) );
774  $msg = $this->getContext()->msg( 'difference-missing-revision' )
775  ->params( $oldid )
776  ->numParams( 1 )
777  ->parseAsBlock();
778  $this->getContext()->getOutput()->addHTML( $msg );
779  return;
780  }
781 
782  $contentHandler = $rev->getContentHandler();
783  $de = $contentHandler->createDifferenceEngine(
784  $this->getContext(),
785  $oldid,
786  $diff,
787  $rcid,
788  $purge,
789  $unhide
790  );
791 
792  // DifferenceEngine directly fetched the revision:
793  $this->mRevIdFetched = $de->mNewid;
794  $de->showDiffPage( $diffOnly );
795 
796  // Run view updates for the newer revision being diffed (and shown
797  // below the diff if not $diffOnly).
798  list( $old, $new ) = $de->mapDiffPrevNext( $oldid, $diff );
799  // New can be false, convert it to 0 - this conveniently means the latest revision
800  $this->mPage->doViewUpdates( $user, (int)$new );
801  }
802 
814  protected function showCssOrJsPage( $showCacheHint = true ) {
815  $outputPage = $this->getContext()->getOutput();
816 
817  if ( $showCacheHint ) {
818  $dir = $this->getContext()->getLanguage()->getDir();
819  $lang = $this->getContext()->getLanguage()->getHtmlCode();
820 
821  $outputPage->wrapWikiMsg(
822  "<div id='mw-clearyourcache' lang='$lang' dir='$dir' class='mw-content-$dir'>\n$1\n</div>",
823  'clearyourcache'
824  );
825  }
826 
827  $this->fetchContentObject();
828 
829  if ( $this->mContentObject ) {
830  // Give hooks a chance to customise the output
832  'ShowRawCssJs',
833  [ $this->mContentObject, $this->getTitle(), $outputPage ] )
834  ) {
835  // If no legacy hooks ran, display the content of the parser output, including RL modules,
836  // but excluding metadata like categories and language links
837  $po = $this->mContentObject->getParserOutput( $this->getTitle() );
838  $outputPage->addParserOutputContent( $po );
839  }
840  }
841  }
842 
850  public function getRobotPolicy( $action, $pOutput = null ) {
851  global $wgArticleRobotPolicies, $wgNamespaceRobotPolicies, $wgDefaultRobotPolicy;
852 
853  $ns = $this->getTitle()->getNamespace();
854 
855  # Don't index user and user talk pages for blocked users (bug 11443)
856  if ( ( $ns == NS_USER || $ns == NS_USER_TALK ) && !$this->getTitle()->isSubpage() ) {
857  $specificTarget = null;
858  $vagueTarget = null;
859  $titleText = $this->getTitle()->getText();
860  if ( IP::isValid( $titleText ) ) {
861  $vagueTarget = $titleText;
862  } else {
863  $specificTarget = $titleText;
864  }
865  if ( Block::newFromTarget( $specificTarget, $vagueTarget ) instanceof Block ) {
866  return [
867  'index' => 'noindex',
868  'follow' => 'nofollow'
869  ];
870  }
871  }
872 
873  if ( $this->mPage->getId() === 0 || $this->getOldID() ) {
874  # Non-articles (special pages etc), and old revisions
875  return [
876  'index' => 'noindex',
877  'follow' => 'nofollow'
878  ];
879  } elseif ( $this->getContext()->getOutput()->isPrintable() ) {
880  # Discourage indexing of printable versions, but encourage following
881  return [
882  'index' => 'noindex',
883  'follow' => 'follow'
884  ];
885  } elseif ( $this->getContext()->getRequest()->getInt( 'curid' ) ) {
886  # For ?curid=x urls, disallow indexing
887  return [
888  'index' => 'noindex',
889  'follow' => 'follow'
890  ];
891  }
892 
893  # Otherwise, construct the policy based on the various config variables.
894  $policy = self::formatRobotPolicy( $wgDefaultRobotPolicy );
895 
896  if ( isset( $wgNamespaceRobotPolicies[$ns] ) ) {
897  # Honour customised robot policies for this namespace
898  $policy = array_merge(
899  $policy,
900  self::formatRobotPolicy( $wgNamespaceRobotPolicies[$ns] )
901  );
902  }
903  if ( $this->getTitle()->canUseNoindex() && is_object( $pOutput ) && $pOutput->getIndexPolicy() ) {
904  # __INDEX__ and __NOINDEX__ magic words, if allowed. Incorporates
905  # a final sanity check that we have really got the parser output.
906  $policy = array_merge(
907  $policy,
908  [ 'index' => $pOutput->getIndexPolicy() ]
909  );
910  }
911 
912  if ( isset( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] ) ) {
913  # (bug 14900) site config can override user-defined __INDEX__ or __NOINDEX__
914  $policy = array_merge(
915  $policy,
916  self::formatRobotPolicy( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] )
917  );
918  }
919 
920  return $policy;
921  }
922 
930  public static function formatRobotPolicy( $policy ) {
931  if ( is_array( $policy ) ) {
932  return $policy;
933  } elseif ( !$policy ) {
934  return [];
935  }
936 
937  $policy = explode( ',', $policy );
938  $policy = array_map( 'trim', $policy );
939 
940  $arr = [];
941  foreach ( $policy as $var ) {
942  if ( in_array( $var, [ 'index', 'noindex' ] ) ) {
943  $arr['index'] = $var;
944  } elseif ( in_array( $var, [ 'follow', 'nofollow' ] ) ) {
945  $arr['follow'] = $var;
946  }
947  }
948 
949  return $arr;
950  }
951 
959  public function showRedirectedFromHeader() {
961 
962  $context = $this->getContext();
963  $outputPage = $context->getOutput();
964  $request = $context->getRequest();
965  $rdfrom = $request->getVal( 'rdfrom' );
966 
967  // Construct a URL for the current page view, but with the target title
968  $query = $request->getValues();
969  unset( $query['rdfrom'] );
970  unset( $query['title'] );
971  if ( $this->getTitle()->isRedirect() ) {
972  // Prevent double redirects
973  $query['redirect'] = 'no';
974  }
975  $redirectTargetUrl = $this->getTitle()->getLinkURL( $query );
976 
977  if ( isset( $this->mRedirectedFrom ) ) {
978  // Avoid PHP 7.1 warning of passing $this by reference
979  $articlePage = $this;
980 
981  // This is an internally redirected page view.
982  // We'll need a backlink to the source page for navigation.
983  if ( Hooks::run( 'ArticleViewRedirect', [ &$articlePage ] ) ) {
984  $redir = Linker::linkKnown(
985  $this->mRedirectedFrom,
986  null,
987  [],
988  [ 'redirect' => 'no' ]
989  );
990 
991  $outputPage->addSubtitle( "<span class=\"mw-redirectedfrom\">" .
992  $context->msg( 'redirectedfrom' )->rawParams( $redir )->parse()
993  . "</span>" );
994 
995  // Add the script to update the displayed URL and
996  // set the fragment if one was specified in the redirect
997  $outputPage->addJsConfigVars( [
998  'wgInternalRedirectTargetUrl' => $redirectTargetUrl,
999  ] );
1000  $outputPage->addModules( 'mediawiki.action.view.redirect' );
1001 
1002  // Add a <link rel="canonical"> tag
1003  $outputPage->setCanonicalUrl( $this->getTitle()->getCanonicalURL() );
1004 
1005  // Tell the output object that the user arrived at this article through a redirect
1006  $outputPage->setRedirectedFrom( $this->mRedirectedFrom );
1007 
1008  return true;
1009  }
1010  } elseif ( $rdfrom ) {
1011  // This is an externally redirected view, from some other wiki.
1012  // If it was reported from a trusted site, supply a backlink.
1013  if ( $wgRedirectSources && preg_match( $wgRedirectSources, $rdfrom ) ) {
1014  $redir = Linker::makeExternalLink( $rdfrom, $rdfrom );
1015  $outputPage->addSubtitle( "<span class=\"mw-redirectedfrom\">" .
1016  $context->msg( 'redirectedfrom' )->rawParams( $redir )->parse()
1017  . "</span>" );
1018 
1019  // Add the script to update the displayed URL
1020  $outputPage->addJsConfigVars( [
1021  'wgInternalRedirectTargetUrl' => $redirectTargetUrl,
1022  ] );
1023  $outputPage->addModules( 'mediawiki.action.view.redirect' );
1024 
1025  return true;
1026  }
1027  }
1028 
1029  return false;
1030  }
1031 
1036  public function showNamespaceHeader() {
1037  if ( $this->getTitle()->isTalkPage() ) {
1038  if ( !wfMessage( 'talkpageheader' )->isDisabled() ) {
1039  $this->getContext()->getOutput()->wrapWikiMsg(
1040  "<div class=\"mw-talkpageheader\">\n$1\n</div>",
1041  [ 'talkpageheader' ]
1042  );
1043  }
1044  }
1045  }
1046 
1050  public function showViewFooter() {
1051  # check if we're displaying a [[User talk:x.x.x.x]] anonymous talk page
1052  if ( $this->getTitle()->getNamespace() == NS_USER_TALK
1053  && IP::isValid( $this->getTitle()->getText() )
1054  ) {
1055  $this->getContext()->getOutput()->addWikiMsg( 'anontalkpagetext' );
1056  }
1057 
1058  // Show a footer allowing the user to patrol the shown revision or page if possible
1059  $patrolFooterShown = $this->showPatrolFooter();
1060 
1061  Hooks::run( 'ArticleViewFooter', [ $this, $patrolFooterShown ] );
1062  }
1063 
1073  public function showPatrolFooter() {
1074  global $wgUseNPPatrol, $wgUseRCPatrol, $wgUseFilePatrol, $wgEnableAPI, $wgEnableWriteAPI;
1075 
1076  $outputPage = $this->getContext()->getOutput();
1077  $user = $this->getContext()->getUser();
1078  $title = $this->getTitle();
1079  $rc = false;
1080 
1081  if ( !$title->quickUserCan( 'patrol', $user )
1082  || !( $wgUseRCPatrol || $wgUseNPPatrol
1083  || ( $wgUseFilePatrol && $title->inNamespace( NS_FILE ) ) )
1084  ) {
1085  // Patrolling is disabled or the user isn't allowed to
1086  return false;
1087  }
1088 
1089  if ( $this->mRevision
1090  && !RecentChange::isInRCLifespan( $this->mRevision->getTimestamp(), 21600 )
1091  ) {
1092  // The current revision is already older than what could be in the RC table
1093  // 6h tolerance because the RC might not be cleaned out regularly
1094  return false;
1095  }
1096 
1097  // Check for cached results
1098  $key = wfMemcKey( 'unpatrollable-page', $title->getArticleID() );
1100  if ( $cache->get( $key ) ) {
1101  return false;
1102  }
1103 
1104  $dbr = wfGetDB( DB_SLAVE );
1105  $oldestRevisionTimestamp = $dbr->selectField(
1106  'revision',
1107  'MIN( rev_timestamp )',
1108  [ 'rev_page' => $title->getArticleID() ],
1109  __METHOD__
1110  );
1111 
1112  // New page patrol: Get the timestamp of the oldest revison which
1113  // the revision table holds for the given page. Then we look
1114  // whether it's within the RC lifespan and if it is, we try
1115  // to get the recentchanges row belonging to that entry
1116  // (with rc_new = 1).
1117  $recentPageCreation = false;
1118  if ( $oldestRevisionTimestamp
1119  && RecentChange::isInRCLifespan( $oldestRevisionTimestamp, 21600 )
1120  ) {
1121  // 6h tolerance because the RC might not be cleaned out regularly
1122  $recentPageCreation = true;
1124  [
1125  'rc_new' => 1,
1126  'rc_timestamp' => $oldestRevisionTimestamp,
1127  'rc_namespace' => $title->getNamespace(),
1128  'rc_cur_id' => $title->getArticleID()
1129  ],
1130  __METHOD__
1131  );
1132  if ( $rc ) {
1133  // Use generic patrol message for new pages
1134  $markPatrolledMsg = wfMessage( 'markaspatrolledtext' );
1135  }
1136  }
1137 
1138  // File patrol: Get the timestamp of the latest upload for this page,
1139  // check whether it is within the RC lifespan and if it is, we try
1140  // to get the recentchanges row belonging to that entry
1141  // (with rc_type = RC_LOG, rc_log_type = upload).
1142  $recentFileUpload = false;
1143  if ( ( !$rc || $rc->getAttribute( 'rc_patrolled' ) ) && $wgUseFilePatrol
1144  && $title->getNamespace() === NS_FILE ) {
1145  // Retrieve timestamp of most recent upload
1146  $newestUploadTimestamp = $dbr->selectField(
1147  'image',
1148  'MAX( img_timestamp )',
1149  [ 'img_name' => $title->getDBkey() ],
1150  __METHOD__
1151  );
1152  if ( $newestUploadTimestamp
1153  && RecentChange::isInRCLifespan( $newestUploadTimestamp, 21600 )
1154  ) {
1155  // 6h tolerance because the RC might not be cleaned out regularly
1156  $recentFileUpload = true;
1158  [
1159  'rc_type' => RC_LOG,
1160  'rc_log_type' => 'upload',
1161  'rc_timestamp' => $newestUploadTimestamp,
1162  'rc_namespace' => NS_FILE,
1163  'rc_cur_id' => $title->getArticleID()
1164  ],
1165  __METHOD__,
1166  [ 'USE INDEX' => 'rc_timestamp' ]
1167  );
1168  if ( $rc ) {
1169  // Use patrol message specific to files
1170  $markPatrolledMsg = wfMessage( 'markaspatrolledtext-file' );
1171  }
1172  }
1173  }
1174 
1175  if ( !$recentPageCreation && !$recentFileUpload ) {
1176  // Page creation and latest upload (for files) is too old to be in RC
1177 
1178  // We definitely can't patrol so cache the information
1179  // When a new file version is uploaded, the cache is cleared
1180  $cache->set( $key, '1' );
1181 
1182  return false;
1183  }
1184 
1185  if ( !$rc ) {
1186  // Don't cache: This can be hit if the page gets accessed very fast after
1187  // its creation / latest upload or in case we have high slave lag. In case
1188  // the revision is too old, we will already return above.
1189  return false;
1190  }
1191 
1192  if ( $rc->getAttribute( 'rc_patrolled' ) ) {
1193  // Patrolled RC entry around
1194 
1195  // Cache the information we gathered above in case we can't patrol
1196  // Don't cache in case we can patrol as this could change
1197  $cache->set( $key, '1' );
1198 
1199  return false;
1200  }
1201 
1202  if ( $rc->getPerformer()->equals( $user ) ) {
1203  // Don't show a patrol link for own creations/uploads. If the user could
1204  // patrol them, they already would be patrolled
1205  return false;
1206  }
1207 
1208  $rcid = $rc->getAttribute( 'rc_id' );
1209 
1210  $token = $user->getEditToken( $rcid );
1211 
1212  $outputPage->preventClickjacking();
1213  if ( $wgEnableAPI && $wgEnableWriteAPI && $user->isAllowed( 'writeapi' ) ) {
1214  $outputPage->addModules( 'mediawiki.page.patrol.ajax' );
1215  }
1216 
1218  $title,
1219  $markPatrolledMsg->escaped(),
1220  [],
1221  [
1222  'action' => 'markpatrolled',
1223  'rcid' => $rcid,
1224  'token' => $token,
1225  ]
1226  );
1227 
1228  $outputPage->addHTML(
1229  "<div class='patrollink' data-mw='interface'>" .
1230  wfMessage( 'markaspatrolledlink' )->rawParams( $link )->escaped() .
1231  '</div>'
1232  );
1233 
1234  return true;
1235  }
1236 
1243  public static function purgePatrolFooterCache( $articleID ) {
1245  $cache->delete( wfMemcKey( 'unpatrollable-page', $articleID ) );
1246  }
1247 
1252  public function showMissingArticle() {
1254 
1255  $outputPage = $this->getContext()->getOutput();
1256  // Whether the page is a root user page of an existing user (but not a subpage)
1257  $validUserPage = false;
1258 
1259  $title = $this->getTitle();
1260 
1261  # Show info in user (talk) namespace. Does the user exist? Is he blocked?
1262  if ( $title->getNamespace() == NS_USER
1263  || $title->getNamespace() == NS_USER_TALK
1264  ) {
1265  $rootPart = explode( '/', $title->getText() )[0];
1266  $user = User::newFromName( $rootPart, false /* allow IP users*/ );
1267  $ip = User::isIP( $rootPart );
1268  $block = Block::newFromTarget( $user, $user );
1269 
1270  if ( !( $user && $user->isLoggedIn() ) && !$ip ) { # User does not exist
1271  $outputPage->wrapWikiMsg( "<div class=\"mw-userpage-userdoesnotexist error\">\n\$1\n</div>",
1272  [ 'userpage-userdoesnotexist-view', wfEscapeWikiText( $rootPart ) ] );
1273  } elseif ( !is_null( $block ) && $block->getType() != Block::TYPE_AUTO ) {
1274  # Show log extract if the user is currently blocked
1276  $outputPage,
1277  'block',
1278  MWNamespace::getCanonicalName( NS_USER ) . ':' . $block->getTarget(),
1279  '',
1280  [
1281  'lim' => 1,
1282  'showIfEmpty' => false,
1283  'msgKey' => [
1284  'blocked-notice-logextract',
1285  $user->getName() # Support GENDER in notice
1286  ]
1287  ]
1288  );
1289  $validUserPage = !$title->isSubpage();
1290  } else {
1291  $validUserPage = !$title->isSubpage();
1292  }
1293  }
1294 
1295  Hooks::run( 'ShowMissingArticle', [ $this ] );
1296 
1297  # Show delete and move logs if there were any such events.
1298  # The logging query can DOS the site when bots/crawlers cause 404 floods,
1299  # so be careful showing this. 404 pages must be cheap as they are hard to cache.
1301  $key = wfMemcKey( 'page-recent-delete', md5( $title->getPrefixedText() ) );
1302  $loggedIn = $this->getContext()->getUser()->isLoggedIn();
1303  if ( $loggedIn || $cache->get( $key ) ) {
1304  $logTypes = [ 'delete', 'move' ];
1305  $conds = [ "log_action != 'revision'" ];
1306  // Give extensions a chance to hide their (unrelated) log entries
1307  Hooks::run( 'Article::MissingArticleConditions', [ &$conds, $logTypes ] );
1309  $outputPage,
1310  $logTypes,
1311  $title,
1312  '',
1313  [
1314  'lim' => 10,
1315  'conds' => $conds,
1316  'showIfEmpty' => false,
1317  'msgKey' => [ $loggedIn
1318  ? 'moveddeleted-notice'
1319  : 'moveddeleted-notice-recent'
1320  ]
1321  ]
1322  );
1323  }
1324 
1325  if ( !$this->mPage->hasViewableContent() && $wgSend404Code && !$validUserPage ) {
1326  // If there's no backing content, send a 404 Not Found
1327  // for better machine handling of broken links.
1328  $this->getContext()->getRequest()->response()->statusHeader( 404 );
1329  }
1330 
1331  // Also apply the robot policy for nonexisting pages (even if a 404 was used for sanity)
1332  $policy = $this->getRobotPolicy( 'view' );
1333  $outputPage->setIndexPolicy( $policy['index'] );
1334  $outputPage->setFollowPolicy( $policy['follow'] );
1335 
1336  $hookResult = Hooks::run( 'BeforeDisplayNoArticleText', [ $this ] );
1337 
1338  if ( !$hookResult ) {
1339  return;
1340  }
1341 
1342  # Show error message
1343  $oldid = $this->getOldID();
1344  if ( !$oldid && $title->getNamespace() === NS_MEDIAWIKI && $title->hasSourceText() ) {
1345  $outputPage->addParserOutput( $this->getContentObject()->getParserOutput( $title ) );
1346  } else {
1347  if ( $oldid ) {
1348  $text = wfMessage( 'missing-revision', $oldid )->plain();
1349  } elseif ( $title->quickUserCan( 'create', $this->getContext()->getUser() )
1350  && $title->quickUserCan( 'edit', $this->getContext()->getUser() )
1351  ) {
1352  $message = $this->getContext()->getUser()->isLoggedIn() ? 'noarticletext' : 'noarticletextanon';
1353  $text = wfMessage( $message )->plain();
1354  } else {
1355  $text = wfMessage( 'noarticletext-nopermission' )->plain();
1356  }
1357 
1358  $dir = $this->getContext()->getLanguage()->getDir();
1359  $lang = $this->getContext()->getLanguage()->getCode();
1360  $outputPage->addWikiText( Xml::openElement( 'div', [
1361  'class' => "noarticletext mw-content-$dir",
1362  'dir' => $dir,
1363  'lang' => $lang,
1364  ] ) . "\n$text\n</div>" );
1365  }
1366  }
1367 
1374  public function showDeletedRevisionHeader() {
1375  if ( !$this->mRevision->isDeleted( Revision::DELETED_TEXT ) ) {
1376  // Not deleted
1377  return true;
1378  }
1379 
1380  $outputPage = $this->getContext()->getOutput();
1381  $user = $this->getContext()->getUser();
1382  // If the user is not allowed to see it...
1383  if ( !$this->mRevision->userCan( Revision::DELETED_TEXT, $user ) ) {
1384  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1385  'rev-deleted-text-permission' );
1386 
1387  return false;
1388  // If the user needs to confirm that they want to see it...
1389  } elseif ( $this->getContext()->getRequest()->getInt( 'unhide' ) != 1 ) {
1390  # Give explanation and add a link to view the revision...
1391  $oldid = intval( $this->getOldID() );
1392  $link = $this->getTitle()->getFullURL( "oldid={$oldid}&unhide=1" );
1393  $msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ?
1394  'rev-suppressed-text-unhide' : 'rev-deleted-text-unhide';
1395  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1396  [ $msg, $link ] );
1397 
1398  return false;
1399  // We are allowed to see...
1400  } else {
1401  $msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ?
1402  'rev-suppressed-text-view' : 'rev-deleted-text-view';
1403  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n", $msg );
1404 
1405  return true;
1406  }
1407  }
1408 
1417  public function setOldSubtitle( $oldid = 0 ) {
1418  // Avoid PHP 7.1 warning of passing $this by reference
1419  $articlePage = $this;
1420 
1421  if ( !Hooks::run( 'DisplayOldSubtitle', [ &$articlePage, &$oldid ] ) ) {
1422  return;
1423  }
1424 
1425  $context = $this->getContext();
1426  $unhide = $context->getRequest()->getInt( 'unhide' ) == 1;
1427 
1428  # Cascade unhide param in links for easy deletion browsing
1429  $extraParams = [];
1430  if ( $unhide ) {
1431  $extraParams['unhide'] = 1;
1432  }
1433 
1434  if ( $this->mRevision && $this->mRevision->getId() === $oldid ) {
1435  $revision = $this->mRevision;
1436  } else {
1437  $revision = Revision::newFromId( $oldid );
1438  }
1439 
1440  $timestamp = $revision->getTimestamp();
1441 
1442  $current = ( $oldid == $this->mPage->getLatest() );
1443  $language = $context->getLanguage();
1444  $user = $context->getUser();
1445 
1446  $td = $language->userTimeAndDate( $timestamp, $user );
1447  $tddate = $language->userDate( $timestamp, $user );
1448  $tdtime = $language->userTime( $timestamp, $user );
1449 
1450  # Show user links if allowed to see them. If hidden, then show them only if requested...
1451  $userlinks = Linker::revUserTools( $revision, !$unhide );
1452 
1453  $infomsg = $current && !$context->msg( 'revision-info-current' )->isDisabled()
1454  ? 'revision-info-current'
1455  : 'revision-info';
1456 
1457  $outputPage = $context->getOutput();
1458  $outputPage->addSubtitle( "<div id=\"mw-{$infomsg}\">" .
1459  $context->msg( $infomsg, $td )
1460  ->rawParams( $userlinks )
1461  ->params( $revision->getId(), $tddate, $tdtime, $revision->getUserText() )
1462  ->rawParams( Linker::revComment( $revision, true, true ) )
1463  ->parse() .
1464  "</div>"
1465  );
1466 
1467  $lnk = $current
1468  ? $context->msg( 'currentrevisionlink' )->escaped()
1470  $this->getTitle(),
1471  $context->msg( 'currentrevisionlink' )->escaped(),
1472  [],
1473  $extraParams
1474  );
1475  $curdiff = $current
1476  ? $context->msg( 'diff' )->escaped()
1478  $this->getTitle(),
1479  $context->msg( 'diff' )->escaped(),
1480  [],
1481  [
1482  'diff' => 'cur',
1483  'oldid' => $oldid
1484  ] + $extraParams
1485  );
1486  $prev = $this->getTitle()->getPreviousRevisionID( $oldid );
1487  $prevlink = $prev
1489  $this->getTitle(),
1490  $context->msg( 'previousrevision' )->escaped(),
1491  [],
1492  [
1493  'direction' => 'prev',
1494  'oldid' => $oldid
1495  ] + $extraParams
1496  )
1497  : $context->msg( 'previousrevision' )->escaped();
1498  $prevdiff = $prev
1500  $this->getTitle(),
1501  $context->msg( 'diff' )->escaped(),
1502  [],
1503  [
1504  'diff' => 'prev',
1505  'oldid' => $oldid
1506  ] + $extraParams
1507  )
1508  : $context->msg( 'diff' )->escaped();
1509  $nextlink = $current
1510  ? $context->msg( 'nextrevision' )->escaped()
1512  $this->getTitle(),
1513  $context->msg( 'nextrevision' )->escaped(),
1514  [],
1515  [
1516  'direction' => 'next',
1517  'oldid' => $oldid
1518  ] + $extraParams
1519  );
1520  $nextdiff = $current
1521  ? $context->msg( 'diff' )->escaped()
1523  $this->getTitle(),
1524  $context->msg( 'diff' )->escaped(),
1525  [],
1526  [
1527  'diff' => 'next',
1528  'oldid' => $oldid
1529  ] + $extraParams
1530  );
1531 
1532  $cdel = Linker::getRevDeleteLink( $user, $revision, $this->getTitle() );
1533  if ( $cdel !== '' ) {
1534  $cdel .= ' ';
1535  }
1536 
1537  $outputPage->addSubtitle( "<div id=\"mw-revision-nav\">" . $cdel .
1538  $context->msg( 'revision-nav' )->rawParams(
1539  $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff
1540  )->escaped() . "</div>" );
1541  }
1542 
1554  public function viewRedirect( $target, $appendSubtitle = true, $forceKnown = false ) {
1555  $lang = $this->getTitle()->getPageLanguage();
1556  $out = $this->getContext()->getOutput();
1557  if ( $appendSubtitle ) {
1558  $out->addSubtitle( wfMessage( 'redirectpagesub' ) );
1559  }
1560  $out->addModuleStyles( 'mediawiki.action.view.redirectPage' );
1561  return static::getRedirectHeaderHtml( $lang, $target, $forceKnown );
1562  }
1563 
1576  public static function getRedirectHeaderHtml( Language $lang, $target, $forceKnown = false ) {
1577  if ( !is_array( $target ) ) {
1578  $target = [ $target ];
1579  }
1580 
1581  $html = '<ul class="redirectText">';
1583  foreach ( $target as $title ) {
1584  $html .= '<li>' . Linker::link(
1585  $title,
1586  htmlspecialchars( $title->getFullText() ),
1587  [],
1588  // Make sure wiki page redirects are not followed
1589  $title->isRedirect() ? [ 'redirect' => 'no' ] : [],
1590  ( $forceKnown ? [ 'known', 'noclasses' ] : [] )
1591  ) . '</li>';
1592  }
1593  $html .= '</ul>';
1594 
1595  $redirectToText = wfMessage( 'redirectto' )->inLanguage( $lang )->escaped();
1596 
1597  return '<div class="redirectMsg">' .
1598  '<p>' . $redirectToText . '</p>' .
1599  $html .
1600  '</div>';
1601  }
1602 
1611  public function addHelpLink( $to, $overrideBaseUrl = false ) {
1612  $msg = wfMessage(
1613  'namespace-' . $this->getTitle()->getNamespace() . '-helppage'
1614  );
1615 
1616  $out = $this->getContext()->getOutput();
1617  if ( !$msg->isDisabled() ) {
1618  $helpUrl = Skin::makeUrl( $msg->plain() );
1619  $out->addHelpLink( $helpUrl, true );
1620  } else {
1621  $out->addHelpLink( $to, $overrideBaseUrl );
1622  }
1623  }
1624 
1628  public function render() {
1629  $this->getContext()->getRequest()->response()->header( 'X-Robots-Tag: noindex' );
1630  $this->getContext()->getOutput()->setArticleBodyOnly( true );
1631  $this->getContext()->getOutput()->enableSectionEditLinks( false );
1632  $this->view();
1633  }
1634 
1638  public function protect() {
1639  $form = new ProtectionForm( $this );
1640  $form->execute();
1641  }
1642 
1646  public function unprotect() {
1647  $this->protect();
1648  }
1649 
1653  public function delete() {
1654  # This code desperately needs to be totally rewritten
1655 
1656  $title = $this->getTitle();
1657  $context = $this->getContext();
1658  $user = $context->getUser();
1659  $request = $context->getRequest();
1660 
1661  # Check permissions
1662  $permissionErrors = $title->getUserPermissionsErrors( 'delete', $user );
1663  if ( count( $permissionErrors ) ) {
1664  throw new PermissionsError( 'delete', $permissionErrors );
1665  }
1666 
1667  # Read-only check...
1668  if ( wfReadOnly() ) {
1669  throw new ReadOnlyError;
1670  }
1671 
1672  # Better double-check that it hasn't been deleted yet!
1673  $this->mPage->loadPageData(
1675  );
1676  if ( !$this->mPage->exists() ) {
1677  $deleteLogPage = new LogPage( 'delete' );
1678  $outputPage = $context->getOutput();
1679  $outputPage->setPageTitle( $context->msg( 'cannotdelete-title', $title->getPrefixedText() ) );
1680  $outputPage->wrapWikiMsg( "<div class=\"error mw-error-cannotdelete\">\n$1\n</div>",
1681  [ 'cannotdelete', wfEscapeWikiText( $title->getPrefixedText() ) ]
1682  );
1683  $outputPage->addHTML(
1684  Xml::element( 'h2', null, $deleteLogPage->getName()->text() )
1685  );
1687  $outputPage,
1688  'delete',
1689  $title
1690  );
1691 
1692  return;
1693  }
1694 
1695  $deleteReasonList = $request->getText( 'wpDeleteReasonList', 'other' );
1696  $deleteReason = $request->getText( 'wpReason' );
1697 
1698  if ( $deleteReasonList == 'other' ) {
1699  $reason = $deleteReason;
1700  } elseif ( $deleteReason != '' ) {
1701  // Entry from drop down menu + additional comment
1702  $colonseparator = wfMessage( 'colon-separator' )->inContentLanguage()->text();
1703  $reason = $deleteReasonList . $colonseparator . $deleteReason;
1704  } else {
1705  $reason = $deleteReasonList;
1706  }
1707 
1708  if ( $request->wasPosted() && $user->matchEditToken( $request->getVal( 'wpEditToken' ),
1709  [ 'delete', $this->getTitle()->getPrefixedText() ] )
1710  ) {
1711  # Flag to hide all contents of the archived revisions
1712  $suppress = $request->getVal( 'wpSuppress' ) && $user->isAllowed( 'suppressrevision' );
1713 
1714  $this->doDelete( $reason, $suppress );
1715 
1716  WatchAction::doWatchOrUnwatch( $request->getCheck( 'wpWatch' ), $title, $user );
1717 
1718  return;
1719  }
1720 
1721  // Generate deletion reason
1722  $hasHistory = false;
1723  if ( !$reason ) {
1724  try {
1725  $reason = $this->generateReason( $hasHistory );
1726  } catch ( Exception $e ) {
1727  # if a page is horribly broken, we still want to be able to
1728  # delete it. So be lenient about errors here.
1729  wfDebug( "Error while building auto delete summary: $e" );
1730  $reason = '';
1731  }
1732  }
1733 
1734  // If the page has a history, insert a warning
1735  if ( $hasHistory ) {
1736  $title = $this->getTitle();
1737 
1738  // The following can use the real revision count as this is only being shown for users
1739  // that can delete this page.
1740  // This, as a side-effect, also makes sure that the following query isn't being run for
1741  // pages with a larger history, unless the user has the 'bigdelete' right
1742  // (and is about to delete this page).
1743  $dbr = wfGetDB( DB_SLAVE );
1744  $revisions = $edits = (int)$dbr->selectField(
1745  'revision',
1746  'COUNT(rev_page)',
1747  [ 'rev_page' => $title->getArticleID() ],
1748  __METHOD__
1749  );
1750 
1751  // @todo FIXME: i18n issue/patchwork message
1752  $context->getOutput()->addHTML(
1753  '<strong class="mw-delete-warning-revisions">' .
1754  $context->msg( 'historywarning' )->numParams( $revisions )->parse() .
1755  $context->msg( 'word-separator' )->escaped() . Linker::linkKnown( $title,
1756  $context->msg( 'history' )->escaped(),
1757  [],
1758  [ 'action' => 'history' ] ) .
1759  '</strong>'
1760  );
1761 
1762  if ( $title->isBigDeletion() ) {
1763  global $wgDeleteRevisionsLimit;
1764  $context->getOutput()->wrapWikiMsg( "<div class='error'>\n$1\n</div>\n",
1765  [
1766  'delete-warning-toobig',
1767  $context->getLanguage()->formatNum( $wgDeleteRevisionsLimit )
1768  ]
1769  );
1770  }
1771  }
1772 
1773  $this->confirmDelete( $reason );
1774  }
1775 
1781  public function confirmDelete( $reason ) {
1782  wfDebug( "Article::confirmDelete\n" );
1783 
1784  $title = $this->getTitle();
1785  $ctx = $this->getContext();
1786  $outputPage = $ctx->getOutput();
1787  $useMediaWikiUIEverywhere = $ctx->getConfig()->get( 'UseMediaWikiUIEverywhere' );
1788  $outputPage->setPageTitle( wfMessage( 'delete-confirm', $title->getPrefixedText() ) );
1789  $outputPage->addBacklinkSubtitle( $title );
1790  $outputPage->setRobotPolicy( 'noindex,nofollow' );
1791  $backlinkCache = $title->getBacklinkCache();
1792  if ( $backlinkCache->hasLinks( 'pagelinks' ) || $backlinkCache->hasLinks( 'templatelinks' ) ) {
1793  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1794  'deleting-backlinks-warning' );
1795  }
1796  $outputPage->addWikiMsg( 'confirmdeletetext' );
1797 
1798  Hooks::run( 'ArticleConfirmDelete', [ $this, $outputPage, &$reason ] );
1799 
1800  $user = $this->getContext()->getUser();
1801 
1802  if ( $user->isAllowed( 'suppressrevision' ) ) {
1803  $suppress = Html::openElement( 'div', [ 'id' => 'wpDeleteSuppressRow' ] ) .
1804  Xml::checkLabel( wfMessage( 'revdelete-suppress' )->text(),
1805  'wpSuppress', 'wpSuppress', false, [ 'tabindex' => '4' ] ) .
1806  Html::closeElement( 'div' );
1807  } else {
1808  $suppress = '';
1809  }
1810  $checkWatch = $user->getBoolOption( 'watchdeletion' ) || $user->isWatched( $title );
1811 
1812  $form = Html::openElement( 'form', [ 'method' => 'post',
1813  'action' => $title->getLocalURL( 'action=delete' ), 'id' => 'deleteconfirm' ] ) .
1814  Html::openElement( 'fieldset', [ 'id' => 'mw-delete-table' ] ) .
1815  Html::element( 'legend', null, wfMessage( 'delete-legend' )->text() ) .
1816  Html::openElement( 'div', [ 'id' => 'mw-deleteconfirm-table' ] ) .
1817  Html::openElement( 'div', [ 'id' => 'wpDeleteReasonListRow' ] ) .
1818  Html::label( wfMessage( 'deletecomment' )->text(), 'wpDeleteReasonList' ) .
1819  '&nbsp;' .
1821  'wpDeleteReasonList',
1822  wfMessage( 'deletereason-dropdown' )->inContentLanguage()->text(),
1823  wfMessage( 'deletereasonotherlist' )->inContentLanguage()->text(),
1824  '',
1825  'wpReasonDropDown',
1826  1
1827  ) .
1828  Html::closeElement( 'div' ) .
1829  Html::openElement( 'div', [ 'id' => 'wpDeleteReasonRow' ] ) .
1830  Html::label( wfMessage( 'deleteotherreason' )->text(), 'wpReason' ) .
1831  '&nbsp;' .
1832  Html::input( 'wpReason', $reason, 'text', [
1833  'size' => '60',
1834  'maxlength' => '255',
1835  'tabindex' => '2',
1836  'id' => 'wpReason',
1837  'class' => 'mw-ui-input-inline',
1838  'autofocus'
1839  ] ) .
1840  Html::closeElement( 'div' );
1841 
1842  # Disallow watching if user is not logged in
1843  if ( $user->isLoggedIn() ) {
1844  $form .=
1845  Xml::checkLabel( wfMessage( 'watchthis' )->text(),
1846  'wpWatch', 'wpWatch', $checkWatch, [ 'tabindex' => '3' ] );
1847  }
1848 
1849  $form .=
1850  Html::openElement( 'div' ) .
1851  $suppress .
1852  Xml::submitButton( wfMessage( 'deletepage' )->text(),
1853  [
1854  'name' => 'wpConfirmB',
1855  'id' => 'wpConfirmB',
1856  'tabindex' => '5',
1857  'class' => $useMediaWikiUIEverywhere ? 'mw-ui-button mw-ui-destructive' : '',
1858  ]
1859  ) .
1860  Html::closeElement( 'div' ) .
1861  Html::closeElement( 'div' ) .
1862  Xml::closeElement( 'fieldset' ) .
1863  Html::hidden(
1864  'wpEditToken',
1865  $user->getEditToken( [ 'delete', $title->getPrefixedText() ] )
1866  ) .
1867  Xml::closeElement( 'form' );
1868 
1869  if ( $user->isAllowed( 'editinterface' ) ) {
1871  $ctx->msg( 'deletereason-dropdown' )->inContentLanguage()->getTitle(),
1872  wfMessage( 'delete-edit-reasonlist' )->escaped(),
1873  [],
1874  [ 'action' => 'edit' ]
1875  );
1876  $form .= '<p class="mw-delete-editreasons">' . $link . '</p>';
1877  }
1878 
1879  $outputPage->addHTML( $form );
1880 
1881  $deleteLogPage = new LogPage( 'delete' );
1882  $outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) );
1883  LogEventsList::showLogExtract( $outputPage, 'delete', $title );
1884  }
1885 
1891  public function doDelete( $reason, $suppress = false ) {
1892  $error = '';
1893  $context = $this->getContext();
1894  $outputPage = $context->getOutput();
1895  $user = $context->getUser();
1896  $status = $this->mPage->doDeleteArticleReal( $reason, $suppress, 0, true, $error, $user );
1897 
1898  if ( $status->isGood() ) {
1899  $deleted = $this->getTitle()->getPrefixedText();
1900 
1901  $outputPage->setPageTitle( wfMessage( 'actioncomplete' ) );
1902  $outputPage->setRobotPolicy( 'noindex,nofollow' );
1903 
1904  $loglink = '[[Special:Log/delete|' . wfMessage( 'deletionlog' )->text() . ']]';
1905 
1906  $outputPage->addWikiMsg( 'deletedtext', wfEscapeWikiText( $deleted ), $loglink );
1907 
1908  Hooks::run( 'ArticleDeleteAfterSuccess', [ $this->getTitle(), $outputPage ] );
1909 
1910  $outputPage->returnToMain( false );
1911  } else {
1912  $outputPage->setPageTitle(
1913  wfMessage( 'cannotdelete-title',
1914  $this->getTitle()->getPrefixedText() )
1915  );
1916 
1917  if ( $error == '' ) {
1918  $outputPage->addWikiText(
1919  "<div class=\"error mw-error-cannotdelete\">\n" . $status->getWikiText() . "\n</div>"
1920  );
1921  $deleteLogPage = new LogPage( 'delete' );
1922  $outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) );
1923 
1925  $outputPage,
1926  'delete',
1927  $this->getTitle()
1928  );
1929  } else {
1930  $outputPage->addHTML( $error );
1931  }
1932  }
1933  }
1934 
1935  /* Caching functions */
1936 
1944  protected function tryFileCache() {
1945  static $called = false;
1946 
1947  if ( $called ) {
1948  wfDebug( "Article::tryFileCache(): called twice!?\n" );
1949  return false;
1950  }
1951 
1952  $called = true;
1953  if ( $this->isFileCacheable() ) {
1954  $cache = new HTMLFileCache( $this->getTitle(), 'view' );
1955  if ( $cache->isCacheGood( $this->mPage->getTouched() ) ) {
1956  wfDebug( "Article::tryFileCache(): about to load file\n" );
1957  $cache->loadFromFileCache( $this->getContext() );
1958  return true;
1959  } else {
1960  wfDebug( "Article::tryFileCache(): starting buffer\n" );
1961  ob_start( [ &$cache, 'saveToFileCache' ] );
1962  }
1963  } else {
1964  wfDebug( "Article::tryFileCache(): not cacheable\n" );
1965  }
1966 
1967  return false;
1968  }
1969 
1974  public function isFileCacheable() {
1975  $cacheable = false;
1976 
1977  if ( HTMLFileCache::useFileCache( $this->getContext() ) ) {
1978  $cacheable = $this->mPage->getId()
1979  && !$this->mRedirectedFrom && !$this->getTitle()->isRedirect();
1980  // Extension may have reason to disable file caching on some pages.
1981  if ( $cacheable ) {
1982  // Avoid PHP 7.1 warning of passing $this by reference
1983  $articlePage = $this;
1984  $cacheable = Hooks::run( 'IsFileCacheable', [ &$articlePage ] );
1985  }
1986  }
1987 
1988  return $cacheable;
1989  }
1990 
2004  public function getParserOutput( $oldid = null, User $user = null ) {
2005  // XXX: bypasses mParserOptions and thus setParserOptions()
2006 
2007  if ( $user === null ) {
2008  $parserOptions = $this->getParserOptions();
2009  } else {
2010  $parserOptions = $this->mPage->makeParserOptions( $user );
2011  }
2012 
2013  return $this->mPage->getParserOutput( $parserOptions, $oldid );
2014  }
2015 
2023  if ( $this->mParserOptions ) {
2024  throw new MWException( "can't change parser options after they have already been set" );
2025  }
2026 
2027  // clone, so if $options is modified later, it doesn't confuse the parser cache.
2028  $this->mParserOptions = clone $options;
2029  }
2030 
2035  public function getParserOptions() {
2036  if ( !$this->mParserOptions ) {
2037  $this->mParserOptions = $this->mPage->makeParserOptions( $this->getContext() );
2038  }
2039  // Clone to allow modifications of the return value without affecting cache
2040  return clone $this->mParserOptions;
2041  }
2042 
2049  public function setContext( $context ) {
2050  $this->mContext = $context;
2051  }
2052 
2059  public function getContext() {
2060  if ( $this->mContext instanceof IContextSource ) {
2061  return $this->mContext;
2062  } else {
2063  wfDebug( __METHOD__ . " called and \$mContext is null. " .
2064  "Return RequestContext::getMain(); for sanity\n" );
2065  return RequestContext::getMain();
2066  }
2067  }
2068 
2076  public function __get( $fname ) {
2077  if ( property_exists( $this->mPage, $fname ) ) {
2078  # wfWarn( "Access to raw $fname field " . __CLASS__ );
2079  return $this->mPage->$fname;
2080  }
2081  trigger_error( 'Inaccessible property via __get(): ' . $fname, E_USER_NOTICE );
2082  }
2083 
2091  public function __set( $fname, $fvalue ) {
2092  if ( property_exists( $this->mPage, $fname ) ) {
2093  # wfWarn( "Access to raw $fname field of " . __CLASS__ );
2094  $this->mPage->$fname = $fvalue;
2095  // Note: extensions may want to toss on new fields
2096  } elseif ( !in_array( $fname, [ 'mContext', 'mPage' ] ) ) {
2097  $this->mPage->$fname = $fvalue;
2098  } else {
2099  trigger_error( 'Inaccessible property via __set(): ' . $fname, E_USER_NOTICE );
2100  }
2101  }
2102 
2107  public function checkFlags( $flags ) {
2108  return $this->mPage->checkFlags( $flags );
2109  }
2110 
2115  public function checkTouched() {
2116  return $this->mPage->checkTouched();
2117  }
2118 
2123  public function clearPreparedEdit() {
2124  $this->mPage->clearPreparedEdit();
2125  }
2126 
2131  public function doDeleteArticleReal(
2132  $reason, $suppress = false, $u1 = null, $u2 = null, &$error = '', User $user = null
2133  ) {
2134  return $this->mPage->doDeleteArticleReal(
2135  $reason, $suppress, $u1, $u2, $error, $user
2136  );
2137  }
2138 
2143  public function doDeleteUpdates( $id, Content $content = null ) {
2144  return $this->mPage->doDeleteUpdates( $id, $content );
2145  }
2146 
2151  public function doEdit( $text, $summary, $flags = 0, $baseRevId = false, $user = null ) {
2152  ContentHandler::deprecated( __METHOD__, '1.21' );
2153  return $this->mPage->doEdit( $text, $summary, $flags, $baseRevId, $user );
2154  }
2155 
2160  public function doEditContent( Content $content, $summary, $flags = 0, $baseRevId = false,
2161  User $user = null, $serialFormat = null
2162  ) {
2163  return $this->mPage->doEditContent( $content, $summary, $flags, $baseRevId,
2164  $user, $serialFormat
2165  );
2166  }
2167 
2172  public function doEditUpdates( Revision $revision, User $user, array $options = [] ) {
2173  return $this->mPage->doEditUpdates( $revision, $user, $options );
2174  }
2175 
2180  public function doPurge() {
2181  return $this->mPage->doPurge();
2182  }
2183 
2188  public function doQuickEditContent(
2189  Content $content, User $user, $comment = '', $minor = false, $serialFormat = null
2190  ) {
2191  return $this->mPage->doQuickEditContent(
2192  $content, $user, $comment, $minor, $serialFormat
2193  );
2194  }
2195 
2200  public function doViewUpdates( User $user, $oldid = 0 ) {
2201  $this->mPage->doViewUpdates( $user, $oldid );
2202  }
2203 
2208  public function exists() {
2209  return $this->mPage->exists();
2210  }
2211 
2216  public function followRedirect() {
2217  return $this->mPage->followRedirect();
2218  }
2219 
2224  public function getActionOverrides() {
2225  return $this->mPage->getActionOverrides();
2226  }
2227 
2232  public function getAutoDeleteReason( &$hasHistory ) {
2233  return $this->mPage->getAutoDeleteReason( $hasHistory );
2234  }
2235 
2240  public function getCategories() {
2241  return $this->mPage->getCategories();
2242  }
2243 
2248  public function getComment( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2249  return $this->mPage->getComment( $audience, $user );
2250  }
2251 
2256  public function getContentHandler() {
2257  return $this->mPage->getContentHandler();
2258  }
2259 
2264  public function getContentModel() {
2265  return $this->mPage->getContentModel();
2266  }
2267 
2272  public function getContributors() {
2273  return $this->mPage->getContributors();
2274  }
2275 
2280  public function getCreator( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2281  return $this->mPage->getCreator( $audience, $user );
2282  }
2283 
2288  public function getDeletionUpdates( Content $content = null ) {
2289  return $this->mPage->getDeletionUpdates( $content );
2290  }
2291 
2296  public function getHiddenCategories() {
2297  return $this->mPage->getHiddenCategories();
2298  }
2299 
2304  public function getId() {
2305  return $this->mPage->getId();
2306  }
2307 
2312  public function getLatest() {
2313  return $this->mPage->getLatest();
2314  }
2315 
2320  public function getLinksTimestamp() {
2321  return $this->mPage->getLinksTimestamp();
2322  }
2323 
2328  public function getMinorEdit() {
2329  return $this->mPage->getMinorEdit();
2330  }
2331 
2336  public function getOldestRevision() {
2337  return $this->mPage->getOldestRevision();
2338  }
2339 
2344  public function getRedirectTarget() {
2345  return $this->mPage->getRedirectTarget();
2346  }
2347 
2352  public function getRedirectURL( $rt ) {
2353  return $this->mPage->getRedirectURL( $rt );
2354  }
2355 
2360  public function getRevision() {
2361  return $this->mPage->getRevision();
2362  }
2363 
2368  public function getText( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2369  ContentHandler::deprecated( __METHOD__, '1.21' );
2370  return $this->mPage->getText( $audience, $user );
2371  }
2372 
2377  public function getTimestamp() {
2378  return $this->mPage->getTimestamp();
2379  }
2380 
2385  public function getTouched() {
2386  return $this->mPage->getTouched();
2387  }
2388 
2393  public function getUndoContent( Revision $undo, Revision $undoafter = null ) {
2394  return $this->mPage->getUndoContent( $undo, $undoafter );
2395  }
2396 
2401  public function getUser( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2402  return $this->mPage->getUser( $audience, $user );
2403  }
2404 
2409  public function getUserText( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2410  return $this->mPage->getUserText( $audience, $user );
2411  }
2412 
2417  public function hasViewableContent() {
2418  return $this->mPage->hasViewableContent();
2419  }
2420 
2425  public function insertOn( $dbw, $pageId = null ) {
2426  return $this->mPage->insertOn( $dbw, $pageId );
2427  }
2428 
2433  public function insertProtectNullRevision( $revCommentMsg, array $limit,
2434  array $expiry, $cascade, $reason, $user = null
2435  ) {
2436  return $this->mPage->insertProtectNullRevision( $revCommentMsg, $limit,
2437  $expiry, $cascade, $reason, $user
2438  );
2439  }
2440 
2445  public function insertRedirect() {
2446  return $this->mPage->insertRedirect();
2447  }
2448 
2453  public function insertRedirectEntry( Title $rt, $oldLatest = null ) {
2454  return $this->mPage->insertRedirectEntry( $rt, $oldLatest );
2455  }
2456 
2461  public function isCountable( $editInfo = false ) {
2462  return $this->mPage->isCountable( $editInfo );
2463  }
2464 
2469  public function isRedirect() {
2470  return $this->mPage->isRedirect();
2471  }
2472 
2477  public function loadFromRow( $data, $from ) {
2478  return $this->mPage->loadFromRow( $data, $from );
2479  }
2480 
2485  public function loadPageData( $from = 'fromdb' ) {
2486  $this->mPage->loadPageData( $from );
2487  }
2488 
2493  public function lockAndGetLatest() {
2494  return $this->mPage->lockAndGetLatest();
2495  }
2496 
2501  public function makeParserOptions( $context ) {
2502  return $this->mPage->makeParserOptions( $context );
2503  }
2504 
2509  public function pageDataFromId( $dbr, $id, $options = [] ) {
2510  return $this->mPage->pageDataFromId( $dbr, $id, $options );
2511  }
2512 
2517  public function pageDataFromTitle( $dbr, $title, $options = [] ) {
2518  return $this->mPage->pageDataFromTitle( $dbr, $title, $options );
2519  }
2520 
2525  public function prepareContentForEdit(
2526  Content $content, $revision = null, User $user = null,
2527  $serialFormat = null, $useCache = true
2528  ) {
2529  return $this->mPage->prepareContentForEdit(
2530  $content, $revision, $user,
2531  $serialFormat, $useCache
2532  );
2533  }
2534 
2539  public function prepareTextForEdit( $text, $revid = null, User $user = null ) {
2540  return $this->mPage->prepareTextForEdit( $text, $revid, $user );
2541  }
2542 
2547  public function protectDescription( array $limit, array $expiry ) {
2548  return $this->mPage->protectDescription( $limit, $expiry );
2549  }
2550 
2555  public function protectDescriptionLog( array $limit, array $expiry ) {
2556  return $this->mPage->protectDescriptionLog( $limit, $expiry );
2557  }
2558 
2563  public function replaceSectionAtRev( $sectionId, Content $sectionContent,
2564  $sectionTitle = '', $baseRevId = null
2565  ) {
2566  return $this->mPage->replaceSectionAtRev( $sectionId, $sectionContent,
2567  $sectionTitle, $baseRevId
2568  );
2569  }
2570 
2575  public function replaceSectionContent(
2576  $sectionId, Content $sectionContent, $sectionTitle = '', $edittime = null
2577  ) {
2578  return $this->mPage->replaceSectionContent(
2579  $sectionId, $sectionContent, $sectionTitle, $edittime
2580  );
2581  }
2582 
2587  public function setTimestamp( $ts ) {
2588  return $this->mPage->setTimestamp( $ts );
2589  }
2590 
2595  public function shouldCheckParserCache( ParserOptions $parserOptions, $oldId ) {
2596  return $this->mPage->shouldCheckParserCache( $parserOptions, $oldId );
2597  }
2598 
2603  public function supportsSections() {
2604  return $this->mPage->supportsSections();
2605  }
2606 
2612  return $this->mPage->triggerOpportunisticLinksUpdate( $parserOutput );
2613  }
2614 
2619  public function updateCategoryCounts( array $added, array $deleted ) {
2620  return $this->mPage->updateCategoryCounts( $added, $deleted );
2621  }
2622 
2627  public function updateIfNewerOn( $dbw, $revision ) {
2628  return $this->mPage->updateIfNewerOn( $dbw, $revision );
2629  }
2630 
2635  public function updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect = null ) {
2636  return $this->mPage->updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect = null );
2637  }
2638 
2643  public function updateRevisionOn( $dbw, $revision, $lastRevision = null,
2644  $lastRevIsRedirect = null
2645  ) {
2646  return $this->mPage->updateRevisionOn( $dbw, $revision, $lastRevision,
2647  $lastRevIsRedirect
2648  );
2649  }
2650 
2659  public function doUpdateRestrictions( array $limit, array $expiry, &$cascade,
2660  $reason, User $user
2661  ) {
2662  return $this->mPage->doUpdateRestrictions( $limit, $expiry, $cascade, $reason, $user );
2663  }
2664 
2672  public function updateRestrictions( $limit = [], $reason = '',
2673  &$cascade = 0, $expiry = []
2674  ) {
2675  return $this->mPage->doUpdateRestrictions(
2676  $limit,
2677  $expiry,
2678  $cascade,
2679  $reason,
2680  $this->getContext()->getUser()
2681  );
2682  }
2683 
2692  public function doDeleteArticle(
2693  $reason, $suppress = false, $u1 = null, $u2 = null, &$error = ''
2694  ) {
2695  return $this->mPage->doDeleteArticle( $reason, $suppress, $u1, $u2, $error );
2696  }
2697 
2707  public function doRollback( $fromP, $summary, $token, $bot, &$resultDetails, User $user = null ) {
2708  $user = is_null( $user ) ? $this->getContext()->getUser() : $user;
2709  return $this->mPage->doRollback( $fromP, $summary, $token, $bot, $resultDetails, $user );
2710  }
2711 
2720  public function commitRollback( $fromP, $summary, $bot, &$resultDetails, User $guser = null ) {
2721  $guser = is_null( $guser ) ? $this->getContext()->getUser() : $guser;
2722  return $this->mPage->commitRollback( $fromP, $summary, $bot, $resultDetails, $guser );
2723  }
2724 
2729  public function generateReason( &$hasHistory ) {
2730  $title = $this->mPage->getTitle();
2732  return $handler->getAutoDeleteReason( $title, $hasHistory );
2733  }
2734 
2740  public static function selectFields() {
2741  wfDeprecated( __METHOD__, '1.24' );
2742  return WikiPage::selectFields();
2743  }
2744 
2750  public static function onArticleCreate( $title ) {
2751  wfDeprecated( __METHOD__, '1.24' );
2753  }
2754 
2760  public static function onArticleDelete( $title ) {
2761  wfDeprecated( __METHOD__, '1.24' );
2763  }
2764 
2770  public static function onArticleEdit( $title ) {
2771  wfDeprecated( __METHOD__, '1.24' );
2773  }
2774 
2782  public static function getAutosummary( $oldtext, $newtext, $flags ) {
2783  return WikiPage::getAutosummary( $oldtext, $newtext, $flags );
2784  }
2785  // ******
2786 }
pageDataFromId($dbr, $id, $options=[])
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2509
__set($fname, $fvalue)
Use PHP's magic __set handler to handle setting of raw WikiPage fields for backwards compatibility...
Definition: Article.php:2091
static newFromName($name, $validate= 'valid')
Static factory method for creation from username.
Definition: User.php:568
getRedirectTarget()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2344
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:1243
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:1554
static newFromID($id, $flags=0)
Create a new Title from an article ID.
Definition: Title.php:417
static closeElement($element)
Returns "".
Definition: Html.php:306
lockAndGetLatest()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2493
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:3261
static onArticleCreate($title)
Definition: Article.php:2750
getUndoContent(Revision $undo, Revision $undoafter=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2393
doDeleteArticleReal($reason, $suppress=false, $u1=null, $u2=null, &$error= '', User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2131
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:1802
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
Definition: deferred.txt:11
Interface for objects which can provide a MediaWiki context on request.
static isInRCLifespan($timestamp, $tolerance=0)
Check whether the given timestamp is new enough to have a RC row with a given tolerance as the recent...
static revComment(Revision $rev, $local=false, $isPublic=false)
Wrap and format the given revision's comment block, if the current user is allowed to view it...
Definition: Linker.php:1656
WikiPage $mPage
The WikiPage object of this instance.
Definition: Article.php:39
wfGetDB($db, $groups=[], $wiki=false)
Get a Database object.
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output $out
Definition: hooks.txt:766
getRobotPolicy($action, $pOutput=null)
Get the robot policy to be used for the current view.
Definition: Article.php:850
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:1422
updateRestrictions($limit=[], $reason= '', &$cascade=0, $expiry=[])
Definition: Article.php:2672
doEditContent(Content $content, $summary, $flags=0, $baseRevId=false, User $user=null, $serialFormat=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2160
getText($audience=Revision::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2368
getLatest()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2312
setParserOptions(ParserOptions $options)
Override the ParserOptions used to render the primary article wikitext.
Definition: Article.php:2022
static linkKnown($target, $html=null, $customAttribs=[], $query=[], $options=[ 'known', 'noclasses'])
Identical to link(), except $options defaults to 'known'.
Definition: Linker.php:264
magic word the default is to use $key to get the and $key value or $key value text $key value html to format the value $key
Definition: hooks.txt:2325
doEditUpdates(Revision $revision, User $user, array $options=[])
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2172
$context
Definition: load.php:44
static getRevDeleteLink(User $user, Revision $rev, Title $title)
Get a revision-deletion link, or disabled link, or nothing, depending on user permissions & the setti...
Definition: Linker.php:2258
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:2208
supportsSections()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2603
static makeUrl($name, $urlaction= '')
Definition: Skin.php:1084
getContent()
Note that getContent does not follow redirects anymore.
Definition: Article.php:205
doRollback($fromP, $summary, $token, $bot, &$resultDetails, User $user=null)
Definition: Article.php:2707
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:2692
protect()
action=protect handler
Definition: Article.php:1638
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:1422
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException'returning false will NOT prevent logging $e
Definition: hooks.txt:1936
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:2461
Wrapper allowing us to handle a system message as a Content object.
getPage()
Get the WikiPage object of this instance.
Definition: Article.php:176
getParserOutput($oldid=null, User $user=null)
#@-
Definition: Article.php:2004
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:2485
if(!isset($args[0])) $lang
ParserOptions $mParserOptions
ParserOptions object for $wgUser articles.
Definition: Article.php:42
Content $mContentObject
Content of the revision we are working on.
Definition: Article.php:54
Special handling for category description pages, showing pages, subcategories and file that belong to...
doDeleteUpdates($id, Content $content=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2143
adjustDisplayTitle(ParserOutput $pOutput)
Adjust title for pages with displaytitle, -{T|}- or language conversion.
Definition: Article.php:747
static hidden($name, $value, array $attribs=[])
Convenience function to produce an input element with type=hidden.
Definition: Html.php:759
shouldCheckParserCache(ParserOptions $parserOptions, $oldId)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2595
doUpdateRestrictions(array $limit, array $expiry, &$cascade, $reason, User $user)
Definition: Article.php:2659
$comment
Class for viewing MediaWiki article and history.
Definition: Article.php:34
null for the local wiki Added in
Definition: hooks.txt:1422
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:2216
confirmDelete($reason)
Output deletion confirmation dialog.
Definition: Article.php:1781
Class for viewing MediaWiki file description pages.
Definition: ImagePage.php:28
triggerOpportunisticLinksUpdate(ParserOutput $parserOutput)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2611
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:2076
it s the revision text itself In either if gzip is the revision text is gzipped $flags
Definition: hooks.txt:2552
getDeletionUpdates(Content $content=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2288
updateIfNewerOn($dbw, $revision)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2627
clearPreparedEdit()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2123
static getMainStashInstance()
Get the cache object for the main stash.
Represents a title within MediaWiki.
Definition: Title.php:34
when a variable name is used in a it is silently declared as a new local masking the global
Definition: design.txt:93
static useFileCache(IContextSource $context)
Check if pages can be cached for this request/user.
static newFromTitle(LinkTarget $linkTarget, $id=0, $flags=0)
Load either the current, or a specified, revision that's attached to a given link target...
Definition: Revision.php:117
getContributors()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2272
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:2720
showMissingArticle()
Show the error text for a missing article.
Definition: Article.php:1252
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:2547
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:2585
Class to simplify the use of log pages.
Definition: LogPage.php:32
getContext()
Gets the context this Article is executed in.
Definition: Article.php:2059
static closeElement($element)
Shortcut to close an XML element.
Definition: Xml.php:118
isRedirect()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2469
__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:2587
protectDescriptionLog(array $limit, array $expiry)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2555
static onArticleDelete($title)
Definition: Article.php:2760
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:2501
wfEscapeWikiText($text)
Escapes the given text so that it may be output using addWikiText() without any linking, formatting, etc.
wfReadOnly()
Check whether the wiki is in read-only mode.
static getMain()
Static methods.
prepareContentForEdit(Content $content, $revision=null, User $user=null, $serialFormat=null, $useCache=true)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2525
const FOR_PUBLIC
Definition: Revision.php:83
static getCanonicalName($index)
Returns the canonical (English) name for a given index.
$wgUseFileCache
This will cache static pages for non-logged-in users to reduce database traffic on public sites...
static deprecated($func, $version, $component=false)
Logs a deprecation warning, visible if $wgDevelopmentWarnings, but only if self::$enableDeprecationWa...
showViewFooter()
Show the footer section of an ordinary page view.
Definition: Article.php:1050
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:1079
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:1008
getRevisionFetched()
Get the fetched Revision object depending on request parameters or null on failure.
Definition: Article.php:451
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:1008
static runLegacyHooks($event, $args=[], $warn=null)
Call a legacy hook that uses text instead of Content objects.
insertProtectNullRevision($revCommentMsg, array $limit, array $expiry, $cascade, $reason, $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2433
const NS_MEDIA
Definition: Defines.php:58
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:959
static getContentText(Content $content=null)
Convenience function for getting flat text from a Content object.
ParserOutput $mParserOutput
Definition: Article.php:75
$summary
getRevision()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2360
it s the revision text itself In either if gzip is set
Definition: hooks.txt:2552
generateReason(&$hasHistory)
Definition: Article.php:2729
static getRedirectHeaderHtml(Language $lang, $target, $forceKnown=false)
Return the HTML for the top of a redirect page.
Definition: Article.php:1576
insertRedirect()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2445
int null $mOldId
The oldid of the article that is to be shown, 0 for the current revision.
Definition: Article.php:60
static openElement($element, $attribs=null)
This opens an XML element.
Definition: Xml.php:109
hasViewableContent()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2417
getComment($audience=Revision::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2248
Base interface for content objects.
Definition: Content.php:34
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return an< a > element with HTML attributes $attribs and contents $html will be returned If you return $ret will be returned and may include noclasses after processing after in associative array form externallinks including delete and has completed for all link tables whether this was an auto creation default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a set this to the key of the message First element is the message additional optional elements are parameters for the key that are processed with wfMessage() -> params() ->parseAsBlock()-offset Set to overwrite offset parameter in $wgRequest set to ''to unsetoffset-wrap String Wrap the message in html(usually something like"&lt
getTitle()
Get the title object of the article.
Definition: Article.php:166
loadFromRow($data, $from)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2477
$cache
Definition: mcc.php:33
getActionOverrides()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2224
doViewUpdates(User $user, $oldid=0)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2200
getTitle()
Get the title object of the article.
Definition: WikiPage.php:217
const NS_CATEGORY
Definition: Defines.php:84
doPurge()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2180
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:1628
static isIP($name)
Does the string match an anonymous IPv4 address?
Definition: User.php:830
const DELETED_RESTRICTED
Definition: Revision.php:79
do that in ParserLimitReportFormat instead use this to modify the parameters of the image and a DIV can begin in one section and end in another Make sure your code can handle that case gracefully See the EditSectionClearerLink extension for an example zero but section is usually empty & $sectionContent
Definition: hooks.txt:2342
const DB_SLAVE
Definition: Defines.php:47
getCategories()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2240
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:916
replaceSectionAtRev($sectionId, Content $sectionContent, $sectionTitle= '', $baseRevId=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2563
static run($event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:131
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at etc Handles the details of getting and saving to the user table of the and dealing with sessions and cookies OutputPage Encapsulates the entire HTML page that will be sent in response to any server request It is used by calling its functions to add text
Definition: design.txt:12
checkFlags($flags)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2107
const NS_FILE
Definition: Defines.php:76
static makeContent($text, Title $title=null, $modelId=null, $format=null)
Convenience function for creating a Content object from a given textual representation.
$wgRedirectSources
If local interwikis are set up which allow redirects, set this regexp to restrict URLs which will be ...
const TYPE_AUTO
Definition: Block.php:78
presenting them properly to the user as errors is done by the caller return true use this to change the list i e etc $rev
Definition: hooks.txt:1588
static getForTitle(Title $title)
Returns the appropriate ContentHandler singleton for the given title.
updateCategoryCounts(array $added, array $deleted)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2619
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:2256
showCssOrJsPage($showCacheHint=true)
Show a page view for a page formatted as CSS or JavaScript.
Definition: Article.php:814
const NS_MEDIAWIKI
Definition: Defines.php:78
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:246
static link($target, $html=null, $customAttribs=[], $query=[], $options=[])
This function returns an HTML link to the given target.
Definition: Linker.php:195
static onArticleEdit(Title $title, Revision $revision=null)
Purge caches on page update etc.
Definition: WikiPage.php:3324
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:1974
getCreator($audience=Revision::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2280
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:1611
newPage(Title $title)
Definition: Article.php:91
Class representing a MediaWiki article and history.
Definition: WikiPage.php:29
static newFromId($id, $flags=0)
Load a page revision from a given revision ID number.
Definition: Revision.php:99
getOldestRevision()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2336
string $mContent
Text of the revision we are working on.
Definition: Article.php:48
static makeExternalLink($url, $text, $escape=true, $linktype= '', $attribs=[], $title=null)
Make an external link.
Definition: Linker.php:1052
clear()
Clear the object.
Definition: Article.php:183
$from
bool $mContentLoaded
Is the content ($mContent) already loaded?
Definition: Article.php:57
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
getAutoDeleteReason(&$hasHistory)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2232
checkTouched()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2115
updateRevisionOn($dbw, $revision, $lastRevision=null, $lastRevIsRedirect=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2643
static selectFields()
Definition: Article.php:2740
prepareTextForEdit($text, $revid=null, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2539
error also a ContextSource you ll probably need to make sure the header is varied on $request
Definition: hooks.txt:2422
static newFromID($id)
Constructor from a page id.
Definition: Article.php:100
if(!defined( 'MEDIAWIKI')) $fname
This file is not a valid entry point, perform no further processing unless MEDIAWIKI is defined...
Definition: Setup.php:35
getId()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2304
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:2377
static input($name, $value= '', $type= 'text', array $attribs=[])
Convenience function to produce an "" element.
Definition: Html.php:676
pageDataFromTitle($dbr, $title, $options=[])
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2517
getMinorEdit()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2328
view()
This is the default action of the index.php entry point: just view the page of the given title...
Definition: Article.php:474
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:1008
static onArticleDelete(Title $title)
Clears caches when article is deleted.
Definition: WikiPage.php:3277
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:1891
showNamespaceHeader()
Show a header specific to the namespace currently being viewed, like [[MediaWiki:Talkpagetext]].
Definition: Article.php:1036
Show an error when a user tries to do something they do not have the necessary permissions for...
updateRedirectOn($dbw, $redirectTitle, $lastRevIsRedirect=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2635
tryFileCache()
checkLastModified returns true if it has taken care of all output to the client that is necessary for...
Definition: Article.php:1944
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:1008
insertRedirectEntry(Title $rt, $oldLatest=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2453
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:1008
doEdit($text, $summary, $flags=0, $baseRevId=false, $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2151
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:363
setOldSubtitle($oldid=0)
Generate the navigation links when browsing through an article revisions It shows the information as:...
Definition: Article.php:1417
static compare($a, $b)
Callback for usort() to do title sorts by (namespace, title)
Definition: Title.php:780
isCurrent()
Returns true if the currently-referenced revision is the current edit to this page (and it exists)...
Definition: Article.php:435
unprotect()
action=unprotect handler (alias)
Definition: Article.php:1646
Handles the page protection UI and backend.
getTouched()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2385
static onArticleEdit($title)
Definition: Article.php:2770
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:766
getUser($audience=Revision::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2401
static newFromWikiPage(WikiPage $page, IContextSource $context)
Create an Article object of the appropriate class for the given page.
Definition: Article.php:146
fetchContent()
Get text of an article from database Does NOT follow redirects.
Definition: Article.php:326
static selectFields()
Return the list of revision fields that should be selected to create a new page.
Definition: WikiPage.php:266
static getAutosummary($oldtext, $newtext, $flags)
Return an applicable autosummary if one exists for the given edit.
Definition: WikiPage.php:3406
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:2575
setContext($context)
Sets the context this Article is executed in.
Definition: Article.php:2049
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:462
const NS_USER_TALK
Definition: Defines.php:73
static revUserTools($rev, $isPublic=false)
Generate a user tool link cluster if the current user is allowed to view it.
Definition: Linker.php:1252
showDiffPage()
Show a diff page according to current request variables.
Definition: Article.php:760
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:2352
Definition: Block.php:22
static getAutosummary($oldtext, $newtext, $flags)
Definition: Article.php:2782
showDeletedRevisionHeader()
If the revision requested for view is deleted, check permissions.
Definition: Article.php:1374
$wgUseETag
Whether MediaWiki should send an ETag header.
setRedirectedFrom(Title $from)
Tell the page view functions that this view was redirected from another page on the wiki...
Definition: Article.php:157
static newFromConds($conds, $fname=__METHOD__, $dbType=DB_SLAVE)
Find the first recent change matching some specific conditions.
static & makeTitle($ns, $title, $fragment= '', $interwiki= '')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:524
getContentModel()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2264
doQuickEditContent(Content $content, User $user, $comment= '', $minor=false, $serialFormat=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2188
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:2342
getLinksTimestamp()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2320
insertOn($dbw, $pageId=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2425
getParserOptions()
Get parser options suitable for rendering the primary article wikitext.
Definition: Article.php:2035
const RC_LOG
Definition: Defines.php:172
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:2409
showPatrolFooter()
If patrol is possible, output a patrol UI box.
Definition: Article.php:1073
static label($label, $id, array $attribs=[])
Convenience function for generating a label for inputs.
Definition: Html.php:743
static formatRobotPolicy($policy)
Converts a String robot policy into an associative array, to allow merging of several policies using ...
Definition: Article.php:930
getHiddenCategories()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2296