MediaWiki  master
Article.php
Go to the documentation of this file.
1 <?php
30 use Wikimedia\IPUtils;
32 
43 class Article implements Page {
48  protected $mContext;
49 
51  protected $mPage;
52 
58 
68 
75  public $mContentLoaded = false;
76 
82  public $mOldId;
83 
85  public $mRedirectedFrom = null;
86 
88  public $mRedirectUrl = false;
89 
95  public $mRevIdFetched = 0;
96 
105  private $fetchResult = null;
106 
114  public $mRevision = null;
115 
121  public $mParserOutput = null;
122 
128  protected $viewIsRenderAction = false;
129 
133  protected $linkRenderer;
134 
140  public function __construct( Title $title, $oldId = null ) {
141  $this->mOldId = $oldId;
142  $this->mPage = $this->newPage( $title );
143  $this->linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
144  }
145 
150  protected function newPage( Title $title ) {
151  return new WikiPage( $title );
152  }
153 
159  public static function newFromID( $id ) {
160  $t = Title::newFromID( $id );
161  return $t == null ? null : new static( $t );
162  }
163 
171  public static function newFromTitle( $title, IContextSource $context ) {
172  if ( NS_MEDIA == $title->getNamespace() ) {
173  // XXX: This should not be here, but where should it go?
174  $title = Title::makeTitle( NS_FILE, $title->getDBkey() );
175  }
176 
177  $page = null;
178  Hooks::run( 'ArticleFromTitle', [ &$title, &$page, $context ] );
179  if ( !$page ) {
180  switch ( $title->getNamespace() ) {
181  case NS_FILE:
182  $page = new ImagePage( $title );
183  break;
184  case NS_CATEGORY:
185  $page = new CategoryPage( $title );
186  break;
187  default:
188  $page = new Article( $title );
189  }
190  }
191  $page->setContext( $context );
192 
193  return $page;
194  }
195 
203  public static function newFromWikiPage( WikiPage $page, IContextSource $context ) {
204  $article = self::newFromTitle( $page->getTitle(), $context );
205  $article->mPage = $page; // override to keep process cached vars
206  return $article;
207  }
208 
214  public function getRedirectedFrom() {
215  return $this->mRedirectedFrom;
216  }
217 
223  public function setRedirectedFrom( Title $from ) {
224  $this->mRedirectedFrom = $from;
225  }
226 
232  public function getTitle() {
233  return $this->mPage->getTitle();
234  }
235 
242  public function getPage() {
243  return $this->mPage;
244  }
245 
249  public function clear() {
250  $this->mContentLoaded = false;
251 
252  $this->mRedirectedFrom = null; # Title object if set
253  $this->mRevIdFetched = 0;
254  $this->mRedirectUrl = false;
255  $this->mRevision = null;
256  $this->mContentObject = null;
257  $this->fetchResult = null;
258 
259  // TODO hard-deprecate direct access to public fields
260 
261  $this->mPage->clear();
262  }
263 
281  protected function getContentObject() {
282  if ( $this->mPage->getId() === 0 ) {
283  $content = $this->getSubstituteContent();
284  } else {
285  $this->fetchContentObject();
287  }
288 
289  return $content;
290  }
291 
297  private function getSubstituteContent() {
298  # If this is a MediaWiki:x message, then load the messages
299  # and return the message value for x.
300  if ( $this->getTitle()->getNamespace() == NS_MEDIAWIKI ) {
301  $text = $this->getTitle()->getDefaultMessageText();
302  if ( $text === false ) {
303  $text = '';
304  }
305 
306  $content = ContentHandler::makeContent( $text, $this->getTitle() );
307  } else {
308  $message = $this->getContext()->getUser()->isLoggedIn() ? 'noarticletext' : 'noarticletextanon';
309  $content = new MessageContent( $message, null );
310  }
311 
312  return $content;
313  }
314 
324  protected function getEmptyPageParserOutput( ParserOptions $options ) {
325  $content = $this->getSubstituteContent();
326 
327  return $content->getParserOutput( $this->getTitle(), 0, $options );
328  }
329 
337  public function getOldID() {
338  if ( $this->mOldId === null ) {
339  $this->mOldId = $this->getOldIDFromRequest();
340  }
341 
342  return $this->mOldId;
343  }
344 
350  public function getOldIDFromRequest() {
351  $this->mRedirectUrl = false;
352 
353  $request = $this->getContext()->getRequest();
354  $oldid = $request->getIntOrNull( 'oldid' );
355 
356  if ( $oldid === null ) {
357  return 0;
358  }
359 
360  if ( $oldid !== 0 ) {
361  # Load the given revision and check whether the page is another one.
362  # In that case, update this instance to reflect the change.
363  if ( $oldid === $this->mPage->getLatest() ) {
364  $this->mRevision = $this->mPage->getRevision();
365  } else {
366  $this->mRevision = Revision::newFromId( $oldid );
367  if ( $this->mRevision !== null ) {
368  // Revision title doesn't match the page title given?
369  if ( $this->mPage->getId() != $this->mRevision->getPage() ) {
370  $function = get_class( $this->mPage ) . '::newFromID';
371  $this->mPage = $function( $this->mRevision->getPage() );
372  }
373  }
374  }
375  }
376 
377  $rl = MediaWikiServices::getInstance()->getRevisionLookup();
378  $oldRev = $this->mRevision ? $this->mRevision->getRevisionRecord() : null;
379  if ( $request->getVal( 'direction' ) == 'next' ) {
380  $nextid = 0;
381  if ( $oldRev ) {
382  $nextRev = $rl->getNextRevision( $oldRev );
383  if ( $nextRev ) {
384  $nextid = $nextRev->getId();
385  }
386  }
387  if ( $nextid ) {
388  $oldid = $nextid;
389  $this->mRevision = null;
390  } else {
391  $this->mRedirectUrl = $this->getTitle()->getFullURL( 'redirect=no' );
392  }
393  } elseif ( $request->getVal( 'direction' ) == 'prev' ) {
394  $previd = 0;
395  if ( $oldRev ) {
396  $prevRev = $rl->getPreviousRevision( $oldRev );
397  if ( $prevRev ) {
398  $previd = $prevRev->getId();
399  }
400  }
401  if ( $previd ) {
402  $oldid = $previd;
403  $this->mRevision = null;
404  }
405  }
406 
407  $this->mRevIdFetched = $this->mRevision ? $this->mRevision->getId() : 0;
408 
409  return $oldid;
410  }
411 
425  protected function fetchContentObject() {
426  if ( !$this->mContentLoaded ) {
427  $this->fetchRevisionRecord();
428  }
429 
430  return $this->mContentObject;
431  }
432 
442  protected function fetchRevisionRecord() {
443  if ( $this->fetchResult ) {
444  return $this->mRevision ? $this->mRevision->getRevisionRecord() : null;
445  }
446 
447  $this->mContentLoaded = true;
448  $this->mContentObject = null;
449 
450  $oldid = $this->getOldID();
451 
452  // $this->mRevision might already be fetched by getOldIDFromRequest()
453  if ( !$this->mRevision ) {
454  if ( !$oldid ) {
455  $this->mRevision = $this->mPage->getRevision();
456 
457  if ( !$this->mRevision ) {
458  wfDebug( __METHOD__ . " failed to find page data for title " .
459  $this->getTitle()->getPrefixedText() . "\n" );
460 
461  // Just for sanity, output for this case is done by showMissingArticle().
462  $this->fetchResult = Status::newFatal( 'noarticletext' );
463  $this->applyContentOverride( $this->makeFetchErrorContent() );
464  return null;
465  }
466  } else {
467  $this->mRevision = Revision::newFromId( $oldid );
468 
469  if ( !$this->mRevision ) {
470  wfDebug( __METHOD__ . " failed to load revision, rev_id $oldid\n" );
471 
472  $this->fetchResult = Status::newFatal( 'missing-revision', $oldid );
473  $this->applyContentOverride( $this->makeFetchErrorContent() );
474  return null;
475  }
476  }
477  }
478 
479  $this->mRevIdFetched = $this->mRevision->getId();
480  $this->fetchResult = Status::newGood( $this->mRevision );
481 
482  if (
483  !$this->mRevision->userCan( RevisionRecord::DELETED_TEXT, $this->getContext()->getUser() )
484  ) {
485  wfDebug( __METHOD__ . " failed to retrieve content of revision " .
486  $this->mRevision->getId() . "\n" );
487 
488  // Just for sanity, output for this case is done by showDeletedRevisionHeader().
489  $this->fetchResult = Status::newFatal( 'rev-deleted-text-permission' );
490  $this->applyContentOverride( $this->makeFetchErrorContent() );
491  return null;
492  }
493 
494  if ( Hooks::isRegistered( 'ArticleAfterFetchContentObject' ) ) {
495  $contentObject = $this->mRevision->getContent(
496  RevisionRecord::FOR_THIS_USER,
497  $this->getContext()->getUser()
498  );
499 
500  $hookContentObject = $contentObject;
501 
502  // Avoid PHP 7.1 warning of passing $this by reference
503  $articlePage = $this;
504 
505  Hooks::run(
506  'ArticleAfterFetchContentObject',
507  [ &$articlePage, &$hookContentObject ],
508  '1.32'
509  );
510 
511  if ( $hookContentObject !== $contentObject ) {
512  // A hook handler is trying to override the content
513  $this->applyContentOverride( $hookContentObject );
514  }
515  }
516 
517  // For B/C only
518  $this->mContentObject = $this->mRevision->getContent(
519  RevisionRecord::FOR_THIS_USER,
520  $this->getContext()->getUser()
521  );
522 
523  return $this->mRevision->getRevisionRecord();
524  }
525 
532  private function makeFetchErrorContent() {
533  if ( !$this->fetchResult || $this->fetchResult->isOK() ) {
534  return null;
535  }
536 
537  return new MessageContent( $this->fetchResult->getMessage() );
538  }
539 
552  private function applyContentOverride( Content $override ) {
553  // Construct a fake revision
554  $rev = new MutableRevisionRecord( $this->getTitle() );
555  $rev->setContent( SlotRecord::MAIN, $override );
556 
557  $this->mRevision = new Revision( $rev );
558 
559  // For B/C only
560  $this->mContentObject = $override;
561  }
562 
568  public function isCurrent() {
569  # If no oldid, this is the current version.
570  if ( $this->getOldID() == 0 ) {
571  return true;
572  }
573 
574  return $this->mPage->exists() && $this->mRevision && $this->mRevision->isCurrent();
575  }
576 
586  public function getRevisionFetched() {
587  $this->fetchRevisionRecord();
588 
589  if ( $this->fetchResult->isOK() ) {
590  return $this->mRevision;
591  }
592  }
593 
602  public function getRevIdFetched() {
603  if ( $this->fetchResult && $this->fetchResult->isOK() ) {
604  return $this->fetchResult->value->getId();
605  } else {
606  return $this->mPage->getLatest();
607  }
608  }
609 
614  public function view() {
615  global $wgUseFileCache;
616 
617  # Get variables from query string
618  # As side effect this will load the revision and update the title
619  # in a revision ID is passed in the request, so this should remain
620  # the first call of this method even if $oldid is used way below.
621  $oldid = $this->getOldID();
622 
623  $user = $this->getContext()->getUser();
624  # Another whitelist check in case getOldID() is altering the title
625  $permErrors = $this->getTitle()->getUserPermissionsErrors( 'read', $user );
626  if ( count( $permErrors ) ) {
627  wfDebug( __METHOD__ . ": denied on secondary read check\n" );
628  throw new PermissionsError( 'read', $permErrors );
629  }
630 
631  $outputPage = $this->getContext()->getOutput();
632  # getOldID() may as well want us to redirect somewhere else
633  if ( $this->mRedirectUrl ) {
634  $outputPage->redirect( $this->mRedirectUrl );
635  wfDebug( __METHOD__ . ": redirecting due to oldid\n" );
636 
637  return;
638  }
639 
640  # If we got diff in the query, we want to see a diff page instead of the article.
641  if ( $this->getContext()->getRequest()->getCheck( 'diff' ) ) {
642  wfDebug( __METHOD__ . ": showing diff page\n" );
643  $this->showDiffPage();
644 
645  return;
646  }
647 
648  # Set page title (may be overridden by DISPLAYTITLE)
649  $outputPage->setPageTitle( $this->getTitle()->getPrefixedText() );
650 
651  $outputPage->setArticleFlag( true );
652  # Allow frames by default
653  $outputPage->allowClickjacking();
654 
655  $parserCache = MediaWikiServices::getInstance()->getParserCache();
656 
657  $parserOptions = $this->getParserOptions();
658  $poOptions = [];
659  # Render printable version, use printable version cache
660  if ( $outputPage->isPrintable() ) {
661  $parserOptions->setIsPrintable( true );
662  $poOptions['enableSectionEditLinks'] = false;
663  } elseif ( $this->viewIsRenderAction || !$this->isCurrent() ||
664  !MediaWikiServices::getInstance()->getPermissionManager()
665  ->quickUserCan( 'edit', $user, $this->getTitle() )
666  ) {
667  $poOptions['enableSectionEditLinks'] = false;
668  }
669 
670  # Try client and file cache
671  if ( $oldid === 0 && $this->mPage->checkTouched() ) {
672  # Try to stream the output from file cache
673  if ( $wgUseFileCache && $this->tryFileCache() ) {
674  wfDebug( __METHOD__ . ": done file cache\n" );
675  # tell wgOut that output is taken care of
676  $outputPage->disable();
677  $this->mPage->doViewUpdates( $user, $oldid );
678 
679  return;
680  }
681  }
682 
683  # Should the parser cache be used?
684  $useParserCache = $this->mPage->shouldCheckParserCache( $parserOptions, $oldid );
685  wfDebug( 'Article::view using parser cache: ' . ( $useParserCache ? 'yes' : 'no' ) . "\n" );
686  if ( $user->getStubThreshold() ) {
687  MediaWikiServices::getInstance()->getStatsdDataFactory()->increment( 'pcache_miss_stub' );
688  }
689 
690  $this->showRedirectedFromHeader();
691  $this->showNamespaceHeader();
692 
693  # Iterate through the possible ways of constructing the output text.
694  # Keep going until $outputDone is set, or we run out of things to do.
695  $pass = 0;
696  $outputDone = false;
697  $this->mParserOutput = false;
698 
699  while ( !$outputDone && ++$pass ) {
700  switch ( $pass ) {
701  case 1:
702  // Avoid PHP 7.1 warning of passing $this by reference
703  $articlePage = $this;
704  Hooks::run( 'ArticleViewHeader', [ &$articlePage, &$outputDone, &$useParserCache ] );
705  break;
706  case 2:
707  # Early abort if the page doesn't exist
708  if ( !$this->mPage->exists() ) {
709  wfDebug( __METHOD__ . ": showing missing article\n" );
710  $this->showMissingArticle();
711  $this->mPage->doViewUpdates( $user );
712  return;
713  }
714 
715  # Try the parser cache
716  if ( $useParserCache ) {
717  $this->mParserOutput = $parserCache->get( $this->mPage, $parserOptions );
718 
719  if ( $this->mParserOutput !== false ) {
720  if ( $oldid ) {
721  wfDebug( __METHOD__ . ": showing parser cache contents for current rev permalink\n" );
722  $this->setOldSubtitle( $oldid );
723  } else {
724  wfDebug( __METHOD__ . ": showing parser cache contents\n" );
725  }
726  $outputPage->addParserOutput( $this->mParserOutput, $poOptions );
727  # Ensure that UI elements requiring revision ID have
728  # the correct version information.
729  $outputPage->setRevisionId( $this->mPage->getLatest() );
730  # Preload timestamp to avoid a DB hit
731  $cachedTimestamp = $this->mParserOutput->getTimestamp();
732  if ( $cachedTimestamp !== null ) {
733  $outputPage->setRevisionTimestamp( $cachedTimestamp );
734  $this->mPage->setTimestamp( $cachedTimestamp );
735  }
736  $outputDone = true;
737  }
738  }
739  break;
740  case 3:
741  # Are we looking at an old revision
742  $rev = $this->fetchRevisionRecord();
743  if ( $oldid && $this->fetchResult->isOK() ) {
744  $this->setOldSubtitle( $oldid );
745 
746  if ( !$this->showDeletedRevisionHeader() ) {
747  wfDebug( __METHOD__ . ": cannot view deleted revision\n" );
748  return;
749  }
750  }
751 
752  # Ensure that UI elements requiring revision ID have
753  # the correct version information.
754  $outputPage->setRevisionId( $this->getRevIdFetched() );
755  # Preload timestamp to avoid a DB hit
756  $outputPage->setRevisionTimestamp( $this->mPage->getTimestamp() );
757 
758  # Pages containing custom CSS or JavaScript get special treatment
759  if ( $this->getTitle()->isSiteConfigPage() || $this->getTitle()->isUserConfigPage() ) {
760  $dir = $this->getContext()->getLanguage()->getDir();
761  $lang = $this->getContext()->getLanguage()->getHtmlCode();
762 
763  $outputPage->wrapWikiMsg(
764  "<div id='mw-clearyourcache' lang='$lang' dir='$dir' class='mw-content-$dir'>\n$1\n</div>",
765  'clearyourcache'
766  );
767  } elseif ( !Hooks::run( 'ArticleRevisionViewCustom', [
768  $rev,
769  $this->getTitle(),
770  $oldid,
771  $outputPage,
772  ] )
773  ) {
774  // NOTE: sync with hooks called in DifferenceEngine::renderNewRevision()
775  // Allow extensions do their own custom view for certain pages
776  $outputDone = true;
777  } elseif ( !Hooks::run( 'ArticleContentViewCustom',
778  [ $this->fetchContentObject(), $this->getTitle(), $outputPage ], '1.32' )
779  ) {
780  // NOTE: sync with hooks called in DifferenceEngine::renderNewRevision()
781  // Allow extensions do their own custom view for certain pages
782  $outputDone = true;
783  }
784  break;
785  case 4:
786  # Run the parse, protected by a pool counter
787  wfDebug( __METHOD__ . ": doing uncached parse\n" );
788 
789  $rev = $this->fetchRevisionRecord();
790  $error = null;
791 
792  if ( $rev ) {
793  $poolArticleView = new PoolWorkArticleView(
794  $this->getPage(),
795  $parserOptions,
796  $this->getRevIdFetched(),
797  $useParserCache,
798  $rev,
799  // permission checking was done earlier via showDeletedRevisionHeader()
800  RevisionRecord::RAW
801  );
802  $ok = $poolArticleView->execute();
803  $error = $poolArticleView->getError();
804  $this->mParserOutput = $poolArticleView->getParserOutput() ?: null;
805 
806  # Don't cache a dirty ParserOutput object
807  if ( $poolArticleView->getIsDirty() ) {
808  $outputPage->setCdnMaxage( 0 );
809  $outputPage->addHTML( "<!-- parser cache is expired, " .
810  "sending anyway due to pool overload-->\n" );
811  }
812  } else {
813  $ok = false;
814  }
815 
816  if ( !$ok ) {
817  if ( $error ) {
818  $outputPage->clearHTML(); // for release() errors
819  $outputPage->enableClientCache( false );
820  $outputPage->setRobotPolicy( 'noindex,nofollow' );
821 
822  $errortext = $error->getWikiText(
823  false, 'view-pool-error', $this->getContext()->getLanguage()
824  );
825  $outputPage->wrapWikiTextAsInterface( 'errorbox', $errortext );
826  }
827  # Connection or timeout error
828  return;
829  }
830 
831  if ( $this->mParserOutput ) {
832  $outputPage->addParserOutput( $this->mParserOutput, $poOptions );
833  }
834 
835  if ( $rev && $this->getRevisionRedirectTarget( $rev ) ) {
836  $outputPage->addSubtitle( "<span id=\"redirectsub\">" .
837  $this->getContext()->msg( 'redirectpagesub' )->parse() . "</span>" );
838  }
839 
840  $outputDone = true;
841  break;
842  # Should be unreachable, but just in case...
843  default:
844  break 2;
845  }
846  }
847 
848  // Get the ParserOutput actually *displayed* here.
849  // Note that $this->mParserOutput is the *current*/oldid version output.
850  // Note that the ArticleViewHeader hook is allowed to set $outputDone to a
851  // ParserOutput instance.
852  $pOutput = ( $outputDone instanceof ParserOutput )
853  ? $outputDone // object fetched by hook
854  : ( $this->mParserOutput ?: null ); // ParserOutput or null, avoid false
855 
856  # Adjust title for main page & pages with displaytitle
857  if ( $pOutput ) {
858  $this->adjustDisplayTitle( $pOutput );
859  }
860 
861  # For the main page, overwrite the <title> element with the con-
862  # tents of 'pagetitle-view-mainpage' instead of the default (if
863  # that's not empty).
864  # This message always exists because it is in the i18n files
865  if ( $this->getTitle()->isMainPage() ) {
866  $msg = wfMessage( 'pagetitle-view-mainpage' )->inContentLanguage();
867  if ( !$msg->isDisabled() ) {
868  $outputPage->setHTMLTitle( $msg->title( $this->getTitle() )->text() );
869  }
870  }
871 
872  # Use adaptive TTLs for CDN so delayed/failed purges are noticed less often.
873  # This could use getTouched(), but that could be scary for major template edits.
874  $outputPage->adaptCdnTTL( $this->mPage->getTimestamp(), IExpiringStore::TTL_DAY );
875 
876  # Check for any __NOINDEX__ tags on the page using $pOutput
877  $policy = $this->getRobotPolicy( 'view', $pOutput ?: null );
878  $outputPage->setIndexPolicy( $policy['index'] );
879  $outputPage->setFollowPolicy( $policy['follow'] ); // FIXME: test this
880 
881  $this->showViewFooter();
882  $this->mPage->doViewUpdates( $user, $oldid ); // FIXME: test this
883 
884  # Load the postEdit module if the user just saved this revision
885  # See also EditPage::setPostEditCookie
886  $request = $this->getContext()->getRequest();
888  $postEdit = $request->getCookie( $cookieKey );
889  if ( $postEdit ) {
890  # Clear the cookie. This also prevents caching of the response.
891  $request->response()->clearCookie( $cookieKey );
892  $outputPage->addJsConfigVars( 'wgPostEdit', $postEdit );
893  $outputPage->addModules( 'mediawiki.action.view.postEdit' ); // FIXME: test this
894  }
895  }
896 
901  private function getRevisionRedirectTarget( RevisionRecord $revision ) {
902  // TODO: find a *good* place for the code that determines the redirect target for
903  // a given revision!
904  // NOTE: Use main slot content. Compare code in DerivedPageDataUpdater::revisionIsRedirect.
905  $content = $revision->getContent( SlotRecord::MAIN );
906  return $content ? $content->getRedirectTarget() : null;
907  }
908 
913  public function adjustDisplayTitle( ParserOutput $pOutput ) {
914  $out = $this->getContext()->getOutput();
915 
916  # Adjust the title if it was set by displaytitle, -{T|}- or language conversion
917  $titleText = $pOutput->getTitleText();
918  if ( strval( $titleText ) !== '' ) {
919  $out->setPageTitle( $titleText );
920  $out->setDisplayTitle( $titleText );
921  }
922  }
923 
928  protected function showDiffPage() {
929  $request = $this->getContext()->getRequest();
930  $user = $this->getContext()->getUser();
931  $diff = $request->getVal( 'diff' );
932  $rcid = $request->getVal( 'rcid' );
933  $diffOnly = $request->getBool( 'diffonly', $user->getOption( 'diffonly' ) );
934  $purge = $request->getVal( 'action' ) == 'purge';
935  $unhide = $request->getInt( 'unhide' ) == 1;
936  $oldid = $this->getOldID();
937 
938  $rev = $this->getRevisionFetched();
939 
940  if ( !$rev ) {
941  $this->getContext()->getOutput()->setPageTitle( wfMessage( 'errorpagetitle' ) );
942  $msg = $this->getContext()->msg( 'difference-missing-revision' )
943  ->params( $oldid )
944  ->numParams( 1 )
945  ->parseAsBlock();
946  $this->getContext()->getOutput()->addHTML( $msg );
947  return;
948  }
949 
950  $contentHandler = $rev->getContentHandler();
951  $de = $contentHandler->createDifferenceEngine(
952  $this->getContext(),
953  $oldid,
954  $diff,
955  $rcid,
956  $purge,
957  $unhide
958  );
959  $de->setSlotDiffOptions( [
960  'diff-type' => $request->getVal( 'diff-type' )
961  ] );
962 
963  // DifferenceEngine directly fetched the revision:
964  $this->mRevIdFetched = $de->getNewid();
965  $de->showDiffPage( $diffOnly );
966 
967  // Run view updates for the newer revision being diffed (and shown
968  // below the diff if not $diffOnly).
969  list( $old, $new ) = $de->mapDiffPrevNext( $oldid, $diff );
970  // New can be false, convert it to 0 - this conveniently means the latest revision
971  $this->mPage->doViewUpdates( $user, (int)$new );
972  }
973 
981  public function getRobotPolicy( $action, ParserOutput $pOutput = null ) {
983 
984  $ns = $this->getTitle()->getNamespace();
985 
986  # Don't index user and user talk pages for blocked users (T13443)
987  if ( ( $ns == NS_USER || $ns == NS_USER_TALK ) && !$this->getTitle()->isSubpage() ) {
988  $specificTarget = null;
989  $vagueTarget = null;
990  $titleText = $this->getTitle()->getText();
991  if ( IPUtils::isValid( $titleText ) ) {
992  $vagueTarget = $titleText;
993  } else {
994  $specificTarget = $titleText;
995  }
996  if ( DatabaseBlock::newFromTarget( $specificTarget, $vagueTarget ) instanceof DatabaseBlock ) {
997  return [
998  'index' => 'noindex',
999  'follow' => 'nofollow'
1000  ];
1001  }
1002  }
1003 
1004  if ( $this->mPage->getId() === 0 || $this->getOldID() ) {
1005  # Non-articles (special pages etc), and old revisions
1006  return [
1007  'index' => 'noindex',
1008  'follow' => 'nofollow'
1009  ];
1010  } elseif ( $this->getContext()->getOutput()->isPrintable() ) {
1011  # Discourage indexing of printable versions, but encourage following
1012  return [
1013  'index' => 'noindex',
1014  'follow' => 'follow'
1015  ];
1016  } elseif ( $this->getContext()->getRequest()->getInt( 'curid' ) ) {
1017  # For ?curid=x urls, disallow indexing
1018  return [
1019  'index' => 'noindex',
1020  'follow' => 'follow'
1021  ];
1022  }
1023 
1024  # Otherwise, construct the policy based on the various config variables.
1026 
1027  if ( isset( $wgNamespaceRobotPolicies[$ns] ) ) {
1028  # Honour customised robot policies for this namespace
1029  $policy = array_merge(
1030  $policy,
1031  self::formatRobotPolicy( $wgNamespaceRobotPolicies[$ns] )
1032  );
1033  }
1034  if ( $this->getTitle()->canUseNoindex() && is_object( $pOutput ) && $pOutput->getIndexPolicy() ) {
1035  # __INDEX__ and __NOINDEX__ magic words, if allowed. Incorporates
1036  # a final sanity check that we have really got the parser output.
1037  $policy = array_merge(
1038  $policy,
1039  [ 'index' => $pOutput->getIndexPolicy() ]
1040  );
1041  }
1042 
1043  if ( isset( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] ) ) {
1044  # (T16900) site config can override user-defined __INDEX__ or __NOINDEX__
1045  $policy = array_merge(
1046  $policy,
1047  self::formatRobotPolicy( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] )
1048  );
1049  }
1050 
1051  return $policy;
1052  }
1053 
1061  public static function formatRobotPolicy( $policy ) {
1062  if ( is_array( $policy ) ) {
1063  return $policy;
1064  } elseif ( !$policy ) {
1065  return [];
1066  }
1067 
1068  $policy = explode( ',', $policy );
1069  $policy = array_map( 'trim', $policy );
1070 
1071  $arr = [];
1072  foreach ( $policy as $var ) {
1073  if ( in_array( $var, [ 'index', 'noindex' ] ) ) {
1074  $arr['index'] = $var;
1075  } elseif ( in_array( $var, [ 'follow', 'nofollow' ] ) ) {
1076  $arr['follow'] = $var;
1077  }
1078  }
1079 
1080  return $arr;
1081  }
1082 
1090  public function showRedirectedFromHeader() {
1091  global $wgRedirectSources;
1092 
1093  $context = $this->getContext();
1094  $outputPage = $context->getOutput();
1095  $request = $context->getRequest();
1096  $rdfrom = $request->getVal( 'rdfrom' );
1097 
1098  // Construct a URL for the current page view, but with the target title
1099  $query = $request->getValues();
1100  unset( $query['rdfrom'] );
1101  unset( $query['title'] );
1102  if ( $this->getTitle()->isRedirect() ) {
1103  // Prevent double redirects
1104  $query['redirect'] = 'no';
1105  }
1106  $redirectTargetUrl = $this->getTitle()->getLinkURL( $query );
1107 
1108  if ( isset( $this->mRedirectedFrom ) ) {
1109  // Avoid PHP 7.1 warning of passing $this by reference
1110  $articlePage = $this;
1111 
1112  // This is an internally redirected page view.
1113  // We'll need a backlink to the source page for navigation.
1114  if ( Hooks::run( 'ArticleViewRedirect', [ &$articlePage ] ) ) {
1115  $redir = $this->linkRenderer->makeKnownLink(
1116  $this->mRedirectedFrom,
1117  null,
1118  [],
1119  [ 'redirect' => 'no' ]
1120  );
1121 
1122  $outputPage->addSubtitle( "<span class=\"mw-redirectedfrom\">" .
1123  $context->msg( 'redirectedfrom' )->rawParams( $redir )->parse()
1124  . "</span>" );
1125 
1126  // Add the script to update the displayed URL and
1127  // set the fragment if one was specified in the redirect
1128  $outputPage->addJsConfigVars( [
1129  'wgInternalRedirectTargetUrl' => $redirectTargetUrl,
1130  ] );
1131  $outputPage->addModules( 'mediawiki.action.view.redirect' );
1132 
1133  // Add a <link rel="canonical"> tag
1134  $outputPage->setCanonicalUrl( $this->getTitle()->getCanonicalURL() );
1135 
1136  // Tell the output object that the user arrived at this article through a redirect
1137  $outputPage->setRedirectedFrom( $this->mRedirectedFrom );
1138 
1139  return true;
1140  }
1141  } elseif ( $rdfrom ) {
1142  // This is an externally redirected view, from some other wiki.
1143  // If it was reported from a trusted site, supply a backlink.
1144  if ( $wgRedirectSources && preg_match( $wgRedirectSources, $rdfrom ) ) {
1145  $redir = Linker::makeExternalLink( $rdfrom, $rdfrom );
1146  $outputPage->addSubtitle( "<span class=\"mw-redirectedfrom\">" .
1147  $context->msg( 'redirectedfrom' )->rawParams( $redir )->parse()
1148  . "</span>" );
1149 
1150  // Add the script to update the displayed URL
1151  $outputPage->addJsConfigVars( [
1152  'wgInternalRedirectTargetUrl' => $redirectTargetUrl,
1153  ] );
1154  $outputPage->addModules( 'mediawiki.action.view.redirect' );
1155 
1156  return true;
1157  }
1158  }
1159 
1160  return false;
1161  }
1162 
1167  public function showNamespaceHeader() {
1168  if ( $this->getTitle()->isTalkPage() && !wfMessage( 'talkpageheader' )->isDisabled() ) {
1169  $this->getContext()->getOutput()->wrapWikiMsg(
1170  "<div class=\"mw-talkpageheader\">\n$1\n</div>",
1171  [ 'talkpageheader' ]
1172  );
1173  }
1174  }
1175 
1179  public function showViewFooter() {
1180  # check if we're displaying a [[User talk:x.x.x.x]] anonymous talk page
1181  if ( $this->getTitle()->getNamespace() == NS_USER_TALK
1182  && IPUtils::isValid( $this->getTitle()->getText() )
1183  ) {
1184  $this->getContext()->getOutput()->addWikiMsg( 'anontalkpagetext' );
1185  }
1186 
1187  // Show a footer allowing the user to patrol the shown revision or page if possible
1188  $patrolFooterShown = $this->showPatrolFooter();
1189 
1190  Hooks::run( 'ArticleViewFooter', [ $this, $patrolFooterShown ] );
1191  }
1192 
1202  public function showPatrolFooter() {
1204 
1205  // Allow hooks to decide whether to not output this at all
1206  if ( !Hooks::run( 'ArticleShowPatrolFooter', [ $this ] ) ) {
1207  return false;
1208  }
1209 
1210  $outputPage = $this->getContext()->getOutput();
1211  $user = $this->getContext()->getUser();
1212  $title = $this->getTitle();
1213  $rc = false;
1214 
1215  if ( !MediaWikiServices::getInstance()->getPermissionManager()
1216  ->quickUserCan( 'patrol', $user, $title )
1218  || ( $wgUseFilePatrol && $title->inNamespace( NS_FILE ) ) )
1219  ) {
1220  // Patrolling is disabled or the user isn't allowed to
1221  return false;
1222  }
1223 
1224  if ( $this->mRevision
1225  && !RecentChange::isInRCLifespan( $this->mRevision->getTimestamp(), 21600 )
1226  ) {
1227  // The current revision is already older than what could be in the RC table
1228  // 6h tolerance because the RC might not be cleaned out regularly
1229  return false;
1230  }
1231 
1232  // Check for cached results
1233  $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
1234  $key = $cache->makeKey( 'unpatrollable-page', $title->getArticleID() );
1235  if ( $cache->get( $key ) ) {
1236  return false;
1237  }
1238 
1239  $dbr = wfGetDB( DB_REPLICA );
1240  $oldestRevisionTimestamp = $dbr->selectField(
1241  'revision',
1242  'MIN( rev_timestamp )',
1243  [ 'rev_page' => $title->getArticleID() ],
1244  __METHOD__
1245  );
1246 
1247  // New page patrol: Get the timestamp of the oldest revison which
1248  // the revision table holds for the given page. Then we look
1249  // whether it's within the RC lifespan and if it is, we try
1250  // to get the recentchanges row belonging to that entry
1251  // (with rc_new = 1).
1252  $recentPageCreation = false;
1253  if ( $oldestRevisionTimestamp
1254  && RecentChange::isInRCLifespan( $oldestRevisionTimestamp, 21600 )
1255  ) {
1256  // 6h tolerance because the RC might not be cleaned out regularly
1257  $recentPageCreation = true;
1259  [
1260  'rc_new' => 1,
1261  'rc_timestamp' => $oldestRevisionTimestamp,
1262  'rc_namespace' => $title->getNamespace(),
1263  'rc_cur_id' => $title->getArticleID()
1264  ],
1265  __METHOD__
1266  );
1267  if ( $rc ) {
1268  // Use generic patrol message for new pages
1269  $markPatrolledMsg = wfMessage( 'markaspatrolledtext' );
1270  }
1271  }
1272 
1273  // File patrol: Get the timestamp of the latest upload for this page,
1274  // check whether it is within the RC lifespan and if it is, we try
1275  // to get the recentchanges row belonging to that entry
1276  // (with rc_type = RC_LOG, rc_log_type = upload).
1277  $recentFileUpload = false;
1278  if ( ( !$rc || $rc->getAttribute( 'rc_patrolled' ) ) && $wgUseFilePatrol
1279  && $title->getNamespace() === NS_FILE ) {
1280  // Retrieve timestamp of most recent upload
1281  $newestUploadTimestamp = $dbr->selectField(
1282  'image',
1283  'MAX( img_timestamp )',
1284  [ 'img_name' => $title->getDBkey() ],
1285  __METHOD__
1286  );
1287  if ( $newestUploadTimestamp
1288  && RecentChange::isInRCLifespan( $newestUploadTimestamp, 21600 )
1289  ) {
1290  // 6h tolerance because the RC might not be cleaned out regularly
1291  $recentFileUpload = true;
1293  [
1294  'rc_type' => RC_LOG,
1295  'rc_log_type' => 'upload',
1296  'rc_timestamp' => $newestUploadTimestamp,
1297  'rc_namespace' => NS_FILE,
1298  'rc_cur_id' => $title->getArticleID()
1299  ],
1300  __METHOD__
1301  );
1302  if ( $rc ) {
1303  // Use patrol message specific to files
1304  $markPatrolledMsg = wfMessage( 'markaspatrolledtext-file' );
1305  }
1306  }
1307  }
1308 
1309  if ( !$recentPageCreation && !$recentFileUpload ) {
1310  // Page creation and latest upload (for files) is too old to be in RC
1311 
1312  // We definitely can't patrol so cache the information
1313  // When a new file version is uploaded, the cache is cleared
1314  $cache->set( $key, '1' );
1315 
1316  return false;
1317  }
1318 
1319  if ( !$rc ) {
1320  // Don't cache: This can be hit if the page gets accessed very fast after
1321  // its creation / latest upload or in case we have high replica DB lag. In case
1322  // the revision is too old, we will already return above.
1323  return false;
1324  }
1325 
1326  if ( $rc->getAttribute( 'rc_patrolled' ) ) {
1327  // Patrolled RC entry around
1328 
1329  // Cache the information we gathered above in case we can't patrol
1330  // Don't cache in case we can patrol as this could change
1331  $cache->set( $key, '1' );
1332 
1333  return false;
1334  }
1335 
1336  if ( $rc->getPerformer()->equals( $user ) ) {
1337  // Don't show a patrol link for own creations/uploads. If the user could
1338  // patrol them, they already would be patrolled
1339  return false;
1340  }
1341 
1342  $outputPage->preventClickjacking();
1343  if ( MediaWikiServices::getInstance()
1345  ->userHasRight( $user, 'writeapi' )
1346  ) {
1347  $outputPage->addModules( 'mediawiki.page.patrol.ajax' );
1348  }
1349 
1350  $link = $this->linkRenderer->makeKnownLink(
1351  $title,
1352  $markPatrolledMsg->text(),
1353  [],
1354  [
1355  'action' => 'markpatrolled',
1356  'rcid' => $rc->getAttribute( 'rc_id' ),
1357  ]
1358  );
1359 
1360  $outputPage->addHTML(
1361  "<div class='patrollink' data-mw='interface'>" .
1362  wfMessage( 'markaspatrolledlink' )->rawParams( $link )->escaped() .
1363  '</div>'
1364  );
1365 
1366  return true;
1367  }
1368 
1375  public static function purgePatrolFooterCache( $articleID ) {
1376  $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
1377  $cache->delete( $cache->makeKey( 'unpatrollable-page', $articleID ) );
1378  }
1379 
1384  public function showMissingArticle() {
1385  global $wgSend404Code;
1386 
1387  $outputPage = $this->getContext()->getOutput();
1388  // Whether the page is a root user page of an existing user (but not a subpage)
1389  $validUserPage = false;
1390 
1391  $title = $this->getTitle();
1392 
1393  $services = MediaWikiServices::getInstance();
1394 
1395  # Show info in user (talk) namespace. Does the user exist? Is he blocked?
1396  if ( $title->getNamespace() == NS_USER
1397  || $title->getNamespace() == NS_USER_TALK
1398  ) {
1399  $rootPart = explode( '/', $title->getText() )[0];
1400  $user = User::newFromName( $rootPart, false /* allow IP users */ );
1401  $ip = User::isIP( $rootPart );
1402  $block = DatabaseBlock::newFromTarget( $user, $user );
1403 
1404  if ( !( $user && $user->isLoggedIn() ) && !$ip ) { # User does not exist
1405  $outputPage->wrapWikiMsg( "<div class=\"mw-userpage-userdoesnotexist error\">\n\$1\n</div>",
1406  [ 'userpage-userdoesnotexist-view', wfEscapeWikiText( $rootPart ) ] );
1407  } elseif (
1408  $block !== null &&
1409  $block->getType() != DatabaseBlock::TYPE_AUTO &&
1410  ( $block->isSitewide() || $user->isBlockedFrom( $title ) )
1411  ) {
1412  // Show log extract if the user is sitewide blocked or is partially
1413  // blocked and not allowed to edit their user page or user talk page
1415  $outputPage,
1416  'block',
1417  $services->getNamespaceInfo()->getCanonicalName( NS_USER ) . ':' .
1418  $block->getTarget(),
1419  '',
1420  [
1421  'lim' => 1,
1422  'showIfEmpty' => false,
1423  'msgKey' => [
1424  'blocked-notice-logextract',
1425  $user->getName() # Support GENDER in notice
1426  ]
1427  ]
1428  );
1429  $validUserPage = !$title->isSubpage();
1430  } else {
1431  $validUserPage = !$title->isSubpage();
1432  }
1433  }
1434 
1435  Hooks::run( 'ShowMissingArticle', [ $this ] );
1436 
1437  # Show delete and move logs if there were any such events.
1438  # The logging query can DOS the site when bots/crawlers cause 404 floods,
1439  # so be careful showing this. 404 pages must be cheap as they are hard to cache.
1440  $dbCache = ObjectCache::getInstance( 'db-replicated' );
1441  $key = $dbCache->makeKey( 'page-recent-delete', md5( $title->getPrefixedText() ) );
1442  $loggedIn = $this->getContext()->getUser()->isLoggedIn();
1443  $sessionExists = $this->getContext()->getRequest()->getSession()->isPersistent();
1444  if ( $loggedIn || $dbCache->get( $key ) || $sessionExists ) {
1445  $logTypes = [ 'delete', 'move', 'protect' ];
1446 
1447  $dbr = wfGetDB( DB_REPLICA );
1448 
1449  $conds = [ 'log_action != ' . $dbr->addQuotes( 'revision' ) ];
1450  // Give extensions a chance to hide their (unrelated) log entries
1451  Hooks::run( 'Article::MissingArticleConditions', [ &$conds, $logTypes ] );
1453  $outputPage,
1454  $logTypes,
1455  $title,
1456  '',
1457  [
1458  'lim' => 10,
1459  'conds' => $conds,
1460  'showIfEmpty' => false,
1461  'msgKey' => [ $loggedIn || $sessionExists
1462  ? 'moveddeleted-notice'
1463  : 'moveddeleted-notice-recent'
1464  ]
1465  ]
1466  );
1467  }
1468 
1469  if ( !$this->mPage->hasViewableContent() && $wgSend404Code && !$validUserPage ) {
1470  // If there's no backing content, send a 404 Not Found
1471  // for better machine handling of broken links.
1472  $this->getContext()->getRequest()->response()->statusHeader( 404 );
1473  }
1474 
1475  // Also apply the robot policy for nonexisting pages (even if a 404 was used for sanity)
1476  $policy = $this->getRobotPolicy( 'view' );
1477  $outputPage->setIndexPolicy( $policy['index'] );
1478  $outputPage->setFollowPolicy( $policy['follow'] );
1479 
1480  $hookResult = Hooks::run( 'BeforeDisplayNoArticleText', [ $this ] );
1481 
1482  if ( !$hookResult ) {
1483  return;
1484  }
1485 
1486  # Show error message
1487  $oldid = $this->getOldID();
1488  $pm = MediaWikiServices::getInstance()->getPermissionManager();
1489  if ( !$oldid && $title->getNamespace() === NS_MEDIAWIKI && $title->hasSourceText() ) {
1490  // use fake Content object for system message
1491  $parserOptions = ParserOptions::newCanonical( 'canonical' );
1492  $outputPage->addParserOutput( $this->getEmptyPageParserOutput( $parserOptions ) );
1493  } else {
1494  if ( $oldid ) {
1495  $text = wfMessage( 'missing-revision', $oldid )->plain();
1496  } elseif ( $pm->quickUserCan( 'create', $this->getContext()->getUser(), $title ) &&
1497  $pm->quickUserCan( 'edit', $this->getContext()->getUser(), $title )
1498  ) {
1499  $message = $this->getContext()->getUser()->isLoggedIn() ? 'noarticletext' : 'noarticletextanon';
1500  $text = wfMessage( $message )->plain();
1501  } else {
1502  $text = wfMessage( 'noarticletext-nopermission' )->plain();
1503  }
1504 
1505  $dir = $this->getContext()->getLanguage()->getDir();
1506  $lang = $this->getContext()->getLanguage()->getHtmlCode();
1507  $outputPage->addWikiTextAsInterface( Xml::openElement( 'div', [
1508  'class' => "noarticletext mw-content-$dir",
1509  'dir' => $dir,
1510  'lang' => $lang,
1511  ] ) . "\n$text\n</div>" );
1512  }
1513  }
1514 
1521  public function showDeletedRevisionHeader() {
1522  if ( !$this->mRevision->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
1523  // Not deleted
1524  return true;
1525  }
1526 
1527  $outputPage = $this->getContext()->getOutput();
1528  $user = $this->getContext()->getUser();
1529  // If the user is not allowed to see it...
1530  if ( !$this->mRevision->userCan( RevisionRecord::DELETED_TEXT, $user ) ) {
1531  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1532  'rev-deleted-text-permission' );
1533 
1534  return false;
1535  // If the user needs to confirm that they want to see it...
1536  } elseif ( $this->getContext()->getRequest()->getInt( 'unhide' ) != 1 ) {
1537  # Give explanation and add a link to view the revision...
1538  $oldid = intval( $this->getOldID() );
1539  $link = $this->getTitle()->getFullURL( "oldid={$oldid}&unhide=1" );
1540  $msg = $this->mRevision->isDeleted( RevisionRecord::DELETED_RESTRICTED ) ?
1541  'rev-suppressed-text-unhide' : 'rev-deleted-text-unhide';
1542  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1543  [ $msg, $link ] );
1544 
1545  return false;
1546  // We are allowed to see...
1547  } else {
1548  $msg = $this->mRevision->isDeleted( RevisionRecord::DELETED_RESTRICTED ) ?
1549  'rev-suppressed-text-view' : 'rev-deleted-text-view';
1550  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n", $msg );
1551 
1552  return true;
1553  }
1554  }
1555 
1564  public function setOldSubtitle( $oldid = 0 ) {
1565  // Avoid PHP 7.1 warning of passing $this by reference
1566  $articlePage = $this;
1567 
1568  if ( !Hooks::run( 'DisplayOldSubtitle', [ &$articlePage, &$oldid ] ) ) {
1569  return;
1570  }
1571 
1572  $context = $this->getContext();
1573  $unhide = $context->getRequest()->getInt( 'unhide' ) == 1;
1574 
1575  # Cascade unhide param in links for easy deletion browsing
1576  $extraParams = [];
1577  if ( $unhide ) {
1578  $extraParams['unhide'] = 1;
1579  }
1580 
1581  if ( $this->mRevision && $this->mRevision->getId() === $oldid ) {
1582  $revision = $this->mRevision;
1583  } else {
1584  $revision = Revision::newFromId( $oldid );
1585  }
1586 
1587  $timestamp = $revision->getTimestamp();
1588 
1589  $current = ( $oldid == $this->mPage->getLatest() );
1590  $language = $context->getLanguage();
1591  $user = $context->getUser();
1592 
1593  $td = $language->userTimeAndDate( $timestamp, $user );
1594  $tddate = $language->userDate( $timestamp, $user );
1595  $tdtime = $language->userTime( $timestamp, $user );
1596 
1597  # Show user links if allowed to see them. If hidden, then show them only if requested...
1598  $userlinks = Linker::revUserTools( $revision, !$unhide );
1599 
1600  $infomsg = $current && !$context->msg( 'revision-info-current' )->isDisabled()
1601  ? 'revision-info-current'
1602  : 'revision-info';
1603 
1604  $outputPage = $context->getOutput();
1605  $revisionInfo = "<div id=\"mw-{$infomsg}\">" .
1606  $context->msg( $infomsg, $td )
1607  ->rawParams( $userlinks )
1608  ->params( $revision->getId(), $tddate, $tdtime, $revision->getUserText() )
1609  ->rawParams( Linker::revComment( $revision, true, true ) )
1610  ->parse() .
1611  "</div>";
1612 
1613  $lnk = $current
1614  ? $context->msg( 'currentrevisionlink' )->escaped()
1615  : $this->linkRenderer->makeKnownLink(
1616  $this->getTitle(),
1617  $context->msg( 'currentrevisionlink' )->text(),
1618  [],
1619  $extraParams
1620  );
1621  $curdiff = $current
1622  ? $context->msg( 'diff' )->escaped()
1623  : $this->linkRenderer->makeKnownLink(
1624  $this->getTitle(),
1625  $context->msg( 'diff' )->text(),
1626  [],
1627  [
1628  'diff' => 'cur',
1629  'oldid' => $oldid
1630  ] + $extraParams
1631  );
1632  $rl = MediaWikiServices::getInstance()->getRevisionLookup();
1633  $prevExist = (bool)$rl->getPreviousRevision( $revision->getRevisionRecord() );
1634  $prevlink = $prevExist
1635  ? $this->linkRenderer->makeKnownLink(
1636  $this->getTitle(),
1637  $context->msg( 'previousrevision' )->text(),
1638  [],
1639  [
1640  'direction' => 'prev',
1641  'oldid' => $oldid
1642  ] + $extraParams
1643  )
1644  : $context->msg( 'previousrevision' )->escaped();
1645  $prevdiff = $prevExist
1646  ? $this->linkRenderer->makeKnownLink(
1647  $this->getTitle(),
1648  $context->msg( 'diff' )->text(),
1649  [],
1650  [
1651  'diff' => 'prev',
1652  'oldid' => $oldid
1653  ] + $extraParams
1654  )
1655  : $context->msg( 'diff' )->escaped();
1656  $nextlink = $current
1657  ? $context->msg( 'nextrevision' )->escaped()
1658  : $this->linkRenderer->makeKnownLink(
1659  $this->getTitle(),
1660  $context->msg( 'nextrevision' )->text(),
1661  [],
1662  [
1663  'direction' => 'next',
1664  'oldid' => $oldid
1665  ] + $extraParams
1666  );
1667  $nextdiff = $current
1668  ? $context->msg( 'diff' )->escaped()
1669  : $this->linkRenderer->makeKnownLink(
1670  $this->getTitle(),
1671  $context->msg( 'diff' )->text(),
1672  [],
1673  [
1674  'diff' => 'next',
1675  'oldid' => $oldid
1676  ] + $extraParams
1677  );
1678 
1679  $cdel = Linker::getRevDeleteLink( $user, $revision, $this->getTitle() );
1680  if ( $cdel !== '' ) {
1681  $cdel .= ' ';
1682  }
1683 
1684  // the outer div is need for styling the revision info and nav in MobileFrontend
1685  $outputPage->addSubtitle( "<div class=\"mw-revision warningbox\">" . $revisionInfo .
1686  "<div id=\"mw-revision-nav\">" . $cdel .
1687  $context->msg( 'revision-nav' )->rawParams(
1688  $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff
1689  )->escaped() . "</div></div>" );
1690  }
1691 
1705  public function viewRedirect( $target, $appendSubtitle = true, $forceKnown = false ) {
1706  $lang = $this->getTitle()->getPageLanguage();
1707  $out = $this->getContext()->getOutput();
1708  if ( $appendSubtitle ) {
1709  $out->addSubtitle( wfMessage( 'redirectpagesub' ) );
1710  }
1711  $out->addModuleStyles( 'mediawiki.action.view.redirectPage' );
1712  return static::getRedirectHeaderHtml( $lang, $target, $forceKnown );
1713  }
1714 
1727  public static function getRedirectHeaderHtml( Language $lang, $target, $forceKnown = false ) {
1728  if ( !is_array( $target ) ) {
1729  $target = [ $target ];
1730  }
1731 
1732  $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
1733 
1734  $html = '<ul class="redirectText">';
1736  foreach ( $target as $title ) {
1737  if ( $forceKnown ) {
1738  $link = $linkRenderer->makeKnownLink(
1739  $title,
1740  $title->getFullText(),
1741  [],
1742  // Make sure wiki page redirects are not followed
1743  $title->isRedirect() ? [ 'redirect' => 'no' ] : []
1744  );
1745  } else {
1746  $link = $linkRenderer->makeLink(
1747  $title,
1748  $title->getFullText(),
1749  [],
1750  // Make sure wiki page redirects are not followed
1751  $title->isRedirect() ? [ 'redirect' => 'no' ] : []
1752  );
1753  }
1754  $html .= '<li>' . $link . '</li>';
1755  }
1756  $html .= '</ul>';
1757 
1758  $redirectToText = wfMessage( 'redirectto' )->inLanguage( $lang )->escaped();
1759 
1760  return '<div class="redirectMsg">' .
1761  '<p>' . $redirectToText . '</p>' .
1762  $html .
1763  '</div>';
1764  }
1765 
1774  public function addHelpLink( $to, $overrideBaseUrl = false ) {
1775  $msg = wfMessage(
1776  'namespace-' . $this->getTitle()->getNamespace() . '-helppage'
1777  );
1778 
1779  $out = $this->getContext()->getOutput();
1780  if ( !$msg->isDisabled() ) {
1781  $helpUrl = Skin::makeUrl( $msg->plain() );
1782  $out->addHelpLink( $helpUrl, true );
1783  } else {
1784  $out->addHelpLink( $to, $overrideBaseUrl );
1785  }
1786  }
1787 
1791  public function render() {
1792  $this->getContext()->getRequest()->response()->header( 'X-Robots-Tag: noindex' );
1793  $this->getContext()->getOutput()->setArticleBodyOnly( true );
1794  // We later set 'enableSectionEditLinks=false' based on this; also used by ImagePage
1795  $this->viewIsRenderAction = true;
1796  $this->view();
1797  }
1798 
1802  public function protect() {
1803  $form = new ProtectionForm( $this );
1804  $form->execute();
1805  }
1806 
1810  public function unprotect() {
1811  $this->protect();
1812  }
1813 
1817  public function delete() {
1818  # This code desperately needs to be totally rewritten
1819 
1820  $title = $this->getTitle();
1821  $context = $this->getContext();
1822  $user = $context->getUser();
1823  $request = $context->getRequest();
1824 
1825  # Check permissions
1826  $permissionErrors = $title->getUserPermissionsErrors( 'delete', $user );
1827  if ( count( $permissionErrors ) ) {
1828  throw new PermissionsError( 'delete', $permissionErrors );
1829  }
1830 
1831  # Read-only check...
1832  if ( wfReadOnly() ) {
1833  throw new ReadOnlyError;
1834  }
1835 
1836  # Better double-check that it hasn't been deleted yet!
1837  $this->mPage->loadPageData(
1838  $request->wasPosted() ? WikiPage::READ_LATEST : WikiPage::READ_NORMAL
1839  );
1840  if ( !$this->mPage->exists() ) {
1841  $deleteLogPage = new LogPage( 'delete' );
1842  $outputPage = $context->getOutput();
1843  $outputPage->setPageTitle( $context->msg( 'cannotdelete-title', $title->getPrefixedText() ) );
1844  $outputPage->wrapWikiMsg( "<div class=\"error mw-error-cannotdelete\">\n$1\n</div>",
1845  [ 'cannotdelete', wfEscapeWikiText( $title->getPrefixedText() ) ]
1846  );
1847  $outputPage->addHTML(
1848  Xml::element( 'h2', null, $deleteLogPage->getName()->text() )
1849  );
1851  $outputPage,
1852  'delete',
1853  $title
1854  );
1855 
1856  return;
1857  }
1858 
1859  $deleteReasonList = $request->getText( 'wpDeleteReasonList', 'other' );
1860  $deleteReason = $request->getText( 'wpReason' );
1861 
1862  if ( $deleteReasonList == 'other' ) {
1863  $reason = $deleteReason;
1864  } elseif ( $deleteReason != '' ) {
1865  // Entry from drop down menu + additional comment
1866  $colonseparator = wfMessage( 'colon-separator' )->inContentLanguage()->text();
1867  $reason = $deleteReasonList . $colonseparator . $deleteReason;
1868  } else {
1869  $reason = $deleteReasonList;
1870  }
1871 
1872  if ( $request->wasPosted() && $user->matchEditToken( $request->getVal( 'wpEditToken' ),
1873  [ 'delete', $this->getTitle()->getPrefixedText() ] )
1874  ) {
1875  # Flag to hide all contents of the archived revisions
1876 
1877  $suppress = $request->getCheck( 'wpSuppress' ) && MediaWikiServices::getInstance()
1878  ->getPermissionManager()
1879  ->userHasRight( $user, 'suppressrevision' );
1880 
1881  $this->doDelete( $reason, $suppress );
1882 
1883  WatchAction::doWatchOrUnwatch( $request->getCheck( 'wpWatch' ), $title, $user );
1884 
1885  return;
1886  }
1887 
1888  // Generate deletion reason
1889  $hasHistory = false;
1890  if ( !$reason ) {
1891  try {
1892  $reason = $this->generateReason( $hasHistory );
1893  } catch ( Exception $e ) {
1894  # if a page is horribly broken, we still want to be able to
1895  # delete it. So be lenient about errors here.
1896  wfDebug( "Error while building auto delete summary: $e" );
1897  $reason = '';
1898  }
1899  }
1900 
1901  // If the page has a history, insert a warning
1902  if ( $hasHistory ) {
1903  $title = $this->getTitle();
1904 
1905  // The following can use the real revision count as this is only being shown for users
1906  // that can delete this page.
1907  // This, as a side-effect, also makes sure that the following query isn't being run for
1908  // pages with a larger history, unless the user has the 'bigdelete' right
1909  // (and is about to delete this page).
1910  $dbr = wfGetDB( DB_REPLICA );
1911  $revisions = $edits = (int)$dbr->selectField(
1912  'revision',
1913  'COUNT(rev_page)',
1914  [ 'rev_page' => $title->getArticleID() ],
1915  __METHOD__
1916  );
1917 
1918  // @todo i18n issue/patchwork message
1919  $context->getOutput()->addHTML(
1920  '<strong class="mw-delete-warning-revisions">' .
1921  $context->msg( 'historywarning' )->numParams( $revisions )->parse() .
1922  $context->msg( 'word-separator' )->escaped() . $this->linkRenderer->makeKnownLink(
1923  $title,
1924  $context->msg( 'history' )->text(),
1925  [],
1926  [ 'action' => 'history' ] ) .
1927  '</strong>'
1928  );
1929 
1930  if ( $title->isBigDeletion() ) {
1931  global $wgDeleteRevisionsLimit;
1932  $context->getOutput()->wrapWikiMsg( "<div class='error'>\n$1\n</div>\n",
1933  [
1934  'delete-warning-toobig',
1935  $context->getLanguage()->formatNum( $wgDeleteRevisionsLimit )
1936  ]
1937  );
1938  }
1939  }
1940 
1941  $this->confirmDelete( $reason );
1942  }
1943 
1949  public function confirmDelete( $reason ) {
1950  wfDebug( "Article::confirmDelete\n" );
1951 
1952  $title = $this->getTitle();
1953  $ctx = $this->getContext();
1954  $outputPage = $ctx->getOutput();
1955  $outputPage->setPageTitle( wfMessage( 'delete-confirm', $title->getPrefixedText() ) );
1956  $outputPage->addBacklinkSubtitle( $title );
1957  $outputPage->setRobotPolicy( 'noindex,nofollow' );
1958  $outputPage->addModules( 'mediawiki.action.delete' );
1959 
1960  $backlinkCache = $title->getBacklinkCache();
1961  if ( $backlinkCache->hasLinks( 'pagelinks' ) || $backlinkCache->hasLinks( 'templatelinks' ) ) {
1962  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1963  'deleting-backlinks-warning' );
1964  }
1965 
1966  $subpageQueryLimit = 51;
1967  $subpages = $title->getSubpages( $subpageQueryLimit );
1968  $subpageCount = count( $subpages );
1969  if ( $subpageCount > 0 ) {
1970  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1971  [ 'deleting-subpages-warning', Message::numParam( $subpageCount ) ] );
1972  }
1973  $outputPage->addWikiMsg( 'confirmdeletetext' );
1974 
1975  Hooks::run( 'ArticleConfirmDelete', [ $this, $outputPage, &$reason ] );
1976 
1977  $user = $this->getContext()->getUser();
1978  $checkWatch = $user->getBoolOption( 'watchdeletion' ) || $user->isWatched( $title );
1979 
1980  $outputPage->enableOOUI();
1981 
1982  $fields = [];
1983 
1984  $options = Xml::listDropDownOptions(
1985  $ctx->msg( 'deletereason-dropdown' )->inContentLanguage()->text(),
1986  [ 'other' => $ctx->msg( 'deletereasonotherlist' )->inContentLanguage()->text() ]
1987  );
1988  $options = Xml::listDropDownOptionsOoui( $options );
1989 
1990  $fields[] = new OOUI\FieldLayout(
1991  new OOUI\DropdownInputWidget( [
1992  'name' => 'wpDeleteReasonList',
1993  'inputId' => 'wpDeleteReasonList',
1994  'tabIndex' => 1,
1995  'infusable' => true,
1996  'value' => '',
1997  'options' => $options
1998  ] ),
1999  [
2000  'label' => $ctx->msg( 'deletecomment' )->text(),
2001  'align' => 'top',
2002  ]
2003  );
2004 
2005  // HTML maxlength uses "UTF-16 code units", which means that characters outside BMP
2006  // (e.g. emojis) count for two each. This limit is overridden in JS to instead count
2007  // Unicode codepoints.
2008  $fields[] = new OOUI\FieldLayout(
2009  new OOUI\TextInputWidget( [
2010  'name' => 'wpReason',
2011  'inputId' => 'wpReason',
2012  'tabIndex' => 2,
2014  'infusable' => true,
2015  'value' => $reason,
2016  'autofocus' => true,
2017  ] ),
2018  [
2019  'label' => $ctx->msg( 'deleteotherreason' )->text(),
2020  'align' => 'top',
2021  ]
2022  );
2023 
2024  if ( $user->isLoggedIn() ) {
2025  $fields[] = new OOUI\FieldLayout(
2026  new OOUI\CheckboxInputWidget( [
2027  'name' => 'wpWatch',
2028  'inputId' => 'wpWatch',
2029  'tabIndex' => 3,
2030  'selected' => $checkWatch,
2031  ] ),
2032  [
2033  'label' => $ctx->msg( 'watchthis' )->text(),
2034  'align' => 'inline',
2035  'infusable' => true,
2036  ]
2037  );
2038  }
2039  $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
2040  if ( $permissionManager->userHasRight( $user, 'suppressrevision' ) ) {
2041  $fields[] = new OOUI\FieldLayout(
2042  new OOUI\CheckboxInputWidget( [
2043  'name' => 'wpSuppress',
2044  'inputId' => 'wpSuppress',
2045  'tabIndex' => 4,
2046  ] ),
2047  [
2048  'label' => $ctx->msg( 'revdelete-suppress' )->text(),
2049  'align' => 'inline',
2050  'infusable' => true,
2051  ]
2052  );
2053  }
2054 
2055  $fields[] = new OOUI\FieldLayout(
2056  new OOUI\ButtonInputWidget( [
2057  'name' => 'wpConfirmB',
2058  'inputId' => 'wpConfirmB',
2059  'tabIndex' => 5,
2060  'value' => $ctx->msg( 'deletepage' )->text(),
2061  'label' => $ctx->msg( 'deletepage' )->text(),
2062  'flags' => [ 'primary', 'destructive' ],
2063  'type' => 'submit',
2064  ] ),
2065  [
2066  'align' => 'top',
2067  ]
2068  );
2069 
2070  $fieldset = new OOUI\FieldsetLayout( [
2071  'label' => $ctx->msg( 'delete-legend' )->text(),
2072  'id' => 'mw-delete-table',
2073  'items' => $fields,
2074  ] );
2075 
2076  $form = new OOUI\FormLayout( [
2077  'method' => 'post',
2078  'action' => $title->getLocalURL( 'action=delete' ),
2079  'id' => 'deleteconfirm',
2080  ] );
2081  $form->appendContent(
2082  $fieldset,
2083  new OOUI\HtmlSnippet(
2084  Html::hidden( 'wpEditToken', $user->getEditToken( [ 'delete', $title->getPrefixedText() ] ) )
2085  )
2086  );
2087 
2088  $outputPage->addHTML(
2089  new OOUI\PanelLayout( [
2090  'classes' => [ 'deletepage-wrapper' ],
2091  'expanded' => false,
2092  'padded' => true,
2093  'framed' => true,
2094  'content' => $form,
2095  ] )
2096  );
2097 
2098  if ( $permissionManager->userHasRight( $user, 'editinterface' ) ) {
2099  $link = $this->linkRenderer->makeKnownLink(
2100  $ctx->msg( 'deletereason-dropdown' )->inContentLanguage()->getTitle(),
2101  wfMessage( 'delete-edit-reasonlist' )->text(),
2102  [],
2103  [ 'action' => 'edit' ]
2104  );
2105  $outputPage->addHTML( '<p class="mw-delete-editreasons">' . $link . '</p>' );
2106  }
2107 
2108  $deleteLogPage = new LogPage( 'delete' );
2109  $outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) );
2110  LogEventsList::showLogExtract( $outputPage, 'delete', $title );
2111  }
2112 
2121  public function doDelete( $reason, $suppress = false, $immediate = false ) {
2122  $error = '';
2123  $context = $this->getContext();
2124  $outputPage = $context->getOutput();
2125  $user = $context->getUser();
2126  $status = $this->mPage->doDeleteArticleReal( $reason, $suppress, 0, true, $error, $user,
2127  [], 'delete', $immediate );
2128 
2129  if ( $status->isOK() ) {
2130  $deleted = $this->getTitle()->getPrefixedText();
2131 
2132  $outputPage->setPageTitle( wfMessage( 'actioncomplete' ) );
2133  $outputPage->setRobotPolicy( 'noindex,nofollow' );
2134 
2135  if ( $status->isGood() ) {
2136  $loglink = '[[Special:Log/delete|' . wfMessage( 'deletionlog' )->text() . ']]';
2137  $outputPage->addWikiMsg( 'deletedtext', wfEscapeWikiText( $deleted ), $loglink );
2138  Hooks::run( 'ArticleDeleteAfterSuccess', [ $this->getTitle(), $outputPage ] );
2139  } else {
2140  $outputPage->addWikiMsg( 'delete-scheduled', wfEscapeWikiText( $deleted ) );
2141  }
2142 
2143  $outputPage->returnToMain( false );
2144  } else {
2145  $outputPage->setPageTitle(
2146  wfMessage( 'cannotdelete-title',
2147  $this->getTitle()->getPrefixedText() )
2148  );
2149 
2150  if ( $error == '' ) {
2151  $outputPage->wrapWikiTextAsInterface(
2152  'error mw-error-cannotdelete',
2153  $status->getWikiText( false, false, $context->getLanguage() )
2154  );
2155  $deleteLogPage = new LogPage( 'delete' );
2156  $outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) );
2157 
2159  $outputPage,
2160  'delete',
2161  $this->getTitle()
2162  );
2163  } else {
2164  $outputPage->addHTML( $error );
2165  }
2166  }
2167  }
2168 
2169  /* Caching functions */
2170 
2178  protected function tryFileCache() {
2179  static $called = false;
2180 
2181  if ( $called ) {
2182  wfDebug( "Article::tryFileCache(): called twice!?\n" );
2183  return false;
2184  }
2185 
2186  $called = true;
2187  if ( $this->isFileCacheable() ) {
2188  $cache = new HTMLFileCache( $this->getTitle(), 'view' );
2189  if ( $cache->isCacheGood( $this->mPage->getTouched() ) ) {
2190  wfDebug( "Article::tryFileCache(): about to load file\n" );
2191  $cache->loadFromFileCache( $this->getContext() );
2192  return true;
2193  } else {
2194  wfDebug( "Article::tryFileCache(): starting buffer\n" );
2195  ob_start( [ &$cache, 'saveToFileCache' ] );
2196  }
2197  } else {
2198  wfDebug( "Article::tryFileCache(): not cacheable\n" );
2199  }
2200 
2201  return false;
2202  }
2203 
2209  public function isFileCacheable( $mode = HTMLFileCache::MODE_NORMAL ) {
2210  $cacheable = false;
2211 
2212  if ( HTMLFileCache::useFileCache( $this->getContext(), $mode ) ) {
2213  $cacheable = $this->mPage->getId()
2214  && !$this->mRedirectedFrom && !$this->getTitle()->isRedirect();
2215  // Extension may have reason to disable file caching on some pages.
2216  if ( $cacheable ) {
2217  // Avoid PHP 7.1 warning of passing $this by reference
2218  $articlePage = $this;
2219  $cacheable = Hooks::run( 'IsFileCacheable', [ &$articlePage ] );
2220  }
2221  }
2222 
2223  return $cacheable;
2224  }
2225 
2239  public function getParserOutput( $oldid = null, User $user = null ) {
2240  // XXX: bypasses mParserOptions and thus setParserOptions()
2241 
2242  if ( $user === null ) {
2243  $parserOptions = $this->getParserOptions();
2244  } else {
2245  $parserOptions = $this->mPage->makeParserOptions( $user );
2246  }
2247 
2248  return $this->mPage->getParserOutput( $parserOptions, $oldid );
2249  }
2250 
2257  public function setParserOptions( ParserOptions $options ) {
2258  if ( $this->mParserOptions ) {
2259  throw new MWException( "can't change parser options after they have already been set" );
2260  }
2261 
2262  // clone, so if $options is modified later, it doesn't confuse the parser cache.
2263  $this->mParserOptions = clone $options;
2264  }
2265 
2270  public function getParserOptions() {
2271  if ( !$this->mParserOptions ) {
2272  $this->mParserOptions = $this->mPage->makeParserOptions( $this->getContext() );
2273  }
2274  // Clone to allow modifications of the return value without affecting cache
2275  return clone $this->mParserOptions;
2276  }
2277 
2284  public function setContext( $context ) {
2285  $this->mContext = $context;
2286  }
2287 
2294  public function getContext() {
2295  if ( $this->mContext instanceof IContextSource ) {
2296  return $this->mContext;
2297  } else {
2298  wfDebug( __METHOD__ . " called and \$mContext is null. " .
2299  "Return RequestContext::getMain(); for sanity\n" );
2300  return RequestContext::getMain();
2301  }
2302  }
2303 
2311  public function __get( $fname ) {
2312  if ( property_exists( $this->mPage, $fname ) ) {
2313  # wfWarn( "Access to raw $fname field " . __CLASS__ );
2314  return $this->mPage->$fname;
2315  }
2316  trigger_error( 'Inaccessible property via __get(): ' . $fname, E_USER_NOTICE );
2317  }
2318 
2326  public function __set( $fname, $fvalue ) {
2327  if ( property_exists( $this->mPage, $fname ) ) {
2328  # wfWarn( "Access to raw $fname field of " . __CLASS__ );
2329  $this->mPage->$fname = $fvalue;
2330  // Note: extensions may want to toss on new fields
2331  } elseif ( !in_array( $fname, [ 'mContext', 'mPage' ] ) ) {
2332  $this->mPage->$fname = $fvalue;
2333  } else {
2334  trigger_error( 'Inaccessible property via __set(): ' . $fname, E_USER_NOTICE );
2335  }
2336  }
2337 
2344  public function checkFlags( $flags ) {
2345  return $this->mPage->checkFlags( $flags );
2346  }
2347 
2353  public function checkTouched() {
2354  return $this->mPage->checkTouched();
2355  }
2356 
2361  public function clearPreparedEdit() {
2362  $this->mPage->clearPreparedEdit();
2363  }
2364 
2378  public function doDeleteArticleReal(
2379  $reason, $suppress = false, $u1 = null, $u2 = null, &$error = '', User $user = null,
2380  $tags = [], $immediate = false
2381  ) {
2382  return $this->mPage->doDeleteArticleReal(
2383  $reason, $suppress, $u1, $u2, $error, $user, $tags, 'delete', $immediate
2384  );
2385  }
2386 
2395  public function doDeleteUpdates(
2396  $id,
2397  Content $content = null,
2398  $revision = null,
2399  User $user = null
2400  ) {
2401  $this->mPage->doDeleteUpdates( $id, $content, $revision, $user );
2402  }
2403 
2416  public function doEditContent( Content $content, $summary, $flags = 0, $originalRevId = false,
2417  User $user = null, $serialFormat = null
2418  ) {
2419  wfDeprecated( __METHOD__, '1.29' );
2420  return $this->mPage->doEditContent( $content, $summary, $flags, $originalRevId,
2421  $user, $serialFormat
2422  );
2423  }
2424 
2432  public function doEditUpdates( Revision $revision, User $user, array $options = [] ) {
2433  $this->mPage->doEditUpdates( $revision, $user, $options );
2434  }
2435 
2443  public function doPurge() {
2444  return $this->mPage->doPurge();
2445  }
2446 
2453  public function doViewUpdates( User $user, $oldid = 0 ) {
2454  $this->mPage->doViewUpdates( $user, $oldid );
2455  }
2456 
2462  public function exists() {
2463  return $this->mPage->exists();
2464  }
2465 
2471  public function followRedirect() {
2472  return $this->mPage->followRedirect();
2473  }
2474 
2480  public function getActionOverrides() {
2481  return $this->mPage->getActionOverrides();
2482  }
2483 
2490  public function getAutoDeleteReason( &$hasHistory ) {
2491  return $this->mPage->getAutoDeleteReason( $hasHistory );
2492  }
2493 
2499  public function getCategories() {
2500  return $this->mPage->getCategories();
2501  }
2502 
2510  public function getComment( $audience = RevisionRecord::FOR_PUBLIC, User $user = null ) {
2511  return $this->mPage->getComment( $audience, $user );
2512  }
2513 
2519  public function getContentHandler() {
2520  return $this->mPage->getContentHandler();
2521  }
2522 
2528  public function getContentModel() {
2529  return $this->mPage->getContentModel();
2530  }
2531 
2537  public function getContributors() {
2538  return $this->mPage->getContributors();
2539  }
2540 
2548  public function getCreator( $audience = RevisionRecord::FOR_PUBLIC, User $user = null ) {
2549  return $this->mPage->getCreator( $audience, $user );
2550  }
2551 
2558  public function getDeletionUpdates( Content $content = null ) {
2559  return $this->mPage->getDeletionUpdates( $content );
2560  }
2561 
2567  public function getHiddenCategories() {
2568  return $this->mPage->getHiddenCategories();
2569  }
2570 
2576  public function getId() {
2577  return $this->mPage->getId();
2578  }
2579 
2585  public function getLatest() {
2586  return $this->mPage->getLatest();
2587  }
2588 
2594  public function getLinksTimestamp() {
2595  return $this->mPage->getLinksTimestamp();
2596  }
2597 
2603  public function getMinorEdit() {
2604  return $this->mPage->getMinorEdit();
2605  }
2606 
2612  public function getOldestRevision() {
2613  return $this->mPage->getOldestRevision();
2614  }
2615 
2621  public function getRedirectTarget() {
2622  return $this->mPage->getRedirectTarget();
2623  }
2624 
2631  public function getRedirectURL( $rt ) {
2632  return $this->mPage->getRedirectURL( $rt );
2633  }
2634 
2640  public function getRevision() {
2641  return $this->mPage->getRevision();
2642  }
2643 
2649  public function getTimestamp() {
2650  return $this->mPage->getTimestamp();
2651  }
2652 
2658  public function getTouched() {
2659  return $this->mPage->getTouched();
2660  }
2661 
2669  public function getUndoContent( Revision $undo, Revision $undoafter = null ) {
2670  return $this->mPage->getUndoContent( $undo, $undoafter );
2671  }
2672 
2680  public function getUser( $audience = RevisionRecord::FOR_PUBLIC, User $user = null ) {
2681  return $this->mPage->getUser( $audience, $user );
2682  }
2683 
2691  public function getUserText( $audience = RevisionRecord::FOR_PUBLIC, User $user = null ) {
2692  return $this->mPage->getUserText( $audience, $user );
2693  }
2694 
2700  public function hasViewableContent() {
2701  return $this->mPage->hasViewableContent();
2702  }
2703 
2711  public function insertOn( $dbw, $pageId = null ) {
2712  return $this->mPage->insertOn( $dbw, $pageId );
2713  }
2714 
2726  public function insertProtectNullRevision( $revCommentMsg, array $limit,
2727  array $expiry, $cascade, $reason, $user = null
2728  ) {
2729  return $this->mPage->insertProtectNullRevision( $revCommentMsg, $limit,
2730  $expiry, $cascade, $reason, $user
2731  );
2732  }
2733 
2739  public function insertRedirect() {
2740  return $this->mPage->insertRedirect();
2741  }
2742 
2750  public function insertRedirectEntry( Title $rt, $oldLatest = null ) {
2751  return $this->mPage->insertRedirectEntry( $rt, $oldLatest );
2752  }
2753 
2760  public function isCountable( $editInfo = false ) {
2761  return $this->mPage->isCountable( $editInfo );
2762  }
2763 
2769  public function isRedirect() {
2770  return $this->mPage->isRedirect();
2771  }
2772 
2779  public function loadFromRow( $data, $from ) {
2780  $this->mPage->loadFromRow( $data, $from );
2781  }
2782 
2788  public function loadPageData( $from = 'fromdb' ) {
2789  $this->mPage->loadPageData( $from );
2790  }
2791 
2797  public function lockAndGetLatest() {
2798  return $this->mPage->lockAndGetLatest();
2799  }
2800 
2807  public function makeParserOptions( $context ) {
2808  return $this->mPage->makeParserOptions( $context );
2809  }
2810 
2819  public function pageDataFromId( $dbr, $id, $options = [] ) {
2820  return $this->mPage->pageDataFromId( $dbr, $id, $options );
2821  }
2822 
2831  public function pageDataFromTitle( $dbr, $title, $options = [] ) {
2832  return $this->mPage->pageDataFromTitle( $dbr, $title, $options );
2833  }
2834 
2845  public function prepareContentForEdit(
2846  Content $content, $revision = null, User $user = null,
2847  $serialFormat = null, $useCache = true
2848  ) {
2849  return $this->mPage->prepareContentForEdit(
2850  $content, $revision, $user,
2851  $serialFormat, $useCache
2852  );
2853  }
2854 
2862  public function protectDescription( array $limit, array $expiry ) {
2863  return $this->mPage->protectDescription( $limit, $expiry );
2864  }
2865 
2873  public function protectDescriptionLog( array $limit, array $expiry ) {
2874  return $this->mPage->protectDescriptionLog( $limit, $expiry );
2875  }
2876 
2886  public function replaceSectionAtRev( $sectionId, Content $sectionContent,
2887  $sectionTitle = '', $baseRevId = null
2888  ) {
2889  return $this->mPage->replaceSectionAtRev( $sectionId, $sectionContent,
2890  $sectionTitle, $baseRevId
2891  );
2892  }
2893 
2903  public function replaceSectionContent(
2904  $sectionId, Content $sectionContent, $sectionTitle = '', $edittime = null
2905  ) {
2906  return $this->mPage->replaceSectionContent(
2907  $sectionId, $sectionContent, $sectionTitle, $edittime
2908  );
2909  }
2910 
2916  public function setTimestamp( $ts ) {
2917  $this->mPage->setTimestamp( $ts );
2918  }
2919 
2927  public function shouldCheckParserCache( ParserOptions $parserOptions, $oldId ) {
2928  return $this->mPage->shouldCheckParserCache( $parserOptions, $oldId );
2929  }
2930 
2936  public function supportsSections() {
2937  return $this->mPage->supportsSections();
2938  }
2939 
2945  public function triggerOpportunisticLinksUpdate( ParserOutput $parserOutput ) {
2946  $this->mPage->triggerOpportunisticLinksUpdate( $parserOutput );
2947  }
2948 
2956  public function updateCategoryCounts( array $added, array $deleted, $id = 0 ) {
2957  $this->mPage->updateCategoryCounts( $added, $deleted, $id );
2958  }
2959 
2967  public function updateIfNewerOn( $dbw, $revision ) {
2968  return $this->mPage->updateIfNewerOn( $dbw, $revision );
2969  }
2970 
2979  public function updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect = null ) {
2980  return $this->mPage->updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect );
2981  }
2982 
2992  public function updateRevisionOn( $dbw, $revision, $lastRevision = null,
2993  $lastRevIsRedirect = null
2994  ) {
2995  return $this->mPage->updateRevisionOn( $dbw, $revision, $lastRevision,
2996  $lastRevIsRedirect
2997  );
2998  }
2999 
3008  public function doUpdateRestrictions( array $limit, array $expiry, &$cascade,
3009  $reason, User $user
3010  ) {
3011  return $this->mPage->doUpdateRestrictions( $limit, $expiry, $cascade, $reason, $user );
3012  }
3013 
3021  public function updateRestrictions( $limit = [], $reason = '',
3022  &$cascade = 0, $expiry = []
3023  ) {
3024  return $this->mPage->doUpdateRestrictions(
3025  $limit,
3026  $expiry,
3027  $cascade,
3028  $reason,
3029  $this->getContext()->getUser()
3030  );
3031  }
3032 
3044  public function doDeleteArticle(
3045  $reason, $suppress = false, $u1 = null, $u2 = null, &$error = '', $immediate = false
3046  ) {
3047  return $this->mPage->doDeleteArticle( $reason, $suppress, $u1, $u2, $error,
3048  null, $immediate );
3049  }
3050 
3060  public function doRollback( $fromP, $summary, $token, $bot, &$resultDetails, User $user = null ) {
3061  if ( !$user ) {
3062  $user = $this->getContext()->getUser();
3063  }
3064 
3065  return $this->mPage->doRollback( $fromP, $summary, $token, $bot, $resultDetails, $user );
3066  }
3067 
3076  public function commitRollback( $fromP, $summary, $bot, &$resultDetails, User $guser = null ) {
3077  if ( !$guser ) {
3078  $guser = $this->getContext()->getUser();
3079  }
3080 
3081  return $this->mPage->commitRollback( $fromP, $summary, $bot, $resultDetails, $guser );
3082  }
3083 
3088  public function generateReason( &$hasHistory ) {
3089  $title = $this->mPage->getTitle();
3090  $handler = ContentHandler::getForTitle( $title );
3091  return $handler->getAutoDeleteReason( $title, $hasHistory );
3092  }
3093 
3094  // ******
3095 }
ReadOnlyError
Show an error when the wiki is locked/read-only and the user tries to do something that requires writ...
Definition: ReadOnlyError.php:28
$wgSend404Code
$wgSend404Code
Some web hosts attempt to rewrite all responses with a 404 (not found) status code,...
Definition: DefaultSettings.php:3530
Article\showMissingArticle
showMissingArticle()
Show the error text for a missing article.
Definition: Article.php:1384
Article\checkFlags
checkFlags( $flags)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2344
ParserOptions
Set options of the Parser.
Definition: ParserOptions.php:42
Article\$mContentObject
Content null $mContentObject
Content of the main slot of $this->mRevision.
Definition: Article.php:67
HTMLFileCache\useFileCache
static useFileCache(IContextSource $context, $mode=self::MODE_NORMAL)
Check if pages can be cached for this request/user.
Definition: HTMLFileCache.php:93
Article\$mContext
IContextSource null $mContext
The context this Article is executed in.
Definition: Article.php:48
Page
Interface for type hinting (accepts WikiPage, Article, ImagePage, CategoryPage)
Definition: Page.php:29
Article\isRedirect
isRedirect()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2769
Article\getCategories
getCategories()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2499
Article\doDeleteArticleReal
doDeleteArticleReal( $reason, $suppress=false, $u1=null, $u2=null, &$error='', User $user=null, $tags=[], $immediate=false)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2378
Revision\RevisionRecord
Page revision base class.
Definition: RevisionRecord.php:46
ParserOutput
Definition: ParserOutput.php:25
Article\formatRobotPolicy
static formatRobotPolicy( $policy)
Converts a String robot policy into an associative array, to allow merging of several policies using ...
Definition: Article.php:1061
StatusValue\newFatal
static newFatal( $message,... $parameters)
Factory function for fatal errors.
Definition: StatusValue.php:69
Article\$viewIsRenderAction
bool $viewIsRenderAction
Whether render() was called.
Definition: Article.php:128
Article\getRedirectTarget
getRedirectTarget()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2621
Article\view
view()
This is the default action of the index.php entry point: just view the page of the given title.
Definition: Article.php:614
Revision\newFromId
static newFromId( $id, $flags=0)
Load a page revision from a given revision ID number.
Definition: Revision.php:119
Xml\listDropDownOptionsOoui
static listDropDownOptionsOoui( $options)
Convert options for a drop-down box into a format accepted by OOUI\DropdownInputWidget etc.
Definition: Xml.php:581
Article\getContentModel
getContentModel()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2528
Article\getLinksTimestamp
getLinksTimestamp()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2594
Article\getOldIDFromRequest
getOldIDFromRequest()
Sets $this->mRedirectUrl to a correct URL if the query parameters are incorrect.
Definition: Article.php:350
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:130
Article\showPatrolFooter
showPatrolFooter()
If patrol is possible, output a patrol UI box.
Definition: Article.php:1202
$lang
if(!isset( $args[0])) $lang
Definition: testCompression.php:35
RecentChange\newFromConds
static newFromConds( $conds, $fname=__METHOD__, $dbType=DB_REPLICA)
Find the first recent change matching some specific conditions.
Definition: RecentChange.php:208
Article\getComment
getComment( $audience=RevisionRecord::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2510
Article\tryFileCache
tryFileCache()
checkLastModified returns true if it has taken care of all output to the client that is necessary for...
Definition: Article.php:2178
Article\getContentHandler
getContentHandler()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2519
Article\makeFetchErrorContent
makeFetchErrorContent()
Returns a Content object representing any error in $this->fetchContent, or null if there is no such e...
Definition: Article.php:532
Article\clearPreparedEdit
clearPreparedEdit()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2361
HTMLFileCache
Page view caching in the file system.
Definition: HTMLFileCache.php:33
Article\lockAndGetLatest
lockAndGetLatest()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2797
MediaWiki\Linker\LinkRenderer
Class that generates HTML links for pages.
Definition: LinkRenderer.php:41
Article\supportsSections
supportsSections()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2936
Article\getOldestRevision
getOldestRevision()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2612
Article\getDeletionUpdates
getDeletionUpdates(Content $content=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2558
Article\checkTouched
checkTouched()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2353
Article\getRevision
getRevision()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2640
RC_LOG
const RC_LOG
Definition: Defines.php:124
CategoryPage
Definition: CategoryPage.php:30
WikiPage
Class representing a MediaWiki article and history.
Definition: WikiPage.php:46
PoolWorkArticleView
Definition: PoolWorkArticleView.php:28
NS_FILE
const NS_FILE
Definition: Defines.php:66
Linker\revComment
static revComment(Revision $rev, $local=false, $isPublic=false, $useParentheses=true)
Wrap and format the given revision's comment block, if the current user is allowed to view it.
Definition: Linker.php:1574
wfReadOnly
wfReadOnly()
Check whether the wiki is in read-only mode.
Definition: GlobalFunctions.php:1170
Article\$mRevision
Revision null $mRevision
Revision to be shown.
Definition: Article.php:114
User\newFromName
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition: User.php:537
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1263
Article\showDiffPage
showDiffPage()
Show a diff page according to current request variables.
Definition: Article.php:928
ContentHandler\getForTitle
static getForTitle(Title $title)
Returns the appropriate ContentHandler singleton for the given title.
Definition: ContentHandler.php:201
$wgArticleRobotPolicies
$wgArticleRobotPolicies
Robot policies per article.
Definition: DefaultSettings.php:8027
ImagePage
Definition: ImagePage.php:34
PermissionsError
Show an error when a user tries to do something they do not have the necessary permissions for.
Definition: PermissionsError.php:30
Article\confirmDelete
confirmDelete( $reason)
Output deletion confirmation dialog.
Definition: Article.php:1949
Article\updateCategoryCounts
updateCategoryCounts(array $added, array $deleted, $id=0)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2956
Article\doDeleteArticle
doDeleteArticle( $reason, $suppress=false, $u1=null, $u2=null, &$error='', $immediate=false)
Definition: Article.php:3044
Article\adjustDisplayTitle
adjustDisplayTitle(ParserOutput $pOutput)
Adjust title for pages with displaytitle, -{T|}- or language conversion.
Definition: Article.php:913
Article\showNamespaceHeader
showNamespaceHeader()
Show a header specific to the namespace currently being viewed, like [[MediaWiki:Talkpagetext]].
Definition: Article.php:1167
Article\shouldCheckParserCache
shouldCheckParserCache(ParserOptions $parserOptions, $oldId)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2927
$wgUseRCPatrol
$wgUseRCPatrol
Use RC Patrolling to check for vandalism (from recent changes and watchlists) New pages and new files...
Definition: DefaultSettings.php:6900
Xml\openElement
static openElement( $element, $attribs=null)
This opens an XML element.
Definition: Xml.php:108
$wgUseNPPatrol
$wgUseNPPatrol
Use new page patrolling to check new pages on Special:Newpages.
Definition: DefaultSettings.php:6916
Article\$linkRenderer
LinkRenderer $linkRenderer
Definition: Article.php:133
ProtectionForm
Handles the page protection UI and backend.
Definition: ProtectionForm.php:31
Article\doDelete
doDelete( $reason, $suppress=false, $immediate=false)
Perform a deletion and output success or failure messages.
Definition: Article.php:2121
Article\protectDescriptionLog
protectDescriptionLog(array $limit, array $expiry)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2873
Wikimedia\Rdbms\IDatabase
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:38
Article\getRevisionRedirectTarget
getRevisionRedirectTarget(RevisionRecord $revision)
Definition: Article.php:901
Article\$mRedirectedFrom
Title null $mRedirectedFrom
Title from which we were redirected here, if any.
Definition: Article.php:85
Article\doEditContent
doEditContent(Content $content, $summary, $flags=0, $originalRevId=false, User $user=null, $serialFormat=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2416
$dbr
$dbr
Definition: testCompression.php:52
Status
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition: Status.php:40
Article\getSubstituteContent
getSubstituteContent()
Returns Content object to use when the page does not exist.
Definition: Article.php:297
Revision
Definition: Revision.php:40
Article\unprotect
unprotect()
action=unprotect handler (alias)
Definition: Article.php:1810
Article\insertRedirect
insertRedirect()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2739
MediaWiki\Block\DatabaseBlock
A DatabaseBlock (unlike a SystemBlock) is stored in the database, may give rise to autoblocks and may...
Definition: DatabaseBlock.php:54
Article\getRedirectedFrom
getRedirectedFrom()
Get the page this view was redirected from.
Definition: Article.php:214
Article\fetchContentObject
fetchContentObject()
Get text content object Does NOT follow redirects.
Definition: Article.php:425
Article\clear
clear()
Clear the object.
Definition: Article.php:249
Article\updateRedirectOn
updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2979
Article\newFromWikiPage
static newFromWikiPage(WikiPage $page, IContextSource $context)
Create an Article object of the appropriate class for the given page.
Definition: Article.php:203
Article\getTouched
getTouched()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2658
MWException
MediaWiki exception.
Definition: MWException.php:26
Article\getUserText
getUserText( $audience=RevisionRecord::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2691
Article\getTitle
getTitle()
Get the title object of the article.
Definition: Article.php:232
Article\updateRevisionOn
updateRevisionOn( $dbw, $revision, $lastRevision=null, $lastRevIsRedirect=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2992
Article\render
render()
Handle action=render.
Definition: Article.php:1791
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
Definition: GlobalFunctions.php:1044
Article\getCreator
getCreator( $audience=RevisionRecord::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2548
Article\insertProtectNullRevision
insertProtectNullRevision( $revCommentMsg, array $limit, array $expiry, $cascade, $reason, $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2726
Article\$mParserOutput
ParserOutput null false $mParserOutput
The ParserOutput generated for viewing the page, initialized by view().
Definition: Article.php:121
Article\addHelpLink
addHelpLink( $to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
Definition: Article.php:1774
Article\exists
exists()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2462
Article\setRedirectedFrom
setRedirectedFrom(Title $from)
Tell the page view functions that this view was redirected from another page on the wiki.
Definition: Article.php:223
getPermissionManager
getPermissionManager()
$wgUseFileCache
$wgUseFileCache
This will cache static pages for non-logged-in users to reduce database traffic on public sites.
Definition: DefaultSettings.php:2652
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2562
Article\applyContentOverride
applyContentOverride(Content $override)
Applies a content override by constructing a fake Revision object and assigning it to mRevision.
Definition: Article.php:552
Article\__construct
__construct(Title $title, $oldId=null)
Constructor and clear the article.
Definition: Article.php:140
Article\showDeletedRevisionHeader
showDeletedRevisionHeader()
If the revision requested for view is deleted, check permissions.
Definition: Article.php:1521
IExpiringStore\TTL_DAY
const TTL_DAY
Definition: IExpiringStore.php:35
Article\isFileCacheable
isFileCacheable( $mode=HTMLFileCache::MODE_NORMAL)
Check if the page can be cached.
Definition: Article.php:2209
Article\doRollback
doRollback( $fromP, $summary, $token, $bot, &$resultDetails, User $user=null)
Definition: Article.php:3060
WikiPage\getTitle
getTitle()
Get the title object of the article.
Definition: WikiPage.php:297
LogPage
Class to simplify the use of log pages.
Definition: LogPage.php:33
Article\setOldSubtitle
setOldSubtitle( $oldid=0)
Generate the navigation links when browsing through an article revisions It shows the information as:...
Definition: Article.php:1564
Xml\element
static element( $element, $attribs=null, $contents='', $allowShortTag=true)
Format an XML element with given attributes and, optionally, text content.
Definition: Xml.php:41
ObjectCache\getInstance
static getInstance( $id)
Get a cached instance of the specified type of cache object.
Definition: ObjectCache.php:78
Article\getPage
getPage()
Get the WikiPage object of this instance.
Definition: Article.php:242
Article\getContext
getContext()
Gets the context this Article is executed in.
Definition: Article.php:2294
User\isIP
static isIP( $name)
Does the string match an anonymous IP address?
Definition: User.php:948
Article\$mParserOptions
ParserOptions null $mParserOptions
ParserOptions object for $wgUser articles.
Definition: Article.php:57
WatchAction\doWatchOrUnwatch
static doWatchOrUnwatch( $watch, Title $title, User $user)
Watch or unwatch a page.
Definition: WatchAction.php:91
Article\pageDataFromId
pageDataFromId( $dbr, $id, $options=[])
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2819
$title
$title
Definition: testCompression.php:36
Article\getRobotPolicy
getRobotPolicy( $action, ParserOutput $pOutput=null)
Get the robot policy to be used for the current view.
Definition: Article.php:981
Article\insertOn
insertOn( $dbw, $pageId=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2711
Linker\makeExternalLink
static makeExternalLink( $url, $text, $escape=true, $linktype='', $attribs=[], $title=null)
Make an external link.
Definition: Linker.php:849
Title\makeTitle
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:584
Article\getRedirectURL
getRedirectURL( $rt)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2631
LogEventsList\showLogExtract
static showLogExtract(&$out, $types=[], $page='', $user='', $param=[])
Show log extract.
Definition: LogEventsList.php:624
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
Article\newFromID
static newFromID( $id)
Constructor from a page id.
Definition: Article.php:159
Article\newPage
newPage(Title $title)
Definition: Article.php:150
NS_CATEGORY
const NS_CATEGORY
Definition: Defines.php:74
Article\getUndoContent
getUndoContent(Revision $undo, Revision $undoafter=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2669
Article\prepareContentForEdit
prepareContentForEdit(Content $content, $revision=null, User $user=null, $serialFormat=null, $useCache=true)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2845
$wgDeleteRevisionsLimit
$wgDeleteRevisionsLimit
Optional to restrict deletion of pages with higher revision counts to users with the 'bigdelete' perm...
Definition: DefaultSettings.php:5525
Linker\revUserTools
static revUserTools( $rev, $isPublic=false, $useParentheses=true)
Generate a user tool link cluster if the current user is allowed to view it.
Definition: Linker.php:1125
Article\$mRevIdFetched
int $mRevIdFetched
Revision ID of revision that was loaded.
Definition: Article.php:95
$wgUseFilePatrol
$wgUseFilePatrol
Use file patrolling to check new files on Special:Newfiles.
Definition: DefaultSettings.php:6927
Article\getLatest
getLatest()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2585
wfDebug
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Definition: GlobalFunctions.php:913
Article\isCurrent
isCurrent()
Returns true if the currently-referenced revision is the current edit to this page (and it exists).
Definition: Article.php:568
ContentHandler\makeContent
static makeContent( $text, Title $title=null, $modelId=null, $format=null)
Convenience function for creating a Content object from a given textual representation.
Definition: ContentHandler.php:135
Article\getAutoDeleteReason
getAutoDeleteReason(&$hasHistory)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2490
Article\$mContentLoaded
bool $mContentLoaded
Is the target revision loaded? Set by fetchRevisionRecord().
Definition: Article.php:75
Article\setParserOptions
setParserOptions(ParserOptions $options)
Override the ParserOptions used to render the primary article wikitext.
Definition: Article.php:2257
Article\isCountable
isCountable( $editInfo=false)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2760
ParserOutput\getTitleText
getTitleText()
Definition: ParserOutput.php:550
Article\doPurge
doPurge()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2443
$content
$content
Definition: router.php:78
NS_USER_TALK
const NS_USER_TALK
Definition: Defines.php:63
Article\hasViewableContent
hasViewableContent()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2700
Article\$mOldId
int null $mOldId
The oldid of the article that was requested to be shown, 0 for the current revision.
Definition: Article.php:82
NS_MEDIA
const NS_MEDIA
Definition: Defines.php:48
Article\getContributors
getContributors()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2537
StatusValue\newGood
static newGood( $value=null)
Factory function for good results.
Definition: StatusValue.php:81
Revision\MutableRevisionRecord
Definition: MutableRevisionRecord.php:42
HTMLFileCache\MODE_NORMAL
const MODE_NORMAL
Definition: HTMLFileCache.php:34
Article\loadFromRow
loadFromRow( $data, $from)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2779
Article\getEmptyPageParserOutput
getEmptyPageParserOutput(ParserOptions $options)
Returns ParserOutput to use when a page does not exist.
Definition: Article.php:324
Linker\getRevDeleteLink
static getRevDeleteLink(User $user, Revision $rev, LinkTarget $title)
Get a revision-deletion link, or disabled link, or nothing, depending on user permissions & the setti...
Definition: Linker.php:2107
Article\protect
protect()
action=protect handler
Definition: Article.php:1802
Article\getTimestamp
getTimestamp()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2649
ParserOptions\newCanonical
static newCanonical( $context=null, $userLang=null)
Creates a "canonical" ParserOptions object.
Definition: ParserOptions.php:1077
wfEscapeWikiText
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
Definition: GlobalFunctions.php:1550
Article\getMinorEdit
getMinorEdit()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2603
Article\commitRollback
commitRollback( $fromP, $summary, $bot, &$resultDetails, User $guser=null)
Definition: Article.php:3076
$wgNamespaceRobotPolicies
$wgNamespaceRobotPolicies
Robot policies per namespaces.
Definition: DefaultSettings.php:7999
Article\getParserOutput
getParserOutput( $oldid=null, User $user=null)
#-
Definition: Article.php:2239
Article\getContentObject
getContentObject()
Returns a Content object representing the pages effective display content, not necessarily the revisi...
Definition: Article.php:281
Article\followRedirect
followRedirect()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2471
Article\getHiddenCategories
getHiddenCategories()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2567
RequestContext\getMain
static getMain()
Get the RequestContext object associated with the main request.
Definition: RequestContext.php:447
Article\setTimestamp
setTimestamp( $ts)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2916
IContextSource
Interface for objects which can provide a MediaWiki context on request.
Definition: IContextSource.php:53
$context
$context
Definition: load.php:40
Article\$mPage
WikiPage null $mPage
The WikiPage object of this instance.
Definition: Article.php:51
Article\__get
__get( $fname)
Use PHP's magic __get handler to handle accessing of raw WikiPage fields for backwards compatibility.
Definition: Article.php:2311
Content
Base interface for content objects.
Definition: Content.php:34
CommentStore\COMMENT_CHARACTER_LIMIT
const COMMENT_CHARACTER_LIMIT
Maximum length of a comment in UTF-8 characters.
Definition: CommentStore.php:37
Article\replaceSectionAtRev
replaceSectionAtRev( $sectionId, Content $sectionContent, $sectionTitle='', $baseRevId=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2886
Hooks\isRegistered
static isRegistered( $name)
Returns true if a hook has a function registered to it.
Definition: Hooks.php:80
Article\fetchRevisionRecord
fetchRevisionRecord()
Fetches the revision to work on.
Definition: Article.php:442
Title
Represents a title within MediaWiki.
Definition: Title.php:42
Skin\makeUrl
static makeUrl( $name, $urlaction='')
Definition: Skin.php:1209
Article\getUser
getUser( $audience=RevisionRecord::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2680
$cache
$cache
Definition: mcc.php:33
Article\replaceSectionContent
replaceSectionContent( $sectionId, Content $sectionContent, $sectionTitle='', $edittime=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2903
MediaWiki\Linker\LinkRenderer\makeKnownLink
makeKnownLink(LinkTarget $target, $text=null, array $extraAttribs=[], array $query=[])
Definition: LinkRenderer.php:290
Article\getRevIdFetched
getRevIdFetched()
Use this to fetch the rev ID used on page views.
Definition: Article.php:602
MessageContent
Wrapper allowing us to handle a system message as a Content object.
Definition: MessageContent.php:36
Article\makeParserOptions
makeParserOptions( $context)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2807
Article\showRedirectedFromHeader
showRedirectedFromHeader()
If this request is a redirect view, send "redirected from" subtitle to the output.
Definition: Article.php:1090
RecentChange\isInRCLifespan
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...
Definition: RecentChange.php:1131
Revision\RevisionRecord\getContent
getContent( $role, $audience=self::FOR_PUBLIC, User $user=null)
Returns the Content of the given slot of this revision.
Definition: RevisionRecord.php:167
NS_USER
const NS_USER
Definition: Defines.php:62
Article\getId
getId()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2576
Article\getActionOverrides
getActionOverrides()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2480
Article\$mRedirectUrl
string bool $mRedirectUrl
URL to redirect to or false if none.
Definition: Article.php:88
Article\doUpdateRestrictions
doUpdateRestrictions(array $limit, array $expiry, &$cascade, $reason, User $user)
Definition: Article.php:3008
Article\updateRestrictions
updateRestrictions( $limit=[], $reason='', &$cascade=0, $expiry=[])
Definition: Article.php:3021
Article\protectDescription
protectDescription(array $limit, array $expiry)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2862
Article\getParserOptions
getParserOptions()
Get parser options suitable for rendering the primary article wikitext.
Definition: Article.php:2270
MediaWiki\Edit\PreparedEdit
Represents information returned by WikiPage::prepareContentForEdit()
Definition: PreparedEdit.php:35
$wgRedirectSources
$wgRedirectSources
If local interwikis are set up which allow redirects, set this regexp to restrict URLs which will be ...
Definition: DefaultSettings.php:3996
Article\updateIfNewerOn
updateIfNewerOn( $dbw, $revision)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2967
NS_MEDIAWIKI
const NS_MEDIAWIKI
Definition: Defines.php:68
$t
$t
Definition: testCompression.php:71
Article
Class for viewing MediaWiki article and history.
Definition: Article.php:43
Article\doDeleteUpdates
doDeleteUpdates( $id, Content $content=null, $revision=null, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2395
Article\pageDataFromTitle
pageDataFromTitle( $dbr, $title, $options=[])
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2831
Article\triggerOpportunisticLinksUpdate
triggerOpportunisticLinksUpdate(ParserOutput $parserOutput)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2945
Article\getOldID
getOldID()
Definition: Article.php:337
EditPage\POST_EDIT_COOKIE_KEY_PREFIX
const POST_EDIT_COOKIE_KEY_PREFIX
Prefix of key for cookie used to pass post-edit state.
Definition: EditPage.php:204
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:52
Article\setContext
setContext( $context)
Sets the context this Article is executed in.
Definition: Article.php:2284
Title\newFromID
static newFromID( $id, $flags=0)
Create a new Title from an article ID.
Definition: Title.php:465
Hooks\run
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:200
Article\insertRedirectEntry
insertRedirectEntry(Title $rt, $oldLatest=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2750
MediaWiki\Linker\LinkRenderer\makeLink
makeLink(LinkTarget $target, $text=null, array $extraAttribs=[], array $query=[])
Definition: LinkRenderer.php:153
$wgDefaultRobotPolicy
$wgDefaultRobotPolicy
Default robot policy.
Definition: DefaultSettings.php:7983
Article\getRevisionFetched
getRevisionFetched()
Get the fetched Revision object depending on request parameters or null on failure.
Definition: Article.php:586
Article\viewRedirect
viewRedirect( $target, $appendSubtitle=true, $forceKnown=false)
Return the HTML for the top of a redirect page.
Definition: Article.php:1705
Article\loadPageData
loadPageData( $from='fromdb')
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2788
Language
Internationalisation code.
Definition: Language.php:39
Article\doEditUpdates
doEditUpdates(Revision $revision, User $user, array $options=[])
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2432
Article\__set
__set( $fname, $fvalue)
Use PHP's magic __set handler to handle setting of raw WikiPage fields for backwards compatibility.
Definition: Article.php:2326
Article\showViewFooter
showViewFooter()
Show the footer section of an ordinary page view.
Definition: Article.php:1179
Revision\SlotRecord
Value object representing a content slot associated with a page revision.
Definition: SlotRecord.php:39
Article\getRedirectHeaderHtml
static getRedirectHeaderHtml(Language $lang, $target, $forceKnown=false)
Return the HTML for the top of a redirect page.
Definition: Article.php:1727
Xml\listDropDownOptions
static listDropDownOptions( $list, $params=[])
Build options for a drop-down box from a textual list.
Definition: Xml.php:539
Article\newFromTitle
static newFromTitle( $title, IContextSource $context)
Create an Article object of the appropriate class for the given page.
Definition: Article.php:171
Article\generateReason
generateReason(&$hasHistory)
Definition: Article.php:3088
Article\purgePatrolFooterCache
static purgePatrolFooterCache( $articleID)
Purge the cache used to check if it is worth showing the patrol footer For example,...
Definition: Article.php:1375
Article\doViewUpdates
doViewUpdates(User $user, $oldid=0)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2453
Article\$fetchResult
Status null $fetchResult
represents the outcome of fetchRevisionRecord().
Definition: Article.php:105