MediaWiki  master
Article.php
Go to the documentation of this file.
1 <?php
25 use MediaWiki\HookContainer\ProtectedHookAccessorTrait;
33 use Wikimedia\IPUtils;
34 use Wikimedia\NonSerializable\NonSerializableTrait;
36 
47 class Article implements Page {
48  use ProtectedHookAccessorTrait;
49  use NonSerializableTrait;
50 
56  protected $mContext;
57 
59  protected $mPage;
60 
65  public $mOldId;
66 
68  public $mRedirectedFrom = null;
69 
71  public $mRedirectUrl = false;
72 
77  private $fetchResult = null;
78 
84  public $mParserOutput = null;
85 
91  protected $viewIsRenderAction = false;
92 
96  protected $linkRenderer;
97 
101  private $revisionStore;
102 
112  private $mRevisionRecord = null;
113 
118  public function __construct( Title $title, $oldId = null ) {
119  $this->mOldId = $oldId;
120  $this->mPage = $this->newPage( $title );
121 
122  $services = MediaWikiServices::getInstance();
123  $this->linkRenderer = $services->getLinkRenderer();
124  $this->revisionStore = $services->getRevisionStore();
125  }
126 
131  protected function newPage( Title $title ) {
132  return new WikiPage( $title );
133  }
134 
140  public static function newFromID( $id ) {
141  $t = Title::newFromID( $id );
142  return $t == null ? null : new static( $t );
143  }
144 
152  public static function newFromTitle( $title, IContextSource $context ) {
153  if ( $title->getNamespace() === NS_MEDIA ) {
154  // XXX: This should not be here, but where should it go?
155  $title = Title::makeTitle( NS_FILE, $title->getDBkey() );
156  }
157 
158  $page = null;
159  Hooks::runner()->onArticleFromTitle( $title, $page, $context );
160  if ( !$page ) {
161  switch ( $title->getNamespace() ) {
162  case NS_FILE:
163  $page = new ImagePage( $title );
164  break;
165  case NS_CATEGORY:
166  $page = new CategoryPage( $title );
167  break;
168  default:
169  $page = new Article( $title );
170  }
171  }
172  $page->setContext( $context );
173 
174  return $page;
175  }
176 
184  public static function newFromWikiPage( WikiPage $page, IContextSource $context ) {
185  $article = self::newFromTitle( $page->getTitle(), $context );
186  $article->mPage = $page; // override to keep process cached vars
187  return $article;
188  }
189 
195  public function getRedirectedFrom() {
196  return $this->mRedirectedFrom;
197  }
198 
204  public function setRedirectedFrom( Title $from ) {
205  $this->mRedirectedFrom = $from;
206  }
207 
213  public function getTitle() {
214  return $this->mPage->getTitle();
215  }
216 
223  public function getPage() {
224  return $this->mPage;
225  }
226 
227  public function clear() {
228  $this->mRedirectedFrom = null; # Title object if set
229  $this->mRedirectUrl = false;
230  $this->mRevisionRecord = null;
231  $this->fetchResult = null;
232 
233  // TODO hard-deprecate direct access to public fields
234 
235  $this->mPage->clear();
236  }
237 
255  protected function getContentObject() {
256  wfDeprecated( __METHOD__, '1.32' );
257  $content = null;
258  if ( $this->mPage->getId() === 0 ) {
259  $content = $this->getSubstituteContent();
260  } else {
261  $revision = $this->fetchRevisionRecord();
262  if ( $revision ) {
263  $content = $revision->getContent(
264  SlotRecord::MAIN,
265  RevisionRecord::FOR_THIS_USER,
266  $this->getContext()->getUser()
267  );
268  }
269  }
270  return $content;
271  }
272 
278  private function getSubstituteContent() {
279  # If this is a MediaWiki:x message, then load the messages
280  # and return the message value for x.
281  if ( $this->getTitle()->getNamespace() === NS_MEDIAWIKI ) {
282  $text = $this->getTitle()->getDefaultMessageText();
283  if ( $text === false ) {
284  $text = '';
285  }
286 
287  $content = ContentHandler::makeContent( $text, $this->getTitle() );
288  } else {
289  $message = $this->getContext()->getUser()->isRegistered() ? 'noarticletext' : 'noarticletextanon';
290  $content = new MessageContent( $message, null );
291  }
292 
293  return $content;
294  }
295 
303  public function getOldID() {
304  if ( $this->mOldId === null ) {
305  $this->mOldId = $this->getOldIDFromRequest();
306  }
307 
308  return $this->mOldId;
309  }
310 
316  public function getOldIDFromRequest() {
317  $this->mRedirectUrl = false;
318 
319  $request = $this->getContext()->getRequest();
320  $oldid = $request->getIntOrNull( 'oldid' );
321 
322  if ( $oldid === null ) {
323  return 0;
324  }
325 
326  if ( $oldid !== 0 ) {
327  # Load the given revision and check whether the page is another one.
328  # In that case, update this instance to reflect the change.
329  if ( $oldid === $this->mPage->getLatest() ) {
330  $this->mRevisionRecord = $this->mPage->getRevisionRecord();
331  } else {
332  $this->mRevisionRecord = $this->revisionStore->getRevisionById( $oldid );
333  if ( $this->mRevisionRecord !== null ) {
334  $revPageId = $this->mRevisionRecord->getPageId();
335  // Revision title doesn't match the page title given?
336  if ( $this->mPage->getId() != $revPageId ) {
337  $function = get_class( $this->mPage ) . '::newFromID';
338  $this->mPage = $function( $revPageId );
339  }
340  }
341  }
342  }
343 
344  $oldRev = $this->mRevisionRecord;
345  if ( $request->getVal( 'direction' ) == 'next' ) {
346  $nextid = 0;
347  if ( $oldRev ) {
348  $nextRev = $this->revisionStore->getNextRevision( $oldRev );
349  if ( $nextRev ) {
350  $nextid = $nextRev->getId();
351  }
352  }
353  if ( $nextid ) {
354  $oldid = $nextid;
355  $this->mRevisionRecord = null;
356  } else {
357  $this->mRedirectUrl = $this->getTitle()->getFullURL( 'redirect=no' );
358  }
359  } elseif ( $request->getVal( 'direction' ) == 'prev' ) {
360  $previd = 0;
361  if ( $oldRev ) {
362  $prevRev = $this->revisionStore->getPreviousRevision( $oldRev );
363  if ( $prevRev ) {
364  $previd = $prevRev->getId();
365  }
366  }
367  if ( $previd ) {
368  $oldid = $previd;
369  $this->mRevisionRecord = null;
370  }
371  }
372 
373  return $oldid;
374  }
375 
385  public function fetchRevisionRecord() {
386  if ( $this->fetchResult ) {
387  return $this->mRevisionRecord;
388  }
389 
390  $oldid = $this->getOldID();
391 
392  // $this->mRevisionRecord might already be fetched by getOldIDFromRequest()
393  if ( !$this->mRevisionRecord ) {
394  if ( !$oldid ) {
395  $this->mRevisionRecord = $this->mPage->getRevisionRecord();
396 
397  if ( !$this->mRevisionRecord ) {
398  wfDebug( __METHOD__ . " failed to find page data for title " .
399  $this->getTitle()->getPrefixedText() );
400 
401  // Just for sanity, output for this case is done by showMissingArticle().
402  $this->fetchResult = Status::newFatal( 'noarticletext' );
403  return null;
404  }
405  } else {
406  $this->mRevisionRecord = $this->revisionStore->getRevisionById( $oldid );
407 
408  if ( !$this->mRevisionRecord ) {
409  wfDebug( __METHOD__ . " failed to load revision, rev_id $oldid" );
410 
411  $this->fetchResult = Status::newFatal( 'missing-revision', $oldid );
412  return null;
413  }
414  }
415  }
416 
417  if ( !$this->mRevisionRecord->userCan( RevisionRecord::DELETED_TEXT, $this->getContext()->getAuthority() ) ) {
418  wfDebug( __METHOD__ . " failed to retrieve content of revision " .
419  $this->mRevisionRecord->getId() );
420 
421  // Just for sanity, output for this case is done by showDeletedRevisionHeader().
422  // title used in wikilinks, should not contain whitespaces
423  $this->fetchResult = Status::newFatal(
424  'rev-deleted-text-permission', $this->getTitle()->getPrefixedDBkey() );
425  return null;
426  }
427 
428  $this->fetchResult = Status::newGood( $this->mRevisionRecord );
429  return $this->mRevisionRecord;
430  }
431 
437  public function isCurrent() {
438  # If no oldid, this is the current version.
439  if ( $this->getOldID() == 0 ) {
440  return true;
441  }
442 
443  return $this->mPage->exists() &&
444  $this->mRevisionRecord &&
445  $this->mRevisionRecord->isCurrent();
446  }
447 
457  public function getRevisionFetched() {
458  wfDeprecated( __METHOD__, '1.35' );
459  $revRecord = $this->fetchRevisionRecord();
460 
461  return $revRecord ? new Revision( $revRecord ) : null;
462  }
463 
472  public function getRevIdFetched() {
473  if ( $this->fetchResult && $this->fetchResult->isOK() ) {
474  return $this->fetchResult->value->getId();
475  } else {
476  return $this->mPage->getLatest();
477  }
478  }
479 
484  public function view() {
485  global $wgUseFileCache;
486 
487  # Get variables from query string
488  # As side effect this will load the revision and update the title
489  # in a revision ID is passed in the request, so this should remain
490  # the first call of this method even if $oldid is used way below.
491  $oldid = $this->getOldID();
492 
493  $user = $this->getContext()->getUser();
494  # Another whitelist check in case getOldID() is altering the title
495  $permissionStatus = PermissionStatus::newEmpty();
496  if ( !$this->getContext()->getAuthority()
497  ->authorizeRead( 'read', $this->getTitle(), $permissionStatus )
498  ) {
499  wfDebug( __METHOD__ . ": denied on secondary read check" );
500  throw new PermissionsError( 'read', $permissionStatus );
501  }
502 
503  $outputPage = $this->getContext()->getOutput();
504  # getOldID() may as well want us to redirect somewhere else
505  if ( $this->mRedirectUrl ) {
506  $outputPage->redirect( $this->mRedirectUrl );
507  wfDebug( __METHOD__ . ": redirecting due to oldid" );
508 
509  return;
510  }
511 
512  # If we got diff in the query, we want to see a diff page instead of the article.
513  if ( $this->getContext()->getRequest()->getCheck( 'diff' ) ) {
514  wfDebug( __METHOD__ . ": showing diff page" );
515  $this->showDiffPage();
516 
517  return;
518  }
519 
520  # Set page title (may be overridden by DISPLAYTITLE)
521  $outputPage->setPageTitle( $this->getTitle()->getPrefixedText() );
522 
523  $outputPage->setArticleFlag( true );
524  # Allow frames by default
525  $outputPage->allowClickjacking();
526 
527  $parserOptions = $this->getParserOptions();
528  $poOptions = [];
529  # Allow extensions to vary parser options used for article rendering
530  Hooks::runner()->onArticleParserOptions( $this, $parserOptions );
531  # Render printable version, use printable version cache
532  if ( $outputPage->isPrintable() ) {
533  $parserOptions->setIsPrintable( true );
534  $poOptions['enableSectionEditLinks'] = false;
535  $outputPage->prependHTML(
537  $outputPage->msg( 'printableversion-deprecated-warning' )->escaped()
538  )
539  );
540  } elseif ( $this->viewIsRenderAction || !$this->isCurrent() ||
541  !$this->getContext()->getAuthority()->probablyCan( 'edit', $this->getTitle() )
542  ) {
543  $poOptions['enableSectionEditLinks'] = false;
544  }
545 
546  # Try client and file cache
547  if ( $oldid === 0 && $this->mPage->checkTouched() ) {
548  # Try to stream the output from file cache
549  if ( $wgUseFileCache && $this->tryFileCache() ) {
550  wfDebug( __METHOD__ . ": done file cache" );
551  # tell wgOut that output is taken care of
552  $outputPage->disable();
553  $this->mPage->doViewUpdates( $user, $oldid );
554 
555  return;
556  }
557  }
558 
559  $this->showRedirectedFromHeader();
560  $this->showNamespaceHeader();
561 
562  $continue =
563  $this->generateContentOutput( $user, $parserOptions, $oldid, $outputPage, $poOptions );
564 
565  if ( !$continue ) {
566  return;
567  }
568 
569  # For the main page, overwrite the <title> element with the con-
570  # tents of 'pagetitle-view-mainpage' instead of the default (if
571  # that's not empty).
572  # This message always exists because it is in the i18n files
573  if ( $this->getTitle()->isMainPage() ) {
574  $msg = wfMessage( 'pagetitle-view-mainpage' )->inContentLanguage();
575  if ( !$msg->isDisabled() ) {
576  $outputPage->setHTMLTitle( $msg->title( $this->getTitle() )->text() );
577  }
578  }
579 
580  # Use adaptive TTLs for CDN so delayed/failed purges are noticed less often.
581  # This could use getTouched(), but that could be scary for major template edits.
582  $outputPage->adaptCdnTTL( $this->mPage->getTimestamp(), IExpiringStore::TTL_DAY );
583 
584  $this->showViewFooter();
585  $this->mPage->doViewUpdates( $user, $oldid ); // FIXME: test this
586 
587  # Load the postEdit module if the user just saved this revision
588  # See also EditPage::setPostEditCookie
589  $request = $this->getContext()->getRequest();
591  $postEdit = $request->getCookie( $cookieKey );
592  if ( $postEdit ) {
593  # Clear the cookie. This also prevents caching of the response.
594  $request->response()->clearCookie( $cookieKey );
595  $outputPage->addJsConfigVars( 'wgPostEdit', $postEdit );
596  $outputPage->addModules( 'mediawiki.action.view.postEdit' ); // FIXME: test this
597  }
598  }
599 
612  private function generateContentOutput(
613  User $user,
614  ParserOptions $parserOptions,
615  int $oldid,
616  OutputPage $outputPage,
617  array $textOptions
618  ): bool {
619  # Should the parser cache be used?
620  $useParserCache = true;
621  $pOutput = null;
622  $parserOutputAccess = MediaWikiServices::getInstance()->getParserOutputAccess();
623 
624  // NOTE: $outputDone and $useParserCache may be changed by the hook
625  $this->getHookRunner()->onArticleViewHeader( $this, $outputDone, $useParserCache );
626  if ( $outputDone ) {
627  if ( $outputDone instanceof ParserOutput ) {
628  $pOutput = $outputDone;
629  }
630 
631  if ( $pOutput ) {
632  $this->doOutputMetaData( $pOutput, $outputPage );
633  }
634  return true;
635  }
636 
637  // Early abort if the page doesn't exist
638  if ( !$this->mPage->exists() ) {
639  wfDebug( __METHOD__ . ": showing missing article" );
640  $this->showMissingArticle();
641  $this->mPage->doViewUpdates( $user );
642  return false; // skip all further output to OutputPage
643  }
644 
645  // Try the latest parser cache
646  // NOTE: try latest-revision cache first to avoid loading revision.
647  if ( $useParserCache && !$oldid ) {
648  $pOutput = $parserOutputAccess->getCachedParserOutput(
649  $this->getPage(),
650  $parserOptions,
651  null,
652  ParserOutputAccess::OPT_NO_AUDIENCE_CHECK // we already checked
653  );
654 
655  if ( $pOutput ) {
656  $this->doOutputFromParserCache( $pOutput, $outputPage, $textOptions );
657  $this->doOutputMetaData( $pOutput, $outputPage );
658  return true;
659  }
660  }
661 
662  $rev = $this->fetchRevisionRecord();
663  if ( !$this->fetchResult->isOK() ) {
664  $this->showViewError( $this->fetchResult->getWikiText(
665  false, false, $this->getContext()->getLanguage()
666  ) );
667  return true;
668  }
669 
670  # Are we looking at an old revision
671  if ( $oldid ) {
672  $this->setOldSubtitle( $oldid );
673 
674  if ( !$this->showDeletedRevisionHeader() ) {
675  wfDebug( __METHOD__ . ": cannot view deleted revision" );
676  return false; // skip all further output to OutputPage
677  }
678 
679  // Try the old revision parser cache
680  // NOTE: Repeating cache check for old revision to avoid fetching $rev
681  // before it's absolutely necessary.
682  if ( $useParserCache ) {
683  $pOutput = $parserOutputAccess->getCachedParserOutput(
684  $this->getPage(),
685  $parserOptions,
686  $rev,
687  ParserOutputAccess::OPT_NO_AUDIENCE_CHECK // we already checked in fetchRevisionRevord
688  );
689 
690  if ( $pOutput ) {
691  $this->doOutputFromParserCache( $pOutput, $outputPage, $textOptions );
692  $this->doOutputMetaData( $pOutput, $outputPage );
693  return true;
694  }
695  }
696  }
697 
698  # Ensure that UI elements requiring revision ID have
699  # the correct version information.
700  $outputPage->setRevisionId( $this->getRevIdFetched() );
701  # Preload timestamp to avoid a DB hit
702  $outputPage->setRevisionTimestamp( $this->mPage->getTimestamp() );
703 
704  # Pages containing custom CSS or JavaScript get special treatment
705  if ( $this->getTitle()->isSiteConfigPage() || $this->getTitle()->isUserConfigPage() ) {
706  $dir = $this->getContext()->getLanguage()->getDir();
707  $lang = $this->getContext()->getLanguage()->getHtmlCode();
708 
709  $outputPage->wrapWikiMsg(
710  "<div id='mw-clearyourcache' lang='$lang' dir='$dir' class='mw-content-$dir'>\n$1\n</div>",
711  'clearyourcache'
712  );
713  } elseif ( !$this->getHookRunner()->onArticleRevisionViewCustom(
714  $rev,
715  $this->getTitle(),
716  $oldid,
717  $outputPage )
718  ) {
719  // NOTE: sync with hooks called in DifferenceEngine::renderNewRevision()
720  // Allow extensions do their own custom view for certain pages
721  $this->doOutputMetaData( $pOutput, $outputPage );
722  return true;
723  }
724 
725  # Run the parse, protected by a pool counter
726  wfDebug( __METHOD__ . ": doing uncached parse" );
727 
728  if ( !$rev ) {
729  // No revision, abort! Shouldn't happen.
730  return false;
731  }
732 
733  $opt = 0;
734 
735  // we already checked the cache in case 2, don't check again.
736  $opt |= ParserOutputAccess::OPT_NO_CHECK_CACHE;
737 
738  // we already checked in fetchRevisionRecord()
739  $opt |= ParserOutputAccess::OPT_NO_AUDIENCE_CHECK;
740 
741  if ( !$rev->getId() || !$useParserCache ) {
742  // fake revision or uncacheable options
743  $opt |= ParserOutputAccess::OPT_NO_CACHE;
744  }
745 
746  $renderStatus = $parserOutputAccess->getParserOutput(
747  $this->getPage(),
748  $parserOptions,
749  $rev,
750  $opt
751  );
752 
754  $rev,
755  $renderStatus,
756  $outputPage,
757  $textOptions
758  );
759 
760  if ( !$renderStatus->isOK() ) {
761  return true;
762  }
763 
764  $pOutput = $renderStatus->getValue();
765  $this->doOutputMetaData( $pOutput, $outputPage );
766  return true;
767  }
768 
773  private function doOutputMetaData( ?ParserOutput $pOutput, OutputPage $outputPage ) {
774  # Adjust title for main page & pages with displaytitle
775  if ( $pOutput ) {
776  $this->adjustDisplayTitle( $pOutput );
777  }
778 
779  # Check for any __NOINDEX__ tags on the page using $pOutput
780  $policy = $this->getRobotPolicy( 'view', $pOutput ?: null );
781  $outputPage->setIndexPolicy( $policy['index'] );
782  $outputPage->setFollowPolicy( $policy['follow'] ); // FIXME: test this
783 
784  $this->mParserOutput = $pOutput;
785  }
786 
792  private function doOutputFromParserCache(
793  ParserOutput $pOutput,
794  OutputPage $outputPage,
795  array $textOptions
796  ) {
797  $outputPage->addParserOutput( $pOutput, $textOptions );
798  # Ensure that UI elements requiring revision ID have
799  # the correct version information.
800  $outputPage->setRevisionId( $pOutput->getCacheRevisionId() ?? $this->mPage->getLatest() );
801  # Preload timestamp to avoid a DB hit
802  $cachedTimestamp = $pOutput->getTimestamp();
803  if ( $cachedTimestamp !== null ) {
804  $outputPage->setRevisionTimestamp( $cachedTimestamp );
805  $this->mPage->setTimestamp( $cachedTimestamp );
806  }
807  }
808 
815  private function doOutputFromRenderStatus(
816  ?RevisionRecord $rev,
817  Status $renderStatus,
818  OutputPage $outputPage,
819  array $textOptions
820  ) {
821  global $wgCdnMaxageStale;
822  $ok = $renderStatus->isOK();
823 
824  $pOutput = $ok ? $renderStatus->getValue() : null;
825 
826  // Cache stale ParserOutput object with a short expiry
827  if ( $ok && $renderStatus->hasMessage( 'view-pool-dirty-output' ) ) {
828  $outputPage->setCdnMaxage( $wgCdnMaxageStale );
829  $outputPage->setLastModified( $pOutput->getCacheTime() );
830  $staleReason = $renderStatus->hasMessage( 'view-pool-contention' )
831  ? $this->getContext()->msg( 'view-pool-contention' )
832  : $this->getContext()->msg( 'view-pool-timeout' );
833  $outputPage->addHTML( "<!-- parser cache is expired, " .
834  "sending anyway due to $staleReason-->\n" );
835  }
836 
837  if ( !$renderStatus->isOK() ) {
838  $this->showViewError( $renderStatus->getWikiText(
839  false, 'view-pool-error', $this->getContext()->getLanguage()
840  ) );
841  return;
842  }
843 
844  if ( $pOutput ) {
845  $outputPage->addParserOutput( $pOutput, $textOptions );
846  }
847 
848  if ( $this->getRevisionRedirectTarget( $rev ) ) {
849  $outputPage->addSubtitle( "<span id=\"redirectsub\">" .
850  $this->getContext()->msg( 'redirectpagesub' )->parse() . "</span>" );
851  }
852  }
853 
858  private function getRevisionRedirectTarget( RevisionRecord $revision ) {
859  // TODO: find a *good* place for the code that determines the redirect target for
860  // a given revision!
861  // NOTE: Use main slot content. Compare code in DerivedPageDataUpdater::revisionIsRedirect.
862  $content = $revision->getContent( SlotRecord::MAIN );
863  return $content ? $content->getRedirectTarget() : null;
864  }
865 
870  public function adjustDisplayTitle( ParserOutput $pOutput ) {
871  $out = $this->getContext()->getOutput();
872 
873  # Adjust the title if it was set by displaytitle, -{T|}- or language conversion
874  $titleText = $pOutput->getTitleText();
875  if ( strval( $titleText ) !== '' ) {
876  $out->setPageTitle( $titleText );
877  $out->setDisplayTitle( $titleText );
878  }
879  }
880 
885  protected function showDiffPage() {
886  $request = $this->getContext()->getRequest();
887  $user = $this->getContext()->getUser();
888  $diff = $request->getVal( 'diff' );
889  $rcid = $request->getVal( 'rcid' );
890  $diffOnly = $request->getBool( 'diffonly', $user->getOption( 'diffonly' ) );
891  $purge = $request->getVal( 'action' ) == 'purge';
892  $unhide = $request->getInt( 'unhide' ) == 1;
893  $oldid = $this->getOldID();
894 
895  $rev = $this->fetchRevisionRecord();
896 
897  if ( !$rev ) {
898  // T213621: $rev maybe null due to either lack of permission to view the
899  // revision or actually not existing. So let's try loading it from the id
900  $rev = $this->revisionStore->getRevisionById( $oldid );
901  if ( $rev ) {
902  // Revision exists but $user lacks permission to diff it.
903  // Do nothing here.
904  // The $rev will later be used to create standard diff elements however.
905  } else {
906  $this->getContext()->getOutput()->setPageTitle( wfMessage( 'errorpagetitle' ) );
907  $msg = $this->getContext()->msg( 'difference-missing-revision' )
908  ->params( $oldid )
909  ->numParams( 1 )
910  ->parseAsBlock();
911  $this->getContext()->getOutput()->addHTML( $msg );
912  return;
913  }
914  }
915 
916  $contentHandler = MediaWikiServices::getInstance()
917  ->getContentHandlerFactory()
918  ->getContentHandler(
919  $rev->getSlot( SlotRecord::MAIN, RevisionRecord::RAW )->getModel()
920  );
921  $de = $contentHandler->createDifferenceEngine(
922  $this->getContext(),
923  $oldid,
924  $diff,
925  $rcid,
926  $purge,
927  $unhide
928  );
929  $de->setSlotDiffOptions( [
930  'diff-type' => $request->getVal( 'diff-type' )
931  ] );
932  $de->showDiffPage( $diffOnly );
933 
934  // Run view updates for the newer revision being diffed (and shown
935  // below the diff if not $diffOnly).
936  list( $old, $new ) = $de->mapDiffPrevNext( $oldid, $diff );
937  // New can be false, convert it to 0 - this conveniently means the latest revision
938  $this->mPage->doViewUpdates( $user, (int)$new );
939  }
940 
948  public function getRobotPolicy( $action, ParserOutput $pOutput = null ) {
950 
951  $ns = $this->getTitle()->getNamespace();
952 
953  # Don't index user and user talk pages for blocked users (T13443)
954  if ( ( $ns === NS_USER || $ns === NS_USER_TALK ) && !$this->getTitle()->isSubpage() ) {
955  $specificTarget = null;
956  $vagueTarget = null;
957  $titleText = $this->getTitle()->getText();
958  if ( IPUtils::isValid( $titleText ) ) {
959  $vagueTarget = $titleText;
960  } else {
961  $specificTarget = $titleText;
962  }
963  if ( DatabaseBlock::newFromTarget( $specificTarget, $vagueTarget ) instanceof DatabaseBlock ) {
964  return [
965  'index' => 'noindex',
966  'follow' => 'nofollow'
967  ];
968  }
969  }
970 
971  if ( $this->mPage->getId() === 0 || $this->getOldID() ) {
972  # Non-articles (special pages etc), and old revisions
973  return [
974  'index' => 'noindex',
975  'follow' => 'nofollow'
976  ];
977  } elseif ( $this->getContext()->getOutput()->isPrintable() ) {
978  # Discourage indexing of printable versions, but encourage following
979  return [
980  'index' => 'noindex',
981  'follow' => 'follow'
982  ];
983  } elseif ( $this->getContext()->getRequest()->getInt( 'curid' ) ) {
984  # For ?curid=x urls, disallow indexing
985  return [
986  'index' => 'noindex',
987  'follow' => 'follow'
988  ];
989  }
990 
991  # Otherwise, construct the policy based on the various config variables.
993 
994  if ( isset( $wgNamespaceRobotPolicies[$ns] ) ) {
995  # Honour customised robot policies for this namespace
996  $policy = array_merge(
997  $policy,
998  self::formatRobotPolicy( $wgNamespaceRobotPolicies[$ns] )
999  );
1000  }
1001  if ( $this->getTitle()->canUseNoindex() && is_object( $pOutput ) && $pOutput->getIndexPolicy() ) {
1002  # __INDEX__ and __NOINDEX__ magic words, if allowed. Incorporates
1003  # a final sanity check that we have really got the parser output.
1004  $policy = array_merge(
1005  $policy,
1006  [ 'index' => $pOutput->getIndexPolicy() ]
1007  );
1008  }
1009 
1010  if ( isset( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] ) ) {
1011  # (T16900) site config can override user-defined __INDEX__ or __NOINDEX__
1012  $policy = array_merge(
1013  $policy,
1014  self::formatRobotPolicy( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] )
1015  );
1016  }
1017 
1018  return $policy;
1019  }
1020 
1028  public static function formatRobotPolicy( $policy ) {
1029  if ( is_array( $policy ) ) {
1030  return $policy;
1031  } elseif ( !$policy ) {
1032  return [];
1033  }
1034 
1035  $policy = explode( ',', $policy );
1036  $policy = array_map( 'trim', $policy );
1037 
1038  $arr = [];
1039  foreach ( $policy as $var ) {
1040  if ( in_array( $var, [ 'index', 'noindex' ] ) ) {
1041  $arr['index'] = $var;
1042  } elseif ( in_array( $var, [ 'follow', 'nofollow' ] ) ) {
1043  $arr['follow'] = $var;
1044  }
1045  }
1046 
1047  return $arr;
1048  }
1049 
1057  public function showRedirectedFromHeader() {
1058  global $wgRedirectSources;
1059 
1060  $context = $this->getContext();
1061  $outputPage = $context->getOutput();
1062  $request = $context->getRequest();
1063  $rdfrom = $request->getVal( 'rdfrom' );
1064 
1065  // Construct a URL for the current page view, but with the target title
1066  $query = $request->getValues();
1067  unset( $query['rdfrom'] );
1068  unset( $query['title'] );
1069  if ( $this->getTitle()->isRedirect() ) {
1070  // Prevent double redirects
1071  $query['redirect'] = 'no';
1072  }
1073  $redirectTargetUrl = $this->getTitle()->getLinkURL( $query );
1074 
1075  if ( isset( $this->mRedirectedFrom ) ) {
1076  // This is an internally redirected page view.
1077  // We'll need a backlink to the source page for navigation.
1078  if ( $this->getHookRunner()->onArticleViewRedirect( $this ) ) {
1079  $redir = $this->linkRenderer->makeKnownLink(
1080  $this->mRedirectedFrom,
1081  null,
1082  [],
1083  [ 'redirect' => 'no' ]
1084  );
1085 
1086  $outputPage->addSubtitle( "<span class=\"mw-redirectedfrom\">" .
1087  $context->msg( 'redirectedfrom' )->rawParams( $redir )->parse()
1088  . "</span>" );
1089 
1090  // Add the script to update the displayed URL and
1091  // set the fragment if one was specified in the redirect
1092  $outputPage->addJsConfigVars( [
1093  'wgInternalRedirectTargetUrl' => $redirectTargetUrl,
1094  ] );
1095  $outputPage->addModules( 'mediawiki.action.view.redirect' );
1096 
1097  // Add a <link rel="canonical"> tag
1098  $outputPage->setCanonicalUrl( $this->getTitle()->getCanonicalURL() );
1099 
1100  // Tell the output object that the user arrived at this article through a redirect
1101  $outputPage->setRedirectedFrom( $this->mRedirectedFrom );
1102 
1103  return true;
1104  }
1105  } elseif ( $rdfrom ) {
1106  // This is an externally redirected view, from some other wiki.
1107  // If it was reported from a trusted site, supply a backlink.
1108  if ( $wgRedirectSources && preg_match( $wgRedirectSources, $rdfrom ) ) {
1109  $redir = Linker::makeExternalLink( $rdfrom, $rdfrom );
1110  $outputPage->addSubtitle( "<span class=\"mw-redirectedfrom\">" .
1111  $context->msg( 'redirectedfrom' )->rawParams( $redir )->parse()
1112  . "</span>" );
1113 
1114  // Add the script to update the displayed URL
1115  $outputPage->addJsConfigVars( [
1116  'wgInternalRedirectTargetUrl' => $redirectTargetUrl,
1117  ] );
1118  $outputPage->addModules( 'mediawiki.action.view.redirect' );
1119 
1120  return true;
1121  }
1122  }
1123 
1124  return false;
1125  }
1126 
1131  public function showNamespaceHeader() {
1132  if ( $this->getTitle()->isTalkPage() && !wfMessage( 'talkpageheader' )->isDisabled() ) {
1133  $this->getContext()->getOutput()->wrapWikiMsg(
1134  "<div class=\"mw-talkpageheader\">\n$1\n</div>",
1135  [ 'talkpageheader' ]
1136  );
1137  }
1138  }
1139 
1143  public function showViewFooter() {
1144  # check if we're displaying a [[User talk:x.x.x.x]] anonymous talk page
1145  if ( $this->getTitle()->getNamespace() === NS_USER_TALK
1146  && IPUtils::isValid( $this->getTitle()->getText() )
1147  ) {
1148  $this->getContext()->getOutput()->addWikiMsg( 'anontalkpagetext' );
1149  }
1150 
1151  // Show a footer allowing the user to patrol the shown revision or page if possible
1152  $patrolFooterShown = $this->showPatrolFooter();
1153 
1154  $this->getHookRunner()->onArticleViewFooter( $this, $patrolFooterShown );
1155  }
1156 
1167  public function showPatrolFooter() {
1169 
1170  // Allow hooks to decide whether to not output this at all
1171  if ( !$this->getHookRunner()->onArticleShowPatrolFooter( $this ) ) {
1172  return false;
1173  }
1174 
1175  $outputPage = $this->getContext()->getOutput();
1176  $user = $this->getContext()->getUser();
1177  $title = $this->getTitle();
1178  $rc = false;
1179 
1180  if ( !$this->getContext()->getAuthority()->probablyCan( 'patrol', $title )
1182  || ( $wgUseFilePatrol && $title->inNamespace( NS_FILE ) ) )
1183  ) {
1184  // Patrolling is disabled or the user isn't allowed to
1185  return false;
1186  }
1187 
1188  if ( $this->mRevisionRecord
1189  && !RecentChange::isInRCLifespan( $this->mRevisionRecord->getTimestamp(), 21600 )
1190  ) {
1191  // The current revision is already older than what could be in the RC table
1192  // 6h tolerance because the RC might not be cleaned out regularly
1193  return false;
1194  }
1195 
1196  // Check for cached results
1197  $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
1198  $key = $cache->makeKey( 'unpatrollable-page', $title->getArticleID() );
1199  if ( $cache->get( $key ) ) {
1200  return false;
1201  }
1202 
1203  $dbr = wfGetDB( DB_REPLICA );
1204  $oldestRevisionTimestamp = $dbr->selectField(
1205  'revision',
1206  'MIN( rev_timestamp )',
1207  [ 'rev_page' => $title->getArticleID() ],
1208  __METHOD__
1209  );
1210 
1211  // New page patrol: Get the timestamp of the oldest revison which
1212  // the revision table holds for the given page. Then we look
1213  // whether it's within the RC lifespan and if it is, we try
1214  // to get the recentchanges row belonging to that entry
1215  // (with rc_new = 1).
1216  $recentPageCreation = false;
1217  if ( $oldestRevisionTimestamp
1218  && RecentChange::isInRCLifespan( $oldestRevisionTimestamp, 21600 )
1219  ) {
1220  // 6h tolerance because the RC might not be cleaned out regularly
1221  $recentPageCreation = true;
1223  [
1224  'rc_new' => 1,
1225  'rc_timestamp' => $oldestRevisionTimestamp,
1226  'rc_namespace' => $title->getNamespace(),
1227  'rc_cur_id' => $title->getArticleID()
1228  ],
1229  __METHOD__
1230  );
1231  if ( $rc ) {
1232  // Use generic patrol message for new pages
1233  $markPatrolledMsg = wfMessage( 'markaspatrolledtext' );
1234  }
1235  }
1236 
1237  // File patrol: Get the timestamp of the latest upload for this page,
1238  // check whether it is within the RC lifespan and if it is, we try
1239  // to get the recentchanges row belonging to that entry
1240  // (with rc_type = RC_LOG, rc_log_type = upload).
1241  $recentFileUpload = false;
1242  if ( ( !$rc || $rc->getAttribute( 'rc_patrolled' ) ) && $wgUseFilePatrol
1243  && $title->getNamespace() === NS_FILE ) {
1244  // Retrieve timestamp of most recent upload
1245  $newestUploadTimestamp = $dbr->selectField(
1246  'image',
1247  'MAX( img_timestamp )',
1248  [ 'img_name' => $title->getDBkey() ],
1249  __METHOD__
1250  );
1251  if ( $newestUploadTimestamp
1252  && RecentChange::isInRCLifespan( $newestUploadTimestamp, 21600 )
1253  ) {
1254  // 6h tolerance because the RC might not be cleaned out regularly
1255  $recentFileUpload = true;
1257  [
1258  'rc_type' => RC_LOG,
1259  'rc_log_type' => 'upload',
1260  'rc_timestamp' => $newestUploadTimestamp,
1261  'rc_namespace' => NS_FILE,
1262  'rc_cur_id' => $title->getArticleID()
1263  ],
1264  __METHOD__
1265  );
1266  if ( $rc ) {
1267  // Use patrol message specific to files
1268  $markPatrolledMsg = wfMessage( 'markaspatrolledtext-file' );
1269  }
1270  }
1271  }
1272 
1273  if ( !$recentPageCreation && !$recentFileUpload ) {
1274  // Page creation and latest upload (for files) is too old to be in RC
1275 
1276  // We definitely can't patrol so cache the information
1277  // When a new file version is uploaded, the cache is cleared
1278  $cache->set( $key, '1' );
1279 
1280  return false;
1281  }
1282 
1283  if ( !$rc ) {
1284  // Don't cache: This can be hit if the page gets accessed very fast after
1285  // its creation / latest upload or in case we have high replica DB lag. In case
1286  // the revision is too old, we will already return above.
1287  return false;
1288  }
1289 
1290  if ( $rc->getAttribute( 'rc_patrolled' ) ) {
1291  // Patrolled RC entry around
1292 
1293  // Cache the information we gathered above in case we can't patrol
1294  // Don't cache in case we can patrol as this could change
1295  $cache->set( $key, '1' );
1296 
1297  return false;
1298  }
1299 
1300  if ( $rc->getPerformer()->equals( $user ) ) {
1301  // Don't show a patrol link for own creations/uploads. If the user could
1302  // patrol them, they already would be patrolled
1303  return false;
1304  }
1305 
1306  $outputPage->preventClickjacking();
1307  if ( $this->getContext()->getAuthority()->isAllowed( 'writeapi' ) ) {
1308  $outputPage->addModules( 'mediawiki.misc-authed-curate' );
1309  }
1310 
1311  $link = $this->linkRenderer->makeKnownLink(
1312  $title,
1313  $markPatrolledMsg->text(),
1314  [],
1315  [
1316  'action' => 'markpatrolled',
1317  'rcid' => $rc->getAttribute( 'rc_id' ),
1318  ]
1319  );
1320 
1321  $outputPage->addHTML(
1322  "<div class='patrollink' data-mw='interface'>" .
1323  wfMessage( 'markaspatrolledlink' )->rawParams( $link )->escaped() .
1324  '</div>'
1325  );
1326 
1327  return true;
1328  }
1329 
1336  public static function purgePatrolFooterCache( $articleID ) {
1337  $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
1338  $cache->delete( $cache->makeKey( 'unpatrollable-page', $articleID ) );
1339  }
1340 
1345  public function showMissingArticle() {
1346  global $wgSend404Code;
1347 
1348  $outputPage = $this->getContext()->getOutput();
1349  // Whether the page is a root user page of an existing user (but not a subpage)
1350  $validUserPage = false;
1351 
1352  $title = $this->getTitle();
1353 
1354  $services = MediaWikiServices::getInstance();
1355 
1356  $contextUser = $this->getContext()->getUser();
1357 
1358  # Show info in user (talk) namespace. Does the user exist? Is he blocked?
1359  if ( $title->getNamespace() === NS_USER
1360  || $title->getNamespace() === NS_USER_TALK
1361  ) {
1362  $rootPart = explode( '/', $title->getText() )[0];
1363  $user = User::newFromName( $rootPart, false /* allow IP users */ );
1364  $ip = User::isIP( $rootPart );
1365  $block = DatabaseBlock::newFromTarget( $user, $user );
1366 
1367  if ( $user && $user->isRegistered() && $user->isHidden() &&
1368  !$this->getContext()->getAuthority()->isAllowed( 'hideuser' )
1369  ) {
1370  // T120883 if the user is hidden and the viewer cannot see hidden
1371  // users, pretend like it does not exist at all.
1372  $user = false;
1373  }
1374  if ( !( $user && $user->isRegistered() ) && !$ip ) { # User does not exist
1375  $outputPage->wrapWikiMsg( "<div class=\"mw-userpage-userdoesnotexist error\">\n\$1\n</div>",
1376  [ 'userpage-userdoesnotexist-view', wfEscapeWikiText( $rootPart ) ] );
1377  } elseif (
1378  $block !== null &&
1379  $block->getType() != DatabaseBlock::TYPE_AUTO &&
1380  ( $block->isSitewide() || $user->isBlockedFrom( $title ) )
1381  ) {
1382  // Show log extract if the user is sitewide blocked or is partially
1383  // blocked and not allowed to edit their user page or user talk page
1385  $outputPage,
1386  'block',
1387  $services->getNamespaceInfo()->getCanonicalName( NS_USER ) . ':' .
1388  $block->getTarget(),
1389  '',
1390  [
1391  'lim' => 1,
1392  'showIfEmpty' => false,
1393  'msgKey' => [
1394  'blocked-notice-logextract',
1395  $user->getName() # Support GENDER in notice
1396  ]
1397  ]
1398  );
1399  $validUserPage = !$title->isSubpage();
1400  } else {
1401  $validUserPage = !$title->isSubpage();
1402  }
1403  }
1404 
1405  $this->getHookRunner()->onShowMissingArticle( $this );
1406 
1407  # Show delete and move logs if there were any such events.
1408  # The logging query can DOS the site when bots/crawlers cause 404 floods,
1409  # so be careful showing this. 404 pages must be cheap as they are hard to cache.
1410  $dbCache = ObjectCache::getInstance( 'db-replicated' );
1411  $key = $dbCache->makeKey( 'page-recent-delete', md5( $title->getPrefixedText() ) );
1412  $isRegistered = $contextUser->isRegistered();
1413  $sessionExists = $this->getContext()->getRequest()->getSession()->isPersistent();
1414 
1415  if ( $isRegistered || $dbCache->get( $key ) || $sessionExists ) {
1416  $logTypes = [ 'delete', 'move', 'protect' ];
1417 
1418  $dbr = wfGetDB( DB_REPLICA );
1419 
1420  $conds = [ 'log_action != ' . $dbr->addQuotes( 'revision' ) ];
1421  // Give extensions a chance to hide their (unrelated) log entries
1422  $this->getHookRunner()->onArticle__MissingArticleConditions( $conds, $logTypes );
1424  $outputPage,
1425  $logTypes,
1426  $title,
1427  '',
1428  [
1429  'lim' => 10,
1430  'conds' => $conds,
1431  'showIfEmpty' => false,
1432  'msgKey' => [ $isRegistered || $sessionExists
1433  ? 'moveddeleted-notice'
1434  : 'moveddeleted-notice-recent'
1435  ]
1436  ]
1437  );
1438  }
1439 
1440  if ( !$this->mPage->hasViewableContent() && $wgSend404Code && !$validUserPage ) {
1441  // If there's no backing content, send a 404 Not Found
1442  // for better machine handling of broken links.
1443  $this->getContext()->getRequest()->response()->statusHeader( 404 );
1444  }
1445 
1446  // Also apply the robot policy for nonexisting pages (even if a 404 was used for sanity)
1447  $policy = $this->getRobotPolicy( 'view' );
1448  $outputPage->setIndexPolicy( $policy['index'] );
1449  $outputPage->setFollowPolicy( $policy['follow'] );
1450 
1451  $hookResult = $this->getHookRunner()->onBeforeDisplayNoArticleText( $this );
1452 
1453  if ( !$hookResult ) {
1454  return;
1455  }
1456 
1457  # Show error message
1458  $oldid = $this->getOldID();
1459  if ( !$oldid && $title->getNamespace() === NS_MEDIAWIKI && $title->hasSourceText() ) {
1460  $text = $this->getTitle()->getDefaultMessageText() ?? '';
1461  $outputPage->addWikiTextAsContent( $text );
1462  } else {
1463  if ( $oldid ) {
1464  // T251066: Try loading the revision from the archive table.
1465  // Show link to view it if it exists and the user has permission to view it.
1466  $pa = new PageArchive( $title, $this->getContext()->getConfig() );
1467  $revRecord = $pa->getArchivedRevisionRecord( $oldid );
1468  if ( $revRecord && $revRecord->userCan(
1469  RevisionRecord::DELETED_TEXT,
1470  $this->getContext()->getAuthority()
1471  ) ) {
1472  $text = wfMessage(
1473  'missing-revision-permission', $oldid,
1474  $revRecord->getTimestamp(),
1475  $title->getPrefixedDBkey()
1476  )->plain();
1477  } else {
1478  $text = wfMessage( 'missing-revision', $oldid )->plain();
1479  }
1480 
1481  } elseif ( $this->getContext()->getAuthority()->probablyCan( 'create', $title ) &&
1482  $this->getContext()->getAuthority()->probablyCan( 'edit', $title )
1483  ) {
1484  $message = $isRegistered ? 'noarticletext' : 'noarticletextanon';
1485  $text = wfMessage( $message )->plain();
1486  } else {
1487  $text = wfMessage( 'noarticletext-nopermission' )->plain();
1488  }
1489 
1490  $dir = $this->getContext()->getLanguage()->getDir();
1491  $lang = $this->getContext()->getLanguage()->getHtmlCode();
1492  $outputPage->addWikiTextAsInterface( Xml::openElement( 'div', [
1493  'class' => "noarticletext mw-content-$dir",
1494  'dir' => $dir,
1495  'lang' => $lang,
1496  ] ) . "\n$text\n</div>" );
1497  }
1498  }
1499 
1504  private function showViewError( string $errortext ) {
1505  $outputPage = $this->getContext()->getOutput();
1506  $outputPage->setPageTitle( $this->getContext()->msg( 'errorpagetitle' ) );
1507  $outputPage->enableClientCache( false );
1508  $outputPage->setRobotPolicy( 'noindex,nofollow' );
1509  $outputPage->clearHTML();
1510  $outputPage->wrapWikiTextAsInterface( 'errorbox', $errortext );
1511  }
1512 
1519  public function showDeletedRevisionHeader() {
1520  if ( !$this->mRevisionRecord->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
1521  // Not deleted
1522  return true;
1523  }
1524  $outputPage = $this->getContext()->getOutput();
1525  $user = $this->getContext()->getUser();
1526  // Used in wikilinks, should not contain whitespaces
1527  $titleText = $this->getTitle()->getPrefixedDBkey();
1528  // If the user is not allowed to see it...
1529  if ( !$this->mRevisionRecord->userCan(
1530  RevisionRecord::DELETED_TEXT,
1531  $this->getContext()->getAuthority()
1532  ) ) {
1533  $outputPage->addHtml(
1535  $outputPage->msg( 'rev-deleted-text-permission', $titleText )->parse(),
1536  'plainlinks'
1537  )
1538  );
1539 
1540  return false;
1541  // If the user needs to confirm that they want to see it...
1542  } elseif ( $this->getContext()->getRequest()->getInt( 'unhide' ) != 1 ) {
1543  # Give explanation and add a link to view the revision...
1544  $oldid = intval( $this->getOldID() );
1545  $link = $this->getTitle()->getFullURL( "oldid={$oldid}&unhide=1" );
1546  $msg = $this->mRevisionRecord->isDeleted( RevisionRecord::DELETED_RESTRICTED ) ?
1547  'rev-suppressed-text-unhide' : 'rev-deleted-text-unhide';
1548  $outputPage->addHtml(
1550  $outputPage->msg( $msg, $link )->parse(),
1551  'plainlinks'
1552  )
1553  );
1554 
1555  return false;
1556  // We are allowed to see...
1557  } else {
1558  $msg = $this->mRevisionRecord->isDeleted( RevisionRecord::DELETED_RESTRICTED )
1559  ? [ 'rev-suppressed-text-view', $titleText ]
1560  : [ 'rev-deleted-text-view', $titleText ];
1561  $outputPage->addHtml(
1563  $outputPage->msg( $msg[0], $msg[1] )->parse(),
1564  'plainlinks'
1565  )
1566  );
1567 
1568  return true;
1569  }
1570  }
1571 
1580  public function setOldSubtitle( $oldid = 0 ) {
1581  if ( !$this->getHookRunner()->onDisplayOldSubtitle( $this, $oldid ) ) {
1582  return;
1583  }
1584 
1585  $context = $this->getContext();
1586  $unhide = $context->getRequest()->getInt( 'unhide' ) == 1;
1587 
1588  # Cascade unhide param in links for easy deletion browsing
1589  $extraParams = [];
1590  if ( $unhide ) {
1591  $extraParams['unhide'] = 1;
1592  }
1593 
1594  if ( $this->mRevisionRecord && $this->mRevisionRecord->getId() === $oldid ) {
1595  $revisionRecord = $this->mRevisionRecord;
1596  } else {
1597  $revisionRecord = $this->revisionStore->getRevisionById( $oldid );
1598  }
1599 
1600  $timestamp = $revisionRecord->getTimestamp();
1601 
1602  $current = ( $oldid == $this->mPage->getLatest() );
1603  $language = $context->getLanguage();
1604  $user = $context->getUser();
1605 
1606  $td = $language->userTimeAndDate( $timestamp, $user );
1607  $tddate = $language->userDate( $timestamp, $user );
1608  $tdtime = $language->userTime( $timestamp, $user );
1609 
1610  # Show user links if allowed to see them. If hidden, then show them only if requested...
1611  $userlinks = Linker::revUserTools( $revisionRecord, !$unhide );
1612 
1613  $infomsg = $current && !$context->msg( 'revision-info-current' )->isDisabled()
1614  ? 'revision-info-current'
1615  : 'revision-info';
1616 
1617  $outputPage = $context->getOutput();
1618  $revisionUser = $revisionRecord->getUser();
1619  $revisionInfo = "<div id=\"mw-{$infomsg}\">" .
1620  $context->msg( $infomsg, $td )
1621  ->rawParams( $userlinks )
1622  ->params(
1623  $revisionRecord->getId(),
1624  $tddate,
1625  $tdtime,
1626  $revisionUser ? $revisionUser->getName() : ''
1627  )
1628  ->rawParams( Linker::revComment(
1629  $revisionRecord,
1630  true,
1631  true
1632  ) )
1633  ->parse() .
1634  "</div>";
1635 
1636  $lnk = $current
1637  ? $context->msg( 'currentrevisionlink' )->escaped()
1638  : $this->linkRenderer->makeKnownLink(
1639  $this->getTitle(),
1640  $context->msg( 'currentrevisionlink' )->text(),
1641  [],
1642  $extraParams
1643  );
1644  $curdiff = $current
1645  ? $context->msg( 'diff' )->escaped()
1646  : $this->linkRenderer->makeKnownLink(
1647  $this->getTitle(),
1648  $context->msg( 'diff' )->text(),
1649  [],
1650  [
1651  'diff' => 'cur',
1652  'oldid' => $oldid
1653  ] + $extraParams
1654  );
1655  $prevExist = (bool)$this->revisionStore->getPreviousRevision( $revisionRecord );
1656  $prevlink = $prevExist
1657  ? $this->linkRenderer->makeKnownLink(
1658  $this->getTitle(),
1659  $context->msg( 'previousrevision' )->text(),
1660  [],
1661  [
1662  'direction' => 'prev',
1663  'oldid' => $oldid
1664  ] + $extraParams
1665  )
1666  : $context->msg( 'previousrevision' )->escaped();
1667  $prevdiff = $prevExist
1668  ? $this->linkRenderer->makeKnownLink(
1669  $this->getTitle(),
1670  $context->msg( 'diff' )->text(),
1671  [],
1672  [
1673  'diff' => 'prev',
1674  'oldid' => $oldid
1675  ] + $extraParams
1676  )
1677  : $context->msg( 'diff' )->escaped();
1678  $nextlink = $current
1679  ? $context->msg( 'nextrevision' )->escaped()
1680  : $this->linkRenderer->makeKnownLink(
1681  $this->getTitle(),
1682  $context->msg( 'nextrevision' )->text(),
1683  [],
1684  [
1685  'direction' => 'next',
1686  'oldid' => $oldid
1687  ] + $extraParams
1688  );
1689  $nextdiff = $current
1690  ? $context->msg( 'diff' )->escaped()
1691  : $this->linkRenderer->makeKnownLink(
1692  $this->getTitle(),
1693  $context->msg( 'diff' )->text(),
1694  [],
1695  [
1696  'diff' => 'next',
1697  'oldid' => $oldid
1698  ] + $extraParams
1699  );
1700 
1701  $cdel = Linker::getRevDeleteLink(
1702  $user,
1703  $revisionRecord,
1704  $this->getTitle()
1705  );
1706  if ( $cdel !== '' ) {
1707  $cdel .= ' ';
1708  }
1709 
1710  // the outer div is need for styling the revision info and nav in MobileFrontend
1711  $outputPage->addSubtitle( "<div class=\"mw-revision warningbox\">" . $revisionInfo .
1712  "<div id=\"mw-revision-nav\">" . $cdel .
1713  $context->msg( 'revision-nav' )->rawParams(
1714  $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff
1715  )->escaped() . "</div></div>" );
1716  }
1717 
1731  public function viewRedirect( $target, $appendSubtitle = true, $forceKnown = false ) {
1732  $lang = $this->getTitle()->getPageLanguage();
1733  $out = $this->getContext()->getOutput();
1734  if ( $appendSubtitle ) {
1735  $out->addSubtitle( wfMessage( 'redirectpagesub' ) );
1736  }
1737  $out->addModuleStyles( 'mediawiki.action.view.redirectPage' );
1738  return static::getRedirectHeaderHtml( $lang, $target, $forceKnown );
1739  }
1740 
1753  public static function getRedirectHeaderHtml( Language $lang, $target, $forceKnown = false ) {
1754  if ( !is_array( $target ) ) {
1755  $target = [ $target ];
1756  }
1757 
1758  $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
1759 
1760  $html = '<ul class="redirectText">';
1762  foreach ( $target as $title ) {
1763  if ( $forceKnown ) {
1764  $link = $linkRenderer->makeKnownLink(
1765  $title,
1766  $title->getFullText(),
1767  [],
1768  // Make sure wiki page redirects are not followed
1769  $title->isRedirect() ? [ 'redirect' => 'no' ] : []
1770  );
1771  } else {
1772  $link = $linkRenderer->makeLink(
1773  $title,
1774  $title->getFullText(),
1775  [],
1776  // Make sure wiki page redirects are not followed
1777  $title->isRedirect() ? [ 'redirect' => 'no' ] : []
1778  );
1779  }
1780  $html .= '<li>' . $link . '</li>';
1781  }
1782  $html .= '</ul>';
1783 
1784  $redirectToText = wfMessage( 'redirectto' )->inLanguage( $lang )->escaped();
1785 
1786  return '<div class="redirectMsg">' .
1787  '<p>' . $redirectToText . '</p>' .
1788  $html .
1789  '</div>';
1790  }
1791 
1800  public function addHelpLink( $to, $overrideBaseUrl = false ) {
1801  $out = $this->getContext()->getOutput();
1802  $msg = $out->msg( 'namespace-' . $this->getTitle()->getNamespace() . '-helppage' );
1803 
1804  if ( !$msg->isDisabled() ) {
1805  $title = Title::newFromText( $msg->plain() );
1806  if ( $title instanceof Title ) {
1807  $out->addHelpLink( $title->getLocalURL(), true );
1808  }
1809  } else {
1810  $out->addHelpLink( $to, $overrideBaseUrl );
1811  }
1812  }
1813 
1817  public function render() {
1818  $this->getContext()->getRequest()->response()->header( 'X-Robots-Tag: noindex' );
1819  $this->getContext()->getOutput()->setArticleBodyOnly( true );
1820  // We later set 'enableSectionEditLinks=false' based on this; also used by ImagePage
1821  $this->viewIsRenderAction = true;
1822  $this->view();
1823  }
1824 
1828  public function protect() {
1829  $form = new ProtectionForm( $this );
1830  $form->execute();
1831  }
1832 
1836  public function unprotect() {
1837  $this->protect();
1838  }
1839 
1843  public function delete() {
1844  # This code desperately needs to be totally rewritten
1845 
1846  $title = $this->getTitle();
1847  $context = $this->getContext();
1848  $user = $context->getUser();
1849  $request = $context->getRequest();
1850 
1851  # Check permissions
1852  $permissionStatus = PermissionStatus::newEmpty();
1853  if ( !$context->getAuthority()->authorizeWrite( 'delete', $title, $permissionStatus ) ) {
1854  throw new PermissionsError( 'delete', $permissionStatus );
1855  }
1856 
1857  # Read-only check...
1858  if ( wfReadOnly() ) {
1859  throw new ReadOnlyError;
1860  }
1861 
1862  # Better double-check that it hasn't been deleted yet!
1863  $this->mPage->loadPageData(
1864  $request->wasPosted() ? WikiPage::READ_LATEST : WikiPage::READ_NORMAL
1865  );
1866  if ( !$this->mPage->exists() ) {
1867  $deleteLogPage = new LogPage( 'delete' );
1868  $outputPage = $context->getOutput();
1869  $outputPage->setPageTitle( $context->msg( 'cannotdelete-title', $title->getPrefixedText() ) );
1870  $outputPage->wrapWikiMsg( "<div class=\"error mw-error-cannotdelete\">\n$1\n</div>",
1871  [ 'cannotdelete', wfEscapeWikiText( $title->getPrefixedText() ) ]
1872  );
1873  $outputPage->addHTML(
1874  Xml::element( 'h2', null, $deleteLogPage->getName()->text() )
1875  );
1877  $outputPage,
1878  'delete',
1879  $title
1880  );
1881 
1882  return;
1883  }
1884 
1885  $deleteReasonList = $request->getText( 'wpDeleteReasonList', 'other' );
1886  $deleteReason = $request->getText( 'wpReason' );
1887 
1888  if ( $deleteReasonList == 'other' ) {
1889  $reason = $deleteReason;
1890  } elseif ( $deleteReason != '' ) {
1891  // Entry from drop down menu + additional comment
1892  $colonseparator = wfMessage( 'colon-separator' )->inContentLanguage()->text();
1893  $reason = $deleteReasonList . $colonseparator . $deleteReason;
1894  } else {
1895  $reason = $deleteReasonList;
1896  }
1897 
1898  if ( $request->wasPosted() && $user->matchEditToken( $request->getVal( 'wpEditToken' ),
1899  [ 'delete', $this->getTitle()->getPrefixedText() ] )
1900  ) {
1901  # Flag to hide all contents of the archived revisions
1902 
1903  $suppress = $request->getCheck( 'wpSuppress' ) &&
1904  $context->getAuthority()->isAllowed( 'suppressrevision' );
1905 
1906  $this->doDelete( $reason, $suppress );
1907 
1908  WatchAction::doWatchOrUnwatch( $request->getCheck( 'wpWatch' ), $title, $context->getAuthority() );
1909 
1910  return;
1911  }
1912 
1913  // Generate deletion reason
1914  $hasHistory = false;
1915  if ( !$reason ) {
1916  try {
1917  $reason = $this->getPage()
1918  ->getAutoDeleteReason( $hasHistory );
1919  } catch ( Exception $e ) {
1920  # if a page is horribly broken, we still want to be able to
1921  # delete it. So be lenient about errors here.
1922  wfDebug( "Error while building auto delete summary: $e" );
1923  $reason = '';
1924  }
1925  }
1926 
1927  // If the page has a history, insert a warning
1928  if ( $hasHistory ) {
1929  $title = $this->getTitle();
1930 
1931  // The following can use the real revision count as this is only being shown for users
1932  // that can delete this page.
1933  // This, as a side-effect, also makes sure that the following query isn't being run for
1934  // pages with a larger history, unless the user has the 'bigdelete' right
1935  // (and is about to delete this page).
1936  $dbr = wfGetDB( DB_REPLICA );
1937  $revisions = $edits = (int)$dbr->selectField(
1938  'revision',
1939  'COUNT(rev_page)',
1940  [ 'rev_page' => $title->getArticleID() ],
1941  __METHOD__
1942  );
1943 
1944  // @todo i18n issue/patchwork message
1945  $context->getOutput()->addHTML(
1946  '<strong class="mw-delete-warning-revisions">' .
1947  $context->msg( 'historywarning' )->numParams( $revisions )->parse() .
1948  $context->msg( 'word-separator' )->escaped() . $this->linkRenderer->makeKnownLink(
1949  $title,
1950  $context->msg( 'history' )->text(),
1951  [],
1952  [ 'action' => 'history' ] ) .
1953  '</strong>'
1954  );
1955 
1956  if ( $title->isBigDeletion() ) {
1957  global $wgDeleteRevisionsLimit;
1958  $context->getOutput()->wrapWikiMsg( "<div class='error'>\n$1\n</div>\n",
1959  [
1960  'delete-warning-toobig',
1961  $context->getLanguage()->formatNum( $wgDeleteRevisionsLimit )
1962  ]
1963  );
1964  }
1965  }
1966 
1967  $this->confirmDelete( $reason );
1968  }
1969 
1975  public function confirmDelete( $reason ) {
1976  wfDebug( "Article::confirmDelete" );
1977 
1978  $title = $this->getTitle();
1979  $ctx = $this->getContext();
1980  $outputPage = $ctx->getOutput();
1981  $outputPage->setPageTitle( wfMessage( 'delete-confirm', $title->getPrefixedText() ) );
1982  $outputPage->addBacklinkSubtitle( $title );
1983  $outputPage->setRobotPolicy( 'noindex,nofollow' );
1984  $outputPage->addModules( 'mediawiki.action.delete' );
1985 
1986  $backlinkCache = $title->getBacklinkCache();
1987  if ( $backlinkCache->hasLinks( 'pagelinks' ) || $backlinkCache->hasLinks( 'templatelinks' ) ) {
1988  $outputPage->addHtml(
1990  $outputPage->msg( 'deleting-backlinks-warning' )->parse(),
1991  'plainlinks'
1992  )
1993  );
1994  }
1995 
1996  $subpageQueryLimit = 51;
1997  $subpages = $title->getSubpages( $subpageQueryLimit );
1998  $subpageCount = count( $subpages );
1999  if ( $subpageCount > 0 ) {
2000  $outputPage->addHtml(
2002  $outputPage->msg( 'deleting-subpages-warning', Message::numParam( $subpageCount ) )->parse(),
2003  'plainlinks'
2004  )
2005  );
2006  }
2007  $outputPage->addWikiMsg( 'confirmdeletetext' );
2008 
2009  $this->getHookRunner()->onArticleConfirmDelete( $this, $outputPage, $reason );
2010 
2011  $user = $this->getContext()->getUser();
2012  $checkWatch = $user->getBoolOption( 'watchdeletion' ) || $user->isWatched( $title );
2013 
2014  $outputPage->enableOOUI();
2015 
2016  $fields = [];
2017 
2018  $suppressAllowed = $this->getContext()->getAuthority()->isAllowed( 'suppressrevision' );
2019  $dropDownReason = $ctx->msg( 'deletereason-dropdown' )->inContentLanguage()->text();
2020  // Add additional specific reasons for suppress
2021  if ( $suppressAllowed ) {
2022  $dropDownReason .= "\n" . $ctx->msg( 'deletereason-dropdown-suppress' )
2023  ->inContentLanguage()->text();
2024  }
2025 
2026  $options = Xml::listDropDownOptions(
2027  $dropDownReason,
2028  [ 'other' => $ctx->msg( 'deletereasonotherlist' )->inContentLanguage()->text() ]
2029  );
2030  $options = Xml::listDropDownOptionsOoui( $options );
2031 
2032  $fields[] = new OOUI\FieldLayout(
2033  new OOUI\DropdownInputWidget( [
2034  'name' => 'wpDeleteReasonList',
2035  'inputId' => 'wpDeleteReasonList',
2036  'tabIndex' => 1,
2037  'infusable' => true,
2038  'value' => '',
2039  'options' => $options
2040  ] ),
2041  [
2042  'label' => $ctx->msg( 'deletecomment' )->text(),
2043  'align' => 'top',
2044  ]
2045  );
2046 
2047  // HTML maxlength uses "UTF-16 code units", which means that characters outside BMP
2048  // (e.g. emojis) count for two each. This limit is overridden in JS to instead count
2049  // Unicode codepoints.
2050  $fields[] = new OOUI\FieldLayout(
2051  new OOUI\TextInputWidget( [
2052  'name' => 'wpReason',
2053  'inputId' => 'wpReason',
2054  'tabIndex' => 2,
2056  'infusable' => true,
2057  'value' => $reason,
2058  'autofocus' => true,
2059  ] ),
2060  [
2061  'label' => $ctx->msg( 'deleteotherreason' )->text(),
2062  'align' => 'top',
2063  ]
2064  );
2065 
2066  if ( $user->isRegistered() ) {
2067  $fields[] = new OOUI\FieldLayout(
2068  new OOUI\CheckboxInputWidget( [
2069  'name' => 'wpWatch',
2070  'inputId' => 'wpWatch',
2071  'tabIndex' => 3,
2072  'selected' => $checkWatch,
2073  ] ),
2074  [
2075  'label' => $ctx->msg( 'watchthis' )->text(),
2076  'align' => 'inline',
2077  'infusable' => true,
2078  ]
2079  );
2080  }
2081  if ( $suppressAllowed ) {
2082  $fields[] = new OOUI\FieldLayout(
2083  new OOUI\CheckboxInputWidget( [
2084  'name' => 'wpSuppress',
2085  'inputId' => 'wpSuppress',
2086  'tabIndex' => 4,
2087  ] ),
2088  [
2089  'label' => $ctx->msg( 'revdelete-suppress' )->text(),
2090  'align' => 'inline',
2091  'infusable' => true,
2092  ]
2093  );
2094  }
2095 
2096  $fields[] = new OOUI\FieldLayout(
2097  new OOUI\ButtonInputWidget( [
2098  'name' => 'wpConfirmB',
2099  'inputId' => 'wpConfirmB',
2100  'tabIndex' => 5,
2101  'value' => $ctx->msg( 'deletepage' )->text(),
2102  'label' => $ctx->msg( 'deletepage' )->text(),
2103  'flags' => [ 'primary', 'destructive' ],
2104  'type' => 'submit',
2105  ] ),
2106  [
2107  'align' => 'top',
2108  ]
2109  );
2110 
2111  $fieldset = new OOUI\FieldsetLayout( [
2112  'label' => $ctx->msg( 'delete-legend' )->text(),
2113  'id' => 'mw-delete-table',
2114  'items' => $fields,
2115  ] );
2116 
2117  $form = new OOUI\FormLayout( [
2118  'method' => 'post',
2119  'action' => $title->getLocalURL( 'action=delete' ),
2120  'id' => 'deleteconfirm',
2121  ] );
2122  $form->appendContent(
2123  $fieldset,
2124  new OOUI\HtmlSnippet(
2125  Html::hidden( 'wpEditToken', $user->getEditToken( [ 'delete', $title->getPrefixedText() ] ) )
2126  )
2127  );
2128 
2129  $outputPage->addHTML(
2130  new OOUI\PanelLayout( [
2131  'classes' => [ 'deletepage-wrapper' ],
2132  'expanded' => false,
2133  'padded' => true,
2134  'framed' => true,
2135  'content' => $form,
2136  ] )
2137  );
2138 
2139  if ( $this->getContext()->getAuthority()->isAllowed( 'editinterface' ) ) {
2140  $link = '';
2141  if ( $suppressAllowed ) {
2142  $link .= $this->linkRenderer->makeKnownLink(
2143  $ctx->msg( 'deletereason-dropdown-suppress' )->inContentLanguage()->getTitle(),
2144  $ctx->msg( 'delete-edit-reasonlist-suppress' )->text(),
2145  [],
2146  [ 'action' => 'edit' ]
2147  );
2148  $link .= $ctx->msg( 'pipe-separator' )->escaped();
2149  }
2150  $link .= $this->linkRenderer->makeKnownLink(
2151  $ctx->msg( 'deletereason-dropdown' )->inContentLanguage()->getTitle(),
2152  $ctx->msg( 'delete-edit-reasonlist' )->text(),
2153  [],
2154  [ 'action' => 'edit' ]
2155  );
2156  $outputPage->addHTML( '<p class="mw-delete-editreasons">' . $link . '</p>' );
2157  }
2158 
2159  $deleteLogPage = new LogPage( 'delete' );
2160  $outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) );
2161  LogEventsList::showLogExtract( $outputPage, 'delete', $title );
2162  }
2163 
2172  public function doDelete( $reason, $suppress = false, $immediate = false ) {
2173  $error = '';
2174  $context = $this->getContext();
2175  $outputPage = $context->getOutput();
2176  $user = $context->getUser();
2177  $status = $this->mPage->doDeleteArticleReal(
2178  $reason, $user, $suppress, null, $error,
2179  null, [], 'delete', $immediate
2180  );
2181 
2182  if ( $status->isOK() ) {
2183  $deleted = $this->getTitle()->getPrefixedText();
2184 
2185  $outputPage->setPageTitle( wfMessage( 'actioncomplete' ) );
2186  $outputPage->setRobotPolicy( 'noindex,nofollow' );
2187 
2188  if ( $status->isGood() ) {
2189  $loglink = '[[Special:Log/delete|' . wfMessage( 'deletionlog' )->text() . ']]';
2190  $outputPage->addWikiMsg( 'deletedtext', wfEscapeWikiText( $deleted ), $loglink );
2191  $this->getHookRunner()->onArticleDeleteAfterSuccess( $this->getTitle(), $outputPage );
2192  } else {
2193  $outputPage->addWikiMsg( 'delete-scheduled', wfEscapeWikiText( $deleted ) );
2194  }
2195 
2196  $outputPage->returnToMain( false );
2197  } else {
2198  $outputPage->setPageTitle(
2199  wfMessage( 'cannotdelete-title',
2200  $this->getTitle()->getPrefixedText() )
2201  );
2202 
2203  if ( $error == '' ) {
2204  $outputPage->wrapWikiTextAsInterface(
2205  'error mw-error-cannotdelete',
2206  $status->getWikiText( false, false, $context->getLanguage() )
2207  );
2208  $deleteLogPage = new LogPage( 'delete' );
2209  $outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) );
2210 
2212  $outputPage,
2213  'delete',
2214  $this->getTitle()
2215  );
2216  } else {
2217  $outputPage->addHTML( $error );
2218  }
2219  }
2220  }
2221 
2222  /* Caching functions */
2223 
2231  protected function tryFileCache() {
2232  static $called = false;
2233 
2234  if ( $called ) {
2235  wfDebug( "Article::tryFileCache(): called twice!?" );
2236  return false;
2237  }
2238 
2239  $called = true;
2240  if ( $this->isFileCacheable() ) {
2241  $cache = new HTMLFileCache( $this->getTitle(), 'view' );
2242  if ( $cache->isCacheGood( $this->mPage->getTouched() ) ) {
2243  wfDebug( "Article::tryFileCache(): about to load file" );
2244  $cache->loadFromFileCache( $this->getContext() );
2245  return true;
2246  } else {
2247  wfDebug( "Article::tryFileCache(): starting buffer" );
2248  ob_start( [ &$cache, 'saveToFileCache' ] );
2249  }
2250  } else {
2251  wfDebug( "Article::tryFileCache(): not cacheable" );
2252  }
2253 
2254  return false;
2255  }
2256 
2262  public function isFileCacheable( $mode = HTMLFileCache::MODE_NORMAL ) {
2263  $cacheable = false;
2264 
2265  if ( HTMLFileCache::useFileCache( $this->getContext(), $mode ) ) {
2266  $cacheable = $this->mPage->getId()
2267  && !$this->mRedirectedFrom && !$this->getTitle()->isRedirect();
2268  // Extension may have reason to disable file caching on some pages.
2269  if ( $cacheable ) {
2270  $cacheable = $this->getHookRunner()->onIsFileCacheable( $this );
2271  }
2272  }
2273 
2274  return $cacheable;
2275  }
2276 
2290  public function getParserOutput( $oldid = null, User $user = null ) {
2291  if ( $user === null ) {
2292  $parserOptions = $this->getParserOptions();
2293  } else {
2294  $parserOptions = $this->mPage->makeParserOptions( $user );
2295  }
2296 
2297  return $this->mPage->getParserOutput( $parserOptions, $oldid );
2298  }
2299 
2304  public function getParserOptions() {
2305  return $this->mPage->makeParserOptions( $this->getContext() );
2306  }
2307 
2314  public function setContext( $context ) {
2315  $this->mContext = $context;
2316  }
2317 
2324  public function getContext() {
2325  if ( $this->mContext instanceof IContextSource ) {
2326  return $this->mContext;
2327  } else {
2328  wfDebug( __METHOD__ . " called and \$mContext is null. " .
2329  "Return RequestContext::getMain(); for sanity" );
2330  return RequestContext::getMain();
2331  }
2332  }
2333 
2343  public function __get( $fname ) {
2344  wfDeprecatedMsg( "Accessing Article::\$$fname is deprecated since MediaWiki 1.35",
2345  '1.35' );
2346 
2347  if ( $fname === 'mRevision' ) {
2348  $record = $this->fetchRevisionRecord(); // Ensure that it is loaded
2349  return $record ? new Revision( $record ) : null;
2350  }
2351 
2352  if ( property_exists( $this->mPage, $fname ) ) {
2353  return $this->mPage->$fname;
2354  }
2355  trigger_error( 'Inaccessible property via __get(): ' . $fname, E_USER_NOTICE );
2356  }
2357 
2367  public function __set( $fname, $fvalue ) {
2368  wfDeprecatedMsg( "Setting Article::\$$fname is deprecated since MediaWiki 1.35",
2369  '1.35' );
2370 
2371  if ( $fname === 'mRevision' ) {
2372  $this->mRevisionRecord = $fvalue ?
2373  $fvalue->getRevisionRecord() :
2374  null;
2375  return;
2376  }
2377 
2378  if ( property_exists( $this->mPage, $fname ) ) {
2379  $this->mPage->$fname = $fvalue;
2380  // Note: extensions may want to toss on new fields
2381  } elseif ( !in_array( $fname, [ 'mContext', 'mPage' ] ) ) {
2382  $this->mPage->$fname = $fvalue;
2383  } else {
2384  trigger_error( 'Inaccessible property via __set(): ' . $fname, E_USER_NOTICE );
2385  }
2386  }
2387 
2399  public function checkFlags( $flags ) {
2400  wfDeprecated( __METHOD__, '1.35' );
2401  return $this->mPage->checkFlags( $flags );
2402  }
2403 
2409  public function checkTouched() {
2410  wfDeprecated( __METHOD__, '1.35' );
2411  return $this->mPage->checkTouched();
2412  }
2413 
2419  public function clearPreparedEdit() {
2420  wfDeprecated( __METHOD__, '1.35' );
2421  $this->mPage->clearPreparedEdit();
2422  }
2423 
2432  public function doDeleteUpdates(
2433  $id,
2434  Content $content = null,
2435  $revision = null,
2436  User $user = null
2437  ) {
2438  wfDeprecated( __METHOD__, '1.35' );
2439  $this->mPage->doDeleteUpdates( $id, $content, $revision, $user );
2440  }
2441 
2451  public function doEditUpdates( Revision $revision, User $user, array $options = [] ) {
2452  wfDeprecated( __METHOD__, '1.35' );
2453  $this->mPage->doEditUpdates( $revision, $user, $options );
2454  }
2455 
2463  public function doPurge() {
2464  wfDeprecated( __METHOD__, '1.35' );
2465  return $this->mPage->doPurge();
2466  }
2467 
2474  public function doViewUpdates( User $user, $oldid = 0 ) {
2475  wfDeprecated( __METHOD__, '1.35' );
2476  $this->mPage->doViewUpdates( $user, $oldid );
2477  }
2478 
2484  public function exists() {
2485  wfDeprecated( __METHOD__, '1.35' );
2486  return $this->mPage->exists();
2487  }
2488 
2494  public function followRedirect() {
2495  wfDeprecated( __METHOD__, '1.35' );
2496  return $this->mPage->followRedirect();
2497  }
2498 
2504  public function getActionOverrides() {
2505  return $this->mPage->getActionOverrides();
2506  }
2507 
2514  public function getAutoDeleteReason( &$hasHistory ) {
2515  wfDeprecated( __METHOD__, '1.35' );
2516  return $this->mPage->getAutoDeleteReason( $hasHistory );
2517  }
2518 
2524  public function getCategories() {
2525  wfDeprecated( __METHOD__, '1.35' );
2526  return $this->mPage->getCategories();
2527  }
2528 
2534  public function getContentHandler() {
2535  wfDeprecated( __METHOD__, '1.35' );
2536  return $this->mPage->getContentHandler();
2537  }
2538 
2544  public function getContentModel() {
2545  wfDeprecated( __METHOD__, '1.35' );
2546  return $this->mPage->getContentModel();
2547  }
2548 
2554  public function getContributors() {
2555  wfDeprecated( __METHOD__, '1.35' );
2556  return $this->mPage->getContributors();
2557  }
2558 
2565  public function getDeletionUpdates( Content $content = null ) {
2566  wfDeprecated( __METHOD__, '1.35' );
2567  return $this->mPage->getDeletionUpdates( $content );
2568  }
2569 
2575  public function getHiddenCategories() {
2576  wfDeprecated( __METHOD__, '1.35' );
2577  return $this->mPage->getHiddenCategories();
2578  }
2579 
2585  public function getId() {
2586  wfDeprecated( __METHOD__, '1.35' );
2587  return $this->mPage->getId();
2588  }
2589 
2595  public function getLatest() {
2596  wfDeprecated( __METHOD__, '1.35' );
2597  return $this->mPage->getLatest();
2598  }
2599 
2605  public function getLinksTimestamp() {
2606  wfDeprecated( __METHOD__, '1.35' );
2607  return $this->mPage->getLinksTimestamp();
2608  }
2609 
2615  public function getMinorEdit() {
2616  wfDeprecated( __METHOD__, '1.35' );
2617  return $this->mPage->getMinorEdit();
2618  }
2619 
2624  public function getOldestRevision() {
2625  wfDeprecated( __METHOD__, '1.35' );
2626  return $this->mPage->getOldestRevision();
2627  }
2628 
2634  public function getRedirectTarget() {
2635  wfDeprecated( __METHOD__, '1.35' );
2636  return $this->mPage->getRedirectTarget();
2637  }
2638 
2645  public function getRedirectURL( $rt ) {
2646  wfDeprecated( __METHOD__, '1.35' );
2647  return $this->mPage->getRedirectURL( $rt );
2648  }
2649 
2656  public function getRevision() {
2657  wfDeprecated( __METHOD__, '1.35' );
2658  return $this->mPage->getRevision();
2659  }
2660 
2666  public function getTimestamp() {
2667  wfDeprecated( __METHOD__, '1.35' );
2668  return $this->mPage->getTimestamp();
2669  }
2670 
2676  public function getTouched() {
2677  wfDeprecated( __METHOD__, '1.35' );
2678  return $this->mPage->getTouched();
2679  }
2680 
2689  public function getUndoContent( Revision $undo, Revision $undoafter = null ) {
2690  wfDeprecated( __METHOD__, '1.35' );
2691  return $this->mPage->getUndoContent( $undo, $undoafter );
2692  }
2693 
2699  public function hasViewableContent() {
2700  wfDeprecated( __METHOD__, '1.35' );
2701  return $this->mPage->hasViewableContent();
2702  }
2703 
2711  public function insertOn( $dbw, $pageId = null ) {
2712  wfDeprecated( __METHOD__, '1.35' );
2713  return $this->mPage->insertOn( $dbw, $pageId );
2714  }
2715 
2721  public function insertRedirect() {
2722  wfDeprecated( __METHOD__, '1.35' );
2723  return $this->mPage->insertRedirect();
2724  }
2725 
2733  public function insertRedirectEntry( Title $rt, $oldLatest = null ) {
2734  wfDeprecated( __METHOD__, '1.35' );
2735  return $this->mPage->insertRedirectEntry( $rt, $oldLatest );
2736  }
2737 
2744  public function isCountable( $editInfo = false ) {
2745  wfDeprecated( __METHOD__, '1.35' );
2746  return $this->mPage->isCountable( $editInfo );
2747  }
2748 
2754  public function isRedirect() {
2755  wfDeprecated( __METHOD__, '1.35' );
2756  return $this->mPage->isRedirect();
2757  }
2758 
2765  public function loadFromRow( $data, $from ) {
2766  wfDeprecated( __METHOD__, '1.35' );
2767  $this->mPage->loadFromRow( $data, $from );
2768  }
2769 
2775  public function loadPageData( $from = 'fromdb' ) {
2776  wfDeprecated( __METHOD__, '1.35' );
2777  $this->mPage->loadPageData( $from );
2778  }
2779 
2785  public function lockAndGetLatest() {
2786  wfDeprecated( __METHOD__, '1.35' );
2787  return $this->mPage->lockAndGetLatest();
2788  }
2789 
2796  public function makeParserOptions( $context ) {
2797  wfDeprecated( __METHOD__, '1.35' );
2798  return $this->mPage->makeParserOptions( $context );
2799  }
2800 
2809  public function pageDataFromId( $dbr, $id, $options = [] ) {
2810  wfDeprecated( __METHOD__, '1.35' );
2811  return $this->mPage->pageDataFromId( $dbr, $id, $options );
2812  }
2813 
2822  public function pageDataFromTitle( $dbr, $title, $options = [] ) {
2823  wfDeprecated( __METHOD__, '1.35' );
2824  return $this->mPage->pageDataFromTitle( $dbr, $title, $options );
2825  }
2826 
2840  public function prepareContentForEdit(
2841  Content $content, $revision = null, User $user = null,
2842  $serialFormat = null, $useCache = true
2843  ) {
2844  wfDeprecated( __METHOD__, '1.35' );
2845  return $this->mPage->prepareContentForEdit(
2846  $content, $revision, $user,
2847  $serialFormat, $useCache
2848  );
2849  }
2850 
2858  public function protectDescription( array $limit, array $expiry ) {
2859  wfDeprecated( __METHOD__, '1.35' );
2860  return $this->mPage->protectDescription( $limit, $expiry );
2861  }
2862 
2870  public function protectDescriptionLog( array $limit, array $expiry ) {
2871  wfDeprecated( __METHOD__, '1.35' );
2872  return $this->mPage->protectDescriptionLog( $limit, $expiry );
2873  }
2874 
2884  public function replaceSectionAtRev( $sectionId, Content $sectionContent,
2885  $sectionTitle = '', $baseRevId = null
2886  ) {
2887  wfDeprecated( __METHOD__, '1.35' );
2888  return $this->mPage->replaceSectionAtRev( $sectionId, $sectionContent,
2889  $sectionTitle, $baseRevId
2890  );
2891  }
2892 
2904  public function replaceSectionContent(
2905  $sectionId, Content $sectionContent, $sectionTitle = '', $edittime = null
2906  ) {
2907  wfDeprecated( __METHOD__, '1.35' );
2908  return $this->mPage->replaceSectionContent(
2909  $sectionId, $sectionContent, $sectionTitle, $edittime
2910  );
2911  }
2912 
2918  public function setTimestamp( $ts ) {
2919  wfDeprecated( __METHOD__, '1.35' );
2920  $this->mPage->setTimestamp( $ts );
2921  }
2922 
2930  public function shouldCheckParserCache( ParserOptions $parserOptions, $oldId ) {
2931  wfDeprecated( __METHOD__, '1.35' );
2932  return $this->mPage->shouldCheckParserCache( $parserOptions, $oldId );
2933  }
2934 
2940  public function supportsSections() {
2941  wfDeprecated( __METHOD__, '1.35' );
2942  return $this->mPage->supportsSections();
2943  }
2944 
2950  public function triggerOpportunisticLinksUpdate( ParserOutput $parserOutput ) {
2951  wfDeprecated( __METHOD__, '1.35' );
2952  $this->mPage->triggerOpportunisticLinksUpdate( $parserOutput );
2953  }
2954 
2962  public function updateCategoryCounts( array $added, array $deleted, $id = 0 ) {
2963  wfDeprecated( __METHOD__, '1.35' );
2964  $this->mPage->updateCategoryCounts( $added, $deleted, $id );
2965  }
2966 
2975  public function updateIfNewerOn( $dbw, $revision ) {
2976  wfDeprecated( __METHOD__, '1.35' );
2977  return $this->mPage->updateIfNewerOn( $dbw, $revision );
2978  }
2979 
2988  public function updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect = null ) {
2989  wfDeprecated( __METHOD__, '1.35' );
2990  return $this->mPage->updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect );
2991  }
2992 
3002  public function updateRevisionOn( $dbw, $revision, $lastRevision = null,
3003  $lastRevIsRedirect = null
3004  ) {
3005  wfDeprecated( __METHOD__, '1.35' );
3006  return $this->mPage->updateRevisionOn( $dbw, $revision, $lastRevision,
3007  $lastRevIsRedirect
3008  );
3009  }
3010 
3020  public function doUpdateRestrictions( array $limit, array $expiry, &$cascade,
3021  $reason, User $user
3022  ) {
3023  wfDeprecated( __METHOD__, '1.35' );
3024  return $this->mPage->doUpdateRestrictions( $limit, $expiry, $cascade, $reason, $user );
3025  }
3026 
3035  public function updateRestrictions( $limit = [], $reason = '',
3036  &$cascade = 0, $expiry = []
3037  ) {
3038  wfDeprecated( __METHOD__, '1.35' );
3039  return $this->mPage->doUpdateRestrictions(
3040  $limit,
3041  $expiry,
3042  $cascade,
3043  $reason,
3044  $this->getContext()->getUser()
3045  );
3046  }
3047 
3058  public function doRollback(
3059  $fromP,
3060  $summary,
3061  $token,
3062  $bot,
3063  &$resultDetails,
3064  User $user = null
3065  ) {
3066  wfDeprecated( __METHOD__, '1.35' );
3067  if ( !$user ) {
3068  $user = $this->getContext()->getUser();
3069  }
3070 
3071  return $this->mPage->doRollback( $fromP, $summary, $token, $bot, $resultDetails, $user );
3072  }
3073 
3084  public function commitRollback( $fromP, $summary, $bot, &$resultDetails, User $guser = null ) {
3085  wfDeprecated( __METHOD__, '1.35' );
3086  if ( !$guser ) {
3087  $guser = $this->getContext()->getUser();
3088  }
3089 
3090  return $this->mPage->commitRollback( $fromP, $summary, $bot, $resultDetails, $guser );
3091  }
3092 
3099  public function generateReason( &$hasHistory ) {
3100  wfDeprecated( __METHOD__, '1.35' );
3101  return $this->getPage()->getAutoDeleteReason( $hasHistory );
3102  }
3103 
3104 }
ReadOnlyError
Show an error when the wiki is locked/read-only and the user tries to do something that requires writ...
Definition: ReadOnlyError.php:29
$wgSend404Code
$wgSend404Code
Some web hosts attempt to rewrite all responses with a 404 (not found) status code,...
Definition: DefaultSettings.php:3753
Article\showMissingArticle
showMissingArticle()
Show the error text for a missing article.
Definition: Article.php:1345
Article\checkFlags
checkFlags( $flags)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2399
$wgCdnMaxageStale
$wgCdnMaxageStale
Cache timeout when delivering a stale ParserCache response due to PoolCounter contention.
Definition: DefaultSettings.php:3030
ParserOptions
Set options of the Parser.
Definition: ParserOptions.php:44
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:56
Message\numParam
static numParam( $num)
Definition: Message.php:1038
Page
Interface for type hinting (accepts WikiPage, Article, ImagePage, CategoryPage)
Definition: Page.php:29
Title\newFromText
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:363
Article\isRedirect
isRedirect()
Definition: Article.php:2754
Article\getCategories
getCategories()
Definition: Article.php:2524
Revision\RevisionRecord
Page revision base class.
Definition: RevisionRecord.php:47
Article\doOutputMetaData
doOutputMetaData(?ParserOutput $pOutput, OutputPage $outputPage)
Definition: Article.php:773
OutputPage\addSubtitle
addSubtitle( $str)
Add $str to the subtitle.
Definition: OutputPage.php:1074
ParserOutput
Definition: ParserOutput.php:31
Article\formatRobotPolicy
static formatRobotPolicy( $policy)
Converts a String robot policy into an associative array, to allow merging of several policies using ...
Definition: Article.php:1028
StatusValue\newFatal
static newFatal( $message,... $parameters)
Factory function for fatal errors.
Definition: StatusValue.php:70
NS_MEDIAWIKI
const NS_MEDIAWIKI
Definition: Defines.php:72
Article\$viewIsRenderAction
bool $viewIsRenderAction
Whether render() was called.
Definition: Article.php:91
Article\getRedirectTarget
getRedirectTarget()
Definition: Article.php:2634
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:484
Xml\listDropDownOptionsOoui
static listDropDownOptionsOoui( $options)
Convert options for a drop-down box into a format accepted by OOUI\DropdownInputWidget etc.
Definition: Xml.php:594
Article\getContentModel
getContentModel()
Definition: Article.php:2544
PageArchive
Used to show archived pages and eventually restore them.
Definition: PageArchive.php:32
Article\getLinksTimestamp
getLinksTimestamp()
Definition: Article.php:2605
Article\getOldIDFromRequest
getOldIDFromRequest()
Sets $this->mRedirectUrl to a correct URL if the query parameters are incorrect.
Definition: Article.php:316
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:173
Article\showPatrolFooter
showPatrolFooter()
If patrol is possible, output a patrol UI box.
Definition: Article.php:1167
$lang
if(!isset( $args[0])) $lang
Definition: testCompression.php:37
RecentChange\newFromConds
static newFromConds( $conds, $fname=__METHOD__, $dbType=DB_REPLICA)
Find the first recent change matching some specific conditions.
Definition: RecentChange.php:223
Article\tryFileCache
tryFileCache()
checkLastModified returns true if it has taken care of all output to the client that is necessary for...
Definition: Article.php:2231
Article\getContentHandler
getContentHandler()
Definition: Article.php:2534
Article\clearPreparedEdit
clearPreparedEdit()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2419
Article\$revisionStore
RevisionStore $revisionStore
Definition: Article.php:101
HTMLFileCache
Page view caching in the file system.
Definition: HTMLFileCache.php:33
Page\ParserOutputAccess
Service for getting rendered output of a given page.
Definition: ParserOutputAccess.php:49
Revision\RevisionStore
Service for looking up page revisions.
Definition: RevisionStore.php:89
Article\lockAndGetLatest
lockAndGetLatest()
Definition: Article.php:2785
MediaWiki\Linker\LinkRenderer
Class that generates HTML links for pages.
Definition: LinkRenderer.php:41
Article\supportsSections
supportsSections()
Definition: Article.php:2940
Article\getOldestRevision
getOldestRevision()
Definition: Article.php:2624
Article\getDeletionUpdates
getDeletionUpdates(Content $content=null)
Definition: Article.php:2565
Article\checkTouched
checkTouched()
Definition: Article.php:2409
Article\getRevision
getRevision()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2656
CategoryPage
Special handling for category description pages, showing pages, subcategories and file that belong to...
Definition: CategoryPage.php:30
Revision\RevisionRecord\getTimestamp
getTimestamp()
MCR migration note: this replaces Revision::getTimestamp.
Definition: RevisionRecord.php:475
getAuthority
getAuthority()
WikiPage
Class representing a MediaWiki article and history.
Definition: WikiPage.php:61
Linker\revComment
static revComment( $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:1597
wfReadOnly
wfReadOnly()
Check whether the wiki is in read-only mode.
Definition: GlobalFunctions.php:1135
User\newFromName
static newFromName( $name, $validate='valid')
Definition: User.php:586
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1231
Article\showDiffPage
showDiffPage()
Show a diff page according to current request variables.
Definition: Article.php:885
$wgArticleRobotPolicies
$wgArticleRobotPolicies
Robot policies per article.
Definition: DefaultSettings.php:8577
ImagePage
Class for viewing MediaWiki file description pages.
Definition: ImagePage.php:33
PermissionsError
Show an error when a user tries to do something they do not have the necessary permissions for.
Definition: PermissionsError.php:32
Article\confirmDelete
confirmDelete( $reason)
Output deletion confirmation dialog.
Definition: Article.php:1975
Article\updateCategoryCounts
updateCategoryCounts(array $added, array $deleted, $id=0)
Definition: Article.php:2962
Html\warningBox
static warningBox( $html, $className='')
Return a warning box.
Definition: Html.php:729
Article\doOutputFromRenderStatus
doOutputFromRenderStatus(?RevisionRecord $rev, Status $renderStatus, OutputPage $outputPage, array $textOptions)
Definition: Article.php:815
Article\adjustDisplayTitle
adjustDisplayTitle(ParserOutput $pOutput)
Adjust title for pages with displaytitle, -{T|}- or language conversion.
Definition: Article.php:870
Article\showNamespaceHeader
showNamespaceHeader()
Show a header specific to the namespace currently being viewed, like [[MediaWiki:Talkpagetext]].
Definition: Article.php:1131
Article\shouldCheckParserCache
shouldCheckParserCache(ParserOptions $parserOptions, $oldId)
Definition: Article.php:2930
$wgUseRCPatrol
$wgUseRCPatrol
Use RC Patrolling to check for vandalism (from recent changes and watchlists) New pages and new files...
Definition: DefaultSettings.php:7427
Xml\openElement
static openElement( $element, $attribs=null)
This opens an XML element.
Definition: Xml.php:110
$wgUseNPPatrol
$wgUseNPPatrol
Use new page patrolling to check new pages on Special:Newpages.
Definition: DefaultSettings.php:7443
CacheTime\getCacheRevisionId
getCacheRevisionId()
Definition: CacheTime.php:96
Article\$linkRenderer
LinkRenderer $linkRenderer
Definition: Article.php:96
ProtectionForm
Handles the page protection UI and backend.
Definition: ProtectionForm.php:33
OutputPage\addHTML
addHTML( $text)
Append $text to the body HTML.
Definition: OutputPage.php:1617
Article\doDelete
doDelete( $reason, $suppress=false, $immediate=false)
Perform a deletion and output success or failure messages.
Definition: Article.php:2172
Article\protectDescriptionLog
protectDescriptionLog(array $limit, array $expiry)
Definition: Article.php:2870
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:858
OutputPage\setLastModified
setLastModified( $timestamp)
Override the last modified timestamp.
Definition: OutputPage.php:860
Article\$mRedirectedFrom
Title null $mRedirectedFrom
Title from which we were redirected here, if any.
Definition: Article.php:68
$dbr
$dbr
Definition: testCompression.php:54
Status
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition: Status.php:44
Article\getSubstituteContent
getSubstituteContent()
Returns Content object to use when the page does not exist.
Definition: Article.php:278
Revision
Definition: Revision.php:40
StatusValue\getValue
getValue()
Definition: StatusValue.php:138
Article\unprotect
unprotect()
action=unprotect handler (alias)
Definition: Article.php:1836
RC_LOG
const RC_LOG
Definition: Defines.php:128
Article\insertRedirect
insertRedirect()
Definition: Article.php:2721
Status\getWikiText
getWikiText( $shortContext=false, $longContext=false, $lang=null)
Get the error list as a wikitext formatted list.
Definition: Status.php:189
MediaWiki\Block\DatabaseBlock
A DatabaseBlock (unlike a SystemBlock) is stored in the database, may give rise to autoblocks and may...
Definition: DatabaseBlock.php:53
Article\getRedirectedFrom
getRedirectedFrom()
Get the page this view was redirected from.
Definition: Article.php:195
Article\clear
clear()
Definition: Article.php:227
Article\updateRedirectOn
updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect=null)
Definition: Article.php:2988
Article\newFromWikiPage
static newFromWikiPage(WikiPage $page, IContextSource $context)
Create an Article object of the appropriate class for the given page.
Definition: Article.php:184
wfDeprecatedMsg
wfDeprecatedMsg( $msg, $version=false, $component=false, $callerOffset=2)
Log a deprecation warning with arbitrary message text.
Definition: GlobalFunctions.php:1066
Article\getTouched
getTouched()
Definition: Article.php:2676
Article\getTitle
getTitle()
Get the title object of the article.
Definition: Article.php:213
Article\updateRevisionOn
updateRevisionOn( $dbw, $revision, $lastRevision=null, $lastRevIsRedirect=null)
Definition: Article.php:3002
Article\render
render()
Handle action=render.
Definition: Article.php:1817
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that $function is deprecated.
Definition: GlobalFunctions.php:1034
Article\$mParserOutput
ParserOutput null false $mParserOutput
The ParserOutput generated for viewing the page, initialized by view().
Definition: Article.php:84
Article\addHelpLink
addHelpLink( $to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
Definition: Article.php:1800
Article\exists
exists()
Definition: Article.php:2484
ParserOutput\getTimestamp
getTimestamp()
Definition: ParserOutput.php:681
Article\setRedirectedFrom
setRedirectedFrom(Title $from)
Tell the page view functions that this view was redirected from another page on the wiki.
Definition: Article.php:204
$wgUseFileCache
$wgUseFileCache
This will cache static pages for non-logged-in users to reduce database traffic on public sites.
Definition: DefaultSettings.php:2886
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2467
Article\__construct
__construct(Title $title, $oldId=null)
Definition: Article.php:118
StatusValue\isOK
isOK()
Returns whether the operation completed.
Definition: StatusValue.php:131
Article\showDeletedRevisionHeader
showDeletedRevisionHeader()
If the revision requested for view is deleted, check permissions.
Definition: Article.php:1519
Article\isFileCacheable
isFileCacheable( $mode=HTMLFileCache::MODE_NORMAL)
Check if the page can be cached.
Definition: Article.php:2262
Article\doRollback
doRollback( $fromP, $summary, $token, $bot, &$resultDetails, User $user=null)
Definition: Article.php:3058
WikiPage\getTitle
getTitle()
Get the title object of the article.
Definition: WikiPage.php:317
OutputPage\setRevisionId
setRevisionId( $revid)
Set the revision ID which will be seen by the wiki text parser for things such as embedded {{REVISION...
Definition: OutputPage.php:1682
LogPage
Class to simplify the use of log pages.
Definition: LogPage.php:37
Article\setOldSubtitle
setOldSubtitle( $oldid=0)
Generate the navigation links when browsing through an article revisions It shows the information as:...
Definition: Article.php:1580
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:74
Article\getPage
getPage()
Get the WikiPage object of this instance.
Definition: Article.php:223
Article\getContext
getContext()
Gets the context this Article is executed in.
Definition: Article.php:2324
User\isIP
static isIP( $name)
Does the string match an anonymous IP address?
Definition: User.php:1012
Article\pageDataFromId
pageDataFromId( $dbr, $id, $options=[])
Definition: Article.php:2809
$title
$title
Definition: testCompression.php:38
Article\getRobotPolicy
getRobotPolicy( $action, ParserOutput $pOutput=null)
Get the robot policy to be used for the current view.
Definition: Article.php:948
Article\insertOn
insertOn( $dbw, $pageId=null)
Definition: Article.php:2711
Linker\makeExternalLink
static makeExternalLink( $url, $text, $escape=true, $linktype='', $attribs=[], $title=null)
Make an external link.
Definition: Linker.php:847
Title\makeTitle
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:626
Article\getRedirectURL
getRedirectURL( $rt)
Definition: Article.php:2645
LogEventsList\showLogExtract
static showLogExtract(&$out, $types=[], $page='', $user='', $param=[])
Show log extract.
Definition: LogEventsList.php:602
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
Article\newFromID
static newFromID( $id)
Constructor from a page id.
Definition: Article.php:140
Article\newPage
newPage(Title $title)
Definition: Article.php:131
Article\getUndoContent
getUndoContent(Revision $undo, Revision $undoafter=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2689
Linker\getRevDeleteLink
static getRevDeleteLink(Authority $performer, $rev, LinkTarget $title)
Get a revision-deletion link, or disabled link, or nothing, depending on user permissions & the setti...
Definition: Linker.php:2190
Article\prepareContentForEdit
prepareContentForEdit(Content $content, $revision=null, User $user=null, $serialFormat=null, $useCache=true)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2840
$wgDeleteRevisionsLimit
$wgDeleteRevisionsLimit
Optional to restrict deletion of pages with higher revision counts to users with the 'bigdelete' perm...
Definition: DefaultSettings.php:6051
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:1138
$wgUseFilePatrol
$wgUseFilePatrol
Use file patrolling to check new files on Special:Newfiles.
Definition: DefaultSettings.php:7454
Article\getLatest
getLatest()
Definition: Article.php:2595
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:915
OutputPage
This is one of the Core classes and should be read at least once by any new developers.
Definition: OutputPage.php:50
Article\isCurrent
isCurrent()
Returns true if the currently-referenced revision is the current edit to this page (and it exists).
Definition: Article.php:437
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:142
Article\getAutoDeleteReason
getAutoDeleteReason(&$hasHistory)
Definition: Article.php:2514
Article\isCountable
isCountable( $editInfo=false)
Definition: Article.php:2744
OutputPage\wrapWikiMsg
wrapWikiMsg( $wrap,... $msgSpecs)
This function takes a number of message/argument specifications, wraps them in some overall structure...
Definition: OutputPage.php:4116
Html\hidden
static hidden( $name, $value, array $attribs=[])
Convenience function to produce an input element with type=hidden.
Definition: Html.php:805
ParserOutput\getTitleText
getTitleText()
Definition: ParserOutput.php:594
Article\doPurge
doPurge()
Definition: Article.php:2463
OutputPage\setIndexPolicy
setIndexPolicy( $policy)
Set the index policy for the page, but leave the follow policy un- touched.
Definition: OutputPage.php:898
$content
$content
Definition: router.php:76
OutputPage\setRevisionTimestamp
setRevisionTimestamp( $timestamp)
Set the timestamp of the revision which will be displayed.
Definition: OutputPage.php:1713
NS_MEDIA
const NS_MEDIA
Definition: Defines.php:52
Article\hasViewableContent
hasViewableContent()
Definition: Article.php:2699
Article\$mOldId
int null $mOldId
The oldid of the article that was requested to be shown, 0 for the current revision.
Definition: Article.php:65
Article\getContributors
getContributors()
Definition: Article.php:2554
StatusValue\newGood
static newGood( $value=null)
Factory function for good results.
Definition: StatusValue.php:82
HTMLFileCache\MODE_NORMAL
const MODE_NORMAL
Definition: HTMLFileCache.php:34
Article\loadFromRow
loadFromRow( $data, $from)
Definition: Article.php:2765
WatchAction\doWatchOrUnwatch
static doWatchOrUnwatch( $watch, Title $title, Authority $performer, string $expiry=null)
Watch or unwatch a page.
Definition: WatchAction.php:247
Revision\RevisionRecord\getId
getId( $wikiId=self::LOCAL)
Get revision ID.
Definition: RevisionRecord.php:295
Article\protect
protect()
action=protect handler
Definition: Article.php:1828
Hooks\runner
static runner()
Get a HookRunner instance for calling hooks using the new interfaces.
Definition: Hooks.php:172
Article\getTimestamp
getTimestamp()
Definition: Article.php:2666
Article\doOutputFromParserCache
doOutputFromParserCache(ParserOutput $pOutput, OutputPage $outputPage, array $textOptions)
Definition: Article.php:792
wfEscapeWikiText
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
Definition: GlobalFunctions.php:1505
Article\getMinorEdit
getMinorEdit()
Definition: Article.php:2615
Article\commitRollback
commitRollback( $fromP, $summary, $bot, &$resultDetails, User $guser=null)
Definition: Article.php:3084
$wgNamespaceRobotPolicies
$wgNamespaceRobotPolicies
Robot policies per namespaces.
Definition: DefaultSettings.php:8549
Article\getParserOutput
getParserOutput( $oldid=null, User $user=null)
#-
Definition: Article.php:2290
Article\getContentObject
getContentObject()
Returns a Content object representing the pages effective display content, not necessarily the revisi...
Definition: Article.php:255
NS_USER
const NS_USER
Definition: Defines.php:66
Article\followRedirect
followRedirect()
Definition: Article.php:2494
Article\getHiddenCategories
getHiddenCategories()
Definition: Article.php:2575
RequestContext\getMain
static getMain()
Get the RequestContext object associated with the main request.
Definition: RequestContext.php:476
Article\setTimestamp
setTimestamp( $ts)
Definition: Article.php:2918
IContextSource
Interface for objects which can provide a MediaWiki context on request.
Definition: IContextSource.php:57
MediaWiki\Permissions\PermissionStatus
A StatusValue for permission errors.
Definition: PermissionStatus.php:34
Article\__get
__get( $fname)
Definition: Article.php:2343
OutputPage\setFollowPolicy
setFollowPolicy( $policy)
Set the follow policy for the page, but leave the index policy un- touched.
Definition: OutputPage.php:920
Content
Base interface for content objects.
Definition: Content.php:35
CommentStore\COMMENT_CHARACTER_LIMIT
const COMMENT_CHARACTER_LIMIT
Maximum length of a comment in UTF-8 characters.
Definition: CommentStore.php:48
Article\replaceSectionAtRev
replaceSectionAtRev( $sectionId, Content $sectionContent, $sectionTitle='', $baseRevId=null)
Definition: Article.php:2884
Article\fetchRevisionRecord
fetchRevisionRecord()
Fetches the revision to work on.
Definition: Article.php:385
Title
Represents a title within MediaWiki.
Definition: Title.php:46
OutputPage\setCdnMaxage
setCdnMaxage( $maxage)
Set the value of the "s-maxage" part of the "Cache-control" HTTP header.
Definition: OutputPage.php:2128
$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:2904
MediaWiki\Linker\LinkRenderer\makeKnownLink
makeKnownLink(LinkTarget $target, $text=null, array $extraAttribs=[], array $query=[])
Definition: LinkRenderer.php:225
Article\getRevIdFetched
getRevIdFetched()
Use this to fetch the rev ID used on page views.
Definition: Article.php:472
MessageContent
Wrapper allowing us to handle a system message as a Content object.
Definition: MessageContent.php:36
Article\makeParserOptions
makeParserOptions( $context)
Definition: Article.php:2796
Article\showRedirectedFromHeader
showRedirectedFromHeader()
If this request is a redirect view, send "redirected from" subtitle to the output.
Definition: Article.php:1057
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:1169
NS_CATEGORY
const NS_CATEGORY
Definition: Defines.php:78
NS_USER_TALK
const NS_USER_TALK
Definition: Defines.php:67
Article\getId
getId()
Definition: Article.php:2585
Article\getActionOverrides
getActionOverrides()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2504
StatusValue\hasMessage
hasMessage( $message)
Returns true if the specified message is present as a warning or error.
Definition: StatusValue.php:308
Article\$mRedirectUrl
string bool $mRedirectUrl
URL to redirect to or false if none.
Definition: Article.php:71
Article\doUpdateRestrictions
doUpdateRestrictions(array $limit, array $expiry, &$cascade, $reason, User $user)
Definition: Article.php:3020
Article\updateRestrictions
updateRestrictions( $limit=[], $reason='', &$cascade=0, $expiry=[])
Definition: Article.php:3035
Article\protectDescription
protectDescription(array $limit, array $expiry)
Definition: Article.php:2858
Article\getParserOptions
getParserOptions()
Get parser options suitable for rendering the primary article wikitext.
Definition: Article.php:2304
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:4522
Article\updateIfNewerOn
updateIfNewerOn( $dbw, $revision)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2975
$t
$t
Definition: testCompression.php:74
Article
Class for viewing MediaWiki article and history.
Definition: Article.php:47
Article\doDeleteUpdates
doDeleteUpdates( $id, Content $content=null, $revision=null, User $user=null)
Definition: Article.php:2432
Article\pageDataFromTitle
pageDataFromTitle( $dbr, $title, $options=[])
Definition: Article.php:2822
Article\generateContentOutput
generateContentOutput(User $user, ParserOptions $parserOptions, int $oldid, OutputPage $outputPage, array $textOptions)
Determines the desired ParserOutput and passes it to $outputPage.
Definition: Article.php:612
NS_FILE
const NS_FILE
Definition: Defines.php:70
Article\triggerOpportunisticLinksUpdate
triggerOpportunisticLinksUpdate(ParserOutput $parserOutput)
Definition: Article.php:2950
Article\getOldID
getOldID()
Definition: Article.php:303
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:100
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:66
Article\setContext
setContext( $context)
Sets the context this Article is executed in.
Definition: Article.php:2314
Title\newFromID
static newFromID( $id, $flags=0)
Create a new Title from an article ID.
Definition: Title.php:507
Article\insertRedirectEntry
insertRedirectEntry(Title $rt, $oldLatest=null)
Definition: Article.php:2733
OutputPage\addParserOutput
addParserOutput(ParserOutput $parserOutput, $poOptions=[])
Add everything from a ParserOutput object.
Definition: OutputPage.php:2017
MediaWiki\Linker\LinkRenderer\makeLink
makeLink(LinkTarget $target, $text=null, array $extraAttribs=[], array $query=[])
Definition: LinkRenderer.php:160
$wgDefaultRobotPolicy
$wgDefaultRobotPolicy
Default robot policy.
Definition: DefaultSettings.php:8533
Article\getRevisionFetched
getRevisionFetched()
Get the fetched Revision object depending on request parameters or null on failure.
Definition: Article.php:457
Article\viewRedirect
viewRedirect( $target, $appendSubtitle=true, $forceKnown=false)
Return the HTML for the top of a redirect page.
Definition: Article.php:1731
Article\loadPageData
loadPageData( $from='fromdb')
Definition: Article.php:2775
Language
Internationalisation code See https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation for more...
Definition: Language.php:43
Article\doEditUpdates
doEditUpdates(Revision $revision, User $user, array $options=[])
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2451
Article\showViewError
showViewError(string $errortext)
Show error text for errors generated in Article::view().
Definition: Article.php:1504
Article\$mRevisionRecord
RevisionRecord null $mRevisionRecord
Revision to be shown.
Definition: Article.php:112
Revision\RevisionRecord\getContent
getContent( $role, $audience=self::FOR_PUBLIC, Authority $performer=null)
Returns the Content of the given slot of this revision.
Definition: RevisionRecord.php:172
Article\__set
__set( $fname, $fvalue)
Definition: Article.php:2367
Article\showViewFooter
showViewFooter()
Show the footer section of an ordinary page view.
Definition: Article.php:1143
Revision\SlotRecord
Value object representing a content slot associated with a page revision.
Definition: SlotRecord.php:40
Article\$mPage
WikiPage $mPage
The WikiPage object of this instance.
Definition: Article.php:59
Article\getRedirectHeaderHtml
static getRedirectHeaderHtml(Language $lang, $target, $forceKnown=false)
Return the HTML for the top of a redirect page.
Definition: Article.php:1753
Xml\listDropDownOptions
static listDropDownOptions( $list, $params=[])
Build options for a drop-down box from a textual list.
Definition: Xml.php:545
Article\newFromTitle
static newFromTitle( $title, IContextSource $context)
Create an Article object of the appropriate class for the given page.
Definition: Article.php:152
Article\generateReason
generateReason(&$hasHistory)
Definition: Article.php:3099
Article\purgePatrolFooterCache
static purgePatrolFooterCache( $articleID)
Purge the cache used to check if it is worth showing the patrol footer For example,...
Definition: Article.php:1336
Article\doViewUpdates
doViewUpdates(User $user, $oldid=0)
Definition: Article.php:2474
Article\$fetchResult
Status null $fetchResult
represents the outcome of fetchRevisionRecord().
Definition: Article.php:77