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 
153  private $mRevisionRecord = null;
154 
159  public function __construct( Title $title, $oldId = null ) {
160  $this->mOldId = $oldId;
161  $this->mPage = $this->newPage( $title );
162 
163  $services = MediaWikiServices::getInstance();
164  $this->linkRenderer = $services->getLinkRenderer();
165  $this->permManager = $services->getPermissionManager();
166  $this->revisionStore = $services->getRevisionStore();
167  }
168 
173  protected function newPage( Title $title ) {
174  return new WikiPage( $title );
175  }
176 
182  public static function newFromID( $id ) {
183  $t = Title::newFromID( $id );
184  return $t == null ? null : new static( $t );
185  }
186 
194  public static function newFromTitle( $title, IContextSource $context ) {
195  if ( $title->getNamespace() === NS_MEDIA ) {
196  // XXX: This should not be here, but where should it go?
197  $title = Title::makeTitle( NS_FILE, $title->getDBkey() );
198  }
199 
200  $page = null;
201  Hooks::runner()->onArticleFromTitle( $title, $page, $context );
202  if ( !$page ) {
203  switch ( $title->getNamespace() ) {
204  case NS_FILE:
205  $page = new ImagePage( $title );
206  break;
207  case NS_CATEGORY:
208  $page = new CategoryPage( $title );
209  break;
210  default:
211  $page = new Article( $title );
212  }
213  }
214  $page->setContext( $context );
215 
216  return $page;
217  }
218 
226  public static function newFromWikiPage( WikiPage $page, IContextSource $context ) {
227  $article = self::newFromTitle( $page->getTitle(), $context );
228  $article->mPage = $page; // override to keep process cached vars
229  return $article;
230  }
231 
237  public function getRedirectedFrom() {
238  return $this->mRedirectedFrom;
239  }
240 
246  public function setRedirectedFrom( Title $from ) {
247  $this->mRedirectedFrom = $from;
248  }
249 
255  public function getTitle() {
256  return $this->mPage->getTitle();
257  }
258 
265  public function getPage() {
266  return $this->mPage;
267  }
268 
272  public function clear() {
273  $this->mContentLoaded = false;
274 
275  $this->mRedirectedFrom = null; # Title object if set
276  $this->mRevIdFetched = 0;
277  $this->mRedirectUrl = false;
278  $this->mRevisionRecord = null;
279  $this->mContentObject = null;
280  $this->fetchResult = null;
281 
282  // TODO hard-deprecate direct access to public fields
283 
284  $this->mPage->clear();
285  }
286 
304  protected function getContentObject() {
305  if ( $this->mPage->getId() === 0 ) {
306  $content = $this->getSubstituteContent();
307  } else {
308  $this->fetchContentObject();
310  }
311 
312  return $content;
313  }
314 
320  private function getSubstituteContent() {
321  # If this is a MediaWiki:x message, then load the messages
322  # and return the message value for x.
323  if ( $this->getTitle()->getNamespace() === NS_MEDIAWIKI ) {
324  $text = $this->getTitle()->getDefaultMessageText();
325  if ( $text === false ) {
326  $text = '';
327  }
328 
329  $content = ContentHandler::makeContent( $text, $this->getTitle() );
330  } else {
331  $message = $this->getContext()->getUser()->isLoggedIn() ? 'noarticletext' : 'noarticletextanon';
332  $content = new MessageContent( $message, null );
333  }
334 
335  return $content;
336  }
337 
347  protected function getEmptyPageParserOutput( ParserOptions $options ) {
348  $content = $this->getSubstituteContent();
349 
350  return $content->getParserOutput( $this->getTitle(), 0, $options );
351  }
352 
360  public function getOldID() {
361  if ( $this->mOldId === null ) {
362  $this->mOldId = $this->getOldIDFromRequest();
363  }
364 
365  return $this->mOldId;
366  }
367 
373  public function getOldIDFromRequest() {
374  $this->mRedirectUrl = false;
375 
376  $request = $this->getContext()->getRequest();
377  $oldid = $request->getIntOrNull( 'oldid' );
378 
379  if ( $oldid === null ) {
380  return 0;
381  }
382 
383  if ( $oldid !== 0 ) {
384  # Load the given revision and check whether the page is another one.
385  # In that case, update this instance to reflect the change.
386  if ( $oldid === $this->mPage->getLatest() ) {
387  $this->mRevisionRecord = $this->mPage->getRevisionRecord();
388  } else {
389  $this->mRevisionRecord = $this->revisionStore->getRevisionById( $oldid );
390  if ( $this->mRevisionRecord !== null ) {
391  $revPageId = $this->mRevisionRecord->getPageId();
392  // Revision title doesn't match the page title given?
393  if ( $this->mPage->getId() != $revPageId ) {
394  $function = get_class( $this->mPage ) . '::newFromID';
395  $this->mPage = $function( $revPageId );
396  }
397  }
398  }
399  }
400 
401  $oldRev = $this->mRevisionRecord;
402  if ( $request->getVal( 'direction' ) == 'next' ) {
403  $nextid = 0;
404  if ( $oldRev ) {
405  $nextRev = $this->revisionStore->getNextRevision( $oldRev );
406  if ( $nextRev ) {
407  $nextid = $nextRev->getId();
408  }
409  }
410  if ( $nextid ) {
411  $oldid = $nextid;
412  $this->mRevisionRecord = null;
413  } else {
414  $this->mRedirectUrl = $this->getTitle()->getFullURL( 'redirect=no' );
415  }
416  } elseif ( $request->getVal( 'direction' ) == 'prev' ) {
417  $previd = 0;
418  if ( $oldRev ) {
419  $prevRev = $this->revisionStore->getPreviousRevision( $oldRev );
420  if ( $prevRev ) {
421  $previd = $prevRev->getId();
422  }
423  }
424  if ( $previd ) {
425  $oldid = $previd;
426  $this->mRevisionRecord = null;
427  }
428  }
429 
430  $this->mRevIdFetched = $this->mRevisionRecord ? $this->mRevisionRecord->getId() : 0;
431 
432  return $oldid;
433  }
434 
448  protected function fetchContentObject() {
449  if ( !$this->mContentLoaded ) {
450  $this->fetchRevisionRecord();
451  }
452 
453  return $this->mContentObject;
454  }
455 
467  public function fetchRevisionRecord() {
468  if ( $this->fetchResult ) {
469  return $this->mRevisionRecord;
470  }
471 
472  $this->mContentLoaded = true;
473  $this->mContentObject = null;
474 
475  $oldid = $this->getOldID();
476 
477  // $this->mRevisionRecord might already be fetched by getOldIDFromRequest()
478  if ( !$this->mRevisionRecord ) {
479  if ( !$oldid ) {
480  $this->mRevisionRecord = $this->mPage->getRevisionRecord();
481 
482  if ( !$this->mRevisionRecord ) {
483  wfDebug( __METHOD__ . " failed to find page data for title " .
484  $this->getTitle()->getPrefixedText() );
485 
486  // Just for sanity, output for this case is done by showMissingArticle().
487  $this->fetchResult = Status::newFatal( 'noarticletext' );
488  $this->applyContentOverride( $this->makeFetchErrorContent() );
489  return null;
490  }
491  } else {
492  $this->mRevisionRecord = $this->revisionStore->getRevisionById( $oldid );
493 
494  if ( !$this->mRevisionRecord ) {
495  wfDebug( __METHOD__ . " failed to load revision, rev_id $oldid" );
496 
497  $this->fetchResult = Status::newFatal( 'missing-revision', $oldid );
498  $this->applyContentOverride( $this->makeFetchErrorContent() );
499  return null;
500  }
501  }
502  }
503 
504  $this->mRevIdFetched = $this->mRevisionRecord->getId();
505  $this->fetchResult = Status::newGood( $this->mRevisionRecord );
506 
507  if ( !RevisionRecord::userCanBitfield(
508  $this->mRevisionRecord->getVisibility(),
509  RevisionRecord::DELETED_TEXT,
510  $this->getContext()->getUser()
511  ) ) {
512  wfDebug( __METHOD__ . " failed to retrieve content of revision " .
513  $this->mRevisionRecord->getId() );
514 
515  // Just for sanity, output for this case is done by showDeletedRevisionHeader().
516  $this->fetchResult = Status::newFatal(
517  'rev-deleted-text-permission', $this->getTitle()->getPrefixedText() );
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() {
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  $outputPage->prependHTML(
679  $outputPage->msg( 'printableversion-deprecated-warning' )->escaped()
680  )
681  );
682  } elseif ( $this->viewIsRenderAction || !$this->isCurrent() ||
683  !$this->permManager->quickUserCan( 'edit', $user, $this->getTitle() )
684  ) {
685  $poOptions['enableSectionEditLinks'] = false;
686  }
687 
688  # Try client and file cache
689  if ( $oldid === 0 && $this->mPage->checkTouched() ) {
690  # Try to stream the output from file cache
691  if ( $wgUseFileCache && $this->tryFileCache() ) {
692  wfDebug( __METHOD__ . ": done file cache" );
693  # tell wgOut that output is taken care of
694  $outputPage->disable();
695  $this->mPage->doViewUpdates( $user, $oldid );
696 
697  return;
698  }
699  }
700 
701  # Should the parser cache be used?
702  $useParserCache = $this->mPage->shouldCheckParserCache( $parserOptions, $oldid );
703  wfDebug( 'Article::view using parser cache: ' . ( $useParserCache ? 'yes' : 'no' ) );
704  if ( $user->getStubThreshold() ) {
705  MediaWikiServices::getInstance()->getStatsdDataFactory()->increment( 'pcache_miss_stub' );
706  }
707 
708  $this->showRedirectedFromHeader();
709  $this->showNamespaceHeader();
710 
711  # Iterate through the possible ways of constructing the output text.
712  # Keep going until $outputDone is set, or we run out of things to do.
713  $pass = 0;
714  $outputDone = false;
715  $this->mParserOutput = false;
716 
717  while ( !$outputDone && ++$pass ) {
718  switch ( $pass ) {
719  case 1:
720  $this->getHookRunner()->onArticleViewHeader( $this, $outputDone, $useParserCache );
721  break;
722  case 2:
723  # Early abort if the page doesn't exist
724  if ( !$this->mPage->exists() ) {
725  wfDebug( __METHOD__ . ": showing missing article" );
726  $this->showMissingArticle();
727  $this->mPage->doViewUpdates( $user );
728  return;
729  }
730 
731  # Try the parser cache
732  if ( $useParserCache ) {
733  $this->mParserOutput = $parserCache->get( $this->getPage(), $parserOptions );
734 
735  if ( $this->mParserOutput !== false ) {
736  if ( $oldid ) {
737  wfDebug( __METHOD__ . ": showing parser cache contents for current rev permalink" );
738  $this->setOldSubtitle( $oldid );
739  } else {
740  wfDebug( __METHOD__ . ": showing parser cache contents" );
741  }
742  $outputPage->addParserOutput( $this->mParserOutput, $poOptions );
743  # Ensure that UI elements requiring revision ID have
744  # the correct version information.
745  $outputPage->setRevisionId( $this->mPage->getLatest() );
746  # Preload timestamp to avoid a DB hit
747  $cachedTimestamp = $this->mParserOutput->getTimestamp();
748  if ( $cachedTimestamp !== null ) {
749  $outputPage->setRevisionTimestamp( $cachedTimestamp );
750  $this->mPage->setTimestamp( $cachedTimestamp );
751  }
752  $outputDone = true;
753  }
754  }
755  break;
756  case 3:
757  # Are we looking at an old revision
758  $rev = $this->fetchRevisionRecord();
759  if ( $oldid && $this->fetchResult->isOK() ) {
760  $this->setOldSubtitle( $oldid );
761 
762  if ( !$this->showDeletedRevisionHeader() ) {
763  wfDebug( __METHOD__ . ": cannot view deleted revision" );
764  return;
765  }
766  }
767 
768  # Ensure that UI elements requiring revision ID have
769  # the correct version information.
770  $outputPage->setRevisionId( $this->getRevIdFetched() );
771  # Preload timestamp to avoid a DB hit
772  $outputPage->setRevisionTimestamp( $this->mPage->getTimestamp() );
773 
774  # Pages containing custom CSS or JavaScript get special treatment
775  if ( $this->getTitle()->isSiteConfigPage() || $this->getTitle()->isUserConfigPage() ) {
776  $dir = $this->getContext()->getLanguage()->getDir();
777  $lang = $this->getContext()->getLanguage()->getHtmlCode();
778 
779  $outputPage->wrapWikiMsg(
780  "<div id='mw-clearyourcache' lang='$lang' dir='$dir' class='mw-content-$dir'>\n$1\n</div>",
781  'clearyourcache'
782  );
783  } elseif ( !$this->getHookRunner()->onArticleRevisionViewCustom(
784  $rev,
785  $this->getTitle(),
786  $oldid,
787  $outputPage )
788  ) {
789  // NOTE: sync with hooks called in DifferenceEngine::renderNewRevision()
790  // Allow extensions do their own custom view for certain pages
791  $outputDone = true;
792  }
793  break;
794  case 4:
795  # Run the parse, protected by a pool counter
796  wfDebug( __METHOD__ . ": doing uncached parse" );
797 
798  $rev = $this->fetchRevisionRecord();
799  $error = null;
800 
801  if ( $rev ) {
802  $poolArticleView = new PoolWorkArticleView(
803  $this->getPage(),
804  $parserOptions,
805  $this->getRevIdFetched(),
806  $useParserCache,
807  $rev,
808  // permission checking was done earlier via showDeletedRevisionHeader()
809  RevisionRecord::RAW
810  );
811  $ok = $poolArticleView->execute();
812  $error = $poolArticleView->getError();
813  $this->mParserOutput = $poolArticleView->getParserOutput() ?: null;
814 
815  # Cache stale ParserOutput object with a short expiry
816  if ( $poolArticleView->getIsDirty() ) {
817  $outputPage->setCdnMaxage( $wgCdnMaxageStale );
818  $outputPage->setLastModified( $this->mParserOutput->getCacheTime() );
819  $staleReason = $poolArticleView->getIsFastStale()
820  ? 'pool contention' : 'pool overload';
821  $outputPage->addHTML( "<!-- parser cache is expired, " .
822  "sending anyway due to $staleReason-->\n" );
823  }
824  } else {
825  $ok = false;
826  }
827 
828  if ( !$ok ) {
829  if ( $error ) {
830  $outputPage->clearHTML(); // for release() errors
831  $outputPage->enableClientCache( false );
832  $outputPage->setRobotPolicy( 'noindex,nofollow' );
833 
834  $errortext = $error->getWikiText(
835  false, 'view-pool-error', $this->getContext()->getLanguage()
836  );
837  $outputPage->wrapWikiTextAsInterface( 'errorbox', $errortext );
838  }
839  # Connection or timeout error
840  return;
841  }
842 
843  if ( $this->mParserOutput ) {
844  $outputPage->addParserOutput( $this->mParserOutput, $poOptions );
845  }
846 
847  if ( $rev && $this->getRevisionRedirectTarget( $rev ) ) {
848  $outputPage->addSubtitle( "<span id=\"redirectsub\">" .
849  $this->getContext()->msg( 'redirectpagesub' )->parse() . "</span>" );
850  }
851 
852  $outputDone = true;
853  break;
854  # Should be unreachable, but just in case...
855  default:
856  break 2;
857  }
858  }
859 
860  // Get the ParserOutput actually *displayed* here.
861  // Note that $this->mParserOutput is the *current*/oldid version output.
862  // Note that the ArticleViewHeader hook is allowed to set $outputDone to a
863  // ParserOutput instance.
864  $pOutput = ( $outputDone instanceof ParserOutput )
865  ? $outputDone // object fetched by hook
866  : ( $this->mParserOutput ?: null ); // ParserOutput or null, avoid false
867 
868  # Adjust title for main page & pages with displaytitle
869  if ( $pOutput ) {
870  $this->adjustDisplayTitle( $pOutput );
871  }
872 
873  # For the main page, overwrite the <title> element with the con-
874  # tents of 'pagetitle-view-mainpage' instead of the default (if
875  # that's not empty).
876  # This message always exists because it is in the i18n files
877  if ( $this->getTitle()->isMainPage() ) {
878  $msg = wfMessage( 'pagetitle-view-mainpage' )->inContentLanguage();
879  if ( !$msg->isDisabled() ) {
880  $outputPage->setHTMLTitle( $msg->title( $this->getTitle() )->text() );
881  }
882  }
883 
884  # Use adaptive TTLs for CDN so delayed/failed purges are noticed less often.
885  # This could use getTouched(), but that could be scary for major template edits.
886  $outputPage->adaptCdnTTL( $this->mPage->getTimestamp(), IExpiringStore::TTL_DAY );
887 
888  # Check for any __NOINDEX__ tags on the page using $pOutput
889  $policy = $this->getRobotPolicy( 'view', $pOutput ?: null );
890  $outputPage->setIndexPolicy( $policy['index'] );
891  $outputPage->setFollowPolicy( $policy['follow'] ); // FIXME: test this
892 
893  $this->showViewFooter();
894  $this->mPage->doViewUpdates( $user, $oldid ); // FIXME: test this
895 
896  # Load the postEdit module if the user just saved this revision
897  # See also EditPage::setPostEditCookie
898  $request = $this->getContext()->getRequest();
900  $postEdit = $request->getCookie( $cookieKey );
901  if ( $postEdit ) {
902  # Clear the cookie. This also prevents caching of the response.
903  $request->response()->clearCookie( $cookieKey );
904  $outputPage->addJsConfigVars( 'wgPostEdit', $postEdit );
905  $outputPage->addModules( 'mediawiki.action.view.postEdit' ); // FIXME: test this
906  }
907  }
908 
913  private function getRevisionRedirectTarget( RevisionRecord $revision ) {
914  // TODO: find a *good* place for the code that determines the redirect target for
915  // a given revision!
916  // NOTE: Use main slot content. Compare code in DerivedPageDataUpdater::revisionIsRedirect.
917  $content = $revision->getContent( SlotRecord::MAIN );
918  return $content ? $content->getRedirectTarget() : null;
919  }
920 
925  public function adjustDisplayTitle( ParserOutput $pOutput ) {
926  $out = $this->getContext()->getOutput();
927 
928  # Adjust the title if it was set by displaytitle, -{T|}- or language conversion
929  $titleText = $pOutput->getTitleText();
930  if ( strval( $titleText ) !== '' ) {
931  $out->setPageTitle( $titleText );
932  $out->setDisplayTitle( $titleText );
933  }
934  }
935 
940  protected function showDiffPage() {
941  $request = $this->getContext()->getRequest();
942  $user = $this->getContext()->getUser();
943  $diff = $request->getVal( 'diff' );
944  $rcid = $request->getVal( 'rcid' );
945  $diffOnly = $request->getBool( 'diffonly', $user->getOption( 'diffonly' ) );
946  $purge = $request->getVal( 'action' ) == 'purge';
947  $unhide = $request->getInt( 'unhide' ) == 1;
948  $oldid = $this->getOldID();
949 
950  $rev = $this->fetchRevisionRecord();
951 
952  if ( !$rev ) {
953  $this->getContext()->getOutput()->setPageTitle( wfMessage( 'errorpagetitle' ) );
954  $msg = $this->getContext()->msg( 'difference-missing-revision' )
955  ->params( $oldid )
956  ->numParams( 1 )
957  ->parseAsBlock();
958  $this->getContext()->getOutput()->addHTML( $msg );
959  return;
960  }
961 
962  $contentHandler = MediaWikiServices::getInstance()
963  ->getContentHandlerFactory()
964  ->getContentHandler(
965  $rev->getSlot( SlotRecord::MAIN, RevisionRecord::RAW )->getModel()
966  );
967  $de = $contentHandler->createDifferenceEngine(
968  $this->getContext(),
969  $oldid,
970  $diff,
971  $rcid,
972  $purge,
973  $unhide
974  );
975  $de->setSlotDiffOptions( [
976  'diff-type' => $request->getVal( 'diff-type' )
977  ] );
978 
979  // DifferenceEngine directly fetched the revision:
980  $this->mRevIdFetched = $de->getNewid();
981  $de->showDiffPage( $diffOnly );
982 
983  // Run view updates for the newer revision being diffed (and shown
984  // below the diff if not $diffOnly).
985  list( $old, $new ) = $de->mapDiffPrevNext( $oldid, $diff );
986  // New can be false, convert it to 0 - this conveniently means the latest revision
987  $this->mPage->doViewUpdates( $user, (int)$new );
988  }
989 
997  public function getRobotPolicy( $action, ParserOutput $pOutput = null ) {
999 
1000  $ns = $this->getTitle()->getNamespace();
1001 
1002  # Don't index user and user talk pages for blocked users (T13443)
1003  if ( ( $ns === NS_USER || $ns === NS_USER_TALK ) && !$this->getTitle()->isSubpage() ) {
1004  $specificTarget = null;
1005  $vagueTarget = null;
1006  $titleText = $this->getTitle()->getText();
1007  if ( IPUtils::isValid( $titleText ) ) {
1008  $vagueTarget = $titleText;
1009  } else {
1010  $specificTarget = $titleText;
1011  }
1012  if ( DatabaseBlock::newFromTarget( $specificTarget, $vagueTarget ) instanceof DatabaseBlock ) {
1013  return [
1014  'index' => 'noindex',
1015  'follow' => 'nofollow'
1016  ];
1017  }
1018  }
1019 
1020  if ( $this->mPage->getId() === 0 || $this->getOldID() ) {
1021  # Non-articles (special pages etc), and old revisions
1022  return [
1023  'index' => 'noindex',
1024  'follow' => 'nofollow'
1025  ];
1026  } elseif ( $this->getContext()->getOutput()->isPrintable() ) {
1027  # Discourage indexing of printable versions, but encourage following
1028  return [
1029  'index' => 'noindex',
1030  'follow' => 'follow'
1031  ];
1032  } elseif ( $this->getContext()->getRequest()->getInt( 'curid' ) ) {
1033  # For ?curid=x urls, disallow indexing
1034  return [
1035  'index' => 'noindex',
1036  'follow' => 'follow'
1037  ];
1038  }
1039 
1040  # Otherwise, construct the policy based on the various config variables.
1042 
1043  if ( isset( $wgNamespaceRobotPolicies[$ns] ) ) {
1044  # Honour customised robot policies for this namespace
1045  $policy = array_merge(
1046  $policy,
1047  self::formatRobotPolicy( $wgNamespaceRobotPolicies[$ns] )
1048  );
1049  }
1050  if ( $this->getTitle()->canUseNoindex() && is_object( $pOutput ) && $pOutput->getIndexPolicy() ) {
1051  # __INDEX__ and __NOINDEX__ magic words, if allowed. Incorporates
1052  # a final sanity check that we have really got the parser output.
1053  $policy = array_merge(
1054  $policy,
1055  [ 'index' => $pOutput->getIndexPolicy() ]
1056  );
1057  }
1058 
1059  if ( isset( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] ) ) {
1060  # (T16900) site config can override user-defined __INDEX__ or __NOINDEX__
1061  $policy = array_merge(
1062  $policy,
1063  self::formatRobotPolicy( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] )
1064  );
1065  }
1066 
1067  return $policy;
1068  }
1069 
1077  public static function formatRobotPolicy( $policy ) {
1078  if ( is_array( $policy ) ) {
1079  return $policy;
1080  } elseif ( !$policy ) {
1081  return [];
1082  }
1083 
1084  $policy = explode( ',', $policy );
1085  $policy = array_map( 'trim', $policy );
1086 
1087  $arr = [];
1088  foreach ( $policy as $var ) {
1089  if ( in_array( $var, [ 'index', 'noindex' ] ) ) {
1090  $arr['index'] = $var;
1091  } elseif ( in_array( $var, [ 'follow', 'nofollow' ] ) ) {
1092  $arr['follow'] = $var;
1093  }
1094  }
1095 
1096  return $arr;
1097  }
1098 
1106  public function showRedirectedFromHeader() {
1107  global $wgRedirectSources;
1108 
1109  $context = $this->getContext();
1110  $outputPage = $context->getOutput();
1111  $request = $context->getRequest();
1112  $rdfrom = $request->getVal( 'rdfrom' );
1113 
1114  // Construct a URL for the current page view, but with the target title
1115  $query = $request->getValues();
1116  unset( $query['rdfrom'] );
1117  unset( $query['title'] );
1118  if ( $this->getTitle()->isRedirect() ) {
1119  // Prevent double redirects
1120  $query['redirect'] = 'no';
1121  }
1122  $redirectTargetUrl = $this->getTitle()->getLinkURL( $query );
1123 
1124  if ( isset( $this->mRedirectedFrom ) ) {
1125  // This is an internally redirected page view.
1126  // We'll need a backlink to the source page for navigation.
1127  if ( $this->getHookRunner()->onArticleViewRedirect( $this ) ) {
1128  $redir = $this->linkRenderer->makeKnownLink(
1129  $this->mRedirectedFrom,
1130  null,
1131  [],
1132  [ 'redirect' => 'no' ]
1133  );
1134 
1135  $outputPage->addSubtitle( "<span class=\"mw-redirectedfrom\">" .
1136  $context->msg( 'redirectedfrom' )->rawParams( $redir )->parse()
1137  . "</span>" );
1138 
1139  // Add the script to update the displayed URL and
1140  // set the fragment if one was specified in the redirect
1141  $outputPage->addJsConfigVars( [
1142  'wgInternalRedirectTargetUrl' => $redirectTargetUrl,
1143  ] );
1144  $outputPage->addModules( 'mediawiki.action.view.redirect' );
1145 
1146  // Add a <link rel="canonical"> tag
1147  $outputPage->setCanonicalUrl( $this->getTitle()->getCanonicalURL() );
1148 
1149  // Tell the output object that the user arrived at this article through a redirect
1150  $outputPage->setRedirectedFrom( $this->mRedirectedFrom );
1151 
1152  return true;
1153  }
1154  } elseif ( $rdfrom ) {
1155  // This is an externally redirected view, from some other wiki.
1156  // If it was reported from a trusted site, supply a backlink.
1157  if ( $wgRedirectSources && preg_match( $wgRedirectSources, $rdfrom ) ) {
1158  $redir = Linker::makeExternalLink( $rdfrom, $rdfrom );
1159  $outputPage->addSubtitle( "<span class=\"mw-redirectedfrom\">" .
1160  $context->msg( 'redirectedfrom' )->rawParams( $redir )->parse()
1161  . "</span>" );
1162 
1163  // Add the script to update the displayed URL
1164  $outputPage->addJsConfigVars( [
1165  'wgInternalRedirectTargetUrl' => $redirectTargetUrl,
1166  ] );
1167  $outputPage->addModules( 'mediawiki.action.view.redirect' );
1168 
1169  return true;
1170  }
1171  }
1172 
1173  return false;
1174  }
1175 
1180  public function showNamespaceHeader() {
1181  if ( $this->getTitle()->isTalkPage() && !wfMessage( 'talkpageheader' )->isDisabled() ) {
1182  $this->getContext()->getOutput()->wrapWikiMsg(
1183  "<div class=\"mw-talkpageheader\">\n$1\n</div>",
1184  [ 'talkpageheader' ]
1185  );
1186  }
1187  }
1188 
1192  public function showViewFooter() {
1193  # check if we're displaying a [[User talk:x.x.x.x]] anonymous talk page
1194  if ( $this->getTitle()->getNamespace() === NS_USER_TALK
1195  && IPUtils::isValid( $this->getTitle()->getText() )
1196  ) {
1197  $this->getContext()->getOutput()->addWikiMsg( 'anontalkpagetext' );
1198  }
1199 
1200  // Show a footer allowing the user to patrol the shown revision or page if possible
1201  $patrolFooterShown = $this->showPatrolFooter();
1202 
1203  $this->getHookRunner()->onArticleViewFooter( $this, $patrolFooterShown );
1204  }
1205 
1216  public function showPatrolFooter() {
1218 
1219  // Allow hooks to decide whether to not output this at all
1220  if ( !$this->getHookRunner()->onArticleShowPatrolFooter( $this ) ) {
1221  return false;
1222  }
1223 
1224  $outputPage = $this->getContext()->getOutput();
1225  $user = $this->getContext()->getUser();
1226  $title = $this->getTitle();
1227  $rc = false;
1228 
1229  if ( !$this->permManager->quickUserCan( 'patrol', $user, $title )
1231  || ( $wgUseFilePatrol && $title->inNamespace( NS_FILE ) ) )
1232  ) {
1233  // Patrolling is disabled or the user isn't allowed to
1234  return false;
1235  }
1236 
1237  if ( $this->mRevisionRecord
1238  && !RecentChange::isInRCLifespan( $this->mRevisionRecord->getTimestamp(), 21600 )
1239  ) {
1240  // The current revision is already older than what could be in the RC table
1241  // 6h tolerance because the RC might not be cleaned out regularly
1242  return false;
1243  }
1244 
1245  // Check for cached results
1246  $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
1247  $key = $cache->makeKey( 'unpatrollable-page', $title->getArticleID() );
1248  if ( $cache->get( $key ) ) {
1249  return false;
1250  }
1251 
1252  $dbr = wfGetDB( DB_REPLICA );
1253  $oldestRevisionTimestamp = $dbr->selectField(
1254  'revision',
1255  'MIN( rev_timestamp )',
1256  [ 'rev_page' => $title->getArticleID() ],
1257  __METHOD__
1258  );
1259 
1260  // New page patrol: Get the timestamp of the oldest revison which
1261  // the revision table holds for the given page. Then we look
1262  // whether it's within the RC lifespan and if it is, we try
1263  // to get the recentchanges row belonging to that entry
1264  // (with rc_new = 1).
1265  $recentPageCreation = false;
1266  if ( $oldestRevisionTimestamp
1267  && RecentChange::isInRCLifespan( $oldestRevisionTimestamp, 21600 )
1268  ) {
1269  // 6h tolerance because the RC might not be cleaned out regularly
1270  $recentPageCreation = true;
1272  [
1273  'rc_new' => 1,
1274  'rc_timestamp' => $oldestRevisionTimestamp,
1275  'rc_namespace' => $title->getNamespace(),
1276  'rc_cur_id' => $title->getArticleID()
1277  ],
1278  __METHOD__
1279  );
1280  if ( $rc ) {
1281  // Use generic patrol message for new pages
1282  $markPatrolledMsg = wfMessage( 'markaspatrolledtext' );
1283  }
1284  }
1285 
1286  // File patrol: Get the timestamp of the latest upload for this page,
1287  // check whether it is within the RC lifespan and if it is, we try
1288  // to get the recentchanges row belonging to that entry
1289  // (with rc_type = RC_LOG, rc_log_type = upload).
1290  $recentFileUpload = false;
1291  if ( ( !$rc || $rc->getAttribute( 'rc_patrolled' ) ) && $wgUseFilePatrol
1292  && $title->getNamespace() === NS_FILE ) {
1293  // Retrieve timestamp of most recent upload
1294  $newestUploadTimestamp = $dbr->selectField(
1295  'image',
1296  'MAX( img_timestamp )',
1297  [ 'img_name' => $title->getDBkey() ],
1298  __METHOD__
1299  );
1300  if ( $newestUploadTimestamp
1301  && RecentChange::isInRCLifespan( $newestUploadTimestamp, 21600 )
1302  ) {
1303  // 6h tolerance because the RC might not be cleaned out regularly
1304  $recentFileUpload = true;
1306  [
1307  'rc_type' => RC_LOG,
1308  'rc_log_type' => 'upload',
1309  'rc_timestamp' => $newestUploadTimestamp,
1310  'rc_namespace' => NS_FILE,
1311  'rc_cur_id' => $title->getArticleID()
1312  ],
1313  __METHOD__
1314  );
1315  if ( $rc ) {
1316  // Use patrol message specific to files
1317  $markPatrolledMsg = wfMessage( 'markaspatrolledtext-file' );
1318  }
1319  }
1320  }
1321 
1322  if ( !$recentPageCreation && !$recentFileUpload ) {
1323  // Page creation and latest upload (for files) is too old to be in RC
1324 
1325  // We definitely can't patrol so cache the information
1326  // When a new file version is uploaded, the cache is cleared
1327  $cache->set( $key, '1' );
1328 
1329  return false;
1330  }
1331 
1332  if ( !$rc ) {
1333  // Don't cache: This can be hit if the page gets accessed very fast after
1334  // its creation / latest upload or in case we have high replica DB lag. In case
1335  // the revision is too old, we will already return above.
1336  return false;
1337  }
1338 
1339  if ( $rc->getAttribute( 'rc_patrolled' ) ) {
1340  // Patrolled RC entry around
1341 
1342  // Cache the information we gathered above in case we can't patrol
1343  // Don't cache in case we can patrol as this could change
1344  $cache->set( $key, '1' );
1345 
1346  return false;
1347  }
1348 
1349  if ( $rc->getPerformer()->equals( $user ) ) {
1350  // Don't show a patrol link for own creations/uploads. If the user could
1351  // patrol them, they already would be patrolled
1352  return false;
1353  }
1354 
1355  $outputPage->preventClickjacking();
1356  if ( $this->permManager->userHasRight( $user, 'writeapi' ) ) {
1357  $outputPage->addModules( 'mediawiki.misc-authed-curate' );
1358  }
1359 
1360  $link = $this->linkRenderer->makeKnownLink(
1361  $title,
1362  $markPatrolledMsg->text(),
1363  [],
1364  [
1365  'action' => 'markpatrolled',
1366  'rcid' => $rc->getAttribute( 'rc_id' ),
1367  ]
1368  );
1369 
1370  $outputPage->addHTML(
1371  "<div class='patrollink' data-mw='interface'>" .
1372  wfMessage( 'markaspatrolledlink' )->rawParams( $link )->escaped() .
1373  '</div>'
1374  );
1375 
1376  return true;
1377  }
1378 
1385  public static function purgePatrolFooterCache( $articleID ) {
1386  $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
1387  $cache->delete( $cache->makeKey( 'unpatrollable-page', $articleID ) );
1388  }
1389 
1394  public function showMissingArticle() {
1395  global $wgSend404Code;
1396 
1397  $outputPage = $this->getContext()->getOutput();
1398  // Whether the page is a root user page of an existing user (but not a subpage)
1399  $validUserPage = false;
1400 
1401  $title = $this->getTitle();
1402 
1403  $services = MediaWikiServices::getInstance();
1404 
1405  # Show info in user (talk) namespace. Does the user exist? Is he blocked?
1406  if ( $title->getNamespace() === NS_USER
1407  || $title->getNamespace() === NS_USER_TALK
1408  ) {
1409  $rootPart = explode( '/', $title->getText() )[0];
1410  $user = User::newFromName( $rootPart, false /* allow IP users */ );
1411  $ip = User::isIP( $rootPart );
1412  $block = DatabaseBlock::newFromTarget( $user, $user );
1413 
1414  if ( !( $user && $user->isLoggedIn() ) && !$ip ) { # User does not exist
1415  $outputPage->wrapWikiMsg( "<div class=\"mw-userpage-userdoesnotexist error\">\n\$1\n</div>",
1416  [ 'userpage-userdoesnotexist-view', wfEscapeWikiText( $rootPart ) ] );
1417  } elseif (
1418  $block !== null &&
1419  $block->getType() != DatabaseBlock::TYPE_AUTO &&
1420  ( $block->isSitewide() || $user->isBlockedFrom( $title ) )
1421  ) {
1422  // Show log extract if the user is sitewide blocked or is partially
1423  // blocked and not allowed to edit their user page or user talk page
1425  $outputPage,
1426  'block',
1427  $services->getNamespaceInfo()->getCanonicalName( NS_USER ) . ':' .
1428  $block->getTarget(),
1429  '',
1430  [
1431  'lim' => 1,
1432  'showIfEmpty' => false,
1433  'msgKey' => [
1434  'blocked-notice-logextract',
1435  $user->getName() # Support GENDER in notice
1436  ]
1437  ]
1438  );
1439  $validUserPage = !$title->isSubpage();
1440  } else {
1441  $validUserPage = !$title->isSubpage();
1442  }
1443  }
1444 
1445  $this->getHookRunner()->onShowMissingArticle( $this );
1446 
1447  # Show delete and move logs if there were any such events.
1448  # The logging query can DOS the site when bots/crawlers cause 404 floods,
1449  # so be careful showing this. 404 pages must be cheap as they are hard to cache.
1450  $dbCache = ObjectCache::getInstance( 'db-replicated' );
1451  $key = $dbCache->makeKey( 'page-recent-delete', md5( $title->getPrefixedText() ) );
1452  $loggedIn = $this->getContext()->getUser()->isLoggedIn();
1453  $sessionExists = $this->getContext()->getRequest()->getSession()->isPersistent();
1454  if ( $loggedIn || $dbCache->get( $key ) || $sessionExists ) {
1455  $logTypes = [ 'delete', 'move', 'protect' ];
1456 
1457  $dbr = wfGetDB( DB_REPLICA );
1458 
1459  $conds = [ 'log_action != ' . $dbr->addQuotes( 'revision' ) ];
1460  // Give extensions a chance to hide their (unrelated) log entries
1461  $this->getHookRunner()->onArticle__MissingArticleConditions( $conds, $logTypes );
1463  $outputPage,
1464  $logTypes,
1465  $title,
1466  '',
1467  [
1468  'lim' => 10,
1469  'conds' => $conds,
1470  'showIfEmpty' => false,
1471  'msgKey' => [ $loggedIn || $sessionExists
1472  ? 'moveddeleted-notice'
1473  : 'moveddeleted-notice-recent'
1474  ]
1475  ]
1476  );
1477  }
1478 
1479  if ( !$this->mPage->hasViewableContent() && $wgSend404Code && !$validUserPage ) {
1480  // If there's no backing content, send a 404 Not Found
1481  // for better machine handling of broken links.
1482  $this->getContext()->getRequest()->response()->statusHeader( 404 );
1483  }
1484 
1485  // Also apply the robot policy for nonexisting pages (even if a 404 was used for sanity)
1486  $policy = $this->getRobotPolicy( 'view' );
1487  $outputPage->setIndexPolicy( $policy['index'] );
1488  $outputPage->setFollowPolicy( $policy['follow'] );
1489 
1490  $hookResult = $this->getHookRunner()->onBeforeDisplayNoArticleText( $this );
1491 
1492  if ( !$hookResult ) {
1493  return;
1494  }
1495 
1496  # Show error message
1497  $oldid = $this->getOldID();
1498  $pm = $this->permManager;
1499  if ( !$oldid && $title->getNamespace() === NS_MEDIAWIKI && $title->hasSourceText() ) {
1500  // use fake Content object for system message
1501  $parserOptions = ParserOptions::newCanonical( 'canonical' );
1502  $outputPage->addParserOutput( $this->getEmptyPageParserOutput( $parserOptions ) );
1503  } else {
1504  if ( $oldid ) {
1505  $text = wfMessage( 'missing-revision', $oldid )->plain();
1506  } elseif ( $pm->quickUserCan( 'create', $this->getContext()->getUser(), $title ) &&
1507  $pm->quickUserCan( 'edit', $this->getContext()->getUser(), $title )
1508  ) {
1509  $message = $this->getContext()->getUser()->isLoggedIn() ? 'noarticletext' : 'noarticletextanon';
1510  $text = wfMessage( $message )->plain();
1511  } else {
1512  $text = wfMessage( 'noarticletext-nopermission' )->plain();
1513  }
1514 
1515  $dir = $this->getContext()->getLanguage()->getDir();
1516  $lang = $this->getContext()->getLanguage()->getHtmlCode();
1517  $outputPage->addWikiTextAsInterface( Xml::openElement( 'div', [
1518  'class' => "noarticletext mw-content-$dir",
1519  'dir' => $dir,
1520  'lang' => $lang,
1521  ] ) . "\n$text\n</div>" );
1522  }
1523  }
1524 
1531  public function showDeletedRevisionHeader() {
1532  if ( !$this->mRevisionRecord->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
1533  // Not deleted
1534  return true;
1535  }
1536 
1537  $outputPage = $this->getContext()->getOutput();
1538  $user = $this->getContext()->getUser();
1539  $titleText = $this->getTitle()->getPrefixedText();
1540  // If the user is not allowed to see it...
1541  if ( !RevisionRecord::userCanBitfield(
1542  $this->mRevisionRecord->getVisibility(),
1543  RevisionRecord::DELETED_TEXT,
1544  $user
1545  ) ) {
1546  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1547  [ 'rev-deleted-text-permission', $titleText ] );
1548 
1549  return false;
1550  // If the user needs to confirm that they want to see it...
1551  } elseif ( $this->getContext()->getRequest()->getInt( 'unhide' ) != 1 ) {
1552  # Give explanation and add a link to view the revision...
1553  $oldid = intval( $this->getOldID() );
1554  $link = $this->getTitle()->getFullURL( "oldid={$oldid}&unhide=1" );
1555  $msg = $this->mRevisionRecord->isDeleted( RevisionRecord::DELETED_RESTRICTED ) ?
1556  'rev-suppressed-text-unhide' : 'rev-deleted-text-unhide';
1557  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n", [ $msg, $link ] );
1558 
1559  return false;
1560  // We are allowed to see...
1561  } else {
1562  $msg = $this->mRevisionRecord->isDeleted( RevisionRecord::DELETED_RESTRICTED )
1563  ? [ 'rev-suppressed-text-view', $titleText ]
1564  : [ 'rev-deleted-text-view', $titleText ];
1565  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n", $msg );
1566 
1567  return true;
1568  }
1569  }
1570 
1579  public function setOldSubtitle( $oldid = 0 ) {
1580  if ( !$this->getHookRunner()->onDisplayOldSubtitle( $this, $oldid ) ) {
1581  return;
1582  }
1583 
1584  $context = $this->getContext();
1585  $unhide = $context->getRequest()->getInt( 'unhide' ) == 1;
1586 
1587  # Cascade unhide param in links for easy deletion browsing
1588  $extraParams = [];
1589  if ( $unhide ) {
1590  $extraParams['unhide'] = 1;
1591  }
1592 
1593  if ( $this->mRevisionRecord && $this->mRevisionRecord->getId() === $oldid ) {
1594  $revisionRecord = $this->mRevisionRecord;
1595  } else {
1596  $revisionRecord = $this->revisionStore->getRevisionById( $oldid );
1597  }
1598 
1599  $timestamp = $revisionRecord->getTimestamp();
1600 
1601  $current = ( $oldid == $this->mPage->getLatest() );
1602  $language = $context->getLanguage();
1603  $user = $context->getUser();
1604 
1605  $td = $language->userTimeAndDate( $timestamp, $user );
1606  $tddate = $language->userDate( $timestamp, $user );
1607  $tdtime = $language->userTime( $timestamp, $user );
1608 
1609  # Show user links if allowed to see them. If hidden, then show them only if requested...
1610  $userlinks = Linker::revUserTools( $revisionRecord, !$unhide );
1611 
1612  $infomsg = $current && !$context->msg( 'revision-info-current' )->isDisabled()
1613  ? 'revision-info-current'
1614  : 'revision-info';
1615 
1616  $outputPage = $context->getOutput();
1617  $revisionUser = $revisionRecord->getUser();
1618  $revisionInfo = "<div id=\"mw-{$infomsg}\">" .
1619  $context->msg( $infomsg, $td )
1620  ->rawParams( $userlinks )
1621  ->params(
1622  $revisionRecord->getId(),
1623  $tddate,
1624  $tdtime,
1625  $revisionUser ? $revisionUser->getName() : ''
1626  )
1627  ->rawParams( Linker::revComment(
1628  $revisionRecord,
1629  true,
1630  true
1631  ) )
1632  ->parse() .
1633  "</div>";
1634 
1635  $lnk = $current
1636  ? $context->msg( 'currentrevisionlink' )->escaped()
1637  : $this->linkRenderer->makeKnownLink(
1638  $this->getTitle(),
1639  $context->msg( 'currentrevisionlink' )->text(),
1640  [],
1641  $extraParams
1642  );
1643  $curdiff = $current
1644  ? $context->msg( 'diff' )->escaped()
1645  : $this->linkRenderer->makeKnownLink(
1646  $this->getTitle(),
1647  $context->msg( 'diff' )->text(),
1648  [],
1649  [
1650  'diff' => 'cur',
1651  'oldid' => $oldid
1652  ] + $extraParams
1653  );
1654  $prevExist = (bool)$this->revisionStore->getPreviousRevision( $revisionRecord );
1655  $prevlink = $prevExist
1656  ? $this->linkRenderer->makeKnownLink(
1657  $this->getTitle(),
1658  $context->msg( 'previousrevision' )->text(),
1659  [],
1660  [
1661  'direction' => 'prev',
1662  'oldid' => $oldid
1663  ] + $extraParams
1664  )
1665  : $context->msg( 'previousrevision' )->escaped();
1666  $prevdiff = $prevExist
1667  ? $this->linkRenderer->makeKnownLink(
1668  $this->getTitle(),
1669  $context->msg( 'diff' )->text(),
1670  [],
1671  [
1672  'diff' => 'prev',
1673  'oldid' => $oldid
1674  ] + $extraParams
1675  )
1676  : $context->msg( 'diff' )->escaped();
1677  $nextlink = $current
1678  ? $context->msg( 'nextrevision' )->escaped()
1679  : $this->linkRenderer->makeKnownLink(
1680  $this->getTitle(),
1681  $context->msg( 'nextrevision' )->text(),
1682  [],
1683  [
1684  'direction' => 'next',
1685  'oldid' => $oldid
1686  ] + $extraParams
1687  );
1688  $nextdiff = $current
1689  ? $context->msg( 'diff' )->escaped()
1690  : $this->linkRenderer->makeKnownLink(
1691  $this->getTitle(),
1692  $context->msg( 'diff' )->text(),
1693  [],
1694  [
1695  'diff' => 'next',
1696  'oldid' => $oldid
1697  ] + $extraParams
1698  );
1699 
1700  $cdel = Linker::getRevDeleteLink(
1701  $user,
1702  $revisionRecord,
1703  $this->getTitle()
1704  );
1705  if ( $cdel !== '' ) {
1706  $cdel .= ' ';
1707  }
1708 
1709  // the outer div is need for styling the revision info and nav in MobileFrontend
1710  $outputPage->addSubtitle( "<div class=\"mw-revision warningbox\">" . $revisionInfo .
1711  "<div id=\"mw-revision-nav\">" . $cdel .
1712  $context->msg( 'revision-nav' )->rawParams(
1713  $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff
1714  )->escaped() . "</div></div>" );
1715  }
1716 
1730  public function viewRedirect( $target, $appendSubtitle = true, $forceKnown = false ) {
1731  $lang = $this->getTitle()->getPageLanguage();
1732  $out = $this->getContext()->getOutput();
1733  if ( $appendSubtitle ) {
1734  $out->addSubtitle( wfMessage( 'redirectpagesub' ) );
1735  }
1736  $out->addModuleStyles( 'mediawiki.action.view.redirectPage' );
1737  return static::getRedirectHeaderHtml( $lang, $target, $forceKnown );
1738  }
1739 
1752  public static function getRedirectHeaderHtml( Language $lang, $target, $forceKnown = false ) {
1753  if ( !is_array( $target ) ) {
1754  $target = [ $target ];
1755  }
1756 
1757  $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
1758 
1759  $html = '<ul class="redirectText">';
1761  foreach ( $target as $title ) {
1762  if ( $forceKnown ) {
1763  $link = $linkRenderer->makeKnownLink(
1764  $title,
1765  $title->getFullText(),
1766  [],
1767  // Make sure wiki page redirects are not followed
1768  $title->isRedirect() ? [ 'redirect' => 'no' ] : []
1769  );
1770  } else {
1771  $link = $linkRenderer->makeLink(
1772  $title,
1773  $title->getFullText(),
1774  [],
1775  // Make sure wiki page redirects are not followed
1776  $title->isRedirect() ? [ 'redirect' => 'no' ] : []
1777  );
1778  }
1779  $html .= '<li>' . $link . '</li>';
1780  }
1781  $html .= '</ul>';
1782 
1783  $redirectToText = wfMessage( 'redirectto' )->inLanguage( $lang )->escaped();
1784 
1785  return '<div class="redirectMsg">' .
1786  '<p>' . $redirectToText . '</p>' .
1787  $html .
1788  '</div>';
1789  }
1790 
1799  public function addHelpLink( $to, $overrideBaseUrl = false ) {
1800  $msg = wfMessage(
1801  'namespace-' . $this->getTitle()->getNamespace() . '-helppage'
1802  );
1803 
1804  $out = $this->getContext()->getOutput();
1805  if ( !$msg->isDisabled() ) {
1806  $helpUrl = Skin::makeUrl( $msg->plain() );
1807  $out->addHelpLink( $helpUrl, true );
1808  } else {
1809  $out->addHelpLink( $to, $overrideBaseUrl );
1810  }
1811  }
1812 
1816  public function render() {
1817  $this->getContext()->getRequest()->response()->header( 'X-Robots-Tag: noindex' );
1818  $this->getContext()->getOutput()->setArticleBodyOnly( true );
1819  // We later set 'enableSectionEditLinks=false' based on this; also used by ImagePage
1820  $this->viewIsRenderAction = true;
1821  $this->view();
1822  }
1823 
1827  public function protect() {
1828  $form = new ProtectionForm( $this );
1829  $form->execute();
1830  }
1831 
1835  public function unprotect() {
1836  $this->protect();
1837  }
1838 
1842  public function delete() {
1843  # This code desperately needs to be totally rewritten
1844 
1845  $title = $this->getTitle();
1846  $context = $this->getContext();
1847  $user = $context->getUser();
1848  $request = $context->getRequest();
1849 
1850  # Check permissions
1851  $permissionErrors = $this->permManager->getPermissionErrors( 'delete', $user, $title );
1852  if ( count( $permissionErrors ) ) {
1853  throw new PermissionsError( 'delete', $permissionErrors );
1854  }
1855 
1856  # Read-only check...
1857  if ( wfReadOnly() ) {
1858  throw new ReadOnlyError;
1859  }
1860 
1861  # Better double-check that it hasn't been deleted yet!
1862  $this->mPage->loadPageData(
1863  $request->wasPosted() ? WikiPage::READ_LATEST : WikiPage::READ_NORMAL
1864  );
1865  if ( !$this->mPage->exists() ) {
1866  $deleteLogPage = new LogPage( 'delete' );
1867  $outputPage = $context->getOutput();
1868  $outputPage->setPageTitle( $context->msg( 'cannotdelete-title', $title->getPrefixedText() ) );
1869  $outputPage->wrapWikiMsg( "<div class=\"error mw-error-cannotdelete\">\n$1\n</div>",
1870  [ 'cannotdelete', wfEscapeWikiText( $title->getPrefixedText() ) ]
1871  );
1872  $outputPage->addHTML(
1873  Xml::element( 'h2', null, $deleteLogPage->getName()->text() )
1874  );
1876  $outputPage,
1877  'delete',
1878  $title
1879  );
1880 
1881  return;
1882  }
1883 
1884  $deleteReasonList = $request->getText( 'wpDeleteReasonList', 'other' );
1885  $deleteReason = $request->getText( 'wpReason' );
1886 
1887  if ( $deleteReasonList == 'other' ) {
1888  $reason = $deleteReason;
1889  } elseif ( $deleteReason != '' ) {
1890  // Entry from drop down menu + additional comment
1891  $colonseparator = wfMessage( 'colon-separator' )->inContentLanguage()->text();
1892  $reason = $deleteReasonList . $colonseparator . $deleteReason;
1893  } else {
1894  $reason = $deleteReasonList;
1895  }
1896 
1897  if ( $request->wasPosted() && $user->matchEditToken( $request->getVal( 'wpEditToken' ),
1898  [ 'delete', $this->getTitle()->getPrefixedText() ] )
1899  ) {
1900  # Flag to hide all contents of the archived revisions
1901 
1902  $suppress = $request->getCheck( 'wpSuppress' ) &&
1903  $this->permManager->userHasRight( $user, 'suppressrevision' );
1904 
1905  $this->doDelete( $reason, $suppress );
1906 
1907  WatchAction::doWatchOrUnwatch( $request->getCheck( 'wpWatch' ), $title, $user );
1908 
1909  return;
1910  }
1911 
1912  // Generate deletion reason
1913  $hasHistory = false;
1914  if ( !$reason ) {
1915  try {
1916  $reason = $this->getPage()
1917  ->getAutoDeleteReason( $hasHistory );
1918  } catch ( Exception $e ) {
1919  # if a page is horribly broken, we still want to be able to
1920  # delete it. So be lenient about errors here.
1921  wfDebug( "Error while building auto delete summary: $e" );
1922  $reason = '';
1923  }
1924  }
1925 
1926  // If the page has a history, insert a warning
1927  if ( $hasHistory ) {
1928  $title = $this->getTitle();
1929 
1930  // The following can use the real revision count as this is only being shown for users
1931  // that can delete this page.
1932  // This, as a side-effect, also makes sure that the following query isn't being run for
1933  // pages with a larger history, unless the user has the 'bigdelete' right
1934  // (and is about to delete this page).
1935  $dbr = wfGetDB( DB_REPLICA );
1936  $revisions = $edits = (int)$dbr->selectField(
1937  'revision',
1938  'COUNT(rev_page)',
1939  [ 'rev_page' => $title->getArticleID() ],
1940  __METHOD__
1941  );
1942 
1943  // @todo i18n issue/patchwork message
1944  $context->getOutput()->addHTML(
1945  '<strong class="mw-delete-warning-revisions">' .
1946  $context->msg( 'historywarning' )->numParams( $revisions )->parse() .
1947  $context->msg( 'word-separator' )->escaped() . $this->linkRenderer->makeKnownLink(
1948  $title,
1949  $context->msg( 'history' )->text(),
1950  [],
1951  [ 'action' => 'history' ] ) .
1952  '</strong>'
1953  );
1954 
1955  if ( $title->isBigDeletion() ) {
1956  global $wgDeleteRevisionsLimit;
1957  $context->getOutput()->wrapWikiMsg( "<div class='error'>\n$1\n</div>\n",
1958  [
1959  'delete-warning-toobig',
1960  $context->getLanguage()->formatNum( $wgDeleteRevisionsLimit )
1961  ]
1962  );
1963  }
1964  }
1965 
1966  $this->confirmDelete( $reason );
1967  }
1968 
1974  public function confirmDelete( $reason ) {
1975  wfDebug( "Article::confirmDelete" );
1976 
1977  $title = $this->getTitle();
1978  $ctx = $this->getContext();
1979  $outputPage = $ctx->getOutput();
1980  $outputPage->setPageTitle( wfMessage( 'delete-confirm', $title->getPrefixedText() ) );
1981  $outputPage->addBacklinkSubtitle( $title );
1982  $outputPage->setRobotPolicy( 'noindex,nofollow' );
1983  $outputPage->addModules( 'mediawiki.action.delete' );
1984 
1985  $backlinkCache = $title->getBacklinkCache();
1986  if ( $backlinkCache->hasLinks( 'pagelinks' ) || $backlinkCache->hasLinks( 'templatelinks' ) ) {
1987  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1988  'deleting-backlinks-warning' );
1989  }
1990 
1991  $subpageQueryLimit = 51;
1992  $subpages = $title->getSubpages( $subpageQueryLimit );
1993  $subpageCount = count( $subpages );
1994  if ( $subpageCount > 0 ) {
1995  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1996  [ 'deleting-subpages-warning', Message::numParam( $subpageCount ) ] );
1997  }
1998  $outputPage->addWikiMsg( 'confirmdeletetext' );
1999 
2000  $this->getHookRunner()->onArticleConfirmDelete( $this, $outputPage, $reason );
2001 
2002  $user = $this->getContext()->getUser();
2003  $checkWatch = $user->getBoolOption( 'watchdeletion' ) || $user->isWatched( $title );
2004 
2005  $outputPage->enableOOUI();
2006 
2007  $fields = [];
2008 
2009  $suppressAllowed = $this->permManager->userHasRight( $user, 'suppressrevision' );
2010  $dropDownReason = $ctx->msg( 'deletereason-dropdown' )->inContentLanguage()->text();
2011  // Add additional specific reasons for suppress
2012  if ( $suppressAllowed ) {
2013  $dropDownReason .= "\n" . $ctx->msg( 'deletereason-dropdown-suppress' )
2014  ->inContentLanguage()->text();
2015  }
2016 
2017  $options = Xml::listDropDownOptions(
2018  $dropDownReason,
2019  [ 'other' => $ctx->msg( 'deletereasonotherlist' )->inContentLanguage()->text() ]
2020  );
2021  $options = Xml::listDropDownOptionsOoui( $options );
2022 
2023  $fields[] = new OOUI\FieldLayout(
2024  new OOUI\DropdownInputWidget( [
2025  'name' => 'wpDeleteReasonList',
2026  'inputId' => 'wpDeleteReasonList',
2027  'tabIndex' => 1,
2028  'infusable' => true,
2029  'value' => '',
2030  'options' => $options
2031  ] ),
2032  [
2033  'label' => $ctx->msg( 'deletecomment' )->text(),
2034  'align' => 'top',
2035  ]
2036  );
2037 
2038  // HTML maxlength uses "UTF-16 code units", which means that characters outside BMP
2039  // (e.g. emojis) count for two each. This limit is overridden in JS to instead count
2040  // Unicode codepoints.
2041  $fields[] = new OOUI\FieldLayout(
2042  new OOUI\TextInputWidget( [
2043  'name' => 'wpReason',
2044  'inputId' => 'wpReason',
2045  'tabIndex' => 2,
2047  'infusable' => true,
2048  'value' => $reason,
2049  'autofocus' => true,
2050  ] ),
2051  [
2052  'label' => $ctx->msg( 'deleteotherreason' )->text(),
2053  'align' => 'top',
2054  ]
2055  );
2056 
2057  if ( $user->isLoggedIn() ) {
2058  $fields[] = new OOUI\FieldLayout(
2059  new OOUI\CheckboxInputWidget( [
2060  'name' => 'wpWatch',
2061  'inputId' => 'wpWatch',
2062  'tabIndex' => 3,
2063  'selected' => $checkWatch,
2064  ] ),
2065  [
2066  'label' => $ctx->msg( 'watchthis' )->text(),
2067  'align' => 'inline',
2068  'infusable' => true,
2069  ]
2070  );
2071  }
2072  if ( $suppressAllowed ) {
2073  $fields[] = new OOUI\FieldLayout(
2074  new OOUI\CheckboxInputWidget( [
2075  'name' => 'wpSuppress',
2076  'inputId' => 'wpSuppress',
2077  'tabIndex' => 4,
2078  ] ),
2079  [
2080  'label' => $ctx->msg( 'revdelete-suppress' )->text(),
2081  'align' => 'inline',
2082  'infusable' => true,
2083  ]
2084  );
2085  }
2086 
2087  $fields[] = new OOUI\FieldLayout(
2088  new OOUI\ButtonInputWidget( [
2089  'name' => 'wpConfirmB',
2090  'inputId' => 'wpConfirmB',
2091  'tabIndex' => 5,
2092  'value' => $ctx->msg( 'deletepage' )->text(),
2093  'label' => $ctx->msg( 'deletepage' )->text(),
2094  'flags' => [ 'primary', 'destructive' ],
2095  'type' => 'submit',
2096  ] ),
2097  [
2098  'align' => 'top',
2099  ]
2100  );
2101 
2102  $fieldset = new OOUI\FieldsetLayout( [
2103  'label' => $ctx->msg( 'delete-legend' )->text(),
2104  'id' => 'mw-delete-table',
2105  'items' => $fields,
2106  ] );
2107 
2108  $form = new OOUI\FormLayout( [
2109  'method' => 'post',
2110  'action' => $title->getLocalURL( 'action=delete' ),
2111  'id' => 'deleteconfirm',
2112  ] );
2113  $form->appendContent(
2114  $fieldset,
2115  new OOUI\HtmlSnippet(
2116  Html::hidden( 'wpEditToken', $user->getEditToken( [ 'delete', $title->getPrefixedText() ] ) )
2117  )
2118  );
2119 
2120  $outputPage->addHTML(
2121  new OOUI\PanelLayout( [
2122  'classes' => [ 'deletepage-wrapper' ],
2123  'expanded' => false,
2124  'padded' => true,
2125  'framed' => true,
2126  'content' => $form,
2127  ] )
2128  );
2129 
2130  if ( $this->permManager->userHasRight( $user, 'editinterface' ) ) {
2131  $link = '';
2132  if ( $suppressAllowed ) {
2133  $link .= $this->linkRenderer->makeKnownLink(
2134  $ctx->msg( 'deletereason-dropdown-suppress' )->inContentLanguage()->getTitle(),
2135  $ctx->msg( 'delete-edit-reasonlist-suppress' )->text(),
2136  [],
2137  [ 'action' => 'edit' ]
2138  );
2139  $link .= $ctx->msg( 'pipe-separator' )->escaped();
2140  }
2141  $link .= $this->linkRenderer->makeKnownLink(
2142  $ctx->msg( 'deletereason-dropdown' )->inContentLanguage()->getTitle(),
2143  $ctx->msg( 'delete-edit-reasonlist' )->text(),
2144  [],
2145  [ 'action' => 'edit' ]
2146  );
2147  $outputPage->addHTML( '<p class="mw-delete-editreasons">' . $link . '</p>' );
2148  }
2149 
2150  $deleteLogPage = new LogPage( 'delete' );
2151  $outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) );
2152  LogEventsList::showLogExtract( $outputPage, 'delete', $title );
2153  }
2154 
2163  public function doDelete( $reason, $suppress = false, $immediate = false ) {
2164  $error = '';
2165  $context = $this->getContext();
2166  $outputPage = $context->getOutput();
2167  $user = $context->getUser();
2168  $status = $this->mPage->doDeleteArticleReal(
2169  $reason, $user, $suppress, null, $error,
2170  null, [], 'delete', $immediate
2171  );
2172 
2173  if ( $status->isOK() ) {
2174  $deleted = $this->getTitle()->getPrefixedText();
2175 
2176  $outputPage->setPageTitle( wfMessage( 'actioncomplete' ) );
2177  $outputPage->setRobotPolicy( 'noindex,nofollow' );
2178 
2179  if ( $status->isGood() ) {
2180  $loglink = '[[Special:Log/delete|' . wfMessage( 'deletionlog' )->text() . ']]';
2181  $outputPage->addWikiMsg( 'deletedtext', wfEscapeWikiText( $deleted ), $loglink );
2182  $this->getHookRunner()->onArticleDeleteAfterSuccess( $this->getTitle(), $outputPage );
2183  } else {
2184  $outputPage->addWikiMsg( 'delete-scheduled', wfEscapeWikiText( $deleted ) );
2185  }
2186 
2187  $outputPage->returnToMain( false );
2188  } else {
2189  $outputPage->setPageTitle(
2190  wfMessage( 'cannotdelete-title',
2191  $this->getTitle()->getPrefixedText() )
2192  );
2193 
2194  if ( $error == '' ) {
2195  $outputPage->wrapWikiTextAsInterface(
2196  'error mw-error-cannotdelete',
2197  $status->getWikiText( false, false, $context->getLanguage() )
2198  );
2199  $deleteLogPage = new LogPage( 'delete' );
2200  $outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) );
2201 
2203  $outputPage,
2204  'delete',
2205  $this->getTitle()
2206  );
2207  } else {
2208  $outputPage->addHTML( $error );
2209  }
2210  }
2211  }
2212 
2213  /* Caching functions */
2214 
2222  protected function tryFileCache() {
2223  static $called = false;
2224 
2225  if ( $called ) {
2226  wfDebug( "Article::tryFileCache(): called twice!?" );
2227  return false;
2228  }
2229 
2230  $called = true;
2231  if ( $this->isFileCacheable() ) {
2232  $cache = new HTMLFileCache( $this->getTitle(), 'view' );
2233  if ( $cache->isCacheGood( $this->mPage->getTouched() ) ) {
2234  wfDebug( "Article::tryFileCache(): about to load file" );
2235  $cache->loadFromFileCache( $this->getContext() );
2236  return true;
2237  } else {
2238  wfDebug( "Article::tryFileCache(): starting buffer" );
2239  ob_start( [ &$cache, 'saveToFileCache' ] );
2240  }
2241  } else {
2242  wfDebug( "Article::tryFileCache(): not cacheable" );
2243  }
2244 
2245  return false;
2246  }
2247 
2253  public function isFileCacheable( $mode = HTMLFileCache::MODE_NORMAL ) {
2254  $cacheable = false;
2255 
2256  if ( HTMLFileCache::useFileCache( $this->getContext(), $mode ) ) {
2257  $cacheable = $this->mPage->getId()
2258  && !$this->mRedirectedFrom && !$this->getTitle()->isRedirect();
2259  // Extension may have reason to disable file caching on some pages.
2260  if ( $cacheable ) {
2261  $cacheable = $this->getHookRunner()->onIsFileCacheable( $this );
2262  }
2263  }
2264 
2265  return $cacheable;
2266  }
2267 
2281  public function getParserOutput( $oldid = null, User $user = null ) {
2282  // XXX: bypasses mParserOptions and thus setParserOptions()
2283 
2284  if ( $user === null ) {
2285  $parserOptions = $this->getParserOptions();
2286  } else {
2287  $parserOptions = $this->mPage->makeParserOptions( $user );
2288  }
2289 
2290  return $this->mPage->getParserOutput( $parserOptions, $oldid );
2291  }
2292 
2299  public function setParserOptions( ParserOptions $options ) {
2300  if ( $this->mParserOptions ) {
2301  throw new MWException( "can't change parser options after they have already been set" );
2302  }
2303 
2304  // clone, so if $options is modified later, it doesn't confuse the parser cache.
2305  $this->mParserOptions = clone $options;
2306  }
2307 
2312  public function getParserOptions() {
2313  if ( !$this->mParserOptions ) {
2314  $this->mParserOptions = $this->mPage->makeParserOptions( $this->getContext() );
2315  }
2316  // Clone to allow modifications of the return value without affecting cache
2317  return clone $this->mParserOptions;
2318  }
2319 
2326  public function setContext( $context ) {
2327  $this->mContext = $context;
2328  }
2329 
2336  public function getContext() {
2337  if ( $this->mContext instanceof IContextSource ) {
2338  return $this->mContext;
2339  } else {
2340  wfDebug( __METHOD__ . " called and \$mContext is null. " .
2341  "Return RequestContext::getMain(); for sanity" );
2342  return RequestContext::getMain();
2343  }
2344  }
2345 
2355  public function __get( $fname ) {
2356  wfDeprecatedMsg( "Accessing Article::\$$fname is deprecated since MediaWiki 1.35",
2357  '1.35' );
2358 
2359  if ( $fname === 'mRevision' ) {
2360  $record = $this->fetchRevisionRecord(); // Ensure that it is loaded
2361  return $record ? new Revision( $record ) : null;
2362  }
2363 
2364  if ( property_exists( $this->mPage, $fname ) ) {
2365  return $this->mPage->$fname;
2366  }
2367  trigger_error( 'Inaccessible property via __get(): ' . $fname, E_USER_NOTICE );
2368  }
2369 
2379  public function __set( $fname, $fvalue ) {
2380  wfDeprecatedMsg( "Setting Article::\$$fname is deprecated since MediaWiki 1.35",
2381  '1.35' );
2382 
2383  if ( $fname === 'mRevision' ) {
2384  $this->mRevisionRecord = $fvalue ?
2385  $fvalue->getRevisionRecord() :
2386  null;
2387  return;
2388  }
2389 
2390  if ( property_exists( $this->mPage, $fname ) ) {
2391  $this->mPage->$fname = $fvalue;
2392  // Note: extensions may want to toss on new fields
2393  } elseif ( !in_array( $fname, [ 'mContext', 'mPage' ] ) ) {
2394  $this->mPage->$fname = $fvalue;
2395  } else {
2396  trigger_error( 'Inaccessible property via __set(): ' . $fname, E_USER_NOTICE );
2397  }
2398  }
2399 
2411  public function checkFlags( $flags ) {
2412  wfDeprecated( __METHOD__, '1.35' );
2413  return $this->mPage->checkFlags( $flags );
2414  }
2415 
2421  public function checkTouched() {
2422  wfDeprecated( __METHOD__, '1.35' );
2423  return $this->mPage->checkTouched();
2424  }
2425 
2431  public function clearPreparedEdit() {
2432  wfDeprecated( __METHOD__, '1.35' );
2433  $this->mPage->clearPreparedEdit();
2434  }
2435 
2450  public function doDeleteArticleReal(
2451  $reason, $suppress = false, $u1 = null, $u2 = null, &$error = '', User $user = null,
2452  $tags = [], $immediate = false
2453  ) {
2454  wfDeprecated( __METHOD__, '1.35' );
2455  return $this->mPage->doDeleteArticleReal(
2456  $reason, $suppress, $u1, $u2, $error, $user, $tags, 'delete', $immediate
2457  );
2458  }
2459 
2468  public function doDeleteUpdates(
2469  $id,
2470  Content $content = null,
2471  $revision = null,
2472  User $user = null
2473  ) {
2474  wfDeprecated( __METHOD__, '1.35' );
2475  $this->mPage->doDeleteUpdates( $id, $content, $revision, $user );
2476  }
2477 
2487  public function doEditUpdates( Revision $revision, User $user, array $options = [] ) {
2488  wfDeprecated( __METHOD__, '1.35' );
2489  $this->mPage->doEditUpdates( $revision, $user, $options );
2490  }
2491 
2499  public function doPurge() {
2500  wfDeprecated( __METHOD__, '1.35' );
2501  return $this->mPage->doPurge();
2502  }
2503 
2510  public function doViewUpdates( User $user, $oldid = 0 ) {
2511  wfDeprecated( __METHOD__, '1.35' );
2512  $this->mPage->doViewUpdates( $user, $oldid );
2513  }
2514 
2520  public function exists() {
2521  wfDeprecated( __METHOD__, '1.35' );
2522  return $this->mPage->exists();
2523  }
2524 
2530  public function followRedirect() {
2531  wfDeprecated( __METHOD__, '1.35' );
2532  return $this->mPage->followRedirect();
2533  }
2534 
2540  public function getActionOverrides() {
2541  return $this->mPage->getActionOverrides();
2542  }
2543 
2550  public function getAutoDeleteReason( &$hasHistory ) {
2551  wfDeprecated( __METHOD__, '1.35' );
2552  return $this->mPage->getAutoDeleteReason( $hasHistory );
2553  }
2554 
2560  public function getCategories() {
2561  wfDeprecated( __METHOD__, '1.35' );
2562  return $this->mPage->getCategories();
2563  }
2564 
2573  public function getComment( $audience = RevisionRecord::FOR_PUBLIC, User $user = null ) {
2574  wfDeprecated( __METHOD__, '1.35' );
2575  return $this->mPage->getComment( $audience, $user );
2576  }
2577 
2583  public function getContentHandler() {
2584  wfDeprecated( __METHOD__, '1.35' );
2585  return $this->mPage->getContentHandler();
2586  }
2587 
2593  public function getContentModel() {
2594  wfDeprecated( __METHOD__, '1.35' );
2595  return $this->mPage->getContentModel();
2596  }
2597 
2603  public function getContributors() {
2604  wfDeprecated( __METHOD__, '1.35' );
2605  return $this->mPage->getContributors();
2606  }
2607 
2616  public function getCreator( $audience = RevisionRecord::FOR_PUBLIC, User $user = null ) {
2617  wfDeprecated( __METHOD__, '1.35' );
2618  return $this->mPage->getCreator( $audience, $user );
2619  }
2620 
2627  public function getDeletionUpdates( Content $content = null ) {
2628  wfDeprecated( __METHOD__, '1.35' );
2629  return $this->mPage->getDeletionUpdates( $content );
2630  }
2631 
2637  public function getHiddenCategories() {
2638  wfDeprecated( __METHOD__, '1.35' );
2639  return $this->mPage->getHiddenCategories();
2640  }
2641 
2647  public function getId() {
2648  wfDeprecated( __METHOD__, '1.35' );
2649  return $this->mPage->getId();
2650  }
2651 
2657  public function getLatest() {
2658  wfDeprecated( __METHOD__, '1.35' );
2659  return $this->mPage->getLatest();
2660  }
2661 
2667  public function getLinksTimestamp() {
2668  wfDeprecated( __METHOD__, '1.35' );
2669  return $this->mPage->getLinksTimestamp();
2670  }
2671 
2677  public function getMinorEdit() {
2678  wfDeprecated( __METHOD__, '1.35' );
2679  return $this->mPage->getMinorEdit();
2680  }
2681 
2686  public function getOldestRevision() {
2687  wfDeprecated( __METHOD__, '1.35' );
2688  return $this->mPage->getOldestRevision();
2689  }
2690 
2696  public function getRedirectTarget() {
2697  wfDeprecated( __METHOD__, '1.35' );
2698  return $this->mPage->getRedirectTarget();
2699  }
2700 
2707  public function getRedirectURL( $rt ) {
2708  wfDeprecated( __METHOD__, '1.35' );
2709  return $this->mPage->getRedirectURL( $rt );
2710  }
2711 
2718  public function getRevision() {
2719  wfDeprecated( __METHOD__, '1.35' );
2720  return $this->mPage->getRevision();
2721  }
2722 
2728  public function getTimestamp() {
2729  wfDeprecated( __METHOD__, '1.35' );
2730  return $this->mPage->getTimestamp();
2731  }
2732 
2738  public function getTouched() {
2739  wfDeprecated( __METHOD__, '1.35' );
2740  return $this->mPage->getTouched();
2741  }
2742 
2751  public function getUndoContent( Revision $undo, Revision $undoafter = null ) {
2752  wfDeprecated( __METHOD__, '1.35' );
2753  return $this->mPage->getUndoContent( $undo, $undoafter );
2754  }
2755 
2763  public function getUser( $audience = RevisionRecord::FOR_PUBLIC, User $user = null ) {
2764  wfDeprecated( __METHOD__, '1.35' );
2765  return $this->mPage->getUser( $audience, $user );
2766  }
2767 
2775  public function getUserText( $audience = RevisionRecord::FOR_PUBLIC, User $user = null ) {
2776  wfDeprecated( __METHOD__, '1.35' );
2777  return $this->mPage->getUserText( $audience, $user );
2778  }
2779 
2785  public function hasViewableContent() {
2786  wfDeprecated( __METHOD__, '1.35' );
2787  return $this->mPage->hasViewableContent();
2788  }
2789 
2797  public function insertOn( $dbw, $pageId = null ) {
2798  wfDeprecated( __METHOD__, '1.35' );
2799  return $this->mPage->insertOn( $dbw, $pageId );
2800  }
2801 
2813  public function insertProtectNullRevision( $revCommentMsg, array $limit,
2814  array $expiry, $cascade, $reason, $user = null
2815  ) {
2816  wfDeprecated( __METHOD__, '1.35' );
2817  return $this->mPage->insertProtectNullRevision( $revCommentMsg, $limit,
2818  $expiry, $cascade, $reason, $user
2819  );
2820  }
2821 
2827  public function insertRedirect() {
2828  wfDeprecated( __METHOD__, '1.35' );
2829  return $this->mPage->insertRedirect();
2830  }
2831 
2839  public function insertRedirectEntry( Title $rt, $oldLatest = null ) {
2840  wfDeprecated( __METHOD__, '1.35' );
2841  return $this->mPage->insertRedirectEntry( $rt, $oldLatest );
2842  }
2843 
2850  public function isCountable( $editInfo = false ) {
2851  wfDeprecated( __METHOD__, '1.35' );
2852  return $this->mPage->isCountable( $editInfo );
2853  }
2854 
2860  public function isRedirect() {
2861  wfDeprecated( __METHOD__, '1.35' );
2862  return $this->mPage->isRedirect();
2863  }
2864 
2871  public function loadFromRow( $data, $from ) {
2872  wfDeprecated( __METHOD__, '1.35' );
2873  $this->mPage->loadFromRow( $data, $from );
2874  }
2875 
2881  public function loadPageData( $from = 'fromdb' ) {
2882  wfDeprecated( __METHOD__, '1.35' );
2883  $this->mPage->loadPageData( $from );
2884  }
2885 
2891  public function lockAndGetLatest() {
2892  wfDeprecated( __METHOD__, '1.35' );
2893  return $this->mPage->lockAndGetLatest();
2894  }
2895 
2902  public function makeParserOptions( $context ) {
2903  wfDeprecated( __METHOD__, '1.35' );
2904  return $this->mPage->makeParserOptions( $context );
2905  }
2906 
2915  public function pageDataFromId( $dbr, $id, $options = [] ) {
2916  wfDeprecated( __METHOD__, '1.35' );
2917  return $this->mPage->pageDataFromId( $dbr, $id, $options );
2918  }
2919 
2928  public function pageDataFromTitle( $dbr, $title, $options = [] ) {
2929  wfDeprecated( __METHOD__, '1.35' );
2930  return $this->mPage->pageDataFromTitle( $dbr, $title, $options );
2931  }
2932 
2946  public function prepareContentForEdit(
2947  Content $content, $revision = null, User $user = null,
2948  $serialFormat = null, $useCache = true
2949  ) {
2950  wfDeprecated( __METHOD__, '1.35' );
2951  return $this->mPage->prepareContentForEdit(
2952  $content, $revision, $user,
2953  $serialFormat, $useCache
2954  );
2955  }
2956 
2964  public function protectDescription( array $limit, array $expiry ) {
2965  wfDeprecated( __METHOD__, '1.35' );
2966  return $this->mPage->protectDescription( $limit, $expiry );
2967  }
2968 
2976  public function protectDescriptionLog( array $limit, array $expiry ) {
2977  wfDeprecated( __METHOD__, '1.35' );
2978  return $this->mPage->protectDescriptionLog( $limit, $expiry );
2979  }
2980 
2990  public function replaceSectionAtRev( $sectionId, Content $sectionContent,
2991  $sectionTitle = '', $baseRevId = null
2992  ) {
2993  wfDeprecated( __METHOD__, '1.35' );
2994  return $this->mPage->replaceSectionAtRev( $sectionId, $sectionContent,
2995  $sectionTitle, $baseRevId
2996  );
2997  }
2998 
3010  public function replaceSectionContent(
3011  $sectionId, Content $sectionContent, $sectionTitle = '', $edittime = null
3012  ) {
3013  wfDeprecated( __METHOD__, '1.35' );
3014  return $this->mPage->replaceSectionContent(
3015  $sectionId, $sectionContent, $sectionTitle, $edittime
3016  );
3017  }
3018 
3024  public function setTimestamp( $ts ) {
3025  wfDeprecated( __METHOD__, '1.35' );
3026  $this->mPage->setTimestamp( $ts );
3027  }
3028 
3036  public function shouldCheckParserCache( ParserOptions $parserOptions, $oldId ) {
3037  wfDeprecated( __METHOD__, '1.35' );
3038  return $this->mPage->shouldCheckParserCache( $parserOptions, $oldId );
3039  }
3040 
3046  public function supportsSections() {
3047  wfDeprecated( __METHOD__, '1.35' );
3048  return $this->mPage->supportsSections();
3049  }
3050 
3056  public function triggerOpportunisticLinksUpdate( ParserOutput $parserOutput ) {
3057  wfDeprecated( __METHOD__, '1.35' );
3058  $this->mPage->triggerOpportunisticLinksUpdate( $parserOutput );
3059  }
3060 
3068  public function updateCategoryCounts( array $added, array $deleted, $id = 0 ) {
3069  wfDeprecated( __METHOD__, '1.35' );
3070  $this->mPage->updateCategoryCounts( $added, $deleted, $id );
3071  }
3072 
3081  public function updateIfNewerOn( $dbw, $revision ) {
3082  wfDeprecated( __METHOD__, '1.35' );
3083  return $this->mPage->updateIfNewerOn( $dbw, $revision );
3084  }
3085 
3094  public function updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect = null ) {
3095  wfDeprecated( __METHOD__, '1.35' );
3096  return $this->mPage->updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect );
3097  }
3098 
3108  public function updateRevisionOn( $dbw, $revision, $lastRevision = null,
3109  $lastRevIsRedirect = null
3110  ) {
3111  wfDeprecated( __METHOD__, '1.35' );
3112  return $this->mPage->updateRevisionOn( $dbw, $revision, $lastRevision,
3113  $lastRevIsRedirect
3114  );
3115  }
3116 
3126  public function doUpdateRestrictions( array $limit, array $expiry, &$cascade,
3127  $reason, User $user
3128  ) {
3129  wfDeprecated( __METHOD__, '1.35' );
3130  return $this->mPage->doUpdateRestrictions( $limit, $expiry, $cascade, $reason, $user );
3131  }
3132 
3141  public function updateRestrictions( $limit = [], $reason = '',
3142  &$cascade = 0, $expiry = []
3143  ) {
3144  wfDeprecated( __METHOD__, '1.35' );
3145  return $this->mPage->doUpdateRestrictions(
3146  $limit,
3147  $expiry,
3148  $cascade,
3149  $reason,
3150  $this->getContext()->getUser()
3151  );
3152  }
3153 
3166  public function doDeleteArticle(
3167  $reason, $suppress = false, $u1 = null, $u2 = null, &$error = '', $immediate = false
3168  ) {
3169  wfDeprecated( __METHOD__, '1.35' );
3170  return $this->mPage->doDeleteArticle( $reason, $suppress, $u1, $u2, $error,
3171  null, $immediate );
3172  }
3173 
3184  public function doRollback(
3185  $fromP,
3186  $summary,
3187  $token,
3188  $bot,
3189  &$resultDetails,
3190  User $user = null
3191  ) {
3192  wfDeprecated( __METHOD__, '1.35' );
3193  if ( !$user ) {
3194  $user = $this->getContext()->getUser();
3195  }
3196 
3197  return $this->mPage->doRollback( $fromP, $summary, $token, $bot, $resultDetails, $user );
3198  }
3199 
3210  public function commitRollback( $fromP, $summary, $bot, &$resultDetails, User $guser = null ) {
3211  wfDeprecated( __METHOD__, '1.35' );
3212  if ( !$guser ) {
3213  $guser = $this->getContext()->getUser();
3214  }
3215 
3216  return $this->mPage->commitRollback( $fromP, $summary, $bot, $resultDetails, $guser );
3217  }
3218 
3225  public function generateReason( &$hasHistory ) {
3226  wfDeprecated( __METHOD__, '1.35' );
3227  return $this->getPage()->getAutoDeleteReason( $hasHistory );
3228  }
3229 }
ReadOnlyError
Show an error when the wiki is locked/read-only and the user tries to do something that requires writ...
Definition: ReadOnlyError.php:29
$wgSend404Code
$wgSend404Code
Some web hosts attempt to rewrite all responses with a 404 (not found) status code,...
Definition: DefaultSettings.php:3660
Article\showMissingArticle
showMissingArticle()
Show the error text for a missing article.
Definition: Article.php:1394
Article\checkFlags
checkFlags( $flags)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2411
$wgCdnMaxageStale
$wgCdnMaxageStale
Cache timeout when delivering a stale ParserCache response due to PoolCounter contention.
Definition: DefaultSettings.php:2935
ParserOptions
Set options of the Parser.
Definition: ParserOptions.php:44
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:1048
Page
Interface for type hinting (accepts WikiPage, Article, ImagePage, CategoryPage)
Definition: Page.php:30
Article\isRedirect
isRedirect()
Definition: Article.php:2860
Article\getCategories
getCategories()
Definition: Article.php:2560
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:2450
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:1077
StatusValue\newFatal
static newFatal( $message,... $parameters)
Factory function for fatal errors.
Definition: StatusValue.php:70
Article\$viewIsRenderAction
bool $viewIsRenderAction
Whether render() was called.
Definition: Article.php:125
Article\getRedirectTarget
getRedirectTarget()
Definition: Article.php:2696
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:2593
Article\getLinksTimestamp
getLinksTimestamp()
Definition: Article.php:2667
Article\getOldIDFromRequest
getOldIDFromRequest()
Sets $this->mRedirectUrl to a correct URL if the query parameters are incorrect.
Definition: Article.php:373
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:154
Article\showPatrolFooter
showPatrolFooter()
If patrol is possible, output a patrol UI box.
Definition: Article.php:1216
$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:220
Article\getComment
getComment( $audience=RevisionRecord::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2573
Article\tryFileCache
tryFileCache()
checkLastModified returns true if it has taken care of all output to the client that is necessary for...
Definition: Article.php:2222
Article\getContentHandler
getContentHandler()
Definition: Article.php:2583
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:2431
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:81
Article\lockAndGetLatest
lockAndGetLatest()
Definition: Article.php:2891
MediaWiki\Linker\LinkRenderer
Class that generates HTML links for pages.
Definition: LinkRenderer.php:41
Article\supportsSections
supportsSections()
Definition: Article.php:3046
Article\getOldestRevision
getOldestRevision()
Definition: Article.php:2686
Article\getDeletionUpdates
getDeletionUpdates(Content $content=null)
Definition: Article.php:2627
Article\checkTouched
checkTouched()
Definition: Article.php:2421
Article\getRevision
getRevision()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2718
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
Revision\RevisionRecord\getTimestamp
getTimestamp()
MCR migration note: this replaces Revision::getTimestamp.
Definition: RevisionRecord.php:442
WikiPage
Class representing a MediaWiki article and history.
Definition: WikiPage.php:52
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:1605
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:2195
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:1125
User\newFromName
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition: User.php:538
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1219
Article\showDiffPage
showDiffPage()
Show a diff page according to current request variables.
Definition: Article.php:940
$wgArticleRobotPolicies
$wgArticleRobotPolicies
Robot policies per article.
Definition: DefaultSettings.php:8487
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:31
Article\confirmDelete
confirmDelete( $reason)
Output deletion confirmation dialog.
Definition: Article.php:1974
Article\updateCategoryCounts
updateCategoryCounts(array $added, array $deleted, $id=0)
Definition: Article.php:3068
Html\warningBox
static warningBox( $html, $className='')
Return a warning box.
Definition: Html.php:726
Article\doDeleteArticle
doDeleteArticle( $reason, $suppress=false, $u1=null, $u2=null, &$error='', $immediate=false)
Definition: Article.php:3166
Article\adjustDisplayTitle
adjustDisplayTitle(ParserOutput $pOutput)
Adjust title for pages with displaytitle, -{T|}- or language conversion.
Definition: Article.php:925
Article\showNamespaceHeader
showNamespaceHeader()
Show a header specific to the namespace currently being viewed, like [[MediaWiki:Talkpagetext]].
Definition: Article.php:1180
Article\shouldCheckParserCache
shouldCheckParserCache(ParserOptions $parserOptions, $oldId)
Definition: Article.php:3036
$wgUseRCPatrol
$wgUseRCPatrol
Use RC Patrolling to check for vandalism (from recent changes and watchlists) New pages and new files...
Definition: DefaultSettings.php:7333
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:7349
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:2163
Article\protectDescriptionLog
protectDescriptionLog(array $limit, array $expiry)
Definition: Article.php:2976
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:913
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:44
Article\getSubstituteContent
getSubstituteContent()
Returns Content object to use when the page does not exist.
Definition: Article.php:320
Revision
Definition: Revision.php:40
Article\unprotect
unprotect()
action=unprotect handler (alias)
Definition: Article.php:1835
Article\insertRedirect
insertRedirect()
Definition: Article.php:2827
MediaWiki\Block\DatabaseBlock
A DatabaseBlock (unlike a SystemBlock) is stored in the database, may give rise to autoblocks and may...
Definition: DatabaseBlock.php:52
Article\getRedirectedFrom
getRedirectedFrom()
Get the page this view was redirected from.
Definition: Article.php:237
Article\fetchContentObject
fetchContentObject()
Get text content object Does NOT follow redirects.
Definition: Article.php:448
Article\clear
clear()
Clear the object.
Definition: Article.php:272
Article\updateRedirectOn
updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect=null)
Definition: Article.php:3094
Article\newFromWikiPage
static newFromWikiPage(WikiPage $page, IContextSource $context)
Create an Article object of the appropriate class for the given page.
Definition: Article.php:226
wfDeprecatedMsg
wfDeprecatedMsg( $msg, $version=false, $component=false, $callerOffset=2)
Log a deprecation warning with arbitrary message text.
Definition: GlobalFunctions.php:1058
Article\getTouched
getTouched()
Definition: Article.php:2738
MWException
MediaWiki exception.
Definition: MWException.php:29
Article\getUserText
getUserText( $audience=RevisionRecord::FOR_PUBLIC, User $user=null)
Definition: Article.php:2775
Article\getTitle
getTitle()
Get the title object of the article.
Definition: Article.php:255
Article\updateRevisionOn
updateRevisionOn( $dbw, $revision, $lastRevision=null, $lastRevIsRedirect=null)
Definition: Article.php:3108
Article\render
render()
Handle action=render.
Definition: Article.php:1816
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that $function is deprecated.
Definition: GlobalFunctions.php:1026
Article\getCreator
getCreator( $audience=RevisionRecord::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2616
Article\$permManager
PermissionManager $permManager
Definition: Article.php:135
Article\insertProtectNullRevision
insertProtectNullRevision( $revCommentMsg, array $limit, array $expiry, $cascade, $reason, $user=null)
Definition: Article.php:2813
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:1799
Article\exists
exists()
Definition: Article.php:2520
Article\setRedirectedFrom
setRedirectedFrom(Title $from)
Tell the page view functions that this view was redirected from another page on the wiki.
Definition: Article.php:246
$wgUseFileCache
$wgUseFileCache
This will cache static pages for non-logged-in users to reduce database traffic on public sites.
Definition: DefaultSettings.php:2790
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2467
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)
Definition: Article.php:159
Article\showDeletedRevisionHeader
showDeletedRevisionHeader()
If the revision requested for view is deleted, check permissions.
Definition: Article.php:1531
Article\isFileCacheable
isFileCacheable( $mode=HTMLFileCache::MODE_NORMAL)
Check if the page can be cached.
Definition: Article.php:2253
Article\doRollback
doRollback( $fromP, $summary, $token, $bot, &$resultDetails, User $user=null)
Definition: Article.php:3184
WikiPage\getTitle
getTitle()
Get the title object of the article.
Definition: WikiPage.php:281
LogPage
Class to simplify the use of log pages.
Definition: LogPage.php:37
Article\setOldSubtitle
setOldSubtitle( $oldid=0)
Generate the navigation links when browsing through an article revisions It shows the information as:...
Definition: Article.php:1579
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:265
Article\getContext
getContext()
Gets the context this Article is executed in.
Definition: Article.php:2336
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
Article\pageDataFromId
pageDataFromId( $dbr, $id, $options=[])
Definition: Article.php:2915
$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:997
Article\insertOn
insertOn( $dbw, $pageId=null)
Definition: Article.php:2797
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:592
Article\getRedirectURL
getRedirectURL( $rt)
Definition: Article.php:2707
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:182
Article\newPage
newPage(Title $title)
Definition: Article.php:173
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:2751
Article\prepareContentForEdit
prepareContentForEdit(Content $content, $revision=null, User $user=null, $serialFormat=null, $useCache=true)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2946
$wgDeleteRevisionsLimit
$wgDeleteRevisionsLimit
Optional to restrict deletion of pages with higher revision counts to users with the 'bigdelete' perm...
Definition: DefaultSettings.php:5956
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:7360
Article\getLatest
getLatest()
Definition: Article.php:2657
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:909
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:142
Article\getAutoDeleteReason
getAutoDeleteReason(&$hasHistory)
Definition: Article.php:2550
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:2299
Article\isCountable
isCountable( $editInfo=false)
Definition: Article.php:2850
Revision\RevisionRecord\getId
getId()
Get revision ID.
Definition: RevisionRecord.php:279
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:571
Article\doPurge
doPurge()
Definition: Article.php:2499
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:2785
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:2603
StatusValue\newGood
static newGood( $value=null)
Factory function for good results.
Definition: StatusValue.php:82
Revision\MutableRevisionRecord
Definition: MutableRevisionRecord.php:45
HTMLFileCache\MODE_NORMAL
const MODE_NORMAL
Definition: HTMLFileCache.php:34
Article\loadFromRow
loadFromRow( $data, $from)
Definition: Article.php:2871
Article\getEmptyPageParserOutput
getEmptyPageParserOutput(ParserOptions $options)
Returns ParserOutput to use when a page does not exist.
Definition: Article.php:347
Article\protect
protect()
action=protect handler
Definition: Article.php:1827
Hooks\runner
static runner()
Get a HookRunner instance for calling hooks using the new interfaces.
Definition: Hooks.php:172
Article\getTimestamp
getTimestamp()
Definition: Article.php:2728
ParserOptions\newCanonical
static newCanonical( $context=null, $userLang=null)
Creates a "canonical" ParserOptions object.
Definition: ParserOptions.php:1135
wfEscapeWikiText
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
Definition: GlobalFunctions.php:1487
Article\getMinorEdit
getMinorEdit()
Definition: Article.php:2677
Article\commitRollback
commitRollback( $fromP, $summary, $bot, &$resultDetails, User $guser=null)
Definition: Article.php:3210
$wgNamespaceRobotPolicies
$wgNamespaceRobotPolicies
Robot policies per namespaces.
Definition: DefaultSettings.php:8459
Article\getParserOutput
getParserOutput( $oldid=null, User $user=null)
#-
Definition: Article.php:2281
Article\getContentObject
getContentObject()
Returns a Content object representing the pages effective display content, not necessarily the revisi...
Definition: Article.php:304
Article\followRedirect
followRedirect()
Definition: Article.php:2530
Article\getHiddenCategories
getHiddenCategories()
Definition: Article.php:2637
RequestContext\getMain
static getMain()
Get the RequestContext object associated with the main request.
Definition: RequestContext.php:453
WatchAction\doWatchOrUnwatch
static doWatchOrUnwatch( $watch, Title $title, User $user, string $expiry=null)
Watch or unwatch a page.
Definition: WatchAction.php:172
Article\setTimestamp
setTimestamp( $ts)
Definition: Article.php:3024
IContextSource
Interface for objects which can provide a MediaWiki context on request.
Definition: IContextSource.php:55
Article\__get
__get( $fname)
Definition: Article.php:2355
Content
Base interface for content objects.
Definition: Content.php:35
CommentStore\COMMENT_CHARACTER_LIMIT
const COMMENT_CHARACTER_LIMIT
Maximum length of a comment in UTF-8 characters.
Definition: CommentStore.php:48
Article\replaceSectionAtRev
replaceSectionAtRev( $sectionId, Content $sectionContent, $sectionTitle='', $baseRevId=null)
Definition: Article.php:2990
Article\fetchRevisionRecord
fetchRevisionRecord()
Fetches the revision to work on.
Definition: Article.php:467
Title
Represents a title within MediaWiki.
Definition: Title.php:42
Skin\makeUrl
static makeUrl( $name, $urlaction='')
Definition: Skin.php:1249
Article\getUser
getUser( $audience=RevisionRecord::FOR_PUBLIC, User $user=null)
Definition: Article.php:2763
$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:3010
MediaWiki\Linker\LinkRenderer\makeKnownLink
makeKnownLink(LinkTarget $target, $text=null, array $extraAttribs=[], array $query=[])
Definition: LinkRenderer.php:225
Article\getRevIdFetched
getRevIdFetched()
Use this to fetch the rev ID used on page views.
Definition: Article.php: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:2902
Article\showRedirectedFromHeader
showRedirectedFromHeader()
If this request is a redirect view, send "redirected from" subtitle to the output.
Definition: Article.php:1106
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:1178
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:2647
Article\getActionOverrides
getActionOverrides()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2540
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:3126
Article\updateRestrictions
updateRestrictions( $limit=[], $reason='', &$cascade=0, $expiry=[])
Definition: Article.php:3141
Article\protectDescription
protectDescription(array $limit, array $expiry)
Definition: Article.php:2964
Article\getParserOptions
getParserOptions()
Get parser options suitable for rendering the primary article wikitext.
Definition: Article.php:2312
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:4413
Article\updateIfNewerOn
updateIfNewerOn( $dbw, $revision)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:3081
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:2468
Article\pageDataFromTitle
pageDataFromTitle( $dbr, $title, $options=[])
Definition: Article.php:2928
Article\triggerOpportunisticLinksUpdate
triggerOpportunisticLinksUpdate(ParserOutput $parserOutput)
Definition: Article.php:3056
Article\getOldID
getOldID()
Definition: Article.php:360
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:80
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:2326
Title\newFromID
static newFromID( $id, $flags=0)
Create a new Title from an article ID.
Definition: Title.php:473
Article\insertRedirectEntry
insertRedirectEntry(Title $rt, $oldLatest=null)
Definition: Article.php:2839
MediaWiki\Linker\LinkRenderer\makeLink
makeLink(LinkTarget $target, $text=null, array $extraAttribs=[], array $query=[])
Definition: LinkRenderer.php:160
$wgDefaultRobotPolicy
$wgDefaultRobotPolicy
Default robot policy.
Definition: DefaultSettings.php:8443
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:1730
Article\loadPageData
loadPageData( $from='fromdb')
Definition: Article.php:2881
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:2487
Article\$mRevisionRecord
RevisionRecord null $mRevisionRecord
Revision to be shown.
Definition: Article.php:153
Article\__set
__set( $fname, $fvalue)
Definition: Article.php:2379
Article\showViewFooter
showViewFooter()
Show the footer section of an ordinary page view.
Definition: Article.php:1192
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:1752
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:194
Article\generateReason
generateReason(&$hasHistory)
Definition: Article.php:3225
Article\purgePatrolFooterCache
static purgePatrolFooterCache( $articleID)
Purge the cache used to check if it is worth showing the patrol footer For example,...
Definition: Article.php:1385
Article\doViewUpdates
doViewUpdates(User $user, $oldid=0)
Definition: Article.php:2510
Article\$fetchResult
Status null $fetchResult
represents the outcome of fetchRevisionRecord().
Definition: Article.php:111