MediaWiki  master
Article.php
Go to the documentation of this file.
1 <?php
25 use MediaWiki\HookContainer\ProtectedHookAccessorTrait;
33 use Wikimedia\IPUtils;
35 
46 class Article implements Page {
47  use ProtectedHookAccessorTrait;
48 
54  protected $mContext;
55 
57  protected $mPage;
58 
64 
74 
81  public $mContentLoaded = false;
82 
88  public $mOldId;
89 
91  public $mRedirectedFrom = null;
92 
94  public $mRedirectUrl = false;
95 
101  public $mRevIdFetched = 0;
102 
111  private $fetchResult = null;
112 
118  public $mParserOutput = null;
119 
125  protected $viewIsRenderAction = false;
126 
130  protected $linkRenderer;
131 
135  private $permManager;
136 
140  private $revisionStore;
141 
142  /*
143  * @var RevisionRecord|null Revision to be shown
144  *
145  * Initialized by getOldIDFromRequest() or fetchRevisionRecord(). Normally loaded from the
146  * database, but may be replaced by an extension, or be a fake representing an error message
147  * or some such. While the output of Article::view is typically based on this revision,
148  * it may be overwritten by error messages or replaced by extensions.
149  *
150  * Replaced $mRevision, which was public and is provided in a deprecated manner via
151  * __get and __set
152  */
153  private $mRevisionRecord = null;
154 
160  public function __construct( Title $title, $oldId = null ) {
161  $this->mOldId = $oldId;
162  $this->mPage = $this->newPage( $title );
163 
164  $services = MediaWikiServices::getInstance();
165  $this->linkRenderer = $services->getLinkRenderer();
166  $this->permManager = $services->getPermissionManager();
167  $this->revisionStore = $services->getRevisionStore();
168  }
169 
174  protected function newPage( Title $title ) {
175  return new WikiPage( $title );
176  }
177 
183  public static function newFromID( $id ) {
184  $t = Title::newFromID( $id );
185  return $t == null ? null : new static( $t );
186  }
187 
195  public static function newFromTitle( $title, IContextSource $context ) {
196  if ( NS_MEDIA == $title->getNamespace() ) {
197  // XXX: This should not be here, but where should it go?
198  $title = Title::makeTitle( NS_FILE, $title->getDBkey() );
199  }
200 
201  $page = null;
202  Hooks::runner()->onArticleFromTitle( $title, $page, $context );
203  if ( !$page ) {
204  switch ( $title->getNamespace() ) {
205  case NS_FILE:
206  $page = new ImagePage( $title );
207  break;
208  case NS_CATEGORY:
209  $page = new CategoryPage( $title );
210  break;
211  default:
212  $page = new Article( $title );
213  }
214  }
215  $page->setContext( $context );
216 
217  return $page;
218  }
219 
227  public static function newFromWikiPage( WikiPage $page, IContextSource $context ) {
228  $article = self::newFromTitle( $page->getTitle(), $context );
229  $article->mPage = $page; // override to keep process cached vars
230  return $article;
231  }
232 
238  public function getRedirectedFrom() {
239  return $this->mRedirectedFrom;
240  }
241 
247  public function setRedirectedFrom( Title $from ) {
248  $this->mRedirectedFrom = $from;
249  }
250 
256  public function getTitle() {
257  return $this->mPage->getTitle();
258  }
259 
266  public function getPage() {
267  return $this->mPage;
268  }
269 
273  public function clear() {
274  $this->mContentLoaded = false;
275 
276  $this->mRedirectedFrom = null; # Title object if set
277  $this->mRevIdFetched = 0;
278  $this->mRedirectUrl = false;
279  $this->mRevisionRecord = null;
280  $this->mContentObject = null;
281  $this->fetchResult = null;
282 
283  // TODO hard-deprecate direct access to public fields
284 
285  $this->mPage->clear();
286  }
287 
305  protected function getContentObject() {
306  if ( $this->mPage->getId() === 0 ) {
307  $content = $this->getSubstituteContent();
308  } else {
309  $this->fetchContentObject();
311  }
312 
313  return $content;
314  }
315 
321  private function getSubstituteContent() {
322  # If this is a MediaWiki:x message, then load the messages
323  # and return the message value for x.
324  if ( $this->getTitle()->getNamespace() == NS_MEDIAWIKI ) {
325  $text = $this->getTitle()->getDefaultMessageText();
326  if ( $text === false ) {
327  $text = '';
328  }
329 
330  $content = ContentHandler::makeContent( $text, $this->getTitle() );
331  } else {
332  $message = $this->getContext()->getUser()->isLoggedIn() ? 'noarticletext' : 'noarticletextanon';
333  $content = new MessageContent( $message, null );
334  }
335 
336  return $content;
337  }
338 
348  protected function getEmptyPageParserOutput( ParserOptions $options ) {
349  $content = $this->getSubstituteContent();
350 
351  return $content->getParserOutput( $this->getTitle(), 0, $options );
352  }
353 
361  public function getOldID() {
362  if ( $this->mOldId === null ) {
363  $this->mOldId = $this->getOldIDFromRequest();
364  }
365 
366  return $this->mOldId;
367  }
368 
374  public function getOldIDFromRequest() {
375  $this->mRedirectUrl = false;
376 
377  $request = $this->getContext()->getRequest();
378  $oldid = $request->getIntOrNull( 'oldid' );
379 
380  if ( $oldid === null ) {
381  return 0;
382  }
383 
384  if ( $oldid !== 0 ) {
385  # Load the given revision and check whether the page is another one.
386  # In that case, update this instance to reflect the change.
387  if ( $oldid === $this->mPage->getLatest() ) {
388  $this->mRevisionRecord = $this->mPage->getRevisionRecord();
389  } else {
390  $this->mRevisionRecord = $this->revisionStore->getRevisionById( $oldid );
391  if ( $this->mRevisionRecord !== null ) {
392  $revPageId = $this->mRevisionRecord->getPageId();
393  // Revision title doesn't match the page title given?
394  if ( $this->mPage->getId() != $revPageId ) {
395  $function = get_class( $this->mPage ) . '::newFromID';
396  $this->mPage = $function( $revPageId );
397  }
398  }
399  }
400  }
401 
402  $oldRev = $this->mRevisionRecord;
403  if ( $request->getVal( 'direction' ) == 'next' ) {
404  $nextid = 0;
405  if ( $oldRev ) {
406  $nextRev = $this->revisionStore->getNextRevision( $oldRev );
407  if ( $nextRev ) {
408  $nextid = $nextRev->getId();
409  }
410  }
411  if ( $nextid ) {
412  $oldid = $nextid;
413  $this->mRevisionRecord = null;
414  } else {
415  $this->mRedirectUrl = $this->getTitle()->getFullURL( 'redirect=no' );
416  }
417  } elseif ( $request->getVal( 'direction' ) == 'prev' ) {
418  $previd = 0;
419  if ( $oldRev ) {
420  $prevRev = $this->revisionStore->getPreviousRevision( $oldRev );
421  if ( $prevRev ) {
422  $previd = $prevRev->getId();
423  }
424  }
425  if ( $previd ) {
426  $oldid = $previd;
427  $this->mRevisionRecord = null;
428  }
429  }
430 
431  $this->mRevIdFetched = $this->mRevisionRecord ? $this->mRevisionRecord->getId() : 0;
432 
433  return $oldid;
434  }
435 
449  protected function fetchContentObject() {
450  if ( !$this->mContentLoaded ) {
451  $this->fetchRevisionRecord();
452  }
453 
454  return $this->mContentObject;
455  }
456 
468  public function fetchRevisionRecord() {
469  if ( $this->fetchResult ) {
470  return $this->mRevisionRecord;
471  }
472 
473  $this->mContentLoaded = true;
474  $this->mContentObject = null;
475 
476  $oldid = $this->getOldID();
477 
478  // $this->mRevisionRecord might already be fetched by getOldIDFromRequest()
479  if ( !$this->mRevisionRecord ) {
480  if ( !$oldid ) {
481  $this->mRevisionRecord = $this->mPage->getRevisionRecord();
482 
483  if ( !$this->mRevisionRecord ) {
484  wfDebug( __METHOD__ . " failed to find page data for title " .
485  $this->getTitle()->getPrefixedText() );
486 
487  // Just for sanity, output for this case is done by showMissingArticle().
488  $this->fetchResult = Status::newFatal( 'noarticletext' );
489  $this->applyContentOverride( $this->makeFetchErrorContent() );
490  return null;
491  }
492  } else {
493  $this->mRevisionRecord = $this->revisionStore->getRevisionById( $oldid );
494 
495  if ( !$this->mRevisionRecord ) {
496  wfDebug( __METHOD__ . " failed to load revision, rev_id $oldid" );
497 
498  $this->fetchResult = Status::newFatal( 'missing-revision', $oldid );
499  $this->applyContentOverride( $this->makeFetchErrorContent() );
500  return null;
501  }
502  }
503  }
504 
505  $this->mRevIdFetched = $this->mRevisionRecord->getId();
506  $this->fetchResult = Status::newGood( $this->mRevisionRecord );
507 
508  if ( !RevisionRecord::userCanBitfield(
509  $this->mRevisionRecord->getVisibility(),
510  RevisionRecord::DELETED_TEXT,
511  $this->getContext()->getUser()
512  ) ) {
513  wfDebug( __METHOD__ . " failed to retrieve content of revision " .
514  $this->mRevisionRecord->getId() );
515 
516  // Just for sanity, output for this case is done by showDeletedRevisionHeader().
517  $this->fetchResult = Status::newFatal( 'rev-deleted-text-permission' );
518  $this->applyContentOverride( $this->makeFetchErrorContent() );
519  return null;
520  }
521 
522  // For B/C only
523  $this->mContentObject = $this->mRevisionRecord->getContent(
524  SlotRecord::MAIN,
525  RevisionRecord::FOR_THIS_USER,
526  $this->getContext()->getUser()
527  );
528 
529  return $this->mRevisionRecord;
530  }
531 
538  private function makeFetchErrorContent() {
539  if ( !$this->fetchResult || $this->fetchResult->isOK() ) {
540  return null;
541  }
542 
543  return new MessageContent( $this->fetchResult->getMessage() );
544  }
545 
559  private function applyContentOverride( Content $override ) {
560  // Construct a fake revision
561  $rev = new MutableRevisionRecord( $this->getTitle() );
562  $rev->setContent( SlotRecord::MAIN, $override );
563 
564  $this->mRevisionRecord = $rev;
565 
566  // For B/C only
567  $this->mContentObject = $override;
568  }
569 
575  public function isCurrent() {
576  # If no oldid, this is the current version.
577  if ( $this->getOldID() == 0 ) {
578  return true;
579  }
580 
581  return $this->mPage->exists() &&
582  $this->mRevisionRecord &&
583  $this->mRevisionRecord->isCurrent();
584  }
585 
597  public function getRevisionFetched() {
598  wfDeprecated( __METHOD__, '1.35' );
599  $revRecord = $this->fetchRevisionRecord();
600 
601  return $revRecord ? new Revision( $revRecord ) : null;
602  }
603 
612  public function getRevIdFetched() {
613  if ( $this->fetchResult && $this->fetchResult->isOK() ) {
614  return $this->fetchResult->value->getId();
615  } else {
616  return $this->mPage->getLatest();
617  }
618  }
619 
624  public function view() {
625  global $wgUseFileCache;
626 
627  # Get variables from query string
628  # As side effect this will load the revision and update the title
629  # in a revision ID is passed in the request, so this should remain
630  # the first call of this method even if $oldid is used way below.
631  $oldid = $this->getOldID();
632 
633  $user = $this->getContext()->getUser();
634  # Another whitelist check in case getOldID() is altering the title
635  $permErrors = $this->permManager->getPermissionErrors(
636  'read',
637  $user,
638  $this->getTitle()
639  );
640  if ( count( $permErrors ) ) {
641  wfDebug( __METHOD__ . ": denied on secondary read check" );
642  throw new PermissionsError( 'read', $permErrors );
643  }
644 
645  $outputPage = $this->getContext()->getOutput();
646  # getOldID() may as well want us to redirect somewhere else
647  if ( $this->mRedirectUrl ) {
648  $outputPage->redirect( $this->mRedirectUrl );
649  wfDebug( __METHOD__ . ": redirecting due to oldid" );
650 
651  return;
652  }
653 
654  # If we got diff in the query, we want to see a diff page instead of the article.
655  if ( $this->getContext()->getRequest()->getCheck( 'diff' ) ) {
656  wfDebug( __METHOD__ . ": showing diff page" );
657  $this->showDiffPage();
658 
659  return;
660  }
661 
662  # Set page title (may be overridden by DISPLAYTITLE)
663  $outputPage->setPageTitle( $this->getTitle()->getPrefixedText() );
664 
665  $outputPage->setArticleFlag( true );
666  # Allow frames by default
667  $outputPage->allowClickjacking();
668 
669  $parserCache = MediaWikiServices::getInstance()->getParserCache();
670 
671  $parserOptions = $this->getParserOptions();
672  $poOptions = [];
673  # Render printable version, use printable version cache
674  if ( $outputPage->isPrintable() ) {
675  $parserOptions->setIsPrintable( true );
676  $poOptions['enableSectionEditLinks'] = false;
677  } elseif ( $this->viewIsRenderAction || !$this->isCurrent() ||
678  !$this->permManager->quickUserCan( 'edit', $user, $this->getTitle() )
679  ) {
680  $poOptions['enableSectionEditLinks'] = false;
681  }
682 
683  # Try client and file cache
684  if ( $oldid === 0 && $this->mPage->checkTouched() ) {
685  # Try to stream the output from file cache
686  if ( $wgUseFileCache && $this->tryFileCache() ) {
687  wfDebug( __METHOD__ . ": done file cache" );
688  # tell wgOut that output is taken care of
689  $outputPage->disable();
690  $this->mPage->doViewUpdates( $user, $oldid );
691 
692  return;
693  }
694  }
695 
696  # Should the parser cache be used?
697  $useParserCache = $this->mPage->shouldCheckParserCache( $parserOptions, $oldid );
698  wfDebug( 'Article::view using parser cache: ' . ( $useParserCache ? 'yes' : 'no' ) );
699  if ( $user->getStubThreshold() ) {
700  MediaWikiServices::getInstance()->getStatsdDataFactory()->increment( 'pcache_miss_stub' );
701  }
702 
703  $this->showRedirectedFromHeader();
704  $this->showNamespaceHeader();
705 
706  # Iterate through the possible ways of constructing the output text.
707  # Keep going until $outputDone is set, or we run out of things to do.
708  $pass = 0;
709  $outputDone = false;
710  $this->mParserOutput = false;
711 
712  while ( !$outputDone && ++$pass ) {
713  switch ( $pass ) {
714  case 1:
715  $this->getHookRunner()->onArticleViewHeader( $this, $outputDone, $useParserCache );
716  break;
717  case 2:
718  # Early abort if the page doesn't exist
719  if ( !$this->mPage->exists() ) {
720  wfDebug( __METHOD__ . ": showing missing article" );
721  $this->showMissingArticle();
722  $this->mPage->doViewUpdates( $user );
723  return;
724  }
725 
726  # Try the parser cache
727  if ( $useParserCache ) {
728  $this->mParserOutput = $parserCache->get( $this->getPage(), $parserOptions );
729 
730  if ( $this->mParserOutput !== false ) {
731  if ( $oldid ) {
732  wfDebug( __METHOD__ . ": showing parser cache contents for current rev permalink" );
733  $this->setOldSubtitle( $oldid );
734  } else {
735  wfDebug( __METHOD__ . ": showing parser cache contents" );
736  }
737  $outputPage->addParserOutput( $this->mParserOutput, $poOptions );
738  # Ensure that UI elements requiring revision ID have
739  # the correct version information.
740  $outputPage->setRevisionId( $this->mPage->getLatest() );
741  # Preload timestamp to avoid a DB hit
742  $cachedTimestamp = $this->mParserOutput->getTimestamp();
743  if ( $cachedTimestamp !== null ) {
744  $outputPage->setRevisionTimestamp( $cachedTimestamp );
745  $this->mPage->setTimestamp( $cachedTimestamp );
746  }
747  $outputDone = true;
748  }
749  }
750  break;
751  case 3:
752  # Are we looking at an old revision
753  $rev = $this->fetchRevisionRecord();
754  if ( $oldid && $this->fetchResult->isOK() ) {
755  $this->setOldSubtitle( $oldid );
756 
757  if ( !$this->showDeletedRevisionHeader() ) {
758  wfDebug( __METHOD__ . ": cannot view deleted revision" );
759  return;
760  }
761  }
762 
763  # Ensure that UI elements requiring revision ID have
764  # the correct version information.
765  $outputPage->setRevisionId( $this->getRevIdFetched() );
766  # Preload timestamp to avoid a DB hit
767  $outputPage->setRevisionTimestamp( $this->mPage->getTimestamp() );
768 
769  # Pages containing custom CSS or JavaScript get special treatment
770  if ( $this->getTitle()->isSiteConfigPage() || $this->getTitle()->isUserConfigPage() ) {
771  $dir = $this->getContext()->getLanguage()->getDir();
772  $lang = $this->getContext()->getLanguage()->getHtmlCode();
773 
774  $outputPage->wrapWikiMsg(
775  "<div id='mw-clearyourcache' lang='$lang' dir='$dir' class='mw-content-$dir'>\n$1\n</div>",
776  'clearyourcache'
777  );
778  } elseif ( !$this->getHookRunner()->onArticleRevisionViewCustom(
779  $rev,
780  $this->getTitle(),
781  $oldid,
782  $outputPage )
783  ) {
784  // NOTE: sync with hooks called in DifferenceEngine::renderNewRevision()
785  // Allow extensions do their own custom view for certain pages
786  $outputDone = true;
787  }
788  break;
789  case 4:
790  # Run the parse, protected by a pool counter
791  wfDebug( __METHOD__ . ": doing uncached parse" );
792 
793  $rev = $this->fetchRevisionRecord();
794  $error = null;
795 
796  if ( $rev ) {
797  $poolArticleView = new PoolWorkArticleView(
798  $this->getPage(),
799  $parserOptions,
800  $this->getRevIdFetched(),
801  $useParserCache,
802  $rev,
803  // permission checking was done earlier via showDeletedRevisionHeader()
804  RevisionRecord::RAW
805  );
806  $ok = $poolArticleView->execute();
807  $error = $poolArticleView->getError();
808  $this->mParserOutput = $poolArticleView->getParserOutput() ?: null;
809 
810  # Don't cache a dirty ParserOutput object
811  if ( $poolArticleView->getIsDirty() ) {
812  $outputPage->setCdnMaxage( 0 );
813  $outputPage->addHTML( "<!-- parser cache is expired, " .
814  "sending anyway due to pool overload-->\n" );
815  }
816  } else {
817  $ok = false;
818  }
819 
820  if ( !$ok ) {
821  if ( $error ) {
822  $outputPage->clearHTML(); // for release() errors
823  $outputPage->enableClientCache( false );
824  $outputPage->setRobotPolicy( 'noindex,nofollow' );
825 
826  $errortext = $error->getWikiText(
827  false, 'view-pool-error', $this->getContext()->getLanguage()
828  );
829  $outputPage->wrapWikiTextAsInterface( 'errorbox', $errortext );
830  }
831  # Connection or timeout error
832  return;
833  }
834 
835  if ( $this->mParserOutput ) {
836  $outputPage->addParserOutput( $this->mParserOutput, $poOptions );
837  }
838 
839  if ( $rev && $this->getRevisionRedirectTarget( $rev ) ) {
840  $outputPage->addSubtitle( "<span id=\"redirectsub\">" .
841  $this->getContext()->msg( 'redirectpagesub' )->parse() . "</span>" );
842  }
843 
844  $outputDone = true;
845  break;
846  # Should be unreachable, but just in case...
847  default:
848  break 2;
849  }
850  }
851 
852  // Get the ParserOutput actually *displayed* here.
853  // Note that $this->mParserOutput is the *current*/oldid version output.
854  // Note that the ArticleViewHeader hook is allowed to set $outputDone to a
855  // ParserOutput instance.
856  $pOutput = ( $outputDone instanceof ParserOutput )
857  ? $outputDone // object fetched by hook
858  : ( $this->mParserOutput ?: null ); // ParserOutput or null, avoid false
859 
860  # Adjust title for main page & pages with displaytitle
861  if ( $pOutput ) {
862  $this->adjustDisplayTitle( $pOutput );
863  }
864 
865  # For the main page, overwrite the <title> element with the con-
866  # tents of 'pagetitle-view-mainpage' instead of the default (if
867  # that's not empty).
868  # This message always exists because it is in the i18n files
869  if ( $this->getTitle()->isMainPage() ) {
870  $msg = wfMessage( 'pagetitle-view-mainpage' )->inContentLanguage();
871  if ( !$msg->isDisabled() ) {
872  $outputPage->setHTMLTitle( $msg->title( $this->getTitle() )->text() );
873  }
874  }
875 
876  # Use adaptive TTLs for CDN so delayed/failed purges are noticed less often.
877  # This could use getTouched(), but that could be scary for major template edits.
878  $outputPage->adaptCdnTTL( $this->mPage->getTimestamp(), IExpiringStore::TTL_DAY );
879 
880  # Check for any __NOINDEX__ tags on the page using $pOutput
881  $policy = $this->getRobotPolicy( 'view', $pOutput ?: null );
882  $outputPage->setIndexPolicy( $policy['index'] );
883  $outputPage->setFollowPolicy( $policy['follow'] ); // FIXME: test this
884 
885  $this->showViewFooter();
886  $this->mPage->doViewUpdates( $user, $oldid ); // FIXME: test this
887 
888  # Load the postEdit module if the user just saved this revision
889  # See also EditPage::setPostEditCookie
890  $request = $this->getContext()->getRequest();
892  $postEdit = $request->getCookie( $cookieKey );
893  if ( $postEdit ) {
894  # Clear the cookie. This also prevents caching of the response.
895  $request->response()->clearCookie( $cookieKey );
896  $outputPage->addJsConfigVars( 'wgPostEdit', $postEdit );
897  $outputPage->addModules( 'mediawiki.action.view.postEdit' ); // FIXME: test this
898  }
899  }
900 
905  private function getRevisionRedirectTarget( RevisionRecord $revision ) {
906  // TODO: find a *good* place for the code that determines the redirect target for
907  // a given revision!
908  // NOTE: Use main slot content. Compare code in DerivedPageDataUpdater::revisionIsRedirect.
909  $content = $revision->getContent( SlotRecord::MAIN );
910  return $content ? $content->getRedirectTarget() : null;
911  }
912 
917  public function adjustDisplayTitle( ParserOutput $pOutput ) {
918  $out = $this->getContext()->getOutput();
919 
920  # Adjust the title if it was set by displaytitle, -{T|}- or language conversion
921  $titleText = $pOutput->getTitleText();
922  if ( strval( $titleText ) !== '' ) {
923  $out->setPageTitle( $titleText );
924  $out->setDisplayTitle( $titleText );
925  }
926  }
927 
932  protected function showDiffPage() {
933  $request = $this->getContext()->getRequest();
934  $user = $this->getContext()->getUser();
935  $diff = $request->getVal( 'diff' );
936  $rcid = $request->getVal( 'rcid' );
937  $diffOnly = $request->getBool( 'diffonly', $user->getOption( 'diffonly' ) );
938  $purge = $request->getVal( 'action' ) == 'purge';
939  $unhide = $request->getInt( 'unhide' ) == 1;
940  $oldid = $this->getOldID();
941 
942  $rev = $this->fetchRevisionRecord();
943 
944  if ( !$rev ) {
945  $this->getContext()->getOutput()->setPageTitle( wfMessage( 'errorpagetitle' ) );
946  $msg = $this->getContext()->msg( 'difference-missing-revision' )
947  ->params( $oldid )
948  ->numParams( 1 )
949  ->parseAsBlock();
950  $this->getContext()->getOutput()->addHTML( $msg );
951  return;
952  }
953 
954  $contentHandler = MediaWikiServices::getInstance()
955  ->getContentHandlerFactory()
956  ->getContentHandler(
957  $rev->getSlot( SlotRecord::MAIN, RevisionRecord::RAW )->getModel()
958  );
959  $de = $contentHandler->createDifferenceEngine(
960  $this->getContext(),
961  $oldid,
962  $diff,
963  $rcid,
964  $purge,
965  $unhide
966  );
967  $de->setSlotDiffOptions( [
968  'diff-type' => $request->getVal( 'diff-type' )
969  ] );
970 
971  // DifferenceEngine directly fetched the revision:
972  $this->mRevIdFetched = $de->getNewid();
973  $de->showDiffPage( $diffOnly );
974 
975  // Run view updates for the newer revision being diffed (and shown
976  // below the diff if not $diffOnly).
977  list( $old, $new ) = $de->mapDiffPrevNext( $oldid, $diff );
978  // New can be false, convert it to 0 - this conveniently means the latest revision
979  $this->mPage->doViewUpdates( $user, (int)$new );
980  }
981 
989  public function getRobotPolicy( $action, ParserOutput $pOutput = null ) {
991 
992  $ns = $this->getTitle()->getNamespace();
993 
994  # Don't index user and user talk pages for blocked users (T13443)
995  if ( ( $ns == NS_USER || $ns == NS_USER_TALK ) && !$this->getTitle()->isSubpage() ) {
996  $specificTarget = null;
997  $vagueTarget = null;
998  $titleText = $this->getTitle()->getText();
999  if ( IPUtils::isValid( $titleText ) ) {
1000  $vagueTarget = $titleText;
1001  } else {
1002  $specificTarget = $titleText;
1003  }
1004  if ( DatabaseBlock::newFromTarget( $specificTarget, $vagueTarget ) instanceof DatabaseBlock ) {
1005  return [
1006  'index' => 'noindex',
1007  'follow' => 'nofollow'
1008  ];
1009  }
1010  }
1011 
1012  if ( $this->mPage->getId() === 0 || $this->getOldID() ) {
1013  # Non-articles (special pages etc), and old revisions
1014  return [
1015  'index' => 'noindex',
1016  'follow' => 'nofollow'
1017  ];
1018  } elseif ( $this->getContext()->getOutput()->isPrintable() ) {
1019  # Discourage indexing of printable versions, but encourage following
1020  return [
1021  'index' => 'noindex',
1022  'follow' => 'follow'
1023  ];
1024  } elseif ( $this->getContext()->getRequest()->getInt( 'curid' ) ) {
1025  # For ?curid=x urls, disallow indexing
1026  return [
1027  'index' => 'noindex',
1028  'follow' => 'follow'
1029  ];
1030  }
1031 
1032  # Otherwise, construct the policy based on the various config variables.
1034 
1035  if ( isset( $wgNamespaceRobotPolicies[$ns] ) ) {
1036  # Honour customised robot policies for this namespace
1037  $policy = array_merge(
1038  $policy,
1039  self::formatRobotPolicy( $wgNamespaceRobotPolicies[$ns] )
1040  );
1041  }
1042  if ( $this->getTitle()->canUseNoindex() && is_object( $pOutput ) && $pOutput->getIndexPolicy() ) {
1043  # __INDEX__ and __NOINDEX__ magic words, if allowed. Incorporates
1044  # a final sanity check that we have really got the parser output.
1045  $policy = array_merge(
1046  $policy,
1047  [ 'index' => $pOutput->getIndexPolicy() ]
1048  );
1049  }
1050 
1051  if ( isset( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] ) ) {
1052  # (T16900) site config can override user-defined __INDEX__ or __NOINDEX__
1053  $policy = array_merge(
1054  $policy,
1055  self::formatRobotPolicy( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] )
1056  );
1057  }
1058 
1059  return $policy;
1060  }
1061 
1069  public static function formatRobotPolicy( $policy ) {
1070  if ( is_array( $policy ) ) {
1071  return $policy;
1072  } elseif ( !$policy ) {
1073  return [];
1074  }
1075 
1076  $policy = explode( ',', $policy );
1077  $policy = array_map( 'trim', $policy );
1078 
1079  $arr = [];
1080  foreach ( $policy as $var ) {
1081  if ( in_array( $var, [ 'index', 'noindex' ] ) ) {
1082  $arr['index'] = $var;
1083  } elseif ( in_array( $var, [ 'follow', 'nofollow' ] ) ) {
1084  $arr['follow'] = $var;
1085  }
1086  }
1087 
1088  return $arr;
1089  }
1090 
1098  public function showRedirectedFromHeader() {
1099  global $wgRedirectSources;
1100 
1101  $context = $this->getContext();
1102  $outputPage = $context->getOutput();
1103  $request = $context->getRequest();
1104  $rdfrom = $request->getVal( 'rdfrom' );
1105 
1106  // Construct a URL for the current page view, but with the target title
1107  $query = $request->getValues();
1108  unset( $query['rdfrom'] );
1109  unset( $query['title'] );
1110  if ( $this->getTitle()->isRedirect() ) {
1111  // Prevent double redirects
1112  $query['redirect'] = 'no';
1113  }
1114  $redirectTargetUrl = $this->getTitle()->getLinkURL( $query );
1115 
1116  if ( isset( $this->mRedirectedFrom ) ) {
1117  // This is an internally redirected page view.
1118  // We'll need a backlink to the source page for navigation.
1119  if ( $this->getHookRunner()->onArticleViewRedirect( $this ) ) {
1120  $redir = $this->linkRenderer->makeKnownLink(
1121  $this->mRedirectedFrom,
1122  null,
1123  [],
1124  [ 'redirect' => 'no' ]
1125  );
1126 
1127  $outputPage->addSubtitle( "<span class=\"mw-redirectedfrom\">" .
1128  $context->msg( 'redirectedfrom' )->rawParams( $redir )->parse()
1129  . "</span>" );
1130 
1131  // Add the script to update the displayed URL and
1132  // set the fragment if one was specified in the redirect
1133  $outputPage->addJsConfigVars( [
1134  'wgInternalRedirectTargetUrl' => $redirectTargetUrl,
1135  ] );
1136  $outputPage->addModules( 'mediawiki.action.view.redirect' );
1137 
1138  // Add a <link rel="canonical"> tag
1139  $outputPage->setCanonicalUrl( $this->getTitle()->getCanonicalURL() );
1140 
1141  // Tell the output object that the user arrived at this article through a redirect
1142  $outputPage->setRedirectedFrom( $this->mRedirectedFrom );
1143 
1144  return true;
1145  }
1146  } elseif ( $rdfrom ) {
1147  // This is an externally redirected view, from some other wiki.
1148  // If it was reported from a trusted site, supply a backlink.
1149  if ( $wgRedirectSources && preg_match( $wgRedirectSources, $rdfrom ) ) {
1150  $redir = Linker::makeExternalLink( $rdfrom, $rdfrom );
1151  $outputPage->addSubtitle( "<span class=\"mw-redirectedfrom\">" .
1152  $context->msg( 'redirectedfrom' )->rawParams( $redir )->parse()
1153  . "</span>" );
1154 
1155  // Add the script to update the displayed URL
1156  $outputPage->addJsConfigVars( [
1157  'wgInternalRedirectTargetUrl' => $redirectTargetUrl,
1158  ] );
1159  $outputPage->addModules( 'mediawiki.action.view.redirect' );
1160 
1161  return true;
1162  }
1163  }
1164 
1165  return false;
1166  }
1167 
1172  public function showNamespaceHeader() {
1173  if ( $this->getTitle()->isTalkPage() && !wfMessage( 'talkpageheader' )->isDisabled() ) {
1174  $this->getContext()->getOutput()->wrapWikiMsg(
1175  "<div class=\"mw-talkpageheader\">\n$1\n</div>",
1176  [ 'talkpageheader' ]
1177  );
1178  }
1179  }
1180 
1184  public function showViewFooter() {
1185  # check if we're displaying a [[User talk:x.x.x.x]] anonymous talk page
1186  if ( $this->getTitle()->getNamespace() == NS_USER_TALK
1187  && IPUtils::isValid( $this->getTitle()->getText() )
1188  ) {
1189  $this->getContext()->getOutput()->addWikiMsg( 'anontalkpagetext' );
1190  }
1191 
1192  // Show a footer allowing the user to patrol the shown revision or page if possible
1193  $patrolFooterShown = $this->showPatrolFooter();
1194 
1195  $this->getHookRunner()->onArticleViewFooter( $this, $patrolFooterShown );
1196  }
1197 
1208  public function showPatrolFooter() {
1210 
1211  // Allow hooks to decide whether to not output this at all
1212  if ( !$this->getHookRunner()->onArticleShowPatrolFooter( $this ) ) {
1213  return false;
1214  }
1215 
1216  $outputPage = $this->getContext()->getOutput();
1217  $user = $this->getContext()->getUser();
1218  $title = $this->getTitle();
1219  $rc = false;
1220 
1221  if ( !$this->permManager->quickUserCan( 'patrol', $user, $title )
1223  || ( $wgUseFilePatrol && $title->inNamespace( NS_FILE ) ) )
1224  ) {
1225  // Patrolling is disabled or the user isn't allowed to
1226  return false;
1227  }
1228 
1229  if ( $this->mRevisionRecord
1230  && !RecentChange::isInRCLifespan( $this->mRevisionRecord->getTimestamp(), 21600 )
1231  ) {
1232  // The current revision is already older than what could be in the RC table
1233  // 6h tolerance because the RC might not be cleaned out regularly
1234  return false;
1235  }
1236 
1237  // Check for cached results
1238  $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
1239  $key = $cache->makeKey( 'unpatrollable-page', $title->getArticleID() );
1240  if ( $cache->get( $key ) ) {
1241  return false;
1242  }
1243 
1244  $dbr = wfGetDB( DB_REPLICA );
1245  $oldestRevisionTimestamp = $dbr->selectField(
1246  'revision',
1247  'MIN( rev_timestamp )',
1248  [ 'rev_page' => $title->getArticleID() ],
1249  __METHOD__
1250  );
1251 
1252  // New page patrol: Get the timestamp of the oldest revison which
1253  // the revision table holds for the given page. Then we look
1254  // whether it's within the RC lifespan and if it is, we try
1255  // to get the recentchanges row belonging to that entry
1256  // (with rc_new = 1).
1257  $recentPageCreation = false;
1258  if ( $oldestRevisionTimestamp
1259  && RecentChange::isInRCLifespan( $oldestRevisionTimestamp, 21600 )
1260  ) {
1261  // 6h tolerance because the RC might not be cleaned out regularly
1262  $recentPageCreation = true;
1264  [
1265  'rc_new' => 1,
1266  'rc_timestamp' => $oldestRevisionTimestamp,
1267  'rc_namespace' => $title->getNamespace(),
1268  'rc_cur_id' => $title->getArticleID()
1269  ],
1270  __METHOD__
1271  );
1272  if ( $rc ) {
1273  // Use generic patrol message for new pages
1274  $markPatrolledMsg = wfMessage( 'markaspatrolledtext' );
1275  }
1276  }
1277 
1278  // File patrol: Get the timestamp of the latest upload for this page,
1279  // check whether it is within the RC lifespan and if it is, we try
1280  // to get the recentchanges row belonging to that entry
1281  // (with rc_type = RC_LOG, rc_log_type = upload).
1282  $recentFileUpload = false;
1283  if ( ( !$rc || $rc->getAttribute( 'rc_patrolled' ) ) && $wgUseFilePatrol
1284  && $title->getNamespace() === NS_FILE ) {
1285  // Retrieve timestamp of most recent upload
1286  $newestUploadTimestamp = $dbr->selectField(
1287  'image',
1288  'MAX( img_timestamp )',
1289  [ 'img_name' => $title->getDBkey() ],
1290  __METHOD__
1291  );
1292  if ( $newestUploadTimestamp
1293  && RecentChange::isInRCLifespan( $newestUploadTimestamp, 21600 )
1294  ) {
1295  // 6h tolerance because the RC might not be cleaned out regularly
1296  $recentFileUpload = true;
1298  [
1299  'rc_type' => RC_LOG,
1300  'rc_log_type' => 'upload',
1301  'rc_timestamp' => $newestUploadTimestamp,
1302  'rc_namespace' => NS_FILE,
1303  'rc_cur_id' => $title->getArticleID()
1304  ],
1305  __METHOD__
1306  );
1307  if ( $rc ) {
1308  // Use patrol message specific to files
1309  $markPatrolledMsg = wfMessage( 'markaspatrolledtext-file' );
1310  }
1311  }
1312  }
1313 
1314  if ( !$recentPageCreation && !$recentFileUpload ) {
1315  // Page creation and latest upload (for files) is too old to be in RC
1316 
1317  // We definitely can't patrol so cache the information
1318  // When a new file version is uploaded, the cache is cleared
1319  $cache->set( $key, '1' );
1320 
1321  return false;
1322  }
1323 
1324  if ( !$rc ) {
1325  // Don't cache: This can be hit if the page gets accessed very fast after
1326  // its creation / latest upload or in case we have high replica DB lag. In case
1327  // the revision is too old, we will already return above.
1328  return false;
1329  }
1330 
1331  if ( $rc->getAttribute( 'rc_patrolled' ) ) {
1332  // Patrolled RC entry around
1333 
1334  // Cache the information we gathered above in case we can't patrol
1335  // Don't cache in case we can patrol as this could change
1336  $cache->set( $key, '1' );
1337 
1338  return false;
1339  }
1340 
1341  if ( $rc->getPerformer()->equals( $user ) ) {
1342  // Don't show a patrol link for own creations/uploads. If the user could
1343  // patrol them, they already would be patrolled
1344  return false;
1345  }
1346 
1347  $outputPage->preventClickjacking();
1348  if ( $this->permManager->userHasRight( $user, 'writeapi' ) ) {
1349  $outputPage->addModules( 'mediawiki.misc-authed-curate' );
1350  }
1351 
1352  $link = $this->linkRenderer->makeKnownLink(
1353  $title,
1354  $markPatrolledMsg->text(),
1355  [],
1356  [
1357  'action' => 'markpatrolled',
1358  'rcid' => $rc->getAttribute( 'rc_id' ),
1359  ]
1360  );
1361 
1362  $outputPage->addHTML(
1363  "<div class='patrollink' data-mw='interface'>" .
1364  wfMessage( 'markaspatrolledlink' )->rawParams( $link )->escaped() .
1365  '</div>'
1366  );
1367 
1368  return true;
1369  }
1370 
1377  public static function purgePatrolFooterCache( $articleID ) {
1378  $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
1379  $cache->delete( $cache->makeKey( 'unpatrollable-page', $articleID ) );
1380  }
1381 
1386  public function showMissingArticle() {
1387  global $wgSend404Code;
1388 
1389  $outputPage = $this->getContext()->getOutput();
1390  // Whether the page is a root user page of an existing user (but not a subpage)
1391  $validUserPage = false;
1392 
1393  $title = $this->getTitle();
1394 
1395  $services = MediaWikiServices::getInstance();
1396 
1397  # Show info in user (talk) namespace. Does the user exist? Is he blocked?
1398  if ( $title->getNamespace() == NS_USER
1399  || $title->getNamespace() == NS_USER_TALK
1400  ) {
1401  $rootPart = explode( '/', $title->getText() )[0];
1402  $user = User::newFromName( $rootPart, false /* allow IP users */ );
1403  $ip = User::isIP( $rootPart );
1404  $block = DatabaseBlock::newFromTarget( $user, $user );
1405 
1406  if ( !( $user && $user->isLoggedIn() ) && !$ip ) { # User does not exist
1407  $outputPage->wrapWikiMsg( "<div class=\"mw-userpage-userdoesnotexist error\">\n\$1\n</div>",
1408  [ 'userpage-userdoesnotexist-view', wfEscapeWikiText( $rootPart ) ] );
1409  } elseif (
1410  $block !== null &&
1411  $block->getType() != DatabaseBlock::TYPE_AUTO &&
1412  ( $block->isSitewide() || $user->isBlockedFrom( $title ) )
1413  ) {
1414  // Show log extract if the user is sitewide blocked or is partially
1415  // blocked and not allowed to edit their user page or user talk page
1417  $outputPage,
1418  'block',
1419  $services->getNamespaceInfo()->getCanonicalName( NS_USER ) . ':' .
1420  $block->getTarget(),
1421  '',
1422  [
1423  'lim' => 1,
1424  'showIfEmpty' => false,
1425  'msgKey' => [
1426  'blocked-notice-logextract',
1427  $user->getName() # Support GENDER in notice
1428  ]
1429  ]
1430  );
1431  $validUserPage = !$title->isSubpage();
1432  } else {
1433  $validUserPage = !$title->isSubpage();
1434  }
1435  }
1436 
1437  $this->getHookRunner()->onShowMissingArticle( $this );
1438 
1439  # Show delete and move logs if there were any such events.
1440  # The logging query can DOS the site when bots/crawlers cause 404 floods,
1441  # so be careful showing this. 404 pages must be cheap as they are hard to cache.
1442  $dbCache = ObjectCache::getInstance( 'db-replicated' );
1443  $key = $dbCache->makeKey( 'page-recent-delete', md5( $title->getPrefixedText() ) );
1444  $loggedIn = $this->getContext()->getUser()->isLoggedIn();
1445  $sessionExists = $this->getContext()->getRequest()->getSession()->isPersistent();
1446  if ( $loggedIn || $dbCache->get( $key ) || $sessionExists ) {
1447  $logTypes = [ 'delete', 'move', 'protect' ];
1448 
1449  $dbr = wfGetDB( DB_REPLICA );
1450 
1451  $conds = [ 'log_action != ' . $dbr->addQuotes( 'revision' ) ];
1452  // Give extensions a chance to hide their (unrelated) log entries
1453  $this->getHookRunner()->onArticle__MissingArticleConditions( $conds, $logTypes );
1455  $outputPage,
1456  $logTypes,
1457  $title,
1458  '',
1459  [
1460  'lim' => 10,
1461  'conds' => $conds,
1462  'showIfEmpty' => false,
1463  'msgKey' => [ $loggedIn || $sessionExists
1464  ? 'moveddeleted-notice'
1465  : 'moveddeleted-notice-recent'
1466  ]
1467  ]
1468  );
1469  }
1470 
1471  if ( !$this->mPage->hasViewableContent() && $wgSend404Code && !$validUserPage ) {
1472  // If there's no backing content, send a 404 Not Found
1473  // for better machine handling of broken links.
1474  $this->getContext()->getRequest()->response()->statusHeader( 404 );
1475  }
1476 
1477  // Also apply the robot policy for nonexisting pages (even if a 404 was used for sanity)
1478  $policy = $this->getRobotPolicy( 'view' );
1479  $outputPage->setIndexPolicy( $policy['index'] );
1480  $outputPage->setFollowPolicy( $policy['follow'] );
1481 
1482  $hookResult = $this->getHookRunner()->onBeforeDisplayNoArticleText( $this );
1483 
1484  if ( !$hookResult ) {
1485  return;
1486  }
1487 
1488  # Show error message
1489  $oldid = $this->getOldID();
1490  $pm = $this->permManager;
1491  if ( !$oldid && $title->getNamespace() === NS_MEDIAWIKI && $title->hasSourceText() ) {
1492  // use fake Content object for system message
1493  $parserOptions = ParserOptions::newCanonical( 'canonical' );
1494  $outputPage->addParserOutput( $this->getEmptyPageParserOutput( $parserOptions ) );
1495  } else {
1496  if ( $oldid ) {
1497  $text = wfMessage( 'missing-revision', $oldid )->plain();
1498  } elseif ( $pm->quickUserCan( 'create', $this->getContext()->getUser(), $title ) &&
1499  $pm->quickUserCan( 'edit', $this->getContext()->getUser(), $title )
1500  ) {
1501  $message = $this->getContext()->getUser()->isLoggedIn() ? 'noarticletext' : 'noarticletextanon';
1502  $text = wfMessage( $message )->plain();
1503  } else {
1504  $text = wfMessage( 'noarticletext-nopermission' )->plain();
1505  }
1506 
1507  $dir = $this->getContext()->getLanguage()->getDir();
1508  $lang = $this->getContext()->getLanguage()->getHtmlCode();
1509  $outputPage->addWikiTextAsInterface( Xml::openElement( 'div', [
1510  'class' => "noarticletext mw-content-$dir",
1511  'dir' => $dir,
1512  'lang' => $lang,
1513  ] ) . "\n$text\n</div>" );
1514  }
1515  }
1516 
1523  public function showDeletedRevisionHeader() {
1524  if ( !$this->mRevisionRecord->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
1525  // Not deleted
1526  return true;
1527  }
1528 
1529  $outputPage = $this->getContext()->getOutput();
1530  $user = $this->getContext()->getUser();
1531  // If the user is not allowed to see it...
1532  if ( !RevisionRecord::userCanBitfield(
1533  $this->mRevisionRecord->getVisibility(),
1534  RevisionRecord::DELETED_TEXT,
1535  $user
1536  ) ) {
1537  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1538  'rev-deleted-text-permission' );
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->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1549  [ $msg, $link ] );
1550 
1551  return false;
1552  // We are allowed to see...
1553  } else {
1554  $msg = $this->mRevisionRecord->isDeleted( RevisionRecord::DELETED_RESTRICTED ) ?
1555  'rev-suppressed-text-view' : 'rev-deleted-text-view';
1556  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n", $msg );
1557 
1558  return true;
1559  }
1560  }
1561 
1570  public function setOldSubtitle( $oldid = 0 ) {
1571  if ( !$this->getHookRunner()->onDisplayOldSubtitle( $this, $oldid ) ) {
1572  return;
1573  }
1574 
1575  $context = $this->getContext();
1576  $unhide = $context->getRequest()->getInt( 'unhide' ) == 1;
1577 
1578  # Cascade unhide param in links for easy deletion browsing
1579  $extraParams = [];
1580  if ( $unhide ) {
1581  $extraParams['unhide'] = 1;
1582  }
1583 
1584  if ( $this->mRevisionRecord && $this->mRevisionRecord->getId() === $oldid ) {
1585  $revisionRecord = $this->mRevisionRecord;
1586  } else {
1587  $revisionRecord = $this->revisionStore->getRevisionById( $oldid );
1588  }
1589 
1590  $timestamp = $revisionRecord->getTimestamp();
1591 
1592  $current = ( $oldid == $this->mPage->getLatest() );
1593  $language = $context->getLanguage();
1594  $user = $context->getUser();
1595 
1596  $td = $language->userTimeAndDate( $timestamp, $user );
1597  $tddate = $language->userDate( $timestamp, $user );
1598  $tdtime = $language->userTime( $timestamp, $user );
1599 
1600  # Show user links if allowed to see them. If hidden, then show them only if requested...
1601  $userlinks = Linker::revUserTools( $revisionRecord, !$unhide );
1602 
1603  $infomsg = $current && !$context->msg( 'revision-info-current' )->isDisabled()
1604  ? 'revision-info-current'
1605  : 'revision-info';
1606 
1607  $outputPage = $context->getOutput();
1608  $revisionUser = $revisionRecord->getUser();
1609  $revisionInfo = "<div id=\"mw-{$infomsg}\">" .
1610  $context->msg( $infomsg, $td )
1611  ->rawParams( $userlinks )
1612  ->params(
1613  $revisionRecord->getId(),
1614  $tddate,
1615  $tdtime,
1616  $revisionUser ? $revisionUser->getName() : ''
1617  )
1618  ->rawParams( Linker::revComment(
1619  $revisionRecord,
1620  true,
1621  true
1622  ) )
1623  ->parse() .
1624  "</div>";
1625 
1626  $lnk = $current
1627  ? $context->msg( 'currentrevisionlink' )->escaped()
1628  : $this->linkRenderer->makeKnownLink(
1629  $this->getTitle(),
1630  $context->msg( 'currentrevisionlink' )->text(),
1631  [],
1632  $extraParams
1633  );
1634  $curdiff = $current
1635  ? $context->msg( 'diff' )->escaped()
1636  : $this->linkRenderer->makeKnownLink(
1637  $this->getTitle(),
1638  $context->msg( 'diff' )->text(),
1639  [],
1640  [
1641  'diff' => 'cur',
1642  'oldid' => $oldid
1643  ] + $extraParams
1644  );
1645  $prevExist = (bool)$this->revisionStore->getPreviousRevision( $revisionRecord );
1646  $prevlink = $prevExist
1647  ? $this->linkRenderer->makeKnownLink(
1648  $this->getTitle(),
1649  $context->msg( 'previousrevision' )->text(),
1650  [],
1651  [
1652  'direction' => 'prev',
1653  'oldid' => $oldid
1654  ] + $extraParams
1655  )
1656  : $context->msg( 'previousrevision' )->escaped();
1657  $prevdiff = $prevExist
1658  ? $this->linkRenderer->makeKnownLink(
1659  $this->getTitle(),
1660  $context->msg( 'diff' )->text(),
1661  [],
1662  [
1663  'diff' => 'prev',
1664  'oldid' => $oldid
1665  ] + $extraParams
1666  )
1667  : $context->msg( 'diff' )->escaped();
1668  $nextlink = $current
1669  ? $context->msg( 'nextrevision' )->escaped()
1670  : $this->linkRenderer->makeKnownLink(
1671  $this->getTitle(),
1672  $context->msg( 'nextrevision' )->text(),
1673  [],
1674  [
1675  'direction' => 'next',
1676  'oldid' => $oldid
1677  ] + $extraParams
1678  );
1679  $nextdiff = $current
1680  ? $context->msg( 'diff' )->escaped()
1681  : $this->linkRenderer->makeKnownLink(
1682  $this->getTitle(),
1683  $context->msg( 'diff' )->text(),
1684  [],
1685  [
1686  'diff' => 'next',
1687  'oldid' => $oldid
1688  ] + $extraParams
1689  );
1690 
1691  $cdel = Linker::getRevDeleteLink(
1692  $user,
1693  $revisionRecord,
1694  $this->getTitle()
1695  );
1696  if ( $cdel !== '' ) {
1697  $cdel .= ' ';
1698  }
1699 
1700  // the outer div is need for styling the revision info and nav in MobileFrontend
1701  $outputPage->addSubtitle( "<div class=\"mw-revision warningbox\">" . $revisionInfo .
1702  "<div id=\"mw-revision-nav\">" . $cdel .
1703  $context->msg( 'revision-nav' )->rawParams(
1704  $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff
1705  )->escaped() . "</div></div>" );
1706  }
1707 
1721  public function viewRedirect( $target, $appendSubtitle = true, $forceKnown = false ) {
1722  $lang = $this->getTitle()->getPageLanguage();
1723  $out = $this->getContext()->getOutput();
1724  if ( $appendSubtitle ) {
1725  $out->addSubtitle( wfMessage( 'redirectpagesub' ) );
1726  }
1727  $out->addModuleStyles( 'mediawiki.action.view.redirectPage' );
1728  return static::getRedirectHeaderHtml( $lang, $target, $forceKnown );
1729  }
1730 
1743  public static function getRedirectHeaderHtml( Language $lang, $target, $forceKnown = false ) {
1744  if ( !is_array( $target ) ) {
1745  $target = [ $target ];
1746  }
1747 
1748  $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
1749 
1750  $html = '<ul class="redirectText">';
1752  foreach ( $target as $title ) {
1753  if ( $forceKnown ) {
1754  $link = $linkRenderer->makeKnownLink(
1755  $title,
1756  $title->getFullText(),
1757  [],
1758  // Make sure wiki page redirects are not followed
1759  $title->isRedirect() ? [ 'redirect' => 'no' ] : []
1760  );
1761  } else {
1762  $link = $linkRenderer->makeLink(
1763  $title,
1764  $title->getFullText(),
1765  [],
1766  // Make sure wiki page redirects are not followed
1767  $title->isRedirect() ? [ 'redirect' => 'no' ] : []
1768  );
1769  }
1770  $html .= '<li>' . $link . '</li>';
1771  }
1772  $html .= '</ul>';
1773 
1774  $redirectToText = wfMessage( 'redirectto' )->inLanguage( $lang )->escaped();
1775 
1776  return '<div class="redirectMsg">' .
1777  '<p>' . $redirectToText . '</p>' .
1778  $html .
1779  '</div>';
1780  }
1781 
1790  public function addHelpLink( $to, $overrideBaseUrl = false ) {
1791  $msg = wfMessage(
1792  'namespace-' . $this->getTitle()->getNamespace() . '-helppage'
1793  );
1794 
1795  $out = $this->getContext()->getOutput();
1796  if ( !$msg->isDisabled() ) {
1797  $helpUrl = Skin::makeUrl( $msg->plain() );
1798  $out->addHelpLink( $helpUrl, true );
1799  } else {
1800  $out->addHelpLink( $to, $overrideBaseUrl );
1801  }
1802  }
1803 
1807  public function render() {
1808  $this->getContext()->getRequest()->response()->header( 'X-Robots-Tag: noindex' );
1809  $this->getContext()->getOutput()->setArticleBodyOnly( true );
1810  // We later set 'enableSectionEditLinks=false' based on this; also used by ImagePage
1811  $this->viewIsRenderAction = true;
1812  $this->view();
1813  }
1814 
1818  public function protect() {
1819  $form = new ProtectionForm( $this );
1820  $form->execute();
1821  }
1822 
1826  public function unprotect() {
1827  $this->protect();
1828  }
1829 
1833  public function delete() {
1834  # This code desperately needs to be totally rewritten
1835 
1836  $title = $this->getTitle();
1837  $context = $this->getContext();
1838  $user = $context->getUser();
1839  $request = $context->getRequest();
1840 
1841  # Check permissions
1842  $permissionErrors = $this->permManager->getPermissionErrors( 'delete', $user, $title );
1843  if ( count( $permissionErrors ) ) {
1844  throw new PermissionsError( 'delete', $permissionErrors );
1845  }
1846 
1847  # Read-only check...
1848  if ( wfReadOnly() ) {
1849  throw new ReadOnlyError;
1850  }
1851 
1852  # Better double-check that it hasn't been deleted yet!
1853  $this->mPage->loadPageData(
1854  $request->wasPosted() ? WikiPage::READ_LATEST : WikiPage::READ_NORMAL
1855  );
1856  if ( !$this->mPage->exists() ) {
1857  $deleteLogPage = new LogPage( 'delete' );
1858  $outputPage = $context->getOutput();
1859  $outputPage->setPageTitle( $context->msg( 'cannotdelete-title', $title->getPrefixedText() ) );
1860  $outputPage->wrapWikiMsg( "<div class=\"error mw-error-cannotdelete\">\n$1\n</div>",
1861  [ 'cannotdelete', wfEscapeWikiText( $title->getPrefixedText() ) ]
1862  );
1863  $outputPage->addHTML(
1864  Xml::element( 'h2', null, $deleteLogPage->getName()->text() )
1865  );
1867  $outputPage,
1868  'delete',
1869  $title
1870  );
1871 
1872  return;
1873  }
1874 
1875  $deleteReasonList = $request->getText( 'wpDeleteReasonList', 'other' );
1876  $deleteReason = $request->getText( 'wpReason' );
1877 
1878  if ( $deleteReasonList == 'other' ) {
1879  $reason = $deleteReason;
1880  } elseif ( $deleteReason != '' ) {
1881  // Entry from drop down menu + additional comment
1882  $colonseparator = wfMessage( 'colon-separator' )->inContentLanguage()->text();
1883  $reason = $deleteReasonList . $colonseparator . $deleteReason;
1884  } else {
1885  $reason = $deleteReasonList;
1886  }
1887 
1888  if ( $request->wasPosted() && $user->matchEditToken( $request->getVal( 'wpEditToken' ),
1889  [ 'delete', $this->getTitle()->getPrefixedText() ] )
1890  ) {
1891  # Flag to hide all contents of the archived revisions
1892 
1893  $suppress = $request->getCheck( 'wpSuppress' ) &&
1894  $this->permManager->userHasRight( $user, 'suppressrevision' );
1895 
1896  $this->doDelete( $reason, $suppress );
1897 
1898  WatchAction::doWatchOrUnwatch( $request->getCheck( 'wpWatch' ), $title, $user );
1899 
1900  return;
1901  }
1902 
1903  // Generate deletion reason
1904  $hasHistory = false;
1905  if ( !$reason ) {
1906  try {
1907  $reason = $this->getPage()
1908  ->getAutoDeleteReason( $hasHistory );
1909  } catch ( Exception $e ) {
1910  # if a page is horribly broken, we still want to be able to
1911  # delete it. So be lenient about errors here.
1912  wfDebug( "Error while building auto delete summary: $e" );
1913  $reason = '';
1914  }
1915  }
1916 
1917  // If the page has a history, insert a warning
1918  if ( $hasHistory ) {
1919  $title = $this->getTitle();
1920 
1921  // The following can use the real revision count as this is only being shown for users
1922  // that can delete this page.
1923  // This, as a side-effect, also makes sure that the following query isn't being run for
1924  // pages with a larger history, unless the user has the 'bigdelete' right
1925  // (and is about to delete this page).
1926  $dbr = wfGetDB( DB_REPLICA );
1927  $revisions = $edits = (int)$dbr->selectField(
1928  'revision',
1929  'COUNT(rev_page)',
1930  [ 'rev_page' => $title->getArticleID() ],
1931  __METHOD__
1932  );
1933 
1934  // @todo i18n issue/patchwork message
1935  $context->getOutput()->addHTML(
1936  '<strong class="mw-delete-warning-revisions">' .
1937  $context->msg( 'historywarning' )->numParams( $revisions )->parse() .
1938  $context->msg( 'word-separator' )->escaped() . $this->linkRenderer->makeKnownLink(
1939  $title,
1940  $context->msg( 'history' )->text(),
1941  [],
1942  [ 'action' => 'history' ] ) .
1943  '</strong>'
1944  );
1945 
1946  if ( $title->isBigDeletion() ) {
1947  global $wgDeleteRevisionsLimit;
1948  $context->getOutput()->wrapWikiMsg( "<div class='error'>\n$1\n</div>\n",
1949  [
1950  'delete-warning-toobig',
1951  $context->getLanguage()->formatNum( $wgDeleteRevisionsLimit )
1952  ]
1953  );
1954  }
1955  }
1956 
1957  $this->confirmDelete( $reason );
1958  }
1959 
1965  public function confirmDelete( $reason ) {
1966  wfDebug( "Article::confirmDelete" );
1967 
1968  $title = $this->getTitle();
1969  $ctx = $this->getContext();
1970  $outputPage = $ctx->getOutput();
1971  $outputPage->setPageTitle( wfMessage( 'delete-confirm', $title->getPrefixedText() ) );
1972  $outputPage->addBacklinkSubtitle( $title );
1973  $outputPage->setRobotPolicy( 'noindex,nofollow' );
1974  $outputPage->addModules( 'mediawiki.action.delete' );
1975 
1976  $backlinkCache = $title->getBacklinkCache();
1977  if ( $backlinkCache->hasLinks( 'pagelinks' ) || $backlinkCache->hasLinks( 'templatelinks' ) ) {
1978  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1979  'deleting-backlinks-warning' );
1980  }
1981 
1982  $subpageQueryLimit = 51;
1983  $subpages = $title->getSubpages( $subpageQueryLimit );
1984  $subpageCount = count( $subpages );
1985  if ( $subpageCount > 0 ) {
1986  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1987  [ 'deleting-subpages-warning', Message::numParam( $subpageCount ) ] );
1988  }
1989  $outputPage->addWikiMsg( 'confirmdeletetext' );
1990 
1991  $this->getHookRunner()->onArticleConfirmDelete( $this, $outputPage, $reason );
1992 
1993  $user = $this->getContext()->getUser();
1994  $checkWatch = $user->getBoolOption( 'watchdeletion' ) || $user->isWatched( $title );
1995 
1996  $outputPage->enableOOUI();
1997 
1998  $fields = [];
1999 
2000  $options = Xml::listDropDownOptions(
2001  $ctx->msg( 'deletereason-dropdown' )->inContentLanguage()->text(),
2002  [ 'other' => $ctx->msg( 'deletereasonotherlist' )->inContentLanguage()->text() ]
2003  );
2004  $options = Xml::listDropDownOptionsOoui( $options );
2005 
2006  $fields[] = new OOUI\FieldLayout(
2007  new OOUI\DropdownInputWidget( [
2008  'name' => 'wpDeleteReasonList',
2009  'inputId' => 'wpDeleteReasonList',
2010  'tabIndex' => 1,
2011  'infusable' => true,
2012  'value' => '',
2013  'options' => $options
2014  ] ),
2015  [
2016  'label' => $ctx->msg( 'deletecomment' )->text(),
2017  'align' => 'top',
2018  ]
2019  );
2020 
2021  // HTML maxlength uses "UTF-16 code units", which means that characters outside BMP
2022  // (e.g. emojis) count for two each. This limit is overridden in JS to instead count
2023  // Unicode codepoints.
2024  $fields[] = new OOUI\FieldLayout(
2025  new OOUI\TextInputWidget( [
2026  'name' => 'wpReason',
2027  'inputId' => 'wpReason',
2028  'tabIndex' => 2,
2030  'infusable' => true,
2031  'value' => $reason,
2032  'autofocus' => true,
2033  ] ),
2034  [
2035  'label' => $ctx->msg( 'deleteotherreason' )->text(),
2036  'align' => 'top',
2037  ]
2038  );
2039 
2040  if ( $user->isLoggedIn() ) {
2041  $fields[] = new OOUI\FieldLayout(
2042  new OOUI\CheckboxInputWidget( [
2043  'name' => 'wpWatch',
2044  'inputId' => 'wpWatch',
2045  'tabIndex' => 3,
2046  'selected' => $checkWatch,
2047  ] ),
2048  [
2049  'label' => $ctx->msg( 'watchthis' )->text(),
2050  'align' => 'inline',
2051  'infusable' => true,
2052  ]
2053  );
2054  }
2055  if ( $this->permManager->userHasRight( $user, 'suppressrevision' ) ) {
2056  $fields[] = new OOUI\FieldLayout(
2057  new OOUI\CheckboxInputWidget( [
2058  'name' => 'wpSuppress',
2059  'inputId' => 'wpSuppress',
2060  'tabIndex' => 4,
2061  ] ),
2062  [
2063  'label' => $ctx->msg( 'revdelete-suppress' )->text(),
2064  'align' => 'inline',
2065  'infusable' => true,
2066  ]
2067  );
2068  }
2069 
2070  $fields[] = new OOUI\FieldLayout(
2071  new OOUI\ButtonInputWidget( [
2072  'name' => 'wpConfirmB',
2073  'inputId' => 'wpConfirmB',
2074  'tabIndex' => 5,
2075  'value' => $ctx->msg( 'deletepage' )->text(),
2076  'label' => $ctx->msg( 'deletepage' )->text(),
2077  'flags' => [ 'primary', 'destructive' ],
2078  'type' => 'submit',
2079  ] ),
2080  [
2081  'align' => 'top',
2082  ]
2083  );
2084 
2085  $fieldset = new OOUI\FieldsetLayout( [
2086  'label' => $ctx->msg( 'delete-legend' )->text(),
2087  'id' => 'mw-delete-table',
2088  'items' => $fields,
2089  ] );
2090 
2091  $form = new OOUI\FormLayout( [
2092  'method' => 'post',
2093  'action' => $title->getLocalURL( 'action=delete' ),
2094  'id' => 'deleteconfirm',
2095  ] );
2096  $form->appendContent(
2097  $fieldset,
2098  new OOUI\HtmlSnippet(
2099  Html::hidden( 'wpEditToken', $user->getEditToken( [ 'delete', $title->getPrefixedText() ] ) )
2100  )
2101  );
2102 
2103  $outputPage->addHTML(
2104  new OOUI\PanelLayout( [
2105  'classes' => [ 'deletepage-wrapper' ],
2106  'expanded' => false,
2107  'padded' => true,
2108  'framed' => true,
2109  'content' => $form,
2110  ] )
2111  );
2112 
2113  if ( $this->permManager->userHasRight( $user, 'editinterface' ) ) {
2114  $link = $this->linkRenderer->makeKnownLink(
2115  $ctx->msg( 'deletereason-dropdown' )->inContentLanguage()->getTitle(),
2116  wfMessage( 'delete-edit-reasonlist' )->text(),
2117  [],
2118  [ 'action' => 'edit' ]
2119  );
2120  $outputPage->addHTML( '<p class="mw-delete-editreasons">' . $link . '</p>' );
2121  }
2122 
2123  $deleteLogPage = new LogPage( 'delete' );
2124  $outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) );
2125  LogEventsList::showLogExtract( $outputPage, 'delete', $title );
2126  }
2127 
2136  public function doDelete( $reason, $suppress = false, $immediate = false ) {
2137  $error = '';
2138  $context = $this->getContext();
2139  $outputPage = $context->getOutput();
2140  $user = $context->getUser();
2141  $status = $this->mPage->doDeleteArticleReal(
2142  $reason, $user, $suppress, null, $error,
2143  null, [], 'delete', $immediate
2144  );
2145 
2146  if ( $status->isOK() ) {
2147  $deleted = $this->getTitle()->getPrefixedText();
2148 
2149  $outputPage->setPageTitle( wfMessage( 'actioncomplete' ) );
2150  $outputPage->setRobotPolicy( 'noindex,nofollow' );
2151 
2152  if ( $status->isGood() ) {
2153  $loglink = '[[Special:Log/delete|' . wfMessage( 'deletionlog' )->text() . ']]';
2154  $outputPage->addWikiMsg( 'deletedtext', wfEscapeWikiText( $deleted ), $loglink );
2155  $this->getHookRunner()->onArticleDeleteAfterSuccess( $this->getTitle(), $outputPage );
2156  } else {
2157  $outputPage->addWikiMsg( 'delete-scheduled', wfEscapeWikiText( $deleted ) );
2158  }
2159 
2160  $outputPage->returnToMain( false );
2161  } else {
2162  $outputPage->setPageTitle(
2163  wfMessage( 'cannotdelete-title',
2164  $this->getTitle()->getPrefixedText() )
2165  );
2166 
2167  if ( $error == '' ) {
2168  $outputPage->wrapWikiTextAsInterface(
2169  'error mw-error-cannotdelete',
2170  $status->getWikiText( false, false, $context->getLanguage() )
2171  );
2172  $deleteLogPage = new LogPage( 'delete' );
2173  $outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) );
2174 
2176  $outputPage,
2177  'delete',
2178  $this->getTitle()
2179  );
2180  } else {
2181  $outputPage->addHTML( $error );
2182  }
2183  }
2184  }
2185 
2186  /* Caching functions */
2187 
2195  protected function tryFileCache() {
2196  static $called = false;
2197 
2198  if ( $called ) {
2199  wfDebug( "Article::tryFileCache(): called twice!?" );
2200  return false;
2201  }
2202 
2203  $called = true;
2204  if ( $this->isFileCacheable() ) {
2205  $cache = new HTMLFileCache( $this->getTitle(), 'view' );
2206  if ( $cache->isCacheGood( $this->mPage->getTouched() ) ) {
2207  wfDebug( "Article::tryFileCache(): about to load file" );
2208  $cache->loadFromFileCache( $this->getContext() );
2209  return true;
2210  } else {
2211  wfDebug( "Article::tryFileCache(): starting buffer" );
2212  ob_start( [ &$cache, 'saveToFileCache' ] );
2213  }
2214  } else {
2215  wfDebug( "Article::tryFileCache(): not cacheable" );
2216  }
2217 
2218  return false;
2219  }
2220 
2226  public function isFileCacheable( $mode = HTMLFileCache::MODE_NORMAL ) {
2227  $cacheable = false;
2228 
2229  if ( HTMLFileCache::useFileCache( $this->getContext(), $mode ) ) {
2230  $cacheable = $this->mPage->getId()
2231  && !$this->mRedirectedFrom && !$this->getTitle()->isRedirect();
2232  // Extension may have reason to disable file caching on some pages.
2233  if ( $cacheable ) {
2234  $cacheable = $this->getHookRunner()->onIsFileCacheable( $this );
2235  }
2236  }
2237 
2238  return $cacheable;
2239  }
2240 
2254  public function getParserOutput( $oldid = null, User $user = null ) {
2255  // XXX: bypasses mParserOptions and thus setParserOptions()
2256 
2257  if ( $user === null ) {
2258  $parserOptions = $this->getParserOptions();
2259  } else {
2260  $parserOptions = $this->mPage->makeParserOptions( $user );
2261  }
2262 
2263  return $this->mPage->getParserOutput( $parserOptions, $oldid );
2264  }
2265 
2272  public function setParserOptions( ParserOptions $options ) {
2273  if ( $this->mParserOptions ) {
2274  throw new MWException( "can't change parser options after they have already been set" );
2275  }
2276 
2277  // clone, so if $options is modified later, it doesn't confuse the parser cache.
2278  $this->mParserOptions = clone $options;
2279  }
2280 
2285  public function getParserOptions() {
2286  if ( !$this->mParserOptions ) {
2287  $this->mParserOptions = $this->mPage->makeParserOptions( $this->getContext() );
2288  }
2289  // Clone to allow modifications of the return value without affecting cache
2290  return clone $this->mParserOptions;
2291  }
2292 
2299  public function setContext( $context ) {
2300  $this->mContext = $context;
2301  }
2302 
2309  public function getContext() {
2310  if ( $this->mContext instanceof IContextSource ) {
2311  return $this->mContext;
2312  } else {
2313  wfDebug( __METHOD__ . " called and \$mContext is null. " .
2314  "Return RequestContext::getMain(); for sanity" );
2315  return RequestContext::getMain();
2316  }
2317  }
2318 
2328  public function __get( $fname ) {
2329  wfDeprecated( __METHOD__ . " Access to raw $fname field", '1.35' );
2330 
2331  if ( $fname === 'mRevision' ) {
2332  $record = $this->fetchRevisionRecord(); // Ensure that it is loaded
2333  return $record ? new Revision( $record ) : null;
2334  }
2335 
2336  if ( property_exists( $this->mPage, $fname ) ) {
2337  return $this->mPage->$fname;
2338  }
2339  trigger_error( 'Inaccessible property via __get(): ' . $fname, E_USER_NOTICE );
2340  }
2341 
2351  public function __set( $fname, $fvalue ) {
2352  wfDeprecated( __METHOD__ . " Access to raw $fname field", '1.35' );
2353 
2354  if ( $fname === 'mRevision' ) {
2355  $this->mRevisionRecord = $fvalue ?
2356  $fvalue->getRevisionRecord() :
2357  null;
2358  return;
2359  }
2360 
2361  if ( property_exists( $this->mPage, $fname ) ) {
2362  $this->mPage->$fname = $fvalue;
2363  // Note: extensions may want to toss on new fields
2364  } elseif ( !in_array( $fname, [ 'mContext', 'mPage' ] ) ) {
2365  $this->mPage->$fname = $fvalue;
2366  } else {
2367  trigger_error( 'Inaccessible property via __set(): ' . $fname, E_USER_NOTICE );
2368  }
2369  }
2370 
2382  public function checkFlags( $flags ) {
2383  wfDeprecated( __METHOD__, '1.35' );
2384  return $this->mPage->checkFlags( $flags );
2385  }
2386 
2392  public function checkTouched() {
2393  wfDeprecated( __METHOD__, '1.35' );
2394  return $this->mPage->checkTouched();
2395  }
2396 
2402  public function clearPreparedEdit() {
2403  wfDeprecated( __METHOD__, '1.35' );
2404  $this->mPage->clearPreparedEdit();
2405  }
2406 
2421  public function doDeleteArticleReal(
2422  $reason, $suppress = false, $u1 = null, $u2 = null, &$error = '', User $user = null,
2423  $tags = [], $immediate = false
2424  ) {
2425  wfDeprecated( __METHOD__, '1.35' );
2426  return $this->mPage->doDeleteArticleReal(
2427  $reason, $suppress, $u1, $u2, $error, $user, $tags, 'delete', $immediate
2428  );
2429  }
2430 
2439  public function doDeleteUpdates(
2440  $id,
2441  Content $content = null,
2442  $revision = null,
2443  User $user = null
2444  ) {
2445  wfDeprecated( __METHOD__, '1.35' );
2446  $this->mPage->doDeleteUpdates( $id, $content, $revision, $user );
2447  }
2448 
2458  public function doEditUpdates( Revision $revision, User $user, array $options = [] ) {
2459  wfDeprecated( __METHOD__, '1.35' );
2460  $this->mPage->doEditUpdates( $revision, $user, $options );
2461  }
2462 
2470  public function doPurge() {
2471  wfDeprecated( __METHOD__, '1.35' );
2472  return $this->mPage->doPurge();
2473  }
2474 
2481  public function doViewUpdates( User $user, $oldid = 0 ) {
2482  wfDeprecated( __METHOD__, '1.35' );
2483  $this->mPage->doViewUpdates( $user, $oldid );
2484  }
2485 
2491  public function exists() {
2492  wfDeprecated( __METHOD__, '1.35' );
2493  return $this->mPage->exists();
2494  }
2495 
2501  public function followRedirect() {
2502  wfDeprecated( __METHOD__, '1.35' );
2503  return $this->mPage->followRedirect();
2504  }
2505 
2511  public function getActionOverrides() {
2512  return $this->mPage->getActionOverrides();
2513  }
2514 
2521  public function getAutoDeleteReason( &$hasHistory ) {
2522  wfDeprecated( __METHOD__, '1.35' );
2523  return $this->mPage->getAutoDeleteReason( $hasHistory );
2524  }
2525 
2531  public function getCategories() {
2532  wfDeprecated( __METHOD__, '1.35' );
2533  return $this->mPage->getCategories();
2534  }
2535 
2544  public function getComment( $audience = RevisionRecord::FOR_PUBLIC, User $user = null ) {
2545  wfDeprecated( __METHOD__, '1.35' );
2546  return $this->mPage->getComment( $audience, $user );
2547  }
2548 
2554  public function getContentHandler() {
2555  wfDeprecated( __METHOD__, '1.35' );
2556  return $this->mPage->getContentHandler();
2557  }
2558 
2564  public function getContentModel() {
2565  wfDeprecated( __METHOD__, '1.35' );
2566  return $this->mPage->getContentModel();
2567  }
2568 
2574  public function getContributors() {
2575  wfDeprecated( __METHOD__, '1.35' );
2576  return $this->mPage->getContributors();
2577  }
2578 
2587  public function getCreator( $audience = RevisionRecord::FOR_PUBLIC, User $user = null ) {
2588  wfDeprecated( __METHOD__, '1.35' );
2589  return $this->mPage->getCreator( $audience, $user );
2590  }
2591 
2598  public function getDeletionUpdates( Content $content = null ) {
2599  wfDeprecated( __METHOD__, '1.35' );
2600  return $this->mPage->getDeletionUpdates( $content );
2601  }
2602 
2608  public function getHiddenCategories() {
2609  wfDeprecated( __METHOD__, '1.35' );
2610  return $this->mPage->getHiddenCategories();
2611  }
2612 
2618  public function getId() {
2619  wfDeprecated( __METHOD__, '1.35' );
2620  return $this->mPage->getId();
2621  }
2622 
2628  public function getLatest() {
2629  wfDeprecated( __METHOD__, '1.35' );
2630  return $this->mPage->getLatest();
2631  }
2632 
2638  public function getLinksTimestamp() {
2639  wfDeprecated( __METHOD__, '1.35' );
2640  return $this->mPage->getLinksTimestamp();
2641  }
2642 
2648  public function getMinorEdit() {
2649  wfDeprecated( __METHOD__, '1.35' );
2650  return $this->mPage->getMinorEdit();
2651  }
2652 
2657  public function getOldestRevision() {
2658  wfDeprecated( __METHOD__, '1.35' );
2659  return $this->mPage->getOldestRevision();
2660  }
2661 
2667  public function getRedirectTarget() {
2668  wfDeprecated( __METHOD__, '1.35' );
2669  return $this->mPage->getRedirectTarget();
2670  }
2671 
2678  public function getRedirectURL( $rt ) {
2679  wfDeprecated( __METHOD__, '1.35' );
2680  return $this->mPage->getRedirectURL( $rt );
2681  }
2682 
2689  public function getRevision() {
2690  wfDeprecated( __METHOD__, '1.35' );
2691  return $this->mPage->getRevision();
2692  }
2693 
2699  public function getTimestamp() {
2700  wfDeprecated( __METHOD__, '1.35' );
2701  return $this->mPage->getTimestamp();
2702  }
2703 
2709  public function getTouched() {
2710  wfDeprecated( __METHOD__, '1.35' );
2711  return $this->mPage->getTouched();
2712  }
2713 
2722  public function getUndoContent( Revision $undo, Revision $undoafter = null ) {
2723  wfDeprecated( __METHOD__, '1.35' );
2724  return $this->mPage->getUndoContent( $undo, $undoafter );
2725  }
2726 
2734  public function getUser( $audience = RevisionRecord::FOR_PUBLIC, User $user = null ) {
2735  wfDeprecated( __METHOD__, '1.35' );
2736  return $this->mPage->getUser( $audience, $user );
2737  }
2738 
2746  public function getUserText( $audience = RevisionRecord::FOR_PUBLIC, User $user = null ) {
2747  wfDeprecated( __METHOD__, '1.35' );
2748  return $this->mPage->getUserText( $audience, $user );
2749  }
2750 
2756  public function hasViewableContent() {
2757  wfDeprecated( __METHOD__, '1.35' );
2758  return $this->mPage->hasViewableContent();
2759  }
2760 
2768  public function insertOn( $dbw, $pageId = null ) {
2769  wfDeprecated( __METHOD__, '1.35' );
2770  return $this->mPage->insertOn( $dbw, $pageId );
2771  }
2772 
2784  public function insertProtectNullRevision( $revCommentMsg, array $limit,
2785  array $expiry, $cascade, $reason, $user = null
2786  ) {
2787  wfDeprecated( __METHOD__, '1.35' );
2788  return $this->mPage->insertProtectNullRevision( $revCommentMsg, $limit,
2789  $expiry, $cascade, $reason, $user
2790  );
2791  }
2792 
2798  public function insertRedirect() {
2799  wfDeprecated( __METHOD__, '1.35' );
2800  return $this->mPage->insertRedirect();
2801  }
2802 
2810  public function insertRedirectEntry( Title $rt, $oldLatest = null ) {
2811  wfDeprecated( __METHOD__, '1.35' );
2812  return $this->mPage->insertRedirectEntry( $rt, $oldLatest );
2813  }
2814 
2821  public function isCountable( $editInfo = false ) {
2822  wfDeprecated( __METHOD__, '1.35' );
2823  return $this->mPage->isCountable( $editInfo );
2824  }
2825 
2831  public function isRedirect() {
2832  wfDeprecated( __METHOD__, '1.35' );
2833  return $this->mPage->isRedirect();
2834  }
2835 
2842  public function loadFromRow( $data, $from ) {
2843  wfDeprecated( __METHOD__, '1.35' );
2844  $this->mPage->loadFromRow( $data, $from );
2845  }
2846 
2852  public function loadPageData( $from = 'fromdb' ) {
2853  wfDeprecated( __METHOD__, '1.35' );
2854  $this->mPage->loadPageData( $from );
2855  }
2856 
2862  public function lockAndGetLatest() {
2863  wfDeprecated( __METHOD__, '1.35' );
2864  return $this->mPage->lockAndGetLatest();
2865  }
2866 
2873  public function makeParserOptions( $context ) {
2874  wfDeprecated( __METHOD__, '1.35' );
2875  return $this->mPage->makeParserOptions( $context );
2876  }
2877 
2886  public function pageDataFromId( $dbr, $id, $options = [] ) {
2887  wfDeprecated( __METHOD__, '1.35' );
2888  return $this->mPage->pageDataFromId( $dbr, $id, $options );
2889  }
2890 
2899  public function pageDataFromTitle( $dbr, $title, $options = [] ) {
2900  wfDeprecated( __METHOD__, '1.35' );
2901  return $this->mPage->pageDataFromTitle( $dbr, $title, $options );
2902  }
2903 
2917  public function prepareContentForEdit(
2918  Content $content, $revision = null, User $user = null,
2919  $serialFormat = null, $useCache = true
2920  ) {
2921  wfDeprecated( __METHOD__, '1.35' );
2922  return $this->mPage->prepareContentForEdit(
2923  $content, $revision, $user,
2924  $serialFormat, $useCache
2925  );
2926  }
2927 
2935  public function protectDescription( array $limit, array $expiry ) {
2936  wfDeprecated( __METHOD__, '1.35' );
2937  return $this->mPage->protectDescription( $limit, $expiry );
2938  }
2939 
2947  public function protectDescriptionLog( array $limit, array $expiry ) {
2948  wfDeprecated( __METHOD__, '1.35' );
2949  return $this->mPage->protectDescriptionLog( $limit, $expiry );
2950  }
2951 
2961  public function replaceSectionAtRev( $sectionId, Content $sectionContent,
2962  $sectionTitle = '', $baseRevId = null
2963  ) {
2964  wfDeprecated( __METHOD__, '1.35' );
2965  return $this->mPage->replaceSectionAtRev( $sectionId, $sectionContent,
2966  $sectionTitle, $baseRevId
2967  );
2968  }
2969 
2981  public function replaceSectionContent(
2982  $sectionId, Content $sectionContent, $sectionTitle = '', $edittime = null
2983  ) {
2984  wfDeprecated( __METHOD__, '1.35' );
2985  return $this->mPage->replaceSectionContent(
2986  $sectionId, $sectionContent, $sectionTitle, $edittime
2987  );
2988  }
2989 
2995  public function setTimestamp( $ts ) {
2996  wfDeprecated( __METHOD__, '1.35' );
2997  $this->mPage->setTimestamp( $ts );
2998  }
2999 
3007  public function shouldCheckParserCache( ParserOptions $parserOptions, $oldId ) {
3008  wfDeprecated( __METHOD__, '1.35' );
3009  return $this->mPage->shouldCheckParserCache( $parserOptions, $oldId );
3010  }
3011 
3017  public function supportsSections() {
3018  wfDeprecated( __METHOD__, '1.35' );
3019  return $this->mPage->supportsSections();
3020  }
3021 
3027  public function triggerOpportunisticLinksUpdate( ParserOutput $parserOutput ) {
3028  wfDeprecated( __METHOD__, '1.35' );
3029  $this->mPage->triggerOpportunisticLinksUpdate( $parserOutput );
3030  }
3031 
3039  public function updateCategoryCounts( array $added, array $deleted, $id = 0 ) {
3040  wfDeprecated( __METHOD__, '1.35' );
3041  $this->mPage->updateCategoryCounts( $added, $deleted, $id );
3042  }
3043 
3052  public function updateIfNewerOn( $dbw, $revision ) {
3053  wfDeprecated( __METHOD__, '1.35' );
3054  return $this->mPage->updateIfNewerOn( $dbw, $revision );
3055  }
3056 
3065  public function updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect = null ) {
3066  wfDeprecated( __METHOD__, '1.35' );
3067  return $this->mPage->updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect );
3068  }
3069 
3079  public function updateRevisionOn( $dbw, $revision, $lastRevision = null,
3080  $lastRevIsRedirect = null
3081  ) {
3082  wfDeprecated( __METHOD__, '1.35' );
3083  return $this->mPage->updateRevisionOn( $dbw, $revision, $lastRevision,
3084  $lastRevIsRedirect
3085  );
3086  }
3087 
3097  public function doUpdateRestrictions( array $limit, array $expiry, &$cascade,
3098  $reason, User $user
3099  ) {
3100  wfDeprecated( __METHOD__, '1.35' );
3101  return $this->mPage->doUpdateRestrictions( $limit, $expiry, $cascade, $reason, $user );
3102  }
3103 
3112  public function updateRestrictions( $limit = [], $reason = '',
3113  &$cascade = 0, $expiry = []
3114  ) {
3115  wfDeprecated( __METHOD__, '1.35' );
3116  return $this->mPage->doUpdateRestrictions(
3117  $limit,
3118  $expiry,
3119  $cascade,
3120  $reason,
3121  $this->getContext()->getUser()
3122  );
3123  }
3124 
3137  public function doDeleteArticle(
3138  $reason, $suppress = false, $u1 = null, $u2 = null, &$error = '', $immediate = false
3139  ) {
3140  wfDeprecated( __METHOD__, '1.35' );
3141  return $this->mPage->doDeleteArticle( $reason, $suppress, $u1, $u2, $error,
3142  null, $immediate );
3143  }
3144 
3155  public function doRollback(
3156  $fromP,
3157  $summary,
3158  $token,
3159  $bot,
3160  &$resultDetails,
3161  User $user = null
3162  ) {
3163  wfDeprecated( __METHOD__, '1.35' );
3164  if ( !$user ) {
3165  $user = $this->getContext()->getUser();
3166  }
3167 
3168  return $this->mPage->doRollback( $fromP, $summary, $token, $bot, $resultDetails, $user );
3169  }
3170 
3181  public function commitRollback( $fromP, $summary, $bot, &$resultDetails, User $guser = null ) {
3182  wfDeprecated( __METHOD__, '1.35' );
3183  if ( !$guser ) {
3184  $guser = $this->getContext()->getUser();
3185  }
3186 
3187  return $this->mPage->commitRollback( $fromP, $summary, $bot, $resultDetails, $guser );
3188  }
3189 
3196  public function generateReason( &$hasHistory ) {
3197  wfDeprecated( __METHOD__, '1.35' );
3198  return $this->getPage()->getAutoDeleteReason( $hasHistory );
3199  }
3200 }
ReadOnlyError
Show an error when the wiki is locked/read-only and the user tries to do something that requires writ...
Definition: ReadOnlyError.php:28
$wgSend404Code
$wgSend404Code
Some web hosts attempt to rewrite all responses with a 404 (not found) status code,...
Definition: DefaultSettings.php:3622
Article\showMissingArticle
showMissingArticle()
Show the error text for a missing article.
Definition: Article.php:1386
Article\checkFlags
checkFlags( $flags)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2382
ParserOptions
Set options of the Parser.
Definition: ParserOptions.php:42
Article\$mContentObject
Content null $mContentObject
Content of the main slot of $this->mRevisionRecord.
Definition: Article.php:73
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:54
Message\numParam
static numParam( $num)
Definition: Message.php:1046
Page
Interface for type hinting (accepts WikiPage, Article, ImagePage, CategoryPage)
Definition: Page.php:30
Article\isRedirect
isRedirect()
Definition: Article.php:2831
Article\getCategories
getCategories()
Definition: Article.php:2531
Article\doDeleteArticleReal
doDeleteArticleReal( $reason, $suppress=false, $u1=null, $u2=null, &$error='', User $user=null, $tags=[], $immediate=false)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2421
Revision\RevisionRecord
Page revision base class.
Definition: RevisionRecord.php:46
ParserOutput
Definition: ParserOutput.php:25
Article\formatRobotPolicy
static formatRobotPolicy( $policy)
Converts a String robot policy into an associative array, to allow merging of several policies using ...
Definition: Article.php:1069
StatusValue\newFatal
static newFatal( $message,... $parameters)
Factory function for fatal errors.
Definition: StatusValue.php:69
Article\$viewIsRenderAction
bool $viewIsRenderAction
Whether render() was called.
Definition: Article.php:125
Article\getRedirectTarget
getRedirectTarget()
Definition: Article.php:2667
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:624
Xml\listDropDownOptionsOoui
static listDropDownOptionsOoui( $options)
Convert options for a drop-down box into a format accepted by OOUI\DropdownInputWidget etc.
Definition: Xml.php:585
Article\getContentModel
getContentModel()
Definition: Article.php:2564
Article\getLinksTimestamp
getLinksTimestamp()
Definition: Article.php:2638
Article\getOldIDFromRequest
getOldIDFromRequest()
Sets $this->mRedirectUrl to a correct URL if the query parameters are incorrect.
Definition: Article.php:374
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:146
Article\showPatrolFooter
showPatrolFooter()
If patrol is possible, output a patrol UI box.
Definition: Article.php:1208
$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:208
Article\getComment
getComment( $audience=RevisionRecord::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2544
Article\tryFileCache
tryFileCache()
checkLastModified returns true if it has taken care of all output to the client that is necessary for...
Definition: Article.php:2195
Article\getContentHandler
getContentHandler()
Definition: Article.php:2554
Article\makeFetchErrorContent
makeFetchErrorContent()
Returns a Content object representing any error in $this->fetchContent, or null if there is no such e...
Definition: Article.php:538
Article\clearPreparedEdit
clearPreparedEdit()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2402
Article\$revisionStore
RevisionStore $revisionStore
Definition: Article.php:140
HTMLFileCache
Page view caching in the file system.
Definition: HTMLFileCache.php:33
Revision\RevisionStore
Service for looking up page revisions.
Definition: RevisionStore.php:79
Article\lockAndGetLatest
lockAndGetLatest()
Definition: Article.php:2862
MediaWiki\Linker\LinkRenderer
Class that generates HTML links for pages.
Definition: LinkRenderer.php:43
Article\supportsSections
supportsSections()
Definition: Article.php:3017
Article\getOldestRevision
getOldestRevision()
Definition: Article.php:2657
Article\getDeletionUpdates
getDeletionUpdates(Content $content=null)
Definition: Article.php:2598
Article\checkTouched
checkTouched()
Definition: Article.php:2392
Article\getRevision
getRevision()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2689
RC_LOG
const RC_LOG
Definition: Defines.php:133
CategoryPage
Special handling for category description pages, showing pages, subcategories and file that belong to...
Definition: CategoryPage.php:30
WikiPage
Class representing a MediaWiki article and history.
Definition: WikiPage.php:49
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:1606
Linker\getRevDeleteLink
static getRevDeleteLink(User $user, $rev, LinkTarget $title)
Get a revision-deletion link, or disabled link, or nothing, depending on user permissions & the setti...
Definition: Linker.php:2196
PoolWorkArticleView
Definition: PoolWorkArticleView.php:28
NS_FILE
const NS_FILE
Definition: Defines.php:75
wfReadOnly
wfReadOnly()
Check whether the wiki is in read-only mode.
Definition: GlobalFunctions.php:1104
User\newFromName
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition: User.php:535
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1198
Article\showDiffPage
showDiffPage()
Show a diff page according to current request variables.
Definition: Article.php:932
$wgArticleRobotPolicies
$wgArticleRobotPolicies
Robot policies per article.
Definition: DefaultSettings.php:8414
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:30
Article\confirmDelete
confirmDelete( $reason)
Output deletion confirmation dialog.
Definition: Article.php:1965
Article\updateCategoryCounts
updateCategoryCounts(array $added, array $deleted, $id=0)
Definition: Article.php:3039
Article\doDeleteArticle
doDeleteArticle( $reason, $suppress=false, $u1=null, $u2=null, &$error='', $immediate=false)
Definition: Article.php:3137
Article\adjustDisplayTitle
adjustDisplayTitle(ParserOutput $pOutput)
Adjust title for pages with displaytitle, -{T|}- or language conversion.
Definition: Article.php:917
Article\showNamespaceHeader
showNamespaceHeader()
Show a header specific to the namespace currently being viewed, like [[MediaWiki:Talkpagetext]].
Definition: Article.php:1172
Article\shouldCheckParserCache
shouldCheckParserCache(ParserOptions $parserOptions, $oldId)
Definition: Article.php:3007
$wgUseRCPatrol
$wgUseRCPatrol
Use RC Patrolling to check for vandalism (from recent changes and watchlists) New pages and new files...
Definition: DefaultSettings.php:7264
Xml\openElement
static openElement( $element, $attribs=null)
This opens an XML element.
Definition: Xml.php:108
$wgUseNPPatrol
$wgUseNPPatrol
Use new page patrolling to check new pages on Special:Newpages.
Definition: DefaultSettings.php:7280
Article\$linkRenderer
LinkRenderer $linkRenderer
Definition: Article.php:130
ProtectionForm
Handles the page protection UI and backend.
Definition: ProtectionForm.php:33
Article\doDelete
doDelete( $reason, $suppress=false, $immediate=false)
Perform a deletion and output success or failure messages.
Definition: Article.php:2136
Article\protectDescriptionLog
protectDescriptionLog(array $limit, array $expiry)
Definition: Article.php:2947
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:905
Article\$mRedirectedFrom
Title null $mRedirectedFrom
Title from which we were redirected here, if any.
Definition: Article.php:91
$dbr
$dbr
Definition: testCompression.php:54
Status
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition: Status.php:42
Article\getSubstituteContent
getSubstituteContent()
Returns Content object to use when the page does not exist.
Definition: Article.php:321
Revision
Definition: Revision.php:39
Article\unprotect
unprotect()
action=unprotect handler (alias)
Definition: Article.php:1826
Article\insertRedirect
insertRedirect()
Definition: Article.php:2798
MediaWiki\Block\DatabaseBlock
A DatabaseBlock (unlike a SystemBlock) is stored in the database, may give rise to autoblocks and may...
Definition: DatabaseBlock.php:54
Article\getRedirectedFrom
getRedirectedFrom()
Get the page this view was redirected from.
Definition: Article.php:238
Article\fetchContentObject
fetchContentObject()
Get text content object Does NOT follow redirects.
Definition: Article.php:449
Article\clear
clear()
Clear the object.
Definition: Article.php:273
Article\updateRedirectOn
updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect=null)
Definition: Article.php:3065
Article\newFromWikiPage
static newFromWikiPage(WikiPage $page, IContextSource $context)
Create an Article object of the appropriate class for the given page.
Definition: Article.php:227
Article\getTouched
getTouched()
Definition: Article.php:2709
MWException
MediaWiki exception.
Definition: MWException.php:26
Article\getUserText
getUserText( $audience=RevisionRecord::FOR_PUBLIC, User $user=null)
Definition: Article.php:2746
Article\getTitle
getTitle()
Get the title object of the article.
Definition: Article.php:256
Article\updateRevisionOn
updateRevisionOn( $dbw, $revision, $lastRevision=null, $lastRevIsRedirect=null)
Definition: Article.php:3079
Article\render
render()
Handle action=render.
Definition: Article.php:1807
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that $function is deprecated.
Definition: GlobalFunctions.php:1030
Article\getCreator
getCreator( $audience=RevisionRecord::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2587
Article\$permManager
PermissionManager $permManager
Definition: Article.php:135
Article\insertProtectNullRevision
insertProtectNullRevision( $revCommentMsg, array $limit, array $expiry, $cascade, $reason, $user=null)
Definition: Article.php:2784
Article\$mParserOutput
ParserOutput null false $mParserOutput
The ParserOutput generated for viewing the page, initialized by view().
Definition: Article.php:118
Article\addHelpLink
addHelpLink( $to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
Definition: Article.php:1790
Article\exists
exists()
Definition: Article.php:2491
Article\setRedirectedFrom
setRedirectedFrom(Title $from)
Tell the page view functions that this view was redirected from another page on the wiki.
Definition: Article.php:247
$wgUseFileCache
$wgUseFileCache
This will cache static pages for non-logged-in users to reduce database traffic on public sites.
Definition: DefaultSettings.php:2760
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2463
Article\applyContentOverride
applyContentOverride(Content $override)
Applies a content override by constructing a fake Revision object and assigning it to mRevisionRecord...
Definition: Article.php:559
Article\__construct
__construct(Title $title, $oldId=null)
Constructor and clear the article.
Definition: Article.php:160
Article\showDeletedRevisionHeader
showDeletedRevisionHeader()
If the revision requested for view is deleted, check permissions.
Definition: Article.php:1523
Article\isFileCacheable
isFileCacheable( $mode=HTMLFileCache::MODE_NORMAL)
Check if the page can be cached.
Definition: Article.php:2226
Article\doRollback
doRollback( $fromP, $summary, $token, $bot, &$resultDetails, User $user=null)
Definition: Article.php:3155
WikiPage\getTitle
getTitle()
Get the title object of the article.
Definition: WikiPage.php:310
LogPage
Class to simplify the use of log pages.
Definition: LogPage.php:33
Article\setOldSubtitle
setOldSubtitle( $oldid=0)
Generate the navigation links when browsing through an article revisions It shows the information as:...
Definition: Article.php:1570
Xml\element
static element( $element, $attribs=null, $contents='', $allowShortTag=true)
Format an XML element with given attributes and, optionally, text content.
Definition: Xml.php:41
ObjectCache\getInstance
static getInstance( $id)
Get a cached instance of the specified type of cache object.
Definition: ObjectCache.php:78
Article\getPage
getPage()
Get the WikiPage object of this instance.
Definition: Article.php:266
Article\getContext
getContext()
Gets the context this Article is executed in.
Definition: Article.php:2309
User\isIP
static isIP( $name)
Does the string match an anonymous IP address?
Definition: User.php:948
Article\$mParserOptions
ParserOptions null $mParserOptions
ParserOptions object for $wgUser articles.
Definition: Article.php:63
WatchAction\doWatchOrUnwatch
static doWatchOrUnwatch( $watch, Title $title, User $user)
Watch or unwatch a page.
Definition: WatchAction.php:174
Article\pageDataFromId
pageDataFromId( $dbr, $id, $options=[])
Definition: Article.php:2886
$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:989
Article\insertOn
insertOn( $dbw, $pageId=null)
Definition: Article.php:2768
Linker\makeExternalLink
static makeExternalLink( $url, $text, $escape=true, $linktype='', $attribs=[], $title=null)
Make an external link.
Definition: Linker.php:846
Title\makeTitle
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:595
Article\getRedirectURL
getRedirectURL( $rt)
Definition: Article.php:2678
LogEventsList\showLogExtract
static showLogExtract(&$out, $types=[], $page='', $user='', $param=[])
Show log extract.
Definition: LogEventsList.php:639
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
Article\newFromID
static newFromID( $id)
Constructor from a page id.
Definition: Article.php:183
Article\newPage
newPage(Title $title)
Definition: Article.php:174
NS_CATEGORY
const NS_CATEGORY
Definition: Defines.php:83
Article\getUndoContent
getUndoContent(Revision $undo, Revision $undoafter=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2722
Article\prepareContentForEdit
prepareContentForEdit(Content $content, $revision=null, User $user=null, $serialFormat=null, $useCache=true)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2917
$wgDeleteRevisionsLimit
$wgDeleteRevisionsLimit
Optional to restrict deletion of pages with higher revision counts to users with the 'bigdelete' perm...
Definition: DefaultSettings.php:5894
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:1142
Article\$mRevIdFetched
int $mRevIdFetched
Revision ID of revision that was loaded.
Definition: Article.php:101
$wgUseFilePatrol
$wgUseFilePatrol
Use file patrolling to check new files on Special:Newfiles.
Definition: DefaultSettings.php:7291
Article\getLatest
getLatest()
Definition: Article.php:2628
wfDebug
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Definition: GlobalFunctions.php:913
Article\isCurrent
isCurrent()
Returns true if the currently-referenced revision is the current edit to this page (and it exists).
Definition: Article.php:575
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:140
Article\getAutoDeleteReason
getAutoDeleteReason(&$hasHistory)
Definition: Article.php:2521
Article\$mContentLoaded
bool $mContentLoaded
Is the target revision loaded? Set by fetchRevisionRecord().
Definition: Article.php:81
Article\setParserOptions
setParserOptions(ParserOptions $options)
Override the ParserOptions used to render the primary article wikitext.
Definition: Article.php:2272
Article\isCountable
isCountable( $editInfo=false)
Definition: Article.php:2821
Html\hidden
static hidden( $name, $value, array $attribs=[])
Convenience function to produce an input element with type=hidden.
Definition: Html.php:802
ParserOutput\getTitleText
getTitleText()
Definition: ParserOutput.php:565
Article\doPurge
doPurge()
Definition: Article.php:2470
MediaWiki\Permissions\PermissionManager
A service class for checking permissions To obtain an instance, use MediaWikiServices::getInstance()-...
Definition: PermissionManager.php:49
$content
$content
Definition: router.php:76
NS_USER_TALK
const NS_USER_TALK
Definition: Defines.php:72
Article\hasViewableContent
hasViewableContent()
Definition: Article.php:2756
Article\$mOldId
int null $mOldId
The oldid of the article that was requested to be shown, 0 for the current revision.
Definition: Article.php:88
NS_MEDIA
const NS_MEDIA
Definition: Defines.php:57
Article\getContributors
getContributors()
Definition: Article.php:2574
StatusValue\newGood
static newGood( $value=null)
Factory function for good results.
Definition: StatusValue.php:81
Revision\MutableRevisionRecord
Definition: MutableRevisionRecord.php:43
HTMLFileCache\MODE_NORMAL
const MODE_NORMAL
Definition: HTMLFileCache.php:34
Article\loadFromRow
loadFromRow( $data, $from)
Definition: Article.php:2842
Article\getEmptyPageParserOutput
getEmptyPageParserOutput(ParserOptions $options)
Returns ParserOutput to use when a page does not exist.
Definition: Article.php:348
Article\protect
protect()
action=protect handler
Definition: Article.php:1818
Hooks\runner
static runner()
Get a HookRunner instance for calling hooks using the new interfaces.
Definition: Hooks.php:171
Article\getTimestamp
getTimestamp()
Definition: Article.php:2699
ParserOptions\newCanonical
static newCanonical( $context=null, $userLang=null)
Creates a "canonical" ParserOptions object.
Definition: ParserOptions.php:1125
wfEscapeWikiText
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
Definition: GlobalFunctions.php:1485
Article\getMinorEdit
getMinorEdit()
Definition: Article.php:2648
Article\commitRollback
commitRollback( $fromP, $summary, $bot, &$resultDetails, User $guser=null)
Definition: Article.php:3181
$wgNamespaceRobotPolicies
$wgNamespaceRobotPolicies
Robot policies per namespaces.
Definition: DefaultSettings.php:8386
Article\getParserOutput
getParserOutput( $oldid=null, User $user=null)
#-
Definition: Article.php:2254
Article\getContentObject
getContentObject()
Returns a Content object representing the pages effective display content, not necessarily the revisi...
Definition: Article.php:305
Article\followRedirect
followRedirect()
Definition: Article.php:2501
Article\getHiddenCategories
getHiddenCategories()
Definition: Article.php:2608
RequestContext\getMain
static getMain()
Get the RequestContext object associated with the main request.
Definition: RequestContext.php:451
Article\setTimestamp
setTimestamp( $ts)
Definition: Article.php:2995
IContextSource
Interface for objects which can provide a MediaWiki context on request.
Definition: IContextSource.php:53
$context
$context
Definition: load.php:43
Article\__get
__get( $fname)
Definition: Article.php:2328
Content
Base interface for content objects.
Definition: Content.php:34
CommentStore\COMMENT_CHARACTER_LIMIT
const COMMENT_CHARACTER_LIMIT
Maximum length of a comment in UTF-8 characters.
Definition: CommentStore.php:37
Article\replaceSectionAtRev
replaceSectionAtRev( $sectionId, Content $sectionContent, $sectionTitle='', $baseRevId=null)
Definition: Article.php:2961
Article\fetchRevisionRecord
fetchRevisionRecord()
Fetches the revision to work on.
Definition: Article.php:468
Title
Represents a title within MediaWiki.
Definition: Title.php:42
Skin\makeUrl
static makeUrl( $name, $urlaction='')
Definition: Skin.php:1233
Article\getUser
getUser( $audience=RevisionRecord::FOR_PUBLIC, User $user=null)
Definition: Article.php:2734
$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:2981
MediaWiki\Linker\LinkRenderer\makeKnownLink
makeKnownLink(LinkTarget $target, $text=null, array $extraAttribs=[], array $query=[])
Definition: LinkRenderer.php:313
Article\getRevIdFetched
getRevIdFetched()
Use this to fetch the rev ID used on page views.
Definition: Article.php:612
MessageContent
Wrapper allowing us to handle a system message as a Content object.
Definition: MessageContent.php:36
Article\makeParserOptions
makeParserOptions( $context)
Definition: Article.php:2873
Article\showRedirectedFromHeader
showRedirectedFromHeader()
If this request is a redirect view, send "redirected from" subtitle to the output.
Definition: Article.php:1098
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:1135
Revision\RevisionRecord\getContent
getContent( $role, $audience=self::FOR_PUBLIC, User $user=null)
Returns the Content of the given slot of this revision.
Definition: RevisionRecord.php:167
NS_USER
const NS_USER
Definition: Defines.php:71
Article\getId
getId()
Definition: Article.php:2618
Article\getActionOverrides
getActionOverrides()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2511
Article\$mRedirectUrl
string bool $mRedirectUrl
URL to redirect to or false if none.
Definition: Article.php:94
Article\doUpdateRestrictions
doUpdateRestrictions(array $limit, array $expiry, &$cascade, $reason, User $user)
Definition: Article.php:3097
Article\updateRestrictions
updateRestrictions( $limit=[], $reason='', &$cascade=0, $expiry=[])
Definition: Article.php:3112
Article\protectDescription
protectDescription(array $limit, array $expiry)
Definition: Article.php:2935
Article\getParserOptions
getParserOptions()
Get parser options suitable for rendering the primary article wikitext.
Definition: Article.php:2285
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:4375
Article\updateIfNewerOn
updateIfNewerOn( $dbw, $revision)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:3052
NS_MEDIAWIKI
const NS_MEDIAWIKI
Definition: Defines.php:77
$t
$t
Definition: testCompression.php:74
Article
Class for viewing MediaWiki article and history.
Definition: Article.php:46
Article\doDeleteUpdates
doDeleteUpdates( $id, Content $content=null, $revision=null, User $user=null)
Definition: Article.php:2439
Article\pageDataFromTitle
pageDataFromTitle( $dbr, $title, $options=[])
Definition: Article.php:2899
Article\triggerOpportunisticLinksUpdate
triggerOpportunisticLinksUpdate(ParserOutput $parserOutput)
Definition: Article.php:3027
Article\getOldID
getOldID()
Definition: Article.php:361
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:71
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:55
Article\setContext
setContext( $context)
Sets the context this Article is executed in.
Definition: Article.php:2299
Title\newFromID
static newFromID( $id, $flags=0)
Create a new Title from an article ID.
Definition: Title.php:476
Article\insertRedirectEntry
insertRedirectEntry(Title $rt, $oldLatest=null)
Definition: Article.php:2810
MediaWiki\Linker\LinkRenderer\makeLink
makeLink(LinkTarget $target, $text=null, array $extraAttribs=[], array $query=[])
Definition: LinkRenderer.php:176
$wgDefaultRobotPolicy
$wgDefaultRobotPolicy
Default robot policy.
Definition: DefaultSettings.php:8370
Article\getRevisionFetched
getRevisionFetched()
Get the fetched Revision object depending on request parameters or null on failure.
Definition: Article.php:597
Article\viewRedirect
viewRedirect( $target, $appendSubtitle=true, $forceKnown=false)
Return the HTML for the top of a redirect page.
Definition: Article.php:1721
Article\loadPageData
loadPageData( $from='fromdb')
Definition: Article.php:2852
Language
Internationalisation code See https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation for more...
Definition: Language.php:41
Article\doEditUpdates
doEditUpdates(Revision $revision, User $user, array $options=[])
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2458
Article\$mRevisionRecord
$mRevisionRecord
Definition: Article.php:153
Article\__set
__set( $fname, $fvalue)
Definition: Article.php:2351
Article\showViewFooter
showViewFooter()
Show the footer section of an ordinary page view.
Definition: Article.php:1184
Revision\SlotRecord
Value object representing a content slot associated with a page revision.
Definition: SlotRecord.php:39
Article\$mPage
WikiPage $mPage
The WikiPage object of this instance.
Definition: Article.php:57
Article\getRedirectHeaderHtml
static getRedirectHeaderHtml(Language $lang, $target, $forceKnown=false)
Return the HTML for the top of a redirect page.
Definition: Article.php:1743
Xml\listDropDownOptions
static listDropDownOptions( $list, $params=[])
Build options for a drop-down box from a textual list.
Definition: Xml.php:543
Article\newFromTitle
static newFromTitle( $title, IContextSource $context)
Create an Article object of the appropriate class for the given page.
Definition: Article.php:195
Article\generateReason
generateReason(&$hasHistory)
Definition: Article.php:3196
Article\purgePatrolFooterCache
static purgePatrolFooterCache( $articleID)
Purge the cache used to check if it is worth showing the patrol footer For example,...
Definition: Article.php:1377
Article\doViewUpdates
doViewUpdates(User $user, $oldid=0)
Definition: Article.php:2481
Article\$fetchResult
Status null $fetchResult
represents the outcome of fetchRevisionRecord().
Definition: Article.php:111