MediaWiki  1.28.0
Article.php
Go to the documentation of this file.
1 <?php
34 class Article implements Page {
36  protected $mContext;
37 
39  protected $mPage;
40 
43 
48  public $mContent;
49 
55 
57  public $mContentLoaded = false;
58 
60  public $mOldId;
61 
63  public $mRedirectedFrom = null;
64 
66  public $mRedirectUrl = false;
67 
69  public $mRevIdFetched = 0;
70 
72  public $mRevision = null;
73 
76 
82  public function __construct( Title $title, $oldId = null ) {
83  $this->mOldId = $oldId;
84  $this->mPage = $this->newPage( $title );
85  }
86 
91  protected function newPage( Title $title ) {
92  return new WikiPage( $title );
93  }
94 
100  public static function newFromID( $id ) {
101  $t = Title::newFromID( $id );
102  return $t == null ? null : new static( $t );
103  }
104 
112  public static function newFromTitle( $title, IContextSource $context ) {
113  if ( NS_MEDIA == $title->getNamespace() ) {
114  // FIXME: where should this go?
115  $title = Title::makeTitle( NS_FILE, $title->getDBkey() );
116  }
117 
118  $page = null;
119  Hooks::run( 'ArticleFromTitle', [ &$title, &$page, $context ] );
120  if ( !$page ) {
121  switch ( $title->getNamespace() ) {
122  case NS_FILE:
123  $page = new ImagePage( $title );
124  break;
125  case NS_CATEGORY:
126  $page = new CategoryPage( $title );
127  break;
128  default:
129  $page = new Article( $title );
130  }
131  }
132  $page->setContext( $context );
133 
134  return $page;
135  }
136 
144  public static function newFromWikiPage( WikiPage $page, IContextSource $context ) {
145  $article = self::newFromTitle( $page->getTitle(), $context );
146  $article->mPage = $page; // override to keep process cached vars
147  return $article;
148  }
149 
155  public function getRedirectedFrom() {
156  return $this->mRedirectedFrom;
157  }
158 
164  public function setRedirectedFrom( Title $from ) {
165  $this->mRedirectedFrom = $from;
166  }
167 
173  public function getTitle() {
174  return $this->mPage->getTitle();
175  }
176 
183  public function getPage() {
184  return $this->mPage;
185  }
186 
190  public function clear() {
191  $this->mContentLoaded = false;
192 
193  $this->mRedirectedFrom = null; # Title object if set
194  $this->mRevIdFetched = 0;
195  $this->mRedirectUrl = false;
196 
197  $this->mPage->clear();
198  }
199 
212  public function getContent() {
213  wfDeprecated( __METHOD__, '1.21' );
214  $content = $this->getContentObject();
216  }
217 
233  protected function getContentObject() {
234 
235  if ( $this->mPage->getId() === 0 ) {
236  # If this is a MediaWiki:x message, then load the messages
237  # and return the message value for x.
238  if ( $this->getTitle()->getNamespace() == NS_MEDIAWIKI ) {
239  $text = $this->getTitle()->getDefaultMessageText();
240  if ( $text === false ) {
241  $text = '';
242  }
243 
244  $content = ContentHandler::makeContent( $text, $this->getTitle() );
245  } else {
246  $message = $this->getContext()->getUser()->isLoggedIn() ? 'noarticletext' : 'noarticletextanon';
247  $content = new MessageContent( $message, null, 'parsemag' );
248  }
249  } else {
250  $this->fetchContentObject();
252  }
253 
254  return $content;
255  }
256 
260  public function getOldID() {
261  if ( is_null( $this->mOldId ) ) {
262  $this->mOldId = $this->getOldIDFromRequest();
263  }
264 
265  return $this->mOldId;
266  }
267 
273  public function getOldIDFromRequest() {
274  $this->mRedirectUrl = false;
275 
276  $request = $this->getContext()->getRequest();
277  $oldid = $request->getIntOrNull( 'oldid' );
278 
279  if ( $oldid === null ) {
280  return 0;
281  }
282 
283  if ( $oldid !== 0 ) {
284  # Load the given revision and check whether the page is another one.
285  # In that case, update this instance to reflect the change.
286  if ( $oldid === $this->mPage->getLatest() ) {
287  $this->mRevision = $this->mPage->getRevision();
288  } else {
289  $this->mRevision = Revision::newFromId( $oldid );
290  if ( $this->mRevision !== null ) {
291  // Revision title doesn't match the page title given?
292  if ( $this->mPage->getId() != $this->mRevision->getPage() ) {
293  $function = [ get_class( $this->mPage ), 'newFromID' ];
294  $this->mPage = call_user_func( $function, $this->mRevision->getPage() );
295  }
296  }
297  }
298  }
299 
300  if ( $request->getVal( 'direction' ) == 'next' ) {
301  $nextid = $this->getTitle()->getNextRevisionID( $oldid );
302  if ( $nextid ) {
303  $oldid = $nextid;
304  $this->mRevision = null;
305  } else {
306  $this->mRedirectUrl = $this->getTitle()->getFullURL( 'redirect=no' );
307  }
308  } elseif ( $request->getVal( 'direction' ) == 'prev' ) {
309  $previd = $this->getTitle()->getPreviousRevisionID( $oldid );
310  if ( $previd ) {
311  $oldid = $previd;
312  $this->mRevision = null;
313  }
314  }
315 
316  return $oldid;
317  }
318 
333  function fetchContent() {
334  // BC cruft!
335 
336  wfDeprecated( __METHOD__, '1.21' );
337 
338  if ( $this->mContentLoaded && $this->mContent ) {
339  return $this->mContent;
340  }
341 
342  $content = $this->fetchContentObject();
343 
344  if ( !$content ) {
345  return false;
346  }
347 
348  // @todo Get rid of mContent everywhere!
349  $this->mContent = ContentHandler::getContentText( $content );
351  'ArticleAfterFetchContent',
352  [ &$this, &$this->mContent ],
353  '1.21'
354  );
355 
356  return $this->mContent;
357  }
358 
371  protected function fetchContentObject() {
372  if ( $this->mContentLoaded ) {
373  return $this->mContentObject;
374  }
375 
376  $this->mContentLoaded = true;
377  $this->mContent = null;
378 
379  $oldid = $this->getOldID();
380 
381  # Pre-fill content with error message so that if something
382  # fails we'll have something telling us what we intended.
383  // XXX: this isn't page content but a UI message. horrible.
384  $this->mContentObject = new MessageContent( 'missing-revision', [ $oldid ] );
385 
386  if ( $oldid ) {
387  # $this->mRevision might already be fetched by getOldIDFromRequest()
388  if ( !$this->mRevision ) {
389  $this->mRevision = Revision::newFromId( $oldid );
390  if ( !$this->mRevision ) {
391  wfDebug( __METHOD__ . " failed to retrieve specified revision, id $oldid\n" );
392  return false;
393  }
394  }
395  } else {
396  $oldid = $this->mPage->getLatest();
397  if ( !$oldid ) {
398  wfDebug( __METHOD__ . " failed to find page data for title " .
399  $this->getTitle()->getPrefixedText() . "\n" );
400  return false;
401  }
402 
403  # Update error message with correct oldid
404  $this->mContentObject = new MessageContent( 'missing-revision', [ $oldid ] );
405 
406  $this->mRevision = $this->mPage->getRevision();
407 
408  if ( !$this->mRevision ) {
409  wfDebug( __METHOD__ . " failed to retrieve current page, rev_id $oldid\n" );
410  return false;
411  }
412  }
413 
414  // @todo FIXME: Horrible, horrible! This content-loading interface just plain sucks.
415  // We should instead work with the Revision object when we need it...
416  // Loads if user is allowed
417  $content = $this->mRevision->getContent(
419  $this->getContext()->getUser()
420  );
421 
422  if ( !$content ) {
423  wfDebug( __METHOD__ . " failed to retrieve content of revision " .
424  $this->mRevision->getId() . "\n" );
425  return false;
426  }
427 
428  $this->mContentObject = $content;
429  $this->mRevIdFetched = $this->mRevision->getId();
430 
432  'ArticleAfterFetchContentObject',
433  [ &$this, &$this->mContentObject ],
434  '1.21'
435  );
436 
437  return $this->mContentObject;
438  }
439 
445  public function isCurrent() {
446  # If no oldid, this is the current version.
447  if ( $this->getOldID() == 0 ) {
448  return true;
449  }
450 
451  return $this->mPage->exists() && $this->mRevision && $this->mRevision->isCurrent();
452  }
453 
461  public function getRevisionFetched() {
462  $this->fetchContentObject();
463 
464  return $this->mRevision;
465  }
466 
472  public function getRevIdFetched() {
473  if ( $this->mRevIdFetched ) {
474  return $this->mRevIdFetched;
475  } else {
476  return $this->mPage->getLatest();
477  }
478  }
479 
484  public function view() {
485  global $wgUseFileCache, $wgDebugToolbar;
486 
487  # Get variables from query string
488  # As side effect this will load the revision and update the title
489  # in a revision ID is passed in the request, so this should remain
490  # the first call of this method even if $oldid is used way below.
491  $oldid = $this->getOldID();
492 
493  $user = $this->getContext()->getUser();
494  # Another whitelist check in case getOldID() is altering the title
495  $permErrors = $this->getTitle()->getUserPermissionsErrors( 'read', $user );
496  if ( count( $permErrors ) ) {
497  wfDebug( __METHOD__ . ": denied on secondary read check\n" );
498  throw new PermissionsError( 'read', $permErrors );
499  }
500 
501  $outputPage = $this->getContext()->getOutput();
502  # getOldID() may as well want us to redirect somewhere else
503  if ( $this->mRedirectUrl ) {
504  $outputPage->redirect( $this->mRedirectUrl );
505  wfDebug( __METHOD__ . ": redirecting due to oldid\n" );
506 
507  return;
508  }
509 
510  # If we got diff in the query, we want to see a diff page instead of the article.
511  if ( $this->getContext()->getRequest()->getCheck( 'diff' ) ) {
512  wfDebug( __METHOD__ . ": showing diff page\n" );
513  $this->showDiffPage();
514 
515  return;
516  }
517 
518  # Set page title (may be overridden by DISPLAYTITLE)
519  $outputPage->setPageTitle( $this->getTitle()->getPrefixedText() );
520 
521  $outputPage->setArticleFlag( true );
522  # Allow frames by default
523  $outputPage->allowClickjacking();
524 
525  $parserCache = ParserCache::singleton();
526 
527  $parserOptions = $this->getParserOptions();
528  # Render printable version, use printable version cache
529  if ( $outputPage->isPrintable() ) {
530  $parserOptions->setIsPrintable( true );
531  $parserOptions->setEditSection( false );
532  } elseif ( !$this->isCurrent() || !$this->getTitle()->quickUserCan( 'edit', $user ) ) {
533  $parserOptions->setEditSection( false );
534  }
535 
536  # Try client and file cache
537  if ( !$wgDebugToolbar && $oldid === 0 && $this->mPage->checkTouched() ) {
538  # Try to stream the output from file cache
539  if ( $wgUseFileCache && $this->tryFileCache() ) {
540  wfDebug( __METHOD__ . ": done file cache\n" );
541  # tell wgOut that output is taken care of
542  $outputPage->disable();
543  $this->mPage->doViewUpdates( $user, $oldid );
544 
545  return;
546  }
547  }
548 
549  # Should the parser cache be used?
550  $useParserCache = $this->mPage->shouldCheckParserCache( $parserOptions, $oldid );
551  wfDebug( 'Article::view using parser cache: ' . ( $useParserCache ? 'yes' : 'no' ) . "\n" );
552  if ( $user->getStubThreshold() ) {
553  $this->getContext()->getStats()->increment( 'pcache_miss_stub' );
554  }
555 
556  $this->showRedirectedFromHeader();
557  $this->showNamespaceHeader();
558 
559  # Iterate through the possible ways of constructing the output text.
560  # Keep going until $outputDone is set, or we run out of things to do.
561  $pass = 0;
562  $outputDone = false;
563  $this->mParserOutput = false;
564 
565  while ( !$outputDone && ++$pass ) {
566  switch ( $pass ) {
567  case 1:
568  Hooks::run( 'ArticleViewHeader', [ &$this, &$outputDone, &$useParserCache ] );
569  break;
570  case 2:
571  # Early abort if the page doesn't exist
572  if ( !$this->mPage->exists() ) {
573  wfDebug( __METHOD__ . ": showing missing article\n" );
574  $this->showMissingArticle();
575  $this->mPage->doViewUpdates( $user );
576  return;
577  }
578 
579  # Try the parser cache
580  if ( $useParserCache ) {
581  $this->mParserOutput = $parserCache->get( $this->mPage, $parserOptions );
582 
583  if ( $this->mParserOutput !== false ) {
584  if ( $oldid ) {
585  wfDebug( __METHOD__ . ": showing parser cache contents for current rev permalink\n" );
586  $this->setOldSubtitle( $oldid );
587  } else {
588  wfDebug( __METHOD__ . ": showing parser cache contents\n" );
589  }
590  $outputPage->addParserOutput( $this->mParserOutput );
591  # Ensure that UI elements requiring revision ID have
592  # the correct version information.
593  $outputPage->setRevisionId( $this->mPage->getLatest() );
594  # Preload timestamp to avoid a DB hit
595  $cachedTimestamp = $this->mParserOutput->getTimestamp();
596  if ( $cachedTimestamp !== null ) {
597  $outputPage->setRevisionTimestamp( $cachedTimestamp );
598  $this->mPage->setTimestamp( $cachedTimestamp );
599  }
600  $outputDone = true;
601  }
602  }
603  break;
604  case 3:
605  # This will set $this->mRevision if needed
606  $this->fetchContentObject();
607 
608  # Are we looking at an old revision
609  if ( $oldid && $this->mRevision ) {
610  $this->setOldSubtitle( $oldid );
611 
612  if ( !$this->showDeletedRevisionHeader() ) {
613  wfDebug( __METHOD__ . ": cannot view deleted revision\n" );
614  return;
615  }
616  }
617 
618  # Ensure that UI elements requiring revision ID have
619  # the correct version information.
620  $outputPage->setRevisionId( $this->getRevIdFetched() );
621  # Preload timestamp to avoid a DB hit
622  $outputPage->setRevisionTimestamp( $this->mPage->getTimestamp() );
623 
624  # Pages containing custom CSS or JavaScript get special treatment
625  if ( $this->getTitle()->isCssOrJsPage() || $this->getTitle()->isCssJsSubpage() ) {
626  wfDebug( __METHOD__ . ": showing CSS/JS source\n" );
627  $this->showCssOrJsPage();
628  $outputDone = true;
629  } elseif ( !Hooks::run( 'ArticleContentViewCustom',
630  [ $this->fetchContentObject(), $this->getTitle(), $outputPage ] ) ) {
631 
632  # Allow extensions do their own custom view for certain pages
633  $outputDone = true;
634  } elseif ( !ContentHandler::runLegacyHooks(
635  'ArticleViewCustom',
636  [ $this->fetchContentObject(), $this->getTitle(), $outputPage ],
637  '1.21'
638  ) ) {
639  # Allow extensions do their own custom view for certain pages
640  $outputDone = true;
641  }
642  break;
643  case 4:
644  # Run the parse, protected by a pool counter
645  wfDebug( __METHOD__ . ": doing uncached parse\n" );
646 
647  $content = $this->getContentObject();
648  $poolArticleView = new PoolWorkArticleView( $this->getPage(), $parserOptions,
649  $this->getRevIdFetched(), $useParserCache, $content );
650 
651  if ( !$poolArticleView->execute() ) {
652  $error = $poolArticleView->getError();
653  if ( $error ) {
654  $outputPage->clearHTML(); // for release() errors
655  $outputPage->enableClientCache( false );
656  $outputPage->setRobotPolicy( 'noindex,nofollow' );
657 
658  $errortext = $error->getWikiText( false, 'view-pool-error' );
659  $outputPage->addWikiText( '<div class="errorbox">' . $errortext . '</div>' );
660  }
661  # Connection or timeout error
662  return;
663  }
664 
665  $this->mParserOutput = $poolArticleView->getParserOutput();
666  $outputPage->addParserOutput( $this->mParserOutput );
667  if ( $content->getRedirectTarget() ) {
668  $outputPage->addSubtitle( "<span id=\"redirectsub\">" .
669  $this->getContext()->msg( 'redirectpagesub' )->parse() . "</span>" );
670  }
671 
672  # Don't cache a dirty ParserOutput object
673  if ( $poolArticleView->getIsDirty() ) {
674  $outputPage->setCdnMaxage( 0 );
675  $outputPage->addHTML( "<!-- parser cache is expired, " .
676  "sending anyway due to pool overload-->\n" );
677  }
678 
679  $outputDone = true;
680  break;
681  # Should be unreachable, but just in case...
682  default:
683  break 2;
684  }
685  }
686 
687  # Get the ParserOutput actually *displayed* here.
688  # Note that $this->mParserOutput is the *current*/oldid version output.
689  $pOutput = ( $outputDone instanceof ParserOutput )
690  ? $outputDone // object fetched by hook
691  : $this->mParserOutput;
692 
693  # Adjust title for main page & pages with displaytitle
694  if ( $pOutput ) {
695  $this->adjustDisplayTitle( $pOutput );
696  }
697 
698  # For the main page, overwrite the <title> element with the con-
699  # tents of 'pagetitle-view-mainpage' instead of the default (if
700  # that's not empty).
701  # This message always exists because it is in the i18n files
702  if ( $this->getTitle()->isMainPage() ) {
703  $msg = wfMessage( 'pagetitle-view-mainpage' )->inContentLanguage();
704  if ( !$msg->isDisabled() ) {
705  $outputPage->setHTMLTitle( $msg->title( $this->getTitle() )->text() );
706  }
707  }
708 
709  # Use adaptive TTLs for CDN so delayed/failed purges are noticed less often.
710  # This could use getTouched(), but that could be scary for major template edits.
711  $outputPage->adaptCdnTTL( $this->mPage->getTimestamp(), IExpiringStore::TTL_DAY );
712 
713  # Check for any __NOINDEX__ tags on the page using $pOutput
714  $policy = $this->getRobotPolicy( 'view', $pOutput );
715  $outputPage->setIndexPolicy( $policy['index'] );
716  $outputPage->setFollowPolicy( $policy['follow'] );
717 
718  $this->showViewFooter();
719  $this->mPage->doViewUpdates( $user, $oldid );
720 
721  $outputPage->addModules( 'mediawiki.action.view.postEdit' );
722  }
723 
728  public function adjustDisplayTitle( ParserOutput $pOutput ) {
729  # Adjust the title if it was set by displaytitle, -{T|}- or language conversion
730  $titleText = $pOutput->getTitleText();
731  if ( strval( $titleText ) !== '' ) {
732  $this->getContext()->getOutput()->setPageTitle( $titleText );
733  }
734  }
735 
741  protected function showDiffPage() {
742  $request = $this->getContext()->getRequest();
743  $user = $this->getContext()->getUser();
744  $diff = $request->getVal( 'diff' );
745  $rcid = $request->getVal( 'rcid' );
746  $diffOnly = $request->getBool( 'diffonly', $user->getOption( 'diffonly' ) );
747  $purge = $request->getVal( 'action' ) == 'purge';
748  $unhide = $request->getInt( 'unhide' ) == 1;
749  $oldid = $this->getOldID();
750 
751  $rev = $this->getRevisionFetched();
752 
753  if ( !$rev ) {
754  $this->getContext()->getOutput()->setPageTitle( wfMessage( 'errorpagetitle' ) );
755  $msg = $this->getContext()->msg( 'difference-missing-revision' )
756  ->params( $oldid )
757  ->numParams( 1 )
758  ->parseAsBlock();
759  $this->getContext()->getOutput()->addHTML( $msg );
760  return;
761  }
762 
763  $contentHandler = $rev->getContentHandler();
764  $de = $contentHandler->createDifferenceEngine(
765  $this->getContext(),
766  $oldid,
767  $diff,
768  $rcid,
769  $purge,
770  $unhide
771  );
772 
773  // DifferenceEngine directly fetched the revision:
774  $this->mRevIdFetched = $de->mNewid;
775  $de->showDiffPage( $diffOnly );
776 
777  // Run view updates for the newer revision being diffed (and shown
778  // below the diff if not $diffOnly).
779  list( $old, $new ) = $de->mapDiffPrevNext( $oldid, $diff );
780  // New can be false, convert it to 0 - this conveniently means the latest revision
781  $this->mPage->doViewUpdates( $user, (int)$new );
782  }
783 
795  protected function showCssOrJsPage( $showCacheHint = true ) {
796  $outputPage = $this->getContext()->getOutput();
797 
798  if ( $showCacheHint ) {
799  $dir = $this->getContext()->getLanguage()->getDir();
800  $lang = $this->getContext()->getLanguage()->getHtmlCode();
801 
802  $outputPage->wrapWikiMsg(
803  "<div id='mw-clearyourcache' lang='$lang' dir='$dir' class='mw-content-$dir'>\n$1\n</div>",
804  'clearyourcache'
805  );
806  }
807 
808  $this->fetchContentObject();
809 
810  if ( $this->mContentObject ) {
811  // Give hooks a chance to customise the output
813  'ShowRawCssJs',
814  [ $this->mContentObject, $this->getTitle(), $outputPage ],
815  '1.24'
816  ) ) {
817  // If no legacy hooks ran, display the content of the parser output, including RL modules,
818  // but excluding metadata like categories and language links
819  $po = $this->mContentObject->getParserOutput( $this->getTitle() );
820  $outputPage->addParserOutputContent( $po );
821  }
822  }
823  }
824 
832  public function getRobotPolicy( $action, $pOutput = null ) {
833  global $wgArticleRobotPolicies, $wgNamespaceRobotPolicies, $wgDefaultRobotPolicy;
834 
835  $ns = $this->getTitle()->getNamespace();
836 
837  # Don't index user and user talk pages for blocked users (bug 11443)
838  if ( ( $ns == NS_USER || $ns == NS_USER_TALK ) && !$this->getTitle()->isSubpage() ) {
839  $specificTarget = null;
840  $vagueTarget = null;
841  $titleText = $this->getTitle()->getText();
842  if ( IP::isValid( $titleText ) ) {
843  $vagueTarget = $titleText;
844  } else {
845  $specificTarget = $titleText;
846  }
847  if ( Block::newFromTarget( $specificTarget, $vagueTarget ) instanceof Block ) {
848  return [
849  'index' => 'noindex',
850  'follow' => 'nofollow'
851  ];
852  }
853  }
854 
855  if ( $this->mPage->getId() === 0 || $this->getOldID() ) {
856  # Non-articles (special pages etc), and old revisions
857  return [
858  'index' => 'noindex',
859  'follow' => 'nofollow'
860  ];
861  } elseif ( $this->getContext()->getOutput()->isPrintable() ) {
862  # Discourage indexing of printable versions, but encourage following
863  return [
864  'index' => 'noindex',
865  'follow' => 'follow'
866  ];
867  } elseif ( $this->getContext()->getRequest()->getInt( 'curid' ) ) {
868  # For ?curid=x urls, disallow indexing
869  return [
870  'index' => 'noindex',
871  'follow' => 'follow'
872  ];
873  }
874 
875  # Otherwise, construct the policy based on the various config variables.
876  $policy = self::formatRobotPolicy( $wgDefaultRobotPolicy );
877 
878  if ( isset( $wgNamespaceRobotPolicies[$ns] ) ) {
879  # Honour customised robot policies for this namespace
880  $policy = array_merge(
881  $policy,
882  self::formatRobotPolicy( $wgNamespaceRobotPolicies[$ns] )
883  );
884  }
885  if ( $this->getTitle()->canUseNoindex() && is_object( $pOutput ) && $pOutput->getIndexPolicy() ) {
886  # __INDEX__ and __NOINDEX__ magic words, if allowed. Incorporates
887  # a final sanity check that we have really got the parser output.
888  $policy = array_merge(
889  $policy,
890  [ 'index' => $pOutput->getIndexPolicy() ]
891  );
892  }
893 
894  if ( isset( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] ) ) {
895  # (bug 14900) site config can override user-defined __INDEX__ or __NOINDEX__
896  $policy = array_merge(
897  $policy,
898  self::formatRobotPolicy( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] )
899  );
900  }
901 
902  return $policy;
903  }
904 
912  public static function formatRobotPolicy( $policy ) {
913  if ( is_array( $policy ) ) {
914  return $policy;
915  } elseif ( !$policy ) {
916  return [];
917  }
918 
919  $policy = explode( ',', $policy );
920  $policy = array_map( 'trim', $policy );
921 
922  $arr = [];
923  foreach ( $policy as $var ) {
924  if ( in_array( $var, [ 'index', 'noindex' ] ) ) {
925  $arr['index'] = $var;
926  } elseif ( in_array( $var, [ 'follow', 'nofollow' ] ) ) {
927  $arr['follow'] = $var;
928  }
929  }
930 
931  return $arr;
932  }
933 
941  public function showRedirectedFromHeader() {
943 
944  $context = $this->getContext();
945  $outputPage = $context->getOutput();
946  $request = $context->getRequest();
947  $rdfrom = $request->getVal( 'rdfrom' );
948 
949  // Construct a URL for the current page view, but with the target title
950  $query = $request->getValues();
951  unset( $query['rdfrom'] );
952  unset( $query['title'] );
953  if ( $this->getTitle()->isRedirect() ) {
954  // Prevent double redirects
955  $query['redirect'] = 'no';
956  }
957  $redirectTargetUrl = $this->getTitle()->getLinkURL( $query );
958 
959  if ( isset( $this->mRedirectedFrom ) ) {
960  // This is an internally redirected page view.
961  // We'll need a backlink to the source page for navigation.
962  if ( Hooks::run( 'ArticleViewRedirect', [ &$this ] ) ) {
963  $redir = Linker::linkKnown(
964  $this->mRedirectedFrom,
965  null,
966  [],
967  [ 'redirect' => 'no' ]
968  );
969 
970  $outputPage->addSubtitle( "<span class=\"mw-redirectedfrom\">" .
971  $context->msg( 'redirectedfrom' )->rawParams( $redir )->parse()
972  . "</span>" );
973 
974  // Add the script to update the displayed URL and
975  // set the fragment if one was specified in the redirect
976  $outputPage->addJsConfigVars( [
977  'wgInternalRedirectTargetUrl' => $redirectTargetUrl,
978  ] );
979  $outputPage->addModules( 'mediawiki.action.view.redirect' );
980 
981  // Add a <link rel="canonical"> tag
982  $outputPage->setCanonicalUrl( $this->getTitle()->getCanonicalURL() );
983 
984  // Tell the output object that the user arrived at this article through a redirect
985  $outputPage->setRedirectedFrom( $this->mRedirectedFrom );
986 
987  return true;
988  }
989  } elseif ( $rdfrom ) {
990  // This is an externally redirected view, from some other wiki.
991  // If it was reported from a trusted site, supply a backlink.
992  if ( $wgRedirectSources && preg_match( $wgRedirectSources, $rdfrom ) ) {
993  $redir = Linker::makeExternalLink( $rdfrom, $rdfrom );
994  $outputPage->addSubtitle( "<span class=\"mw-redirectedfrom\">" .
995  $context->msg( 'redirectedfrom' )->rawParams( $redir )->parse()
996  . "</span>" );
997 
998  // Add the script to update the displayed URL
999  $outputPage->addJsConfigVars( [
1000  'wgInternalRedirectTargetUrl' => $redirectTargetUrl,
1001  ] );
1002  $outputPage->addModules( 'mediawiki.action.view.redirect' );
1003 
1004  return true;
1005  }
1006  }
1007 
1008  return false;
1009  }
1010 
1015  public function showNamespaceHeader() {
1016  if ( $this->getTitle()->isTalkPage() ) {
1017  if ( !wfMessage( 'talkpageheader' )->isDisabled() ) {
1018  $this->getContext()->getOutput()->wrapWikiMsg(
1019  "<div class=\"mw-talkpageheader\">\n$1\n</div>",
1020  [ 'talkpageheader' ]
1021  );
1022  }
1023  }
1024  }
1025 
1029  public function showViewFooter() {
1030  # check if we're displaying a [[User talk:x.x.x.x]] anonymous talk page
1031  if ( $this->getTitle()->getNamespace() == NS_USER_TALK
1032  && IP::isValid( $this->getTitle()->getText() )
1033  ) {
1034  $this->getContext()->getOutput()->addWikiMsg( 'anontalkpagetext' );
1035  }
1036 
1037  // Show a footer allowing the user to patrol the shown revision or page if possible
1038  $patrolFooterShown = $this->showPatrolFooter();
1039 
1040  Hooks::run( 'ArticleViewFooter', [ $this, $patrolFooterShown ] );
1041  }
1042 
1052  public function showPatrolFooter() {
1053  global $wgUseNPPatrol, $wgUseRCPatrol, $wgUseFilePatrol, $wgEnableAPI, $wgEnableWriteAPI;
1054 
1055  $outputPage = $this->getContext()->getOutput();
1056  $user = $this->getContext()->getUser();
1057  $title = $this->getTitle();
1058  $rc = false;
1059 
1060  if ( !$title->quickUserCan( 'patrol', $user )
1061  || !( $wgUseRCPatrol || $wgUseNPPatrol
1062  || ( $wgUseFilePatrol && $title->inNamespace( NS_FILE ) ) )
1063  ) {
1064  // Patrolling is disabled or the user isn't allowed to
1065  return false;
1066  }
1067 
1068  if ( $this->mRevision
1069  && !RecentChange::isInRCLifespan( $this->mRevision->getTimestamp(), 21600 )
1070  ) {
1071  // The current revision is already older than what could be in the RC table
1072  // 6h tolerance because the RC might not be cleaned out regularly
1073  return false;
1074  }
1075 
1076  // Check for cached results
1077  $key = wfMemcKey( 'unpatrollable-page', $title->getArticleID() );
1079  if ( $cache->get( $key ) ) {
1080  return false;
1081  }
1082 
1083  $dbr = wfGetDB( DB_REPLICA );
1084  $oldestRevisionTimestamp = $dbr->selectField(
1085  'revision',
1086  'MIN( rev_timestamp )',
1087  [ 'rev_page' => $title->getArticleID() ],
1088  __METHOD__
1089  );
1090 
1091  // New page patrol: Get the timestamp of the oldest revison which
1092  // the revision table holds for the given page. Then we look
1093  // whether it's within the RC lifespan and if it is, we try
1094  // to get the recentchanges row belonging to that entry
1095  // (with rc_new = 1).
1096  $recentPageCreation = false;
1097  if ( $oldestRevisionTimestamp
1098  && RecentChange::isInRCLifespan( $oldestRevisionTimestamp, 21600 )
1099  ) {
1100  // 6h tolerance because the RC might not be cleaned out regularly
1101  $recentPageCreation = true;
1103  [
1104  'rc_new' => 1,
1105  'rc_timestamp' => $oldestRevisionTimestamp,
1106  'rc_namespace' => $title->getNamespace(),
1107  'rc_cur_id' => $title->getArticleID()
1108  ],
1109  __METHOD__
1110  );
1111  if ( $rc ) {
1112  // Use generic patrol message for new pages
1113  $markPatrolledMsg = wfMessage( 'markaspatrolledtext' );
1114  }
1115  }
1116 
1117  // File patrol: Get the timestamp of the latest upload for this page,
1118  // check whether it is within the RC lifespan and if it is, we try
1119  // to get the recentchanges row belonging to that entry
1120  // (with rc_type = RC_LOG, rc_log_type = upload).
1121  $recentFileUpload = false;
1122  if ( ( !$rc || $rc->getAttribute( 'rc_patrolled' ) ) && $wgUseFilePatrol
1123  && $title->getNamespace() === NS_FILE ) {
1124  // Retrieve timestamp of most recent upload
1125  $newestUploadTimestamp = $dbr->selectField(
1126  'image',
1127  'MAX( img_timestamp )',
1128  [ 'img_name' => $title->getDBkey() ],
1129  __METHOD__
1130  );
1131  if ( $newestUploadTimestamp
1132  && RecentChange::isInRCLifespan( $newestUploadTimestamp, 21600 )
1133  ) {
1134  // 6h tolerance because the RC might not be cleaned out regularly
1135  $recentFileUpload = true;
1137  [
1138  'rc_type' => RC_LOG,
1139  'rc_log_type' => 'upload',
1140  'rc_timestamp' => $newestUploadTimestamp,
1141  'rc_namespace' => NS_FILE,
1142  'rc_cur_id' => $title->getArticleID()
1143  ],
1144  __METHOD__,
1145  [ 'USE INDEX' => 'rc_timestamp' ]
1146  );
1147  if ( $rc ) {
1148  // Use patrol message specific to files
1149  $markPatrolledMsg = wfMessage( 'markaspatrolledtext-file' );
1150  }
1151  }
1152  }
1153 
1154  if ( !$recentPageCreation && !$recentFileUpload ) {
1155  // Page creation and latest upload (for files) is too old to be in RC
1156 
1157  // We definitely can't patrol so cache the information
1158  // When a new file version is uploaded, the cache is cleared
1159  $cache->set( $key, '1' );
1160 
1161  return false;
1162  }
1163 
1164  if ( !$rc ) {
1165  // Don't cache: This can be hit if the page gets accessed very fast after
1166  // its creation / latest upload or in case we have high replica DB lag. In case
1167  // the revision is too old, we will already return above.
1168  return false;
1169  }
1170 
1171  if ( $rc->getAttribute( 'rc_patrolled' ) ) {
1172  // Patrolled RC entry around
1173 
1174  // Cache the information we gathered above in case we can't patrol
1175  // Don't cache in case we can patrol as this could change
1176  $cache->set( $key, '1' );
1177 
1178  return false;
1179  }
1180 
1181  if ( $rc->getPerformer()->equals( $user ) ) {
1182  // Don't show a patrol link for own creations/uploads. If the user could
1183  // patrol them, they already would be patrolled
1184  return false;
1185  }
1186 
1187  $rcid = $rc->getAttribute( 'rc_id' );
1188 
1189  $token = $user->getEditToken( $rcid );
1190 
1191  $outputPage->preventClickjacking();
1192  if ( $wgEnableAPI && $wgEnableWriteAPI && $user->isAllowed( 'writeapi' ) ) {
1193  $outputPage->addModules( 'mediawiki.page.patrol.ajax' );
1194  }
1195 
1197  $title,
1198  $markPatrolledMsg->escaped(),
1199  [],
1200  [
1201  'action' => 'markpatrolled',
1202  'rcid' => $rcid,
1203  'token' => $token,
1204  ]
1205  );
1206 
1207  $outputPage->addHTML(
1208  "<div class='patrollink' data-mw='interface'>" .
1209  wfMessage( 'markaspatrolledlink' )->rawParams( $link )->escaped() .
1210  '</div>'
1211  );
1212 
1213  return true;
1214  }
1215 
1222  public static function purgePatrolFooterCache( $articleID ) {
1224  $cache->delete( wfMemcKey( 'unpatrollable-page', $articleID ) );
1225  }
1226 
1231  public function showMissingArticle() {
1233 
1234  $outputPage = $this->getContext()->getOutput();
1235  // Whether the page is a root user page of an existing user (but not a subpage)
1236  $validUserPage = false;
1237 
1238  $title = $this->getTitle();
1239 
1240  # Show info in user (talk) namespace. Does the user exist? Is he blocked?
1241  if ( $title->getNamespace() == NS_USER
1242  || $title->getNamespace() == NS_USER_TALK
1243  ) {
1244  $rootPart = explode( '/', $title->getText() )[0];
1245  $user = User::newFromName( $rootPart, false /* allow IP users*/ );
1246  $ip = User::isIP( $rootPart );
1247  $block = Block::newFromTarget( $user, $user );
1248 
1249  if ( !( $user && $user->isLoggedIn() ) && !$ip ) { # User does not exist
1250  $outputPage->wrapWikiMsg( "<div class=\"mw-userpage-userdoesnotexist error\">\n\$1\n</div>",
1251  [ 'userpage-userdoesnotexist-view', wfEscapeWikiText( $rootPart ) ] );
1252  } elseif ( !is_null( $block ) && $block->getType() != Block::TYPE_AUTO ) {
1253  # Show log extract if the user is currently blocked
1255  $outputPage,
1256  'block',
1257  MWNamespace::getCanonicalName( NS_USER ) . ':' . $block->getTarget(),
1258  '',
1259  [
1260  'lim' => 1,
1261  'showIfEmpty' => false,
1262  'msgKey' => [
1263  'blocked-notice-logextract',
1264  $user->getName() # Support GENDER in notice
1265  ]
1266  ]
1267  );
1268  $validUserPage = !$title->isSubpage();
1269  } else {
1270  $validUserPage = !$title->isSubpage();
1271  }
1272  }
1273 
1274  Hooks::run( 'ShowMissingArticle', [ $this ] );
1275 
1276  # Show delete and move logs if there were any such events.
1277  # The logging query can DOS the site when bots/crawlers cause 404 floods,
1278  # so be careful showing this. 404 pages must be cheap as they are hard to cache.
1280  $key = wfMemcKey( 'page-recent-delete', md5( $title->getPrefixedText() ) );
1281  $loggedIn = $this->getContext()->getUser()->isLoggedIn();
1282  if ( $loggedIn || $cache->get( $key ) ) {
1283  $logTypes = [ 'delete', 'move' ];
1284  $conds = [ "log_action != 'revision'" ];
1285  // Give extensions a chance to hide their (unrelated) log entries
1286  Hooks::run( 'Article::MissingArticleConditions', [ &$conds, $logTypes ] );
1288  $outputPage,
1289  $logTypes,
1290  $title,
1291  '',
1292  [
1293  'lim' => 10,
1294  'conds' => $conds,
1295  'showIfEmpty' => false,
1296  'msgKey' => [ $loggedIn
1297  ? 'moveddeleted-notice'
1298  : 'moveddeleted-notice-recent'
1299  ]
1300  ]
1301  );
1302  }
1303 
1304  if ( !$this->mPage->hasViewableContent() && $wgSend404Code && !$validUserPage ) {
1305  // If there's no backing content, send a 404 Not Found
1306  // for better machine handling of broken links.
1307  $this->getContext()->getRequest()->response()->statusHeader( 404 );
1308  }
1309 
1310  // Also apply the robot policy for nonexisting pages (even if a 404 was used for sanity)
1311  $policy = $this->getRobotPolicy( 'view' );
1312  $outputPage->setIndexPolicy( $policy['index'] );
1313  $outputPage->setFollowPolicy( $policy['follow'] );
1314 
1315  $hookResult = Hooks::run( 'BeforeDisplayNoArticleText', [ $this ] );
1316 
1317  if ( !$hookResult ) {
1318  return;
1319  }
1320 
1321  # Show error message
1322  $oldid = $this->getOldID();
1323  if ( !$oldid && $title->getNamespace() === NS_MEDIAWIKI && $title->hasSourceText() ) {
1324  $outputPage->addParserOutput( $this->getContentObject()->getParserOutput( $title ) );
1325  } else {
1326  if ( $oldid ) {
1327  $text = wfMessage( 'missing-revision', $oldid )->plain();
1328  } elseif ( $title->quickUserCan( 'create', $this->getContext()->getUser() )
1329  && $title->quickUserCan( 'edit', $this->getContext()->getUser() )
1330  ) {
1331  $message = $this->getContext()->getUser()->isLoggedIn() ? 'noarticletext' : 'noarticletextanon';
1332  $text = wfMessage( $message )->plain();
1333  } else {
1334  $text = wfMessage( 'noarticletext-nopermission' )->plain();
1335  }
1336 
1337  $dir = $this->getContext()->getLanguage()->getDir();
1338  $lang = $this->getContext()->getLanguage()->getCode();
1339  $outputPage->addWikiText( Xml::openElement( 'div', [
1340  'class' => "noarticletext mw-content-$dir",
1341  'dir' => $dir,
1342  'lang' => $lang,
1343  ] ) . "\n$text\n</div>" );
1344  }
1345  }
1346 
1353  public function showDeletedRevisionHeader() {
1354  if ( !$this->mRevision->isDeleted( Revision::DELETED_TEXT ) ) {
1355  // Not deleted
1356  return true;
1357  }
1358 
1359  $outputPage = $this->getContext()->getOutput();
1360  $user = $this->getContext()->getUser();
1361  // If the user is not allowed to see it...
1362  if ( !$this->mRevision->userCan( Revision::DELETED_TEXT, $user ) ) {
1363  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1364  'rev-deleted-text-permission' );
1365 
1366  return false;
1367  // If the user needs to confirm that they want to see it...
1368  } elseif ( $this->getContext()->getRequest()->getInt( 'unhide' ) != 1 ) {
1369  # Give explanation and add a link to view the revision...
1370  $oldid = intval( $this->getOldID() );
1371  $link = $this->getTitle()->getFullURL( "oldid={$oldid}&unhide=1" );
1372  $msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ?
1373  'rev-suppressed-text-unhide' : 'rev-deleted-text-unhide';
1374  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1375  [ $msg, $link ] );
1376 
1377  return false;
1378  // We are allowed to see...
1379  } else {
1380  $msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ?
1381  'rev-suppressed-text-view' : 'rev-deleted-text-view';
1382  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n", $msg );
1383 
1384  return true;
1385  }
1386  }
1387 
1396  public function setOldSubtitle( $oldid = 0 ) {
1397  if ( !Hooks::run( 'DisplayOldSubtitle', [ &$this, &$oldid ] ) ) {
1398  return;
1399  }
1400 
1401  $context = $this->getContext();
1402  $unhide = $context->getRequest()->getInt( 'unhide' ) == 1;
1403 
1404  # Cascade unhide param in links for easy deletion browsing
1405  $extraParams = [];
1406  if ( $unhide ) {
1407  $extraParams['unhide'] = 1;
1408  }
1409 
1410  if ( $this->mRevision && $this->mRevision->getId() === $oldid ) {
1411  $revision = $this->mRevision;
1412  } else {
1413  $revision = Revision::newFromId( $oldid );
1414  }
1415 
1416  $timestamp = $revision->getTimestamp();
1417 
1418  $current = ( $oldid == $this->mPage->getLatest() );
1419  $language = $context->getLanguage();
1420  $user = $context->getUser();
1421 
1422  $td = $language->userTimeAndDate( $timestamp, $user );
1423  $tddate = $language->userDate( $timestamp, $user );
1424  $tdtime = $language->userTime( $timestamp, $user );
1425 
1426  # Show user links if allowed to see them. If hidden, then show them only if requested...
1427  $userlinks = Linker::revUserTools( $revision, !$unhide );
1428 
1429  $infomsg = $current && !$context->msg( 'revision-info-current' )->isDisabled()
1430  ? 'revision-info-current'
1431  : 'revision-info';
1432 
1433  $outputPage = $context->getOutput();
1434  $revisionInfo = "<div id=\"mw-{$infomsg}\">" .
1435  $context->msg( $infomsg, $td )
1436  ->rawParams( $userlinks )
1437  ->params( $revision->getId(), $tddate, $tdtime, $revision->getUserText() )
1438  ->rawParams( Linker::revComment( $revision, true, true ) )
1439  ->parse() .
1440  "</div>";
1441 
1442  $lnk = $current
1443  ? $context->msg( 'currentrevisionlink' )->escaped()
1445  $this->getTitle(),
1446  $context->msg( 'currentrevisionlink' )->escaped(),
1447  [],
1448  $extraParams
1449  );
1450  $curdiff = $current
1451  ? $context->msg( 'diff' )->escaped()
1453  $this->getTitle(),
1454  $context->msg( 'diff' )->escaped(),
1455  [],
1456  [
1457  'diff' => 'cur',
1458  'oldid' => $oldid
1459  ] + $extraParams
1460  );
1461  $prev = $this->getTitle()->getPreviousRevisionID( $oldid );
1462  $prevlink = $prev
1464  $this->getTitle(),
1465  $context->msg( 'previousrevision' )->escaped(),
1466  [],
1467  [
1468  'direction' => 'prev',
1469  'oldid' => $oldid
1470  ] + $extraParams
1471  )
1472  : $context->msg( 'previousrevision' )->escaped();
1473  $prevdiff = $prev
1475  $this->getTitle(),
1476  $context->msg( 'diff' )->escaped(),
1477  [],
1478  [
1479  'diff' => 'prev',
1480  'oldid' => $oldid
1481  ] + $extraParams
1482  )
1483  : $context->msg( 'diff' )->escaped();
1484  $nextlink = $current
1485  ? $context->msg( 'nextrevision' )->escaped()
1487  $this->getTitle(),
1488  $context->msg( 'nextrevision' )->escaped(),
1489  [],
1490  [
1491  'direction' => 'next',
1492  'oldid' => $oldid
1493  ] + $extraParams
1494  );
1495  $nextdiff = $current
1496  ? $context->msg( 'diff' )->escaped()
1498  $this->getTitle(),
1499  $context->msg( 'diff' )->escaped(),
1500  [],
1501  [
1502  'diff' => 'next',
1503  'oldid' => $oldid
1504  ] + $extraParams
1505  );
1506 
1507  $cdel = Linker::getRevDeleteLink( $user, $revision, $this->getTitle() );
1508  if ( $cdel !== '' ) {
1509  $cdel .= ' ';
1510  }
1511 
1512  // the outer div is need for styling the revision info and nav in MobileFrontend
1513  $outputPage->addSubtitle( "<div class=\"mw-revision\">" . $revisionInfo .
1514  "<div id=\"mw-revision-nav\">" . $cdel .
1515  $context->msg( 'revision-nav' )->rawParams(
1516  $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff
1517  )->escaped() . "</div></div>" );
1518  }
1519 
1531  public function viewRedirect( $target, $appendSubtitle = true, $forceKnown = false ) {
1532  $lang = $this->getTitle()->getPageLanguage();
1533  $out = $this->getContext()->getOutput();
1534  if ( $appendSubtitle ) {
1535  $out->addSubtitle( wfMessage( 'redirectpagesub' ) );
1536  }
1537  $out->addModuleStyles( 'mediawiki.action.view.redirectPage' );
1538  return static::getRedirectHeaderHtml( $lang, $target, $forceKnown );
1539  }
1540 
1553  public static function getRedirectHeaderHtml( Language $lang, $target, $forceKnown = false ) {
1554  if ( !is_array( $target ) ) {
1555  $target = [ $target ];
1556  }
1557 
1558  $html = '<ul class="redirectText">';
1560  foreach ( $target as $title ) {
1561  $html .= '<li>' . Linker::link(
1562  $title,
1563  htmlspecialchars( $title->getFullText() ),
1564  [],
1565  // Make sure wiki page redirects are not followed
1566  $title->isRedirect() ? [ 'redirect' => 'no' ] : [],
1567  ( $forceKnown ? [ 'known', 'noclasses' ] : [] )
1568  ) . '</li>';
1569  }
1570  $html .= '</ul>';
1571 
1572  $redirectToText = wfMessage( 'redirectto' )->inLanguage( $lang )->escaped();
1573 
1574  return '<div class="redirectMsg">' .
1575  '<p>' . $redirectToText . '</p>' .
1576  $html .
1577  '</div>';
1578  }
1579 
1588  public function addHelpLink( $to, $overrideBaseUrl = false ) {
1589  $msg = wfMessage(
1590  'namespace-' . $this->getTitle()->getNamespace() . '-helppage'
1591  );
1592 
1593  $out = $this->getContext()->getOutput();
1594  if ( !$msg->isDisabled() ) {
1595  $helpUrl = Skin::makeUrl( $msg->plain() );
1596  $out->addHelpLink( $helpUrl, true );
1597  } else {
1598  $out->addHelpLink( $to, $overrideBaseUrl );
1599  }
1600  }
1601 
1605  public function render() {
1606  $this->getContext()->getRequest()->response()->header( 'X-Robots-Tag: noindex' );
1607  $this->getContext()->getOutput()->setArticleBodyOnly( true );
1608  $this->getContext()->getOutput()->enableSectionEditLinks( false );
1609  $this->view();
1610  }
1611 
1615  public function protect() {
1616  $form = new ProtectionForm( $this );
1617  $form->execute();
1618  }
1619 
1623  public function unprotect() {
1624  $this->protect();
1625  }
1626 
1630  public function delete() {
1631  # This code desperately needs to be totally rewritten
1632 
1633  $title = $this->getTitle();
1634  $context = $this->getContext();
1635  $user = $context->getUser();
1636  $request = $context->getRequest();
1637 
1638  # Check permissions
1639  $permissionErrors = $title->getUserPermissionsErrors( 'delete', $user );
1640  if ( count( $permissionErrors ) ) {
1641  throw new PermissionsError( 'delete', $permissionErrors );
1642  }
1643 
1644  # Read-only check...
1645  if ( wfReadOnly() ) {
1646  throw new ReadOnlyError;
1647  }
1648 
1649  # Better double-check that it hasn't been deleted yet!
1650  $this->mPage->loadPageData(
1651  $request->wasPosted() ? WikiPage::READ_LATEST : WikiPage::READ_NORMAL
1652  );
1653  if ( !$this->mPage->exists() ) {
1654  $deleteLogPage = new LogPage( 'delete' );
1655  $outputPage = $context->getOutput();
1656  $outputPage->setPageTitle( $context->msg( 'cannotdelete-title', $title->getPrefixedText() ) );
1657  $outputPage->wrapWikiMsg( "<div class=\"error mw-error-cannotdelete\">\n$1\n</div>",
1658  [ 'cannotdelete', wfEscapeWikiText( $title->getPrefixedText() ) ]
1659  );
1660  $outputPage->addHTML(
1661  Xml::element( 'h2', null, $deleteLogPage->getName()->text() )
1662  );
1664  $outputPage,
1665  'delete',
1666  $title
1667  );
1668 
1669  return;
1670  }
1671 
1672  $deleteReasonList = $request->getText( 'wpDeleteReasonList', 'other' );
1673  $deleteReason = $request->getText( 'wpReason' );
1674 
1675  if ( $deleteReasonList == 'other' ) {
1676  $reason = $deleteReason;
1677  } elseif ( $deleteReason != '' ) {
1678  // Entry from drop down menu + additional comment
1679  $colonseparator = wfMessage( 'colon-separator' )->inContentLanguage()->text();
1680  $reason = $deleteReasonList . $colonseparator . $deleteReason;
1681  } else {
1682  $reason = $deleteReasonList;
1683  }
1684 
1685  if ( $request->wasPosted() && $user->matchEditToken( $request->getVal( 'wpEditToken' ),
1686  [ 'delete', $this->getTitle()->getPrefixedText() ] )
1687  ) {
1688  # Flag to hide all contents of the archived revisions
1689  $suppress = $request->getVal( 'wpSuppress' ) && $user->isAllowed( 'suppressrevision' );
1690 
1691  $this->doDelete( $reason, $suppress );
1692 
1693  WatchAction::doWatchOrUnwatch( $request->getCheck( 'wpWatch' ), $title, $user );
1694 
1695  return;
1696  }
1697 
1698  // Generate deletion reason
1699  $hasHistory = false;
1700  if ( !$reason ) {
1701  try {
1702  $reason = $this->generateReason( $hasHistory );
1703  } catch ( Exception $e ) {
1704  # if a page is horribly broken, we still want to be able to
1705  # delete it. So be lenient about errors here.
1706  wfDebug( "Error while building auto delete summary: $e" );
1707  $reason = '';
1708  }
1709  }
1710 
1711  // If the page has a history, insert a warning
1712  if ( $hasHistory ) {
1713  $title = $this->getTitle();
1714 
1715  // The following can use the real revision count as this is only being shown for users
1716  // that can delete this page.
1717  // This, as a side-effect, also makes sure that the following query isn't being run for
1718  // pages with a larger history, unless the user has the 'bigdelete' right
1719  // (and is about to delete this page).
1720  $dbr = wfGetDB( DB_REPLICA );
1721  $revisions = $edits = (int)$dbr->selectField(
1722  'revision',
1723  'COUNT(rev_page)',
1724  [ 'rev_page' => $title->getArticleID() ],
1725  __METHOD__
1726  );
1727 
1728  // @todo FIXME: i18n issue/patchwork message
1729  $context->getOutput()->addHTML(
1730  '<strong class="mw-delete-warning-revisions">' .
1731  $context->msg( 'historywarning' )->numParams( $revisions )->parse() .
1732  $context->msg( 'word-separator' )->escaped() . Linker::linkKnown( $title,
1733  $context->msg( 'history' )->escaped(),
1734  [],
1735  [ 'action' => 'history' ] ) .
1736  '</strong>'
1737  );
1738 
1739  if ( $title->isBigDeletion() ) {
1740  global $wgDeleteRevisionsLimit;
1741  $context->getOutput()->wrapWikiMsg( "<div class='error'>\n$1\n</div>\n",
1742  [
1743  'delete-warning-toobig',
1744  $context->getLanguage()->formatNum( $wgDeleteRevisionsLimit )
1745  ]
1746  );
1747  }
1748  }
1749 
1750  $this->confirmDelete( $reason );
1751  }
1752 
1758  public function confirmDelete( $reason ) {
1759  wfDebug( "Article::confirmDelete\n" );
1760 
1761  $title = $this->getTitle();
1762  $ctx = $this->getContext();
1763  $outputPage = $ctx->getOutput();
1764  $useMediaWikiUIEverywhere = $ctx->getConfig()->get( 'UseMediaWikiUIEverywhere' );
1765  $outputPage->setPageTitle( wfMessage( 'delete-confirm', $title->getPrefixedText() ) );
1766  $outputPage->addBacklinkSubtitle( $title );
1767  $outputPage->setRobotPolicy( 'noindex,nofollow' );
1768  $backlinkCache = $title->getBacklinkCache();
1769  if ( $backlinkCache->hasLinks( 'pagelinks' ) || $backlinkCache->hasLinks( 'templatelinks' ) ) {
1770  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1771  'deleting-backlinks-warning' );
1772  }
1773  $outputPage->addWikiMsg( 'confirmdeletetext' );
1774 
1775  Hooks::run( 'ArticleConfirmDelete', [ $this, $outputPage, &$reason ] );
1776 
1777  $user = $this->getContext()->getUser();
1778 
1779  if ( $user->isAllowed( 'suppressrevision' ) ) {
1780  $suppress = Html::openElement( 'div', [ 'id' => 'wpDeleteSuppressRow' ] ) .
1781  Xml::checkLabel( wfMessage( 'revdelete-suppress' )->text(),
1782  'wpSuppress', 'wpSuppress', false, [ 'tabindex' => '4' ] ) .
1783  Html::closeElement( 'div' );
1784  } else {
1785  $suppress = '';
1786  }
1787  $checkWatch = $user->getBoolOption( 'watchdeletion' ) || $user->isWatched( $title );
1788 
1789  $form = Html::openElement( 'form', [ 'method' => 'post',
1790  'action' => $title->getLocalURL( 'action=delete' ), 'id' => 'deleteconfirm' ] ) .
1791  Html::openElement( 'fieldset', [ 'id' => 'mw-delete-table' ] ) .
1792  Html::element( 'legend', null, wfMessage( 'delete-legend' )->text() ) .
1793  Html::openElement( 'div', [ 'id' => 'mw-deleteconfirm-table' ] ) .
1794  Html::openElement( 'div', [ 'id' => 'wpDeleteReasonListRow' ] ) .
1795  Html::label( wfMessage( 'deletecomment' )->text(), 'wpDeleteReasonList' ) .
1796  '&nbsp;' .
1798  'wpDeleteReasonList',
1799  wfMessage( 'deletereason-dropdown' )->inContentLanguage()->text(),
1800  wfMessage( 'deletereasonotherlist' )->inContentLanguage()->text(),
1801  '',
1802  'wpReasonDropDown',
1803  1
1804  ) .
1805  Html::closeElement( 'div' ) .
1806  Html::openElement( 'div', [ 'id' => 'wpDeleteReasonRow' ] ) .
1807  Html::label( wfMessage( 'deleteotherreason' )->text(), 'wpReason' ) .
1808  '&nbsp;' .
1809  Html::input( 'wpReason', $reason, 'text', [
1810  'size' => '60',
1811  'maxlength' => '255',
1812  'tabindex' => '2',
1813  'id' => 'wpReason',
1814  'class' => 'mw-ui-input-inline',
1815  'autofocus'
1816  ] ) .
1817  Html::closeElement( 'div' );
1818 
1819  # Disallow watching if user is not logged in
1820  if ( $user->isLoggedIn() ) {
1821  $form .=
1822  Xml::checkLabel( wfMessage( 'watchthis' )->text(),
1823  'wpWatch', 'wpWatch', $checkWatch, [ 'tabindex' => '3' ] );
1824  }
1825 
1826  $form .=
1827  Html::openElement( 'div' ) .
1828  $suppress .
1829  Xml::submitButton( wfMessage( 'deletepage' )->text(),
1830  [
1831  'name' => 'wpConfirmB',
1832  'id' => 'wpConfirmB',
1833  'tabindex' => '5',
1834  'class' => $useMediaWikiUIEverywhere ? 'mw-ui-button mw-ui-destructive' : '',
1835  ]
1836  ) .
1837  Html::closeElement( 'div' ) .
1838  Html::closeElement( 'div' ) .
1839  Xml::closeElement( 'fieldset' ) .
1840  Html::hidden(
1841  'wpEditToken',
1842  $user->getEditToken( [ 'delete', $title->getPrefixedText() ] )
1843  ) .
1844  Xml::closeElement( 'form' );
1845 
1846  if ( $user->isAllowed( 'editinterface' ) ) {
1848  $ctx->msg( 'deletereason-dropdown' )->inContentLanguage()->getTitle(),
1849  wfMessage( 'delete-edit-reasonlist' )->escaped(),
1850  [],
1851  [ 'action' => 'edit' ]
1852  );
1853  $form .= '<p class="mw-delete-editreasons">' . $link . '</p>';
1854  }
1855 
1856  $outputPage->addHTML( $form );
1857 
1858  $deleteLogPage = new LogPage( 'delete' );
1859  $outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) );
1860  LogEventsList::showLogExtract( $outputPage, 'delete', $title );
1861  }
1862 
1868  public function doDelete( $reason, $suppress = false ) {
1869  $error = '';
1870  $context = $this->getContext();
1871  $outputPage = $context->getOutput();
1872  $user = $context->getUser();
1873  $status = $this->mPage->doDeleteArticleReal( $reason, $suppress, 0, true, $error, $user );
1874 
1875  if ( $status->isGood() ) {
1876  $deleted = $this->getTitle()->getPrefixedText();
1877 
1878  $outputPage->setPageTitle( wfMessage( 'actioncomplete' ) );
1879  $outputPage->setRobotPolicy( 'noindex,nofollow' );
1880 
1881  $loglink = '[[Special:Log/delete|' . wfMessage( 'deletionlog' )->text() . ']]';
1882 
1883  $outputPage->addWikiMsg( 'deletedtext', wfEscapeWikiText( $deleted ), $loglink );
1884 
1885  Hooks::run( 'ArticleDeleteAfterSuccess', [ $this->getTitle(), $outputPage ] );
1886 
1887  $outputPage->returnToMain( false );
1888  } else {
1889  $outputPage->setPageTitle(
1890  wfMessage( 'cannotdelete-title',
1891  $this->getTitle()->getPrefixedText() )
1892  );
1893 
1894  if ( $error == '' ) {
1895  $outputPage->addWikiText(
1896  "<div class=\"error mw-error-cannotdelete\">\n" . $status->getWikiText() . "\n</div>"
1897  );
1898  $deleteLogPage = new LogPage( 'delete' );
1899  $outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) );
1900 
1902  $outputPage,
1903  'delete',
1904  $this->getTitle()
1905  );
1906  } else {
1907  $outputPage->addHTML( $error );
1908  }
1909  }
1910  }
1911 
1912  /* Caching functions */
1913 
1921  protected function tryFileCache() {
1922  static $called = false;
1923 
1924  if ( $called ) {
1925  wfDebug( "Article::tryFileCache(): called twice!?\n" );
1926  return false;
1927  }
1928 
1929  $called = true;
1930  if ( $this->isFileCacheable() ) {
1931  $cache = new HTMLFileCache( $this->getTitle(), 'view' );
1932  if ( $cache->isCacheGood( $this->mPage->getTouched() ) ) {
1933  wfDebug( "Article::tryFileCache(): about to load file\n" );
1934  $cache->loadFromFileCache( $this->getContext() );
1935  return true;
1936  } else {
1937  wfDebug( "Article::tryFileCache(): starting buffer\n" );
1938  ob_start( [ &$cache, 'saveToFileCache' ] );
1939  }
1940  } else {
1941  wfDebug( "Article::tryFileCache(): not cacheable\n" );
1942  }
1943 
1944  return false;
1945  }
1946 
1952  public function isFileCacheable( $mode = HTMLFileCache::MODE_NORMAL ) {
1953  $cacheable = false;
1954 
1955  if ( HTMLFileCache::useFileCache( $this->getContext(), $mode ) ) {
1956  $cacheable = $this->mPage->getId()
1957  && !$this->mRedirectedFrom && !$this->getTitle()->isRedirect();
1958  // Extension may have reason to disable file caching on some pages.
1959  if ( $cacheable ) {
1960  $cacheable = Hooks::run( 'IsFileCacheable', [ &$this ] );
1961  }
1962  }
1963 
1964  return $cacheable;
1965  }
1966 
1980  public function getParserOutput( $oldid = null, User $user = null ) {
1981  // XXX: bypasses mParserOptions and thus setParserOptions()
1982 
1983  if ( $user === null ) {
1984  $parserOptions = $this->getParserOptions();
1985  } else {
1986  $parserOptions = $this->mPage->makeParserOptions( $user );
1987  }
1988 
1989  return $this->mPage->getParserOutput( $parserOptions, $oldid );
1990  }
1991 
1999  if ( $this->mParserOptions ) {
2000  throw new MWException( "can't change parser options after they have already been set" );
2001  }
2002 
2003  // clone, so if $options is modified later, it doesn't confuse the parser cache.
2004  $this->mParserOptions = clone $options;
2005  }
2006 
2011  public function getParserOptions() {
2012  if ( !$this->mParserOptions ) {
2013  $this->mParserOptions = $this->mPage->makeParserOptions( $this->getContext() );
2014  }
2015  // Clone to allow modifications of the return value without affecting cache
2016  return clone $this->mParserOptions;
2017  }
2018 
2025  public function setContext( $context ) {
2026  $this->mContext = $context;
2027  }
2028 
2035  public function getContext() {
2036  if ( $this->mContext instanceof IContextSource ) {
2037  return $this->mContext;
2038  } else {
2039  wfDebug( __METHOD__ . " called and \$mContext is null. " .
2040  "Return RequestContext::getMain(); for sanity\n" );
2041  return RequestContext::getMain();
2042  }
2043  }
2044 
2052  public function __get( $fname ) {
2053  if ( property_exists( $this->mPage, $fname ) ) {
2054  # wfWarn( "Access to raw $fname field " . __CLASS__ );
2055  return $this->mPage->$fname;
2056  }
2057  trigger_error( 'Inaccessible property via __get(): ' . $fname, E_USER_NOTICE );
2058  }
2059 
2067  public function __set( $fname, $fvalue ) {
2068  if ( property_exists( $this->mPage, $fname ) ) {
2069  # wfWarn( "Access to raw $fname field of " . __CLASS__ );
2070  $this->mPage->$fname = $fvalue;
2071  // Note: extensions may want to toss on new fields
2072  } elseif ( !in_array( $fname, [ 'mContext', 'mPage' ] ) ) {
2073  $this->mPage->$fname = $fvalue;
2074  } else {
2075  trigger_error( 'Inaccessible property via __set(): ' . $fname, E_USER_NOTICE );
2076  }
2077  }
2078 
2083  public function checkFlags( $flags ) {
2084  return $this->mPage->checkFlags( $flags );
2085  }
2086 
2091  public function checkTouched() {
2092  return $this->mPage->checkTouched();
2093  }
2094 
2099  public function clearPreparedEdit() {
2100  $this->mPage->clearPreparedEdit();
2101  }
2102 
2107  public function doDeleteArticleReal(
2108  $reason, $suppress = false, $u1 = null, $u2 = null, &$error = '', User $user = null,
2109  $tags = []
2110  ) {
2111  return $this->mPage->doDeleteArticleReal(
2112  $reason, $suppress, $u1, $u2, $error, $user, $tags
2113  );
2114  }
2115 
2120  public function doDeleteUpdates( $id, Content $content = null ) {
2121  return $this->mPage->doDeleteUpdates( $id, $content );
2122  }
2123 
2130  public function doEdit( $text, $summary, $flags = 0, $baseRevId = false, $user = null ) {
2131  wfDeprecated( __METHOD__, '1.21' );
2132  return $this->mPage->doEdit( $text, $summary, $flags, $baseRevId, $user );
2133  }
2134 
2139  public function doEditContent( Content $content, $summary, $flags = 0, $baseRevId = false,
2140  User $user = null, $serialFormat = null
2141  ) {
2142  return $this->mPage->doEditContent( $content, $summary, $flags, $baseRevId,
2143  $user, $serialFormat
2144  );
2145  }
2146 
2151  public function doEditUpdates( Revision $revision, User $user, array $options = [] ) {
2152  return $this->mPage->doEditUpdates( $revision, $user, $options );
2153  }
2154 
2159  public function doPurge( $flags = WikiPage::PURGE_ALL ) {
2160  return $this->mPage->doPurge( $flags );
2161  }
2162 
2167  public function getLastPurgeTimestamp() {
2168  return $this->mPage->getLastPurgeTimestamp();
2169  }
2170 
2175  public function doViewUpdates( User $user, $oldid = 0 ) {
2176  $this->mPage->doViewUpdates( $user, $oldid );
2177  }
2178 
2183  public function exists() {
2184  return $this->mPage->exists();
2185  }
2186 
2191  public function followRedirect() {
2192  return $this->mPage->followRedirect();
2193  }
2194 
2199  public function getActionOverrides() {
2200  return $this->mPage->getActionOverrides();
2201  }
2202 
2207  public function getAutoDeleteReason( &$hasHistory ) {
2208  return $this->mPage->getAutoDeleteReason( $hasHistory );
2209  }
2210 
2215  public function getCategories() {
2216  return $this->mPage->getCategories();
2217  }
2218 
2223  public function getComment( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2224  return $this->mPage->getComment( $audience, $user );
2225  }
2226 
2231  public function getContentHandler() {
2232  return $this->mPage->getContentHandler();
2233  }
2234 
2239  public function getContentModel() {
2240  return $this->mPage->getContentModel();
2241  }
2242 
2247  public function getContributors() {
2248  return $this->mPage->getContributors();
2249  }
2250 
2255  public function getCreator( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2256  return $this->mPage->getCreator( $audience, $user );
2257  }
2258 
2263  public function getDeletionUpdates( Content $content = null ) {
2264  return $this->mPage->getDeletionUpdates( $content );
2265  }
2266 
2271  public function getHiddenCategories() {
2272  return $this->mPage->getHiddenCategories();
2273  }
2274 
2279  public function getId() {
2280  return $this->mPage->getId();
2281  }
2282 
2287  public function getLatest() {
2288  return $this->mPage->getLatest();
2289  }
2290 
2295  public function getLinksTimestamp() {
2296  return $this->mPage->getLinksTimestamp();
2297  }
2298 
2303  public function getMinorEdit() {
2304  return $this->mPage->getMinorEdit();
2305  }
2306 
2311  public function getOldestRevision() {
2312  return $this->mPage->getOldestRevision();
2313  }
2314 
2319  public function getRedirectTarget() {
2320  return $this->mPage->getRedirectTarget();
2321  }
2322 
2327  public function getRedirectURL( $rt ) {
2328  return $this->mPage->getRedirectURL( $rt );
2329  }
2330 
2335  public function getRevision() {
2336  return $this->mPage->getRevision();
2337  }
2338 
2344  public function getText( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2345  wfDeprecated( __METHOD__, '1.21' );
2346  return $this->mPage->getText( $audience, $user );
2347  }
2348 
2353  public function getTimestamp() {
2354  return $this->mPage->getTimestamp();
2355  }
2356 
2361  public function getTouched() {
2362  return $this->mPage->getTouched();
2363  }
2364 
2369  public function getUndoContent( Revision $undo, Revision $undoafter = null ) {
2370  return $this->mPage->getUndoContent( $undo, $undoafter );
2371  }
2372 
2377  public function getUser( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2378  return $this->mPage->getUser( $audience, $user );
2379  }
2380 
2385  public function getUserText( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2386  return $this->mPage->getUserText( $audience, $user );
2387  }
2388 
2393  public function hasViewableContent() {
2394  return $this->mPage->hasViewableContent();
2395  }
2396 
2401  public function insertOn( $dbw, $pageId = null ) {
2402  return $this->mPage->insertOn( $dbw, $pageId );
2403  }
2404 
2409  public function insertProtectNullRevision( $revCommentMsg, array $limit,
2410  array $expiry, $cascade, $reason, $user = null
2411  ) {
2412  return $this->mPage->insertProtectNullRevision( $revCommentMsg, $limit,
2413  $expiry, $cascade, $reason, $user
2414  );
2415  }
2416 
2421  public function insertRedirect() {
2422  return $this->mPage->insertRedirect();
2423  }
2424 
2429  public function insertRedirectEntry( Title $rt, $oldLatest = null ) {
2430  return $this->mPage->insertRedirectEntry( $rt, $oldLatest );
2431  }
2432 
2437  public function isCountable( $editInfo = false ) {
2438  return $this->mPage->isCountable( $editInfo );
2439  }
2440 
2445  public function isRedirect() {
2446  return $this->mPage->isRedirect();
2447  }
2448 
2453  public function loadFromRow( $data, $from ) {
2454  return $this->mPage->loadFromRow( $data, $from );
2455  }
2456 
2461  public function loadPageData( $from = 'fromdb' ) {
2462  $this->mPage->loadPageData( $from );
2463  }
2464 
2469  public function lockAndGetLatest() {
2470  return $this->mPage->lockAndGetLatest();
2471  }
2472 
2477  public function makeParserOptions( $context ) {
2478  return $this->mPage->makeParserOptions( $context );
2479  }
2480 
2485  public function pageDataFromId( $dbr, $id, $options = [] ) {
2486  return $this->mPage->pageDataFromId( $dbr, $id, $options );
2487  }
2488 
2493  public function pageDataFromTitle( $dbr, $title, $options = [] ) {
2494  return $this->mPage->pageDataFromTitle( $dbr, $title, $options );
2495  }
2496 
2501  public function prepareContentForEdit(
2502  Content $content, $revision = null, User $user = null,
2503  $serialFormat = null, $useCache = true
2504  ) {
2505  return $this->mPage->prepareContentForEdit(
2506  $content, $revision, $user,
2507  $serialFormat, $useCache
2508  );
2509  }
2510 
2516  public function prepareTextForEdit( $text, $revid = null, User $user = null ) {
2517  return $this->mPage->prepareTextForEdit( $text, $revid, $user );
2518  }
2519 
2524  public function protectDescription( array $limit, array $expiry ) {
2525  return $this->mPage->protectDescription( $limit, $expiry );
2526  }
2527 
2532  public function protectDescriptionLog( array $limit, array $expiry ) {
2533  return $this->mPage->protectDescriptionLog( $limit, $expiry );
2534  }
2535 
2540  public function replaceSectionAtRev( $sectionId, Content $sectionContent,
2541  $sectionTitle = '', $baseRevId = null
2542  ) {
2543  return $this->mPage->replaceSectionAtRev( $sectionId, $sectionContent,
2544  $sectionTitle, $baseRevId
2545  );
2546  }
2547 
2552  public function replaceSectionContent(
2553  $sectionId, Content $sectionContent, $sectionTitle = '', $edittime = null
2554  ) {
2555  return $this->mPage->replaceSectionContent(
2556  $sectionId, $sectionContent, $sectionTitle, $edittime
2557  );
2558  }
2559 
2564  public function setTimestamp( $ts ) {
2565  return $this->mPage->setTimestamp( $ts );
2566  }
2567 
2572  public function shouldCheckParserCache( ParserOptions $parserOptions, $oldId ) {
2573  return $this->mPage->shouldCheckParserCache( $parserOptions, $oldId );
2574  }
2575 
2580  public function supportsSections() {
2581  return $this->mPage->supportsSections();
2582  }
2583 
2589  return $this->mPage->triggerOpportunisticLinksUpdate( $parserOutput );
2590  }
2591 
2596  public function updateCategoryCounts( array $added, array $deleted, $id = 0 ) {
2597  return $this->mPage->updateCategoryCounts( $added, $deleted, $id );
2598  }
2599 
2604  public function updateIfNewerOn( $dbw, $revision ) {
2605  return $this->mPage->updateIfNewerOn( $dbw, $revision );
2606  }
2607 
2612  public function updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect = null ) {
2613  return $this->mPage->updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect = null );
2614  }
2615 
2620  public function updateRevisionOn( $dbw, $revision, $lastRevision = null,
2621  $lastRevIsRedirect = null
2622  ) {
2623  return $this->mPage->updateRevisionOn( $dbw, $revision, $lastRevision,
2624  $lastRevIsRedirect
2625  );
2626  }
2627 
2636  public function doUpdateRestrictions( array $limit, array $expiry, &$cascade,
2637  $reason, User $user
2638  ) {
2639  return $this->mPage->doUpdateRestrictions( $limit, $expiry, $cascade, $reason, $user );
2640  }
2641 
2649  public function updateRestrictions( $limit = [], $reason = '',
2650  &$cascade = 0, $expiry = []
2651  ) {
2652  return $this->mPage->doUpdateRestrictions(
2653  $limit,
2654  $expiry,
2655  $cascade,
2656  $reason,
2657  $this->getContext()->getUser()
2658  );
2659  }
2660 
2669  public function doDeleteArticle(
2670  $reason, $suppress = false, $u1 = null, $u2 = null, &$error = ''
2671  ) {
2672  return $this->mPage->doDeleteArticle( $reason, $suppress, $u1, $u2, $error );
2673  }
2674 
2684  public function doRollback( $fromP, $summary, $token, $bot, &$resultDetails, User $user = null ) {
2685  $user = is_null( $user ) ? $this->getContext()->getUser() : $user;
2686  return $this->mPage->doRollback( $fromP, $summary, $token, $bot, $resultDetails, $user );
2687  }
2688 
2697  public function commitRollback( $fromP, $summary, $bot, &$resultDetails, User $guser = null ) {
2698  $guser = is_null( $guser ) ? $this->getContext()->getUser() : $guser;
2699  return $this->mPage->commitRollback( $fromP, $summary, $bot, $resultDetails, $guser );
2700  }
2701 
2706  public function generateReason( &$hasHistory ) {
2707  $title = $this->mPage->getTitle();
2709  return $handler->getAutoDeleteReason( $title, $hasHistory );
2710  }
2711 
2717  public static function selectFields() {
2718  wfDeprecated( __METHOD__, '1.24' );
2719  return WikiPage::selectFields();
2720  }
2721 
2727  public static function onArticleCreate( $title ) {
2728  wfDeprecated( __METHOD__, '1.24' );
2730  }
2731 
2737  public static function onArticleDelete( $title ) {
2738  wfDeprecated( __METHOD__, '1.24' );
2740  }
2741 
2747  public static function onArticleEdit( $title ) {
2748  wfDeprecated( __METHOD__, '1.24' );
2750  }
2751 
2759  public static function getAutosummary( $oldtext, $newtext, $flags ) {
2760  return WikiPage::getAutosummary( $oldtext, $newtext, $flags );
2761  }
2762  // ******
2763 }
pageDataFromId($dbr, $id, $options=[])
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2485
__set($fname, $fvalue)
Use PHP's magic __set handler to handle setting of raw WikiPage fields for backwards compatibility...
Definition: Article.php:2067
static newFromName($name, $validate= 'valid')
Static factory method for creation from username.
Definition: User.php:525
getRedirectTarget()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2319
static purgePatrolFooterCache($articleID)
Purge the cache used to check if it is worth showing the patrol footer For example, it is done during re-uploads when file patrol is used.
Definition: Article.php:1222
const FOR_THIS_USER
Definition: Revision.php:93
viewRedirect($target, $appendSubtitle=true, $forceKnown=false)
Return the HTML for the top of a redirect page.
Definition: Article.php:1531
static newFromID($id, $flags=0)
Create a new Title from an article ID.
Definition: Title.php:402
static closeElement($element)
Returns "".
Definition: Html.php:305
const PURGE_ALL
Definition: WikiPage.php:89
lockAndGetLatest()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2469
static onArticleCreate(Title $title)
The onArticle*() functions are supposed to be a kind of hooks which should be called whenever any of ...
Definition: WikiPage.php:3372
static onArticleCreate($title)
Definition: Article.php:2727
getUndoContent(Revision $undo, Revision $undoafter=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2369
static getMainWANInstance()
Get the main WAN cache object.
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return an< a > element with HTML attributes $attribs and contents $html will be returned If you return $ret will be returned and may include noclasses & $html
Definition: hooks.txt:1936
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
Definition: deferred.txt:11
Interface for objects which can provide a MediaWiki context on request.
static isInRCLifespan($timestamp, $tolerance=0)
Check whether the given timestamp is new enough to have a RC row with a given tolerance as the recent...
static revComment(Revision $rev, $local=false, $isPublic=false)
Wrap and format the given revision's comment block, if the current user is allowed to view it...
Definition: Linker.php:1550
WikiPage $mPage
The WikiPage object of this instance.
Definition: Article.php:39
wfGetDB($db, $groups=[], $wiki=false)
Get a Database object.
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output $out
Definition: hooks.txt:802
getRobotPolicy($action, $pOutput=null)
Get the robot policy to be used for the current view.
Definition: Article.php:832
the array() calling protocol came about after MediaWiki 1.4rc1.
getRedirectedFrom()
Get the page this view was redirected from.
Definition: Article.php:155
null for the local wiki Added should default to null in handler for backwards compatibility add a value to it if you want to add a cookie that have to vary cache options can modify $query
Definition: hooks.txt:1555
updateRestrictions($limit=[], $reason= '', &$cascade=0, $expiry=[])
Definition: Article.php:2649
doEditContent(Content $content, $summary, $flags=0, $baseRevId=false, User $user=null, $serialFormat=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2139
getText($audience=Revision::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2344
getLatest()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2287
setParserOptions(ParserOptions $options)
Override the ParserOptions used to render the primary article wikitext.
Definition: Article.php:1998
doEditUpdates(Revision $revision, User $user, array $options=[])
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2151
$context
Definition: load.php:50
static getRevDeleteLink(User $user, Revision $rev, Title $title)
Get a revision-deletion link, or disabled link, or nothing, depending on user permissions & the setti...
Definition: Linker.php:2101
if(count($args)==0) $dir
static element($element, $attribs=null, $contents= '', $allowShortTag=true)
Format an XML element with given attributes and, optionally, text content.
Definition: Xml.php:39
exists()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2183
supportsSections()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2580
static makeUrl($name, $urlaction= '')
Definition: Skin.php:1085
getContent()
Note that getContent does not follow redirects anymore.
Definition: Article.php:212
doRollback($fromP, $summary, $token, $bot, &$resultDetails, User $user=null)
Definition: Article.php:2684
Show an error when the wiki is locked/read-only and the user tries to do something that requires writ...
doDeleteArticle($reason, $suppress=false, $u1=null, $u2=null, &$error= '')
Definition: Article.php:2669
protect()
action=protect handler
Definition: Article.php:1615
null for the local wiki Added should default to null in handler for backwards compatibility add a value to it if you want to add a cookie that have to vary cache options can modify prev or next refreshes the diff cache $unhide
Definition: hooks.txt:1555
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException'returning false will NOT prevent logging $e
Definition: hooks.txt:2102
IContextSource $mContext
The context this Article is executed in.
Definition: Article.php:36
Set options of the Parser.
isCountable($editInfo=false)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2437
Wrapper allowing us to handle a system message as a Content object.
getPage()
Get the WikiPage object of this instance.
Definition: Article.php:183
getParserOutput($oldid=null, User $user=null)
#@-
Definition: Article.php:1980
static useFileCache(IContextSource $context, $mode=self::MODE_NORMAL)
Check if pages can be cached for this request/user.
updateCategoryCounts(array $added, array $deleted, $id=0)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2596
string bool $mRedirectUrl
URL to redirect to or false if none.
Definition: Article.php:66
loadPageData($from= 'fromdb')
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2461
if(!isset($args[0])) $lang
ParserOptions $mParserOptions
ParserOptions object for $wgUser articles.
Definition: Article.php:42
Content $mContentObject
Content of the revision we are working on.
Definition: Article.php:54
Special handling for category description pages, showing pages, subcategories and file that belong to...
isFileCacheable($mode=HTMLFileCache::MODE_NORMAL)
Check if the page can be cached.
Definition: Article.php:1952
doDeleteUpdates($id, Content $content=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2120
static newFromConds($conds, $fname=__METHOD__, $dbType=DB_REPLICA)
Find the first recent change matching some specific conditions.
adjustDisplayTitle(ParserOutput $pOutput)
Adjust title for pages with displaytitle, -{T|}- or language conversion.
Definition: Article.php:728
static hidden($name, $value, array $attribs=[])
Convenience function to produce an input element with type=hidden.
Definition: Html.php:758
shouldCheckParserCache(ParserOptions $parserOptions, $oldId)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2572
doUpdateRestrictions(array $limit, array $expiry, &$cascade, $reason, User $user)
Definition: Article.php:2636
Class for viewing MediaWiki article and history.
Definition: Article.php:34
null for the local wiki Added in
Definition: hooks.txt:1555
Using a hook running we can avoid having all this option specific stuff in our mainline code Using the function array $article
Definition: hooks.txt:78
Page view caching in the file system.
followRedirect()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2191
confirmDelete($reason)
Output deletion confirmation dialog.
Definition: Article.php:1758
Class for viewing MediaWiki file description pages.
Definition: ImagePage.php:28
doPurge($flags=WikiPage::PURGE_ALL)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2159
triggerOpportunisticLinksUpdate(ParserOutput $parserOutput)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2588
getOldIDFromRequest()
Sets $this->mRedirectUrl to a correct URL if the query parameters are incorrect.
Definition: Article.php:273
__get($fname)
Use PHP's magic __get handler to handle accessing of raw WikiPage fields for backwards compatibility...
Definition: Article.php:2052
it s the revision text itself In either if gzip is the revision text is gzipped $flags
Definition: hooks.txt:2703
getDeletionUpdates(Content $content=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2263
updateIfNewerOn($dbw, $revision)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2604
clearPreparedEdit()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2099
static getMainStashInstance()
Get the cache object for the main stash.
when a variable name is used in a it is silently declared as a new local masking the global
Definition: design.txt:93
getContributors()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2247
static submitButton($value, $attribs=[])
Convenience function to build an HTML submit button When $wgUseMediaWikiUIEverywhere is true it will ...
Definition: Xml.php:460
commitRollback($fromP, $summary, $bot, &$resultDetails, User $guser=null)
Definition: Article.php:2697
showMissingArticle()
Show the error text for a missing article.
Definition: Article.php:1231
wfDebug($text, $dest= 'all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
static showLogExtract(&$out, $types=[], $page= '', $user= '', $param=[])
Show log extract.
protectDescription(array $limit, array $expiry)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2524
Class to simplify the use of log pages.
Definition: LogPage.php:32
usually copyright or history_copyright This message must be in HTML not wikitext & $link
Definition: hooks.txt:2889
getContext()
Gets the context this Article is executed in.
Definition: Article.php:2035
static closeElement($element)
Shortcut to close an XML element.
Definition: Xml.php:118
isRedirect()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2445
__construct(Title $title, $oldId=null)
Constructor and clear the article.
Definition: Article.php:82
setTimestamp($ts)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2564
protectDescriptionLog(array $limit, array $expiry)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2532
static onArticleDelete($title)
Definition: Article.php:2737
static openElement($element, $attribs=[])
Identical to rawElement(), but has no third parameter and omits the end tag (and the self-closing '/'...
Definition: Html.php:247
Interface for type hinting (accepts WikiPage, Article, ImagePage, CategoryPage)
Definition: Page.php:24
makeParserOptions($context)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2477
wfEscapeWikiText($text)
Escapes the given text so that it may be output using addWikiText() without any linking, formatting, etc.
wfReadOnly()
Check whether the wiki is in read-only mode.
static getMain()
Static methods.
prepareContentForEdit(Content $content, $revision=null, User $user=null, $serialFormat=null, $useCache=true)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2501
const FOR_PUBLIC
Definition: Revision.php:92
static getCanonicalName($index)
Returns the canonical (English) name for a given index.
$wgUseFileCache
This will cache static pages for non-logged-in users to reduce database traffic on public sites...
either a unescaped string or a HtmlArmor object after in associative array form externallinks including delete and has completed for all link tables whether this was an auto creation default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a set this to the key of the message First element is the message additional optional elements are parameters for the key that are processed with wfMessage() -> params() ->parseAsBlock()-offset Set to overwrite offset parameter in $wgRequest set to ''to unsetoffset-wrap String Wrap the message in html(usually something like"&lt
showViewFooter()
Show the footer section of an ordinary page view.
Definition: Article.php:1029
if($limit) $timestamp
static newFromTarget($specificTarget, $vagueTarget=null, $fromMaster=false)
Given a target and the target's type, get an existing Block object if possible.
Definition: Block.php:1082
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context $parserOutput
Definition: hooks.txt:1046
getRevisionFetched()
Get the fetched Revision object depending on request parameters or null on failure.
Definition: Article.php:461
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context $options
Definition: hooks.txt:1046
insertProtectNullRevision($revCommentMsg, array $limit, array $expiry, $cascade, $reason, $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2409
const NS_MEDIA
Definition: Defines.php:44
static isValid($ip)
Validate an IP address.
Definition: IP.php:113
showRedirectedFromHeader()
If this request is a redirect view, send "redirected from" subtitle to the output.
Definition: Article.php:941
static getContentText(Content $content=null)
Convenience function for getting flat text from a Content object.
ParserOutput $mParserOutput
Definition: Article.php:75
$summary
getRevision()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2335
it s the revision text itself In either if gzip is set
Definition: hooks.txt:2703
generateReason(&$hasHistory)
Definition: Article.php:2706
static getRedirectHeaderHtml(Language $lang, $target, $forceKnown=false)
Return the HTML for the top of a redirect page.
Definition: Article.php:1553
insertRedirect()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2421
int null $mOldId
The oldid of the article that is to be shown, 0 for the current revision.
Definition: Article.php:60
static openElement($element, $attribs=null)
This opens an XML element.
Definition: Xml.php:109
hasViewableContent()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2393
getComment($audience=Revision::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2223
Base interface for content objects.
Definition: Content.php:34
getTitle()
Get the title object of the article.
Definition: Article.php:173
loadFromRow($data, $from)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2453
$cache
Definition: mcc.php:33
getActionOverrides()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2199
doViewUpdates(User $user, $oldid=0)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2175
getTitle()
Get the title object of the article.
Definition: WikiPage.php:232
const NS_CATEGORY
Definition: Defines.php:70
doDeleteArticleReal($reason, $suppress=false, $u1=null, $u2=null, &$error= '', User $user=null, $tags=[])
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2107
static newFromTitle($title, IContextSource $context)
Create an Article object of the appropriate class for the given page.
Definition: Article.php:112
wfDeprecated($function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
render()
Handle action=render.
Definition: Article.php:1605
static isIP($name)
Does the string match an anonymous IP address?
Definition: User.php:788
const DELETED_RESTRICTED
Definition: Revision.php:88
do that in ParserLimitReportFormat instead use this to modify the parameters of the image and a DIV can begin in one section and end in another Make sure your code can handle that case gracefully See the EditSectionClearerLink extension for an example zero but section is usually empty & $sectionContent
Definition: hooks.txt:2491
getCategories()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2215
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:953
static linkKnown($target, $html=null, $customAttribs=[], $query=[], $options=[ 'known'])
Identical to link(), except $options defaults to 'known'.
Definition: Linker.php:255
replaceSectionAtRev($sectionId, Content $sectionContent, $sectionTitle= '', $baseRevId=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2540
static run($event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:131
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at etc Handles the details of getting and saving to the user table of the and dealing with sessions and cookies OutputPage Encapsulates the entire HTML page that will be sent in response to any server request It is used by calling its functions to add text
Definition: design.txt:12
checkFlags($flags)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2083
const NS_FILE
Definition: Defines.php:62
static makeContent($text, Title $title=null, $modelId=null, $format=null)
Convenience function for creating a Content object from a given textual representation.
$wgRedirectSources
If local interwikis are set up which allow redirects, set this regexp to restrict URLs which will be ...
const TYPE_AUTO
Definition: Block.php:81
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context the output can only depend on parameters provided to this hook not on global state indicating whether full HTML should be generated If generation of HTML may be but other information should still be present in the ParserOutput object to manipulate or replace but no entry for that model exists in $wgContentHandlers if desired whether it is OK to use $contentModel on $title Handler functions that modify $ok should generally return false to prevent further hooks from further modifying $ok inclusive false for true for descending in case the handler function wants to provide a converted Content object Note that $result getContentModel() must return $toModel. 'CustomEditor'$rcid is used in generating this variable which contains information about the new such as the revision s whether the revision was marked as a minor edit or etc which include things like revision author revision RevisionDelete link and more some of which may have been injected with the DiffRevisionTools hook $nextlink
Definition: hooks.txt:1156
presenting them properly to the user as errors is done by the caller return true use this to change the list i e etc $rev
Definition: hooks.txt:1721
static getForTitle(Title $title)
Returns the appropriate ContentHandler singleton for the given title.
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
getContentHandler()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2231
showCssOrJsPage($showCacheHint=true)
Show a page view for a page formatted as CSS or JavaScript.
Definition: Article.php:795
const NS_MEDIAWIKI
Definition: Defines.php:64
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a local account $user
Definition: hooks.txt:242
static link($target, $html=null, $customAttribs=[], $query=[], $options=[])
This function returns an HTML link to the given target.
Definition: Linker.php:203
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context the output can only depend on parameters provided to this hook not on global state indicating whether full HTML should be generated If generation of HTML may be but other information should still be present in the ParserOutput object to manipulate or replace but no entry for that model exists in $wgContentHandlers if desired whether it is OK to use $contentModel on $title Handler functions that modify $ok should generally return false to prevent further hooks from further modifying $ok inclusive false for true for descending in case the handler function wants to provide a converted Content object Note that $result getContentModel() must return $toModel. 'CustomEditor'$rcid is used in generating this variable which contains information about the new such as the revision s whether the revision was marked as a minor edit or not
Definition: hooks.txt:1156
static onArticleEdit(Title $title, Revision $revision=null)
Purge caches on page update etc.
Definition: WikiPage.php:3447
Title $mRedirectedFrom
Title from which we were redirected here.
Definition: Article.php:63
const DELETED_TEXT
Definition: Revision.php:85
getCreator($audience=Revision::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2255
static singleton()
Get an instance of this object.
Definition: ParserCache.php:36
addHelpLink($to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
Definition: Article.php:1588
newPage(Title $title)
Definition: Article.php:91
Class representing a MediaWiki article and history.
Definition: WikiPage.php:32
static newFromId($id, $flags=0)
Load a page revision from a given revision ID number.
Definition: Revision.php:110
getOldestRevision()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2311
string $mContent
Text of the revision we are working on.
Definition: Article.php:48
static makeExternalLink($url, $text, $escape=true, $linktype= '', $attribs=[], $title=null)
Make an external link.
Definition: Linker.php:934
clear()
Clear the object.
Definition: Article.php:190
$from
bool $mContentLoaded
Is the content ($mContent) already loaded?
Definition: Article.php:57
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
getLastPurgeTimestamp()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2167
getAutoDeleteReason(&$hasHistory)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2207
checkTouched()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2091
updateRevisionOn($dbw, $revision, $lastRevision=null, $lastRevIsRedirect=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2620
static selectFields()
Definition: Article.php:2717
prepareTextForEdit($text, $revid=null, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2516
error also a ContextSource you ll probably need to make sure the header is varied on $request
Definition: hooks.txt:2573
static newFromID($id)
Constructor from a page id.
Definition: Article.php:100
if(!defined( 'MEDIAWIKI')) $fname
This file is not a valid entry point, perform no further processing unless MEDIAWIKI is defined...
Definition: Setup.php:36
getId()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2279
getTimestamp()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2353
static input($name, $value= '', $type= 'text', array $attribs=[])
Convenience function to produce an "" element.
Definition: Html.php:675
pageDataFromTitle($dbr, $title, $options=[])
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2493
getMinorEdit()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2303
view()
This is the default action of the index.php entry point: just view the page of the given title...
Definition: Article.php:484
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content $content
Definition: hooks.txt:1046
static onArticleDelete(Title $title)
Clears caches when article is deleted.
Definition: WikiPage.php:3398
getContentObject()
Returns a Content object representing the pages effective display content, not necessarily the revisi...
Definition: Article.php:233
getOldID()
Definition: Article.php:260
doDelete($reason, $suppress=false)
Perform a deletion and output success or failure messages.
Definition: Article.php:1868
showNamespaceHeader()
Show a header specific to the namespace currently being viewed, like [[MediaWiki:Talkpagetext]].
Definition: Article.php:1015
Show an error when a user tries to do something they do not have the necessary permissions for...
updateRedirectOn($dbw, $redirectTitle, $lastRevIsRedirect=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2612
tryFileCache()
checkLastModified returns true if it has taken care of all output to the client that is necessary for...
Definition: Article.php:1921
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context the output can only depend on parameters provided to this hook not on global state indicating whether full HTML should be generated If generation of HTML may be but other information should still be present in the ParserOutput object to manipulate or replace but no entry for that model exists in $wgContentHandlers if desired whether it is OK to use $contentModel on $title Handler functions that modify $ok should generally return false to prevent further hooks from further modifying $ok inclusive $limit
Definition: hooks.txt:1046
insertRedirectEntry(Title $rt, $oldLatest=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2429
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set $status
Definition: hooks.txt:1046
doEdit($text, $summary, $flags=0, $baseRevId=false, $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2130
static checkLabel($label, $name, $id, $checked=false, $attribs=[])
Convenience function to build an HTML checkbox with a label.
Definition: Xml.php:420
int $mRevIdFetched
Revision ID of revision we are working on.
Definition: Article.php:69
wfMemcKey()
Make a cache key for the local wiki.
fetchContentObject()
Get text content object Does NOT follow redirects.
Definition: Article.php:371
const DB_REPLICA
Definition: defines.php:22
setOldSubtitle($oldid=0)
Generate the navigation links when browsing through an article revisions It shows the information as:...
Definition: Article.php:1396
isCurrent()
Returns true if the currently-referenced revision is the current edit to this page (and it exists)...
Definition: Article.php:445
unprotect()
action=unprotect handler (alias)
Definition: Article.php:1623
Handles the page protection UI and backend.
getTouched()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2361
static onArticleEdit($title)
Definition: Article.php:2747
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output modifiable modifiable after all normalizations have been except for the $wgMaxImageArea check set to true or false to override the $wgMaxImageArea check result gives extension the possibility to transform it themselves $handler
Definition: hooks.txt:802
getUser($audience=Revision::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2377
static newFromWikiPage(WikiPage $page, IContextSource $context)
Create an Article object of the appropriate class for the given page.
Definition: Article.php:144
fetchContent()
Get text of an article from database Does NOT follow redirects.
Definition: Article.php:333
static selectFields()
Return the list of revision fields that should be selected to create a new page.
Definition: WikiPage.php:281
static getAutosummary($oldtext, $newtext, $flags)
Return an applicable autosummary if one exists for the given edit.
Definition: WikiPage.php:3531
static doWatchOrUnwatch($watch, Title $title, User $user)
Watch or unwatch a page.
Definition: WatchAction.php:84
$wgSend404Code
Some web hosts attempt to rewrite all responses with a 404 (not found) status code, mangling or hiding MediaWiki's output.
replaceSectionContent($sectionId, Content $sectionContent, $sectionTitle= '', $edittime=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2552
setContext($context)
Sets the context this Article is executed in.
Definition: Article.php:2025
static listDropDown($name= '', $list= '', $other= '', $selected= '', $class= '', $tabindex=null)
Build a drop-down box from a textual list.
Definition: Xml.php:508
getRevIdFetched()
Use this to fetch the rev ID used on page views.
Definition: Article.php:472
const NS_USER_TALK
Definition: Defines.php:59
static revUserTools($rev, $isPublic=false)
Generate a user tool link cluster if the current user is allowed to view it.
Definition: Linker.php:1141
showDiffPage()
Show a diff page according to current request variables.
Definition: Article.php:741
static element($element, $attribs=[], $contents= '')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
Definition: Html.php:229
getRedirectURL($rt)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2327
Definition: Block.php:25
static getAutosummary($oldtext, $newtext, $flags)
Definition: Article.php:2759
showDeletedRevisionHeader()
If the revision requested for view is deleted, check permissions.
Definition: Article.php:1353
static makeTitle($ns, $title, $fragment= '', $interwiki= '')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:511
setRedirectedFrom(Title $from)
Tell the page view functions that this view was redirected from another page on the wiki...
Definition: Article.php:164
getContentModel()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2239
do that in ParserLimitReportFormat instead use this to modify the parameters of the image and a DIV can begin in one section and end in another Make sure your code can handle that case gracefully See the EditSectionClearerLink extension for an example zero but section is usually empty its values are the globals values before the output is cached $page
Definition: hooks.txt:2491
getLinksTimestamp()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2295
insertOn($dbw, $pageId=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2401
static runLegacyHooks($event, $args=[], $deprecatedVersion=null)
Call a legacy hook that uses text instead of Content objects.
getParserOptions()
Get parser options suitable for rendering the primary article wikitext.
Definition: Article.php:2011
const RC_LOG
Definition: Defines.php:138
Revision $mRevision
Revision we are working on.
Definition: Article.php:72
getUserText($audience=Revision::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2385
showPatrolFooter()
If patrol is possible, output a patrol UI box.
Definition: Article.php:1052
static label($label, $id, array $attribs=[])
Convenience function for generating a label for inputs.
Definition: Html.php:742
static formatRobotPolicy($policy)
Converts a String robot policy into an associative array, to allow merging of several policies using ...
Definition: Article.php:912
getHiddenCategories()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2271