MediaWiki  1.28.1
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 
431  Hooks::run(
432  'ArticleAfterFetchContentObject',
433  [ &$this, &$this->mContentObject ]
434  );
435 
436  return $this->mContentObject;
437  }
438 
444  public function isCurrent() {
445  # If no oldid, this is the current version.
446  if ( $this->getOldID() == 0 ) {
447  return true;
448  }
449 
450  return $this->mPage->exists() && $this->mRevision && $this->mRevision->isCurrent();
451  }
452 
460  public function getRevisionFetched() {
461  $this->fetchContentObject();
462 
463  return $this->mRevision;
464  }
465 
471  public function getRevIdFetched() {
472  if ( $this->mRevIdFetched ) {
473  return $this->mRevIdFetched;
474  } else {
475  return $this->mPage->getLatest();
476  }
477  }
478 
483  public function view() {
484  global $wgUseFileCache, $wgDebugToolbar;
485 
486  # Get variables from query string
487  # As side effect this will load the revision and update the title
488  # in a revision ID is passed in the request, so this should remain
489  # the first call of this method even if $oldid is used way below.
490  $oldid = $this->getOldID();
491 
492  $user = $this->getContext()->getUser();
493  # Another whitelist check in case getOldID() is altering the title
494  $permErrors = $this->getTitle()->getUserPermissionsErrors( 'read', $user );
495  if ( count( $permErrors ) ) {
496  wfDebug( __METHOD__ . ": denied on secondary read check\n" );
497  throw new PermissionsError( 'read', $permErrors );
498  }
499 
500  $outputPage = $this->getContext()->getOutput();
501  # getOldID() may as well want us to redirect somewhere else
502  if ( $this->mRedirectUrl ) {
503  $outputPage->redirect( $this->mRedirectUrl );
504  wfDebug( __METHOD__ . ": redirecting due to oldid\n" );
505 
506  return;
507  }
508 
509  # If we got diff in the query, we want to see a diff page instead of the article.
510  if ( $this->getContext()->getRequest()->getCheck( 'diff' ) ) {
511  wfDebug( __METHOD__ . ": showing diff page\n" );
512  $this->showDiffPage();
513 
514  return;
515  }
516 
517  # Set page title (may be overridden by DISPLAYTITLE)
518  $outputPage->setPageTitle( $this->getTitle()->getPrefixedText() );
519 
520  $outputPage->setArticleFlag( true );
521  # Allow frames by default
522  $outputPage->allowClickjacking();
523 
524  $parserCache = ParserCache::singleton();
525 
526  $parserOptions = $this->getParserOptions();
527  # Render printable version, use printable version cache
528  if ( $outputPage->isPrintable() ) {
529  $parserOptions->setIsPrintable( true );
530  $parserOptions->setEditSection( false );
531  } elseif ( !$this->isCurrent() || !$this->getTitle()->quickUserCan( 'edit', $user ) ) {
532  $parserOptions->setEditSection( false );
533  }
534 
535  # Try client and file cache
536  if ( !$wgDebugToolbar && $oldid === 0 && $this->mPage->checkTouched() ) {
537  # Try to stream the output from file cache
538  if ( $wgUseFileCache && $this->tryFileCache() ) {
539  wfDebug( __METHOD__ . ": done file cache\n" );
540  # tell wgOut that output is taken care of
541  $outputPage->disable();
542  $this->mPage->doViewUpdates( $user, $oldid );
543 
544  return;
545  }
546  }
547 
548  # Should the parser cache be used?
549  $useParserCache = $this->mPage->shouldCheckParserCache( $parserOptions, $oldid );
550  wfDebug( 'Article::view using parser cache: ' . ( $useParserCache ? 'yes' : 'no' ) . "\n" );
551  if ( $user->getStubThreshold() ) {
552  $this->getContext()->getStats()->increment( 'pcache_miss_stub' );
553  }
554 
555  $this->showRedirectedFromHeader();
556  $this->showNamespaceHeader();
557 
558  # Iterate through the possible ways of constructing the output text.
559  # Keep going until $outputDone is set, or we run out of things to do.
560  $pass = 0;
561  $outputDone = false;
562  $this->mParserOutput = false;
563 
564  while ( !$outputDone && ++$pass ) {
565  switch ( $pass ) {
566  case 1:
567  Hooks::run( 'ArticleViewHeader', [ &$this, &$outputDone, &$useParserCache ] );
568  break;
569  case 2:
570  # Early abort if the page doesn't exist
571  if ( !$this->mPage->exists() ) {
572  wfDebug( __METHOD__ . ": showing missing article\n" );
573  $this->showMissingArticle();
574  $this->mPage->doViewUpdates( $user );
575  return;
576  }
577 
578  # Try the parser cache
579  if ( $useParserCache ) {
580  $this->mParserOutput = $parserCache->get( $this->mPage, $parserOptions );
581 
582  if ( $this->mParserOutput !== false ) {
583  if ( $oldid ) {
584  wfDebug( __METHOD__ . ": showing parser cache contents for current rev permalink\n" );
585  $this->setOldSubtitle( $oldid );
586  } else {
587  wfDebug( __METHOD__ . ": showing parser cache contents\n" );
588  }
589  $outputPage->addParserOutput( $this->mParserOutput );
590  # Ensure that UI elements requiring revision ID have
591  # the correct version information.
592  $outputPage->setRevisionId( $this->mPage->getLatest() );
593  # Preload timestamp to avoid a DB hit
594  $cachedTimestamp = $this->mParserOutput->getTimestamp();
595  if ( $cachedTimestamp !== null ) {
596  $outputPage->setRevisionTimestamp( $cachedTimestamp );
597  $this->mPage->setTimestamp( $cachedTimestamp );
598  }
599  $outputDone = true;
600  }
601  }
602  break;
603  case 3:
604  # This will set $this->mRevision if needed
605  $this->fetchContentObject();
606 
607  # Are we looking at an old revision
608  if ( $oldid && $this->mRevision ) {
609  $this->setOldSubtitle( $oldid );
610 
611  if ( !$this->showDeletedRevisionHeader() ) {
612  wfDebug( __METHOD__ . ": cannot view deleted revision\n" );
613  return;
614  }
615  }
616 
617  # Ensure that UI elements requiring revision ID have
618  # the correct version information.
619  $outputPage->setRevisionId( $this->getRevIdFetched() );
620  # Preload timestamp to avoid a DB hit
621  $outputPage->setRevisionTimestamp( $this->mPage->getTimestamp() );
622 
623  # Pages containing custom CSS or JavaScript get special treatment
624  if ( $this->getTitle()->isCssOrJsPage() || $this->getTitle()->isCssJsSubpage() ) {
625  wfDebug( __METHOD__ . ": showing CSS/JS source\n" );
626  $this->showCssOrJsPage();
627  $outputDone = true;
628  } elseif ( !Hooks::run( 'ArticleContentViewCustom',
629  [ $this->fetchContentObject(), $this->getTitle(), $outputPage ] ) ) {
630 
631  # Allow extensions do their own custom view for certain pages
632  $outputDone = true;
633  } elseif ( !ContentHandler::runLegacyHooks(
634  'ArticleViewCustom',
635  [ $this->fetchContentObject(), $this->getTitle(), $outputPage ],
636  '1.21'
637  ) ) {
638  # Allow extensions do their own custom view for certain pages
639  $outputDone = true;
640  }
641  break;
642  case 4:
643  # Run the parse, protected by a pool counter
644  wfDebug( __METHOD__ . ": doing uncached parse\n" );
645 
646  $content = $this->getContentObject();
647  $poolArticleView = new PoolWorkArticleView( $this->getPage(), $parserOptions,
648  $this->getRevIdFetched(), $useParserCache, $content );
649 
650  if ( !$poolArticleView->execute() ) {
651  $error = $poolArticleView->getError();
652  if ( $error ) {
653  $outputPage->clearHTML(); // for release() errors
654  $outputPage->enableClientCache( false );
655  $outputPage->setRobotPolicy( 'noindex,nofollow' );
656 
657  $errortext = $error->getWikiText( false, 'view-pool-error' );
658  $outputPage->addWikiText( '<div class="errorbox">' . $errortext . '</div>' );
659  }
660  # Connection or timeout error
661  return;
662  }
663 
664  $this->mParserOutput = $poolArticleView->getParserOutput();
665  $outputPage->addParserOutput( $this->mParserOutput );
666  if ( $content->getRedirectTarget() ) {
667  $outputPage->addSubtitle( "<span id=\"redirectsub\">" .
668  $this->getContext()->msg( 'redirectpagesub' )->parse() . "</span>" );
669  }
670 
671  # Don't cache a dirty ParserOutput object
672  if ( $poolArticleView->getIsDirty() ) {
673  $outputPage->setCdnMaxage( 0 );
674  $outputPage->addHTML( "<!-- parser cache is expired, " .
675  "sending anyway due to pool overload-->\n" );
676  }
677 
678  $outputDone = true;
679  break;
680  # Should be unreachable, but just in case...
681  default:
682  break 2;
683  }
684  }
685 
686  # Get the ParserOutput actually *displayed* here.
687  # Note that $this->mParserOutput is the *current*/oldid version output.
688  $pOutput = ( $outputDone instanceof ParserOutput )
689  ? $outputDone // object fetched by hook
690  : $this->mParserOutput;
691 
692  # Adjust title for main page & pages with displaytitle
693  if ( $pOutput ) {
694  $this->adjustDisplayTitle( $pOutput );
695  }
696 
697  # For the main page, overwrite the <title> element with the con-
698  # tents of 'pagetitle-view-mainpage' instead of the default (if
699  # that's not empty).
700  # This message always exists because it is in the i18n files
701  if ( $this->getTitle()->isMainPage() ) {
702  $msg = wfMessage( 'pagetitle-view-mainpage' )->inContentLanguage();
703  if ( !$msg->isDisabled() ) {
704  $outputPage->setHTMLTitle( $msg->title( $this->getTitle() )->text() );
705  }
706  }
707 
708  # Use adaptive TTLs for CDN so delayed/failed purges are noticed less often.
709  # This could use getTouched(), but that could be scary for major template edits.
710  $outputPage->adaptCdnTTL( $this->mPage->getTimestamp(), IExpiringStore::TTL_DAY );
711 
712  # Check for any __NOINDEX__ tags on the page using $pOutput
713  $policy = $this->getRobotPolicy( 'view', $pOutput );
714  $outputPage->setIndexPolicy( $policy['index'] );
715  $outputPage->setFollowPolicy( $policy['follow'] );
716 
717  $this->showViewFooter();
718  $this->mPage->doViewUpdates( $user, $oldid );
719 
720  $outputPage->addModules( 'mediawiki.action.view.postEdit' );
721  }
722 
727  public function adjustDisplayTitle( ParserOutput $pOutput ) {
728  # Adjust the title if it was set by displaytitle, -{T|}- or language conversion
729  $titleText = $pOutput->getTitleText();
730  if ( strval( $titleText ) !== '' ) {
731  $this->getContext()->getOutput()->setPageTitle( $titleText );
732  }
733  }
734 
740  protected function showDiffPage() {
741  $request = $this->getContext()->getRequest();
742  $user = $this->getContext()->getUser();
743  $diff = $request->getVal( 'diff' );
744  $rcid = $request->getVal( 'rcid' );
745  $diffOnly = $request->getBool( 'diffonly', $user->getOption( 'diffonly' ) );
746  $purge = $request->getVal( 'action' ) == 'purge';
747  $unhide = $request->getInt( 'unhide' ) == 1;
748  $oldid = $this->getOldID();
749 
750  $rev = $this->getRevisionFetched();
751 
752  if ( !$rev ) {
753  $this->getContext()->getOutput()->setPageTitle( wfMessage( 'errorpagetitle' ) );
754  $msg = $this->getContext()->msg( 'difference-missing-revision' )
755  ->params( $oldid )
756  ->numParams( 1 )
757  ->parseAsBlock();
758  $this->getContext()->getOutput()->addHTML( $msg );
759  return;
760  }
761 
762  $contentHandler = $rev->getContentHandler();
763  $de = $contentHandler->createDifferenceEngine(
764  $this->getContext(),
765  $oldid,
766  $diff,
767  $rcid,
768  $purge,
769  $unhide
770  );
771 
772  // DifferenceEngine directly fetched the revision:
773  $this->mRevIdFetched = $de->mNewid;
774  $de->showDiffPage( $diffOnly );
775 
776  // Run view updates for the newer revision being diffed (and shown
777  // below the diff if not $diffOnly).
778  list( $old, $new ) = $de->mapDiffPrevNext( $oldid, $diff );
779  // New can be false, convert it to 0 - this conveniently means the latest revision
780  $this->mPage->doViewUpdates( $user, (int)$new );
781  }
782 
794  protected function showCssOrJsPage( $showCacheHint = true ) {
795  $outputPage = $this->getContext()->getOutput();
796 
797  if ( $showCacheHint ) {
798  $dir = $this->getContext()->getLanguage()->getDir();
799  $lang = $this->getContext()->getLanguage()->getHtmlCode();
800 
801  $outputPage->wrapWikiMsg(
802  "<div id='mw-clearyourcache' lang='$lang' dir='$dir' class='mw-content-$dir'>\n$1\n</div>",
803  'clearyourcache'
804  );
805  }
806 
807  $this->fetchContentObject();
808 
809  if ( $this->mContentObject ) {
810  // Give hooks a chance to customise the output
812  'ShowRawCssJs',
813  [ $this->mContentObject, $this->getTitle(), $outputPage ],
814  '1.24'
815  ) ) {
816  // If no legacy hooks ran, display the content of the parser output, including RL modules,
817  // but excluding metadata like categories and language links
818  $po = $this->mContentObject->getParserOutput( $this->getTitle() );
819  $outputPage->addParserOutputContent( $po );
820  }
821  }
822  }
823 
831  public function getRobotPolicy( $action, $pOutput = null ) {
832  global $wgArticleRobotPolicies, $wgNamespaceRobotPolicies, $wgDefaultRobotPolicy;
833 
834  $ns = $this->getTitle()->getNamespace();
835 
836  # Don't index user and user talk pages for blocked users (bug 11443)
837  if ( ( $ns == NS_USER || $ns == NS_USER_TALK ) && !$this->getTitle()->isSubpage() ) {
838  $specificTarget = null;
839  $vagueTarget = null;
840  $titleText = $this->getTitle()->getText();
841  if ( IP::isValid( $titleText ) ) {
842  $vagueTarget = $titleText;
843  } else {
844  $specificTarget = $titleText;
845  }
846  if ( Block::newFromTarget( $specificTarget, $vagueTarget ) instanceof Block ) {
847  return [
848  'index' => 'noindex',
849  'follow' => 'nofollow'
850  ];
851  }
852  }
853 
854  if ( $this->mPage->getId() === 0 || $this->getOldID() ) {
855  # Non-articles (special pages etc), and old revisions
856  return [
857  'index' => 'noindex',
858  'follow' => 'nofollow'
859  ];
860  } elseif ( $this->getContext()->getOutput()->isPrintable() ) {
861  # Discourage indexing of printable versions, but encourage following
862  return [
863  'index' => 'noindex',
864  'follow' => 'follow'
865  ];
866  } elseif ( $this->getContext()->getRequest()->getInt( 'curid' ) ) {
867  # For ?curid=x urls, disallow indexing
868  return [
869  'index' => 'noindex',
870  'follow' => 'follow'
871  ];
872  }
873 
874  # Otherwise, construct the policy based on the various config variables.
875  $policy = self::formatRobotPolicy( $wgDefaultRobotPolicy );
876 
877  if ( isset( $wgNamespaceRobotPolicies[$ns] ) ) {
878  # Honour customised robot policies for this namespace
879  $policy = array_merge(
880  $policy,
881  self::formatRobotPolicy( $wgNamespaceRobotPolicies[$ns] )
882  );
883  }
884  if ( $this->getTitle()->canUseNoindex() && is_object( $pOutput ) && $pOutput->getIndexPolicy() ) {
885  # __INDEX__ and __NOINDEX__ magic words, if allowed. Incorporates
886  # a final sanity check that we have really got the parser output.
887  $policy = array_merge(
888  $policy,
889  [ 'index' => $pOutput->getIndexPolicy() ]
890  );
891  }
892 
893  if ( isset( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] ) ) {
894  # (bug 14900) site config can override user-defined __INDEX__ or __NOINDEX__
895  $policy = array_merge(
896  $policy,
897  self::formatRobotPolicy( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] )
898  );
899  }
900 
901  return $policy;
902  }
903 
911  public static function formatRobotPolicy( $policy ) {
912  if ( is_array( $policy ) ) {
913  return $policy;
914  } elseif ( !$policy ) {
915  return [];
916  }
917 
918  $policy = explode( ',', $policy );
919  $policy = array_map( 'trim', $policy );
920 
921  $arr = [];
922  foreach ( $policy as $var ) {
923  if ( in_array( $var, [ 'index', 'noindex' ] ) ) {
924  $arr['index'] = $var;
925  } elseif ( in_array( $var, [ 'follow', 'nofollow' ] ) ) {
926  $arr['follow'] = $var;
927  }
928  }
929 
930  return $arr;
931  }
932 
940  public function showRedirectedFromHeader() {
942 
943  $context = $this->getContext();
944  $outputPage = $context->getOutput();
945  $request = $context->getRequest();
946  $rdfrom = $request->getVal( 'rdfrom' );
947 
948  // Construct a URL for the current page view, but with the target title
949  $query = $request->getValues();
950  unset( $query['rdfrom'] );
951  unset( $query['title'] );
952  if ( $this->getTitle()->isRedirect() ) {
953  // Prevent double redirects
954  $query['redirect'] = 'no';
955  }
956  $redirectTargetUrl = $this->getTitle()->getLinkURL( $query );
957 
958  if ( isset( $this->mRedirectedFrom ) ) {
959  // This is an internally redirected page view.
960  // We'll need a backlink to the source page for navigation.
961  if ( Hooks::run( 'ArticleViewRedirect', [ &$this ] ) ) {
962  $redir = Linker::linkKnown(
963  $this->mRedirectedFrom,
964  null,
965  [],
966  [ 'redirect' => 'no' ]
967  );
968 
969  $outputPage->addSubtitle( "<span class=\"mw-redirectedfrom\">" .
970  $context->msg( 'redirectedfrom' )->rawParams( $redir )->parse()
971  . "</span>" );
972 
973  // Add the script to update the displayed URL and
974  // set the fragment if one was specified in the redirect
975  $outputPage->addJsConfigVars( [
976  'wgInternalRedirectTargetUrl' => $redirectTargetUrl,
977  ] );
978  $outputPage->addModules( 'mediawiki.action.view.redirect' );
979 
980  // Add a <link rel="canonical"> tag
981  $outputPage->setCanonicalUrl( $this->getTitle()->getCanonicalURL() );
982 
983  // Tell the output object that the user arrived at this article through a redirect
984  $outputPage->setRedirectedFrom( $this->mRedirectedFrom );
985 
986  return true;
987  }
988  } elseif ( $rdfrom ) {
989  // This is an externally redirected view, from some other wiki.
990  // If it was reported from a trusted site, supply a backlink.
991  if ( $wgRedirectSources && preg_match( $wgRedirectSources, $rdfrom ) ) {
992  $redir = Linker::makeExternalLink( $rdfrom, $rdfrom );
993  $outputPage->addSubtitle( "<span class=\"mw-redirectedfrom\">" .
994  $context->msg( 'redirectedfrom' )->rawParams( $redir )->parse()
995  . "</span>" );
996 
997  // Add the script to update the displayed URL
998  $outputPage->addJsConfigVars( [
999  'wgInternalRedirectTargetUrl' => $redirectTargetUrl,
1000  ] );
1001  $outputPage->addModules( 'mediawiki.action.view.redirect' );
1002 
1003  return true;
1004  }
1005  }
1006 
1007  return false;
1008  }
1009 
1014  public function showNamespaceHeader() {
1015  if ( $this->getTitle()->isTalkPage() ) {
1016  if ( !wfMessage( 'talkpageheader' )->isDisabled() ) {
1017  $this->getContext()->getOutput()->wrapWikiMsg(
1018  "<div class=\"mw-talkpageheader\">\n$1\n</div>",
1019  [ 'talkpageheader' ]
1020  );
1021  }
1022  }
1023  }
1024 
1028  public function showViewFooter() {
1029  # check if we're displaying a [[User talk:x.x.x.x]] anonymous talk page
1030  if ( $this->getTitle()->getNamespace() == NS_USER_TALK
1031  && IP::isValid( $this->getTitle()->getText() )
1032  ) {
1033  $this->getContext()->getOutput()->addWikiMsg( 'anontalkpagetext' );
1034  }
1035 
1036  // Show a footer allowing the user to patrol the shown revision or page if possible
1037  $patrolFooterShown = $this->showPatrolFooter();
1038 
1039  Hooks::run( 'ArticleViewFooter', [ $this, $patrolFooterShown ] );
1040  }
1041 
1051  public function showPatrolFooter() {
1052  global $wgUseNPPatrol, $wgUseRCPatrol, $wgUseFilePatrol, $wgEnableAPI, $wgEnableWriteAPI;
1053 
1054  $outputPage = $this->getContext()->getOutput();
1055  $user = $this->getContext()->getUser();
1056  $title = $this->getTitle();
1057  $rc = false;
1058 
1059  if ( !$title->quickUserCan( 'patrol', $user )
1060  || !( $wgUseRCPatrol || $wgUseNPPatrol
1061  || ( $wgUseFilePatrol && $title->inNamespace( NS_FILE ) ) )
1062  ) {
1063  // Patrolling is disabled or the user isn't allowed to
1064  return false;
1065  }
1066 
1067  if ( $this->mRevision
1068  && !RecentChange::isInRCLifespan( $this->mRevision->getTimestamp(), 21600 )
1069  ) {
1070  // The current revision is already older than what could be in the RC table
1071  // 6h tolerance because the RC might not be cleaned out regularly
1072  return false;
1073  }
1074 
1075  // Check for cached results
1076  $key = wfMemcKey( 'unpatrollable-page', $title->getArticleID() );
1078  if ( $cache->get( $key ) ) {
1079  return false;
1080  }
1081 
1082  $dbr = wfGetDB( DB_REPLICA );
1083  $oldestRevisionTimestamp = $dbr->selectField(
1084  'revision',
1085  'MIN( rev_timestamp )',
1086  [ 'rev_page' => $title->getArticleID() ],
1087  __METHOD__
1088  );
1089 
1090  // New page patrol: Get the timestamp of the oldest revison which
1091  // the revision table holds for the given page. Then we look
1092  // whether it's within the RC lifespan and if it is, we try
1093  // to get the recentchanges row belonging to that entry
1094  // (with rc_new = 1).
1095  $recentPageCreation = false;
1096  if ( $oldestRevisionTimestamp
1097  && RecentChange::isInRCLifespan( $oldestRevisionTimestamp, 21600 )
1098  ) {
1099  // 6h tolerance because the RC might not be cleaned out regularly
1100  $recentPageCreation = true;
1102  [
1103  'rc_new' => 1,
1104  'rc_timestamp' => $oldestRevisionTimestamp,
1105  'rc_namespace' => $title->getNamespace(),
1106  'rc_cur_id' => $title->getArticleID()
1107  ],
1108  __METHOD__
1109  );
1110  if ( $rc ) {
1111  // Use generic patrol message for new pages
1112  $markPatrolledMsg = wfMessage( 'markaspatrolledtext' );
1113  }
1114  }
1115 
1116  // File patrol: Get the timestamp of the latest upload for this page,
1117  // check whether it is within the RC lifespan and if it is, we try
1118  // to get the recentchanges row belonging to that entry
1119  // (with rc_type = RC_LOG, rc_log_type = upload).
1120  $recentFileUpload = false;
1121  if ( ( !$rc || $rc->getAttribute( 'rc_patrolled' ) ) && $wgUseFilePatrol
1122  && $title->getNamespace() === NS_FILE ) {
1123  // Retrieve timestamp of most recent upload
1124  $newestUploadTimestamp = $dbr->selectField(
1125  'image',
1126  'MAX( img_timestamp )',
1127  [ 'img_name' => $title->getDBkey() ],
1128  __METHOD__
1129  );
1130  if ( $newestUploadTimestamp
1131  && RecentChange::isInRCLifespan( $newestUploadTimestamp, 21600 )
1132  ) {
1133  // 6h tolerance because the RC might not be cleaned out regularly
1134  $recentFileUpload = true;
1136  [
1137  'rc_type' => RC_LOG,
1138  'rc_log_type' => 'upload',
1139  'rc_timestamp' => $newestUploadTimestamp,
1140  'rc_namespace' => NS_FILE,
1141  'rc_cur_id' => $title->getArticleID()
1142  ],
1143  __METHOD__,
1144  [ 'USE INDEX' => 'rc_timestamp' ]
1145  );
1146  if ( $rc ) {
1147  // Use patrol message specific to files
1148  $markPatrolledMsg = wfMessage( 'markaspatrolledtext-file' );
1149  }
1150  }
1151  }
1152 
1153  if ( !$recentPageCreation && !$recentFileUpload ) {
1154  // Page creation and latest upload (for files) is too old to be in RC
1155 
1156  // We definitely can't patrol so cache the information
1157  // When a new file version is uploaded, the cache is cleared
1158  $cache->set( $key, '1' );
1159 
1160  return false;
1161  }
1162 
1163  if ( !$rc ) {
1164  // Don't cache: This can be hit if the page gets accessed very fast after
1165  // its creation / latest upload or in case we have high replica DB lag. In case
1166  // the revision is too old, we will already return above.
1167  return false;
1168  }
1169 
1170  if ( $rc->getAttribute( 'rc_patrolled' ) ) {
1171  // Patrolled RC entry around
1172 
1173  // Cache the information we gathered above in case we can't patrol
1174  // Don't cache in case we can patrol as this could change
1175  $cache->set( $key, '1' );
1176 
1177  return false;
1178  }
1179 
1180  if ( $rc->getPerformer()->equals( $user ) ) {
1181  // Don't show a patrol link for own creations/uploads. If the user could
1182  // patrol them, they already would be patrolled
1183  return false;
1184  }
1185 
1186  $rcid = $rc->getAttribute( 'rc_id' );
1187 
1188  $token = $user->getEditToken( $rcid );
1189 
1190  $outputPage->preventClickjacking();
1191  if ( $wgEnableAPI && $wgEnableWriteAPI && $user->isAllowed( 'writeapi' ) ) {
1192  $outputPage->addModules( 'mediawiki.page.patrol.ajax' );
1193  }
1194 
1196  $title,
1197  $markPatrolledMsg->escaped(),
1198  [],
1199  [
1200  'action' => 'markpatrolled',
1201  'rcid' => $rcid,
1202  'token' => $token,
1203  ]
1204  );
1205 
1206  $outputPage->addHTML(
1207  "<div class='patrollink' data-mw='interface'>" .
1208  wfMessage( 'markaspatrolledlink' )->rawParams( $link )->escaped() .
1209  '</div>'
1210  );
1211 
1212  return true;
1213  }
1214 
1221  public static function purgePatrolFooterCache( $articleID ) {
1223  $cache->delete( wfMemcKey( 'unpatrollable-page', $articleID ) );
1224  }
1225 
1230  public function showMissingArticle() {
1232 
1233  $outputPage = $this->getContext()->getOutput();
1234  // Whether the page is a root user page of an existing user (but not a subpage)
1235  $validUserPage = false;
1236 
1237  $title = $this->getTitle();
1238 
1239  # Show info in user (talk) namespace. Does the user exist? Is he blocked?
1240  if ( $title->getNamespace() == NS_USER
1241  || $title->getNamespace() == NS_USER_TALK
1242  ) {
1243  $rootPart = explode( '/', $title->getText() )[0];
1244  $user = User::newFromName( $rootPart, false /* allow IP users*/ );
1245  $ip = User::isIP( $rootPart );
1246  $block = Block::newFromTarget( $user, $user );
1247 
1248  if ( !( $user && $user->isLoggedIn() ) && !$ip ) { # User does not exist
1249  $outputPage->wrapWikiMsg( "<div class=\"mw-userpage-userdoesnotexist error\">\n\$1\n</div>",
1250  [ 'userpage-userdoesnotexist-view', wfEscapeWikiText( $rootPart ) ] );
1251  } elseif ( !is_null( $block ) && $block->getType() != Block::TYPE_AUTO ) {
1252  # Show log extract if the user is currently blocked
1254  $outputPage,
1255  'block',
1256  MWNamespace::getCanonicalName( NS_USER ) . ':' . $block->getTarget(),
1257  '',
1258  [
1259  'lim' => 1,
1260  'showIfEmpty' => false,
1261  'msgKey' => [
1262  'blocked-notice-logextract',
1263  $user->getName() # Support GENDER in notice
1264  ]
1265  ]
1266  );
1267  $validUserPage = !$title->isSubpage();
1268  } else {
1269  $validUserPage = !$title->isSubpage();
1270  }
1271  }
1272 
1273  Hooks::run( 'ShowMissingArticle', [ $this ] );
1274 
1275  # Show delete and move logs if there were any such events.
1276  # The logging query can DOS the site when bots/crawlers cause 404 floods,
1277  # so be careful showing this. 404 pages must be cheap as they are hard to cache.
1279  $key = wfMemcKey( 'page-recent-delete', md5( $title->getPrefixedText() ) );
1280  $loggedIn = $this->getContext()->getUser()->isLoggedIn();
1281  if ( $loggedIn || $cache->get( $key ) ) {
1282  $logTypes = [ 'delete', 'move' ];
1283  $conds = [ "log_action != 'revision'" ];
1284  // Give extensions a chance to hide their (unrelated) log entries
1285  Hooks::run( 'Article::MissingArticleConditions', [ &$conds, $logTypes ] );
1287  $outputPage,
1288  $logTypes,
1289  $title,
1290  '',
1291  [
1292  'lim' => 10,
1293  'conds' => $conds,
1294  'showIfEmpty' => false,
1295  'msgKey' => [ $loggedIn
1296  ? 'moveddeleted-notice'
1297  : 'moveddeleted-notice-recent'
1298  ]
1299  ]
1300  );
1301  }
1302 
1303  if ( !$this->mPage->hasViewableContent() && $wgSend404Code && !$validUserPage ) {
1304  // If there's no backing content, send a 404 Not Found
1305  // for better machine handling of broken links.
1306  $this->getContext()->getRequest()->response()->statusHeader( 404 );
1307  }
1308 
1309  // Also apply the robot policy for nonexisting pages (even if a 404 was used for sanity)
1310  $policy = $this->getRobotPolicy( 'view' );
1311  $outputPage->setIndexPolicy( $policy['index'] );
1312  $outputPage->setFollowPolicy( $policy['follow'] );
1313 
1314  $hookResult = Hooks::run( 'BeforeDisplayNoArticleText', [ $this ] );
1315 
1316  if ( !$hookResult ) {
1317  return;
1318  }
1319 
1320  # Show error message
1321  $oldid = $this->getOldID();
1322  if ( !$oldid && $title->getNamespace() === NS_MEDIAWIKI && $title->hasSourceText() ) {
1323  $outputPage->addParserOutput( $this->getContentObject()->getParserOutput( $title ) );
1324  } else {
1325  if ( $oldid ) {
1326  $text = wfMessage( 'missing-revision', $oldid )->plain();
1327  } elseif ( $title->quickUserCan( 'create', $this->getContext()->getUser() )
1328  && $title->quickUserCan( 'edit', $this->getContext()->getUser() )
1329  ) {
1330  $message = $this->getContext()->getUser()->isLoggedIn() ? 'noarticletext' : 'noarticletextanon';
1331  $text = wfMessage( $message )->plain();
1332  } else {
1333  $text = wfMessage( 'noarticletext-nopermission' )->plain();
1334  }
1335 
1336  $dir = $this->getContext()->getLanguage()->getDir();
1337  $lang = $this->getContext()->getLanguage()->getCode();
1338  $outputPage->addWikiText( Xml::openElement( 'div', [
1339  'class' => "noarticletext mw-content-$dir",
1340  'dir' => $dir,
1341  'lang' => $lang,
1342  ] ) . "\n$text\n</div>" );
1343  }
1344  }
1345 
1352  public function showDeletedRevisionHeader() {
1353  if ( !$this->mRevision->isDeleted( Revision::DELETED_TEXT ) ) {
1354  // Not deleted
1355  return true;
1356  }
1357 
1358  $outputPage = $this->getContext()->getOutput();
1359  $user = $this->getContext()->getUser();
1360  // If the user is not allowed to see it...
1361  if ( !$this->mRevision->userCan( Revision::DELETED_TEXT, $user ) ) {
1362  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1363  'rev-deleted-text-permission' );
1364 
1365  return false;
1366  // If the user needs to confirm that they want to see it...
1367  } elseif ( $this->getContext()->getRequest()->getInt( 'unhide' ) != 1 ) {
1368  # Give explanation and add a link to view the revision...
1369  $oldid = intval( $this->getOldID() );
1370  $link = $this->getTitle()->getFullURL( "oldid={$oldid}&unhide=1" );
1371  $msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ?
1372  'rev-suppressed-text-unhide' : 'rev-deleted-text-unhide';
1373  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1374  [ $msg, $link ] );
1375 
1376  return false;
1377  // We are allowed to see...
1378  } else {
1379  $msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ?
1380  'rev-suppressed-text-view' : 'rev-deleted-text-view';
1381  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n", $msg );
1382 
1383  return true;
1384  }
1385  }
1386 
1395  public function setOldSubtitle( $oldid = 0 ) {
1396  if ( !Hooks::run( 'DisplayOldSubtitle', [ &$this, &$oldid ] ) ) {
1397  return;
1398  }
1399 
1400  $context = $this->getContext();
1401  $unhide = $context->getRequest()->getInt( 'unhide' ) == 1;
1402 
1403  # Cascade unhide param in links for easy deletion browsing
1404  $extraParams = [];
1405  if ( $unhide ) {
1406  $extraParams['unhide'] = 1;
1407  }
1408 
1409  if ( $this->mRevision && $this->mRevision->getId() === $oldid ) {
1410  $revision = $this->mRevision;
1411  } else {
1412  $revision = Revision::newFromId( $oldid );
1413  }
1414 
1415  $timestamp = $revision->getTimestamp();
1416 
1417  $current = ( $oldid == $this->mPage->getLatest() );
1418  $language = $context->getLanguage();
1419  $user = $context->getUser();
1420 
1421  $td = $language->userTimeAndDate( $timestamp, $user );
1422  $tddate = $language->userDate( $timestamp, $user );
1423  $tdtime = $language->userTime( $timestamp, $user );
1424 
1425  # Show user links if allowed to see them. If hidden, then show them only if requested...
1426  $userlinks = Linker::revUserTools( $revision, !$unhide );
1427 
1428  $infomsg = $current && !$context->msg( 'revision-info-current' )->isDisabled()
1429  ? 'revision-info-current'
1430  : 'revision-info';
1431 
1432  $outputPage = $context->getOutput();
1433  $revisionInfo = "<div id=\"mw-{$infomsg}\">" .
1434  $context->msg( $infomsg, $td )
1435  ->rawParams( $userlinks )
1436  ->params( $revision->getId(), $tddate, $tdtime, $revision->getUserText() )
1437  ->rawParams( Linker::revComment( $revision, true, true ) )
1438  ->parse() .
1439  "</div>";
1440 
1441  $lnk = $current
1442  ? $context->msg( 'currentrevisionlink' )->escaped()
1444  $this->getTitle(),
1445  $context->msg( 'currentrevisionlink' )->escaped(),
1446  [],
1447  $extraParams
1448  );
1449  $curdiff = $current
1450  ? $context->msg( 'diff' )->escaped()
1452  $this->getTitle(),
1453  $context->msg( 'diff' )->escaped(),
1454  [],
1455  [
1456  'diff' => 'cur',
1457  'oldid' => $oldid
1458  ] + $extraParams
1459  );
1460  $prev = $this->getTitle()->getPreviousRevisionID( $oldid );
1461  $prevlink = $prev
1463  $this->getTitle(),
1464  $context->msg( 'previousrevision' )->escaped(),
1465  [],
1466  [
1467  'direction' => 'prev',
1468  'oldid' => $oldid
1469  ] + $extraParams
1470  )
1471  : $context->msg( 'previousrevision' )->escaped();
1472  $prevdiff = $prev
1474  $this->getTitle(),
1475  $context->msg( 'diff' )->escaped(),
1476  [],
1477  [
1478  'diff' => 'prev',
1479  'oldid' => $oldid
1480  ] + $extraParams
1481  )
1482  : $context->msg( 'diff' )->escaped();
1483  $nextlink = $current
1484  ? $context->msg( 'nextrevision' )->escaped()
1486  $this->getTitle(),
1487  $context->msg( 'nextrevision' )->escaped(),
1488  [],
1489  [
1490  'direction' => 'next',
1491  'oldid' => $oldid
1492  ] + $extraParams
1493  );
1494  $nextdiff = $current
1495  ? $context->msg( 'diff' )->escaped()
1497  $this->getTitle(),
1498  $context->msg( 'diff' )->escaped(),
1499  [],
1500  [
1501  'diff' => 'next',
1502  'oldid' => $oldid
1503  ] + $extraParams
1504  );
1505 
1506  $cdel = Linker::getRevDeleteLink( $user, $revision, $this->getTitle() );
1507  if ( $cdel !== '' ) {
1508  $cdel .= ' ';
1509  }
1510 
1511  // the outer div is need for styling the revision info and nav in MobileFrontend
1512  $outputPage->addSubtitle( "<div class=\"mw-revision\">" . $revisionInfo .
1513  "<div id=\"mw-revision-nav\">" . $cdel .
1514  $context->msg( 'revision-nav' )->rawParams(
1515  $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff
1516  )->escaped() . "</div></div>" );
1517  }
1518 
1530  public function viewRedirect( $target, $appendSubtitle = true, $forceKnown = false ) {
1531  $lang = $this->getTitle()->getPageLanguage();
1532  $out = $this->getContext()->getOutput();
1533  if ( $appendSubtitle ) {
1534  $out->addSubtitle( wfMessage( 'redirectpagesub' ) );
1535  }
1536  $out->addModuleStyles( 'mediawiki.action.view.redirectPage' );
1537  return static::getRedirectHeaderHtml( $lang, $target, $forceKnown );
1538  }
1539 
1552  public static function getRedirectHeaderHtml( Language $lang, $target, $forceKnown = false ) {
1553  if ( !is_array( $target ) ) {
1554  $target = [ $target ];
1555  }
1556 
1557  $html = '<ul class="redirectText">';
1559  foreach ( $target as $title ) {
1560  $html .= '<li>' . Linker::link(
1561  $title,
1562  htmlspecialchars( $title->getFullText() ),
1563  [],
1564  // Make sure wiki page redirects are not followed
1565  $title->isRedirect() ? [ 'redirect' => 'no' ] : [],
1566  ( $forceKnown ? [ 'known', 'noclasses' ] : [] )
1567  ) . '</li>';
1568  }
1569  $html .= '</ul>';
1570 
1571  $redirectToText = wfMessage( 'redirectto' )->inLanguage( $lang )->escaped();
1572 
1573  return '<div class="redirectMsg">' .
1574  '<p>' . $redirectToText . '</p>' .
1575  $html .
1576  '</div>';
1577  }
1578 
1587  public function addHelpLink( $to, $overrideBaseUrl = false ) {
1588  $msg = wfMessage(
1589  'namespace-' . $this->getTitle()->getNamespace() . '-helppage'
1590  );
1591 
1592  $out = $this->getContext()->getOutput();
1593  if ( !$msg->isDisabled() ) {
1594  $helpUrl = Skin::makeUrl( $msg->plain() );
1595  $out->addHelpLink( $helpUrl, true );
1596  } else {
1597  $out->addHelpLink( $to, $overrideBaseUrl );
1598  }
1599  }
1600 
1604  public function render() {
1605  $this->getContext()->getRequest()->response()->header( 'X-Robots-Tag: noindex' );
1606  $this->getContext()->getOutput()->setArticleBodyOnly( true );
1607  $this->getContext()->getOutput()->enableSectionEditLinks( false );
1608  $this->view();
1609  }
1610 
1614  public function protect() {
1615  $form = new ProtectionForm( $this );
1616  $form->execute();
1617  }
1618 
1622  public function unprotect() {
1623  $this->protect();
1624  }
1625 
1629  public function delete() {
1630  # This code desperately needs to be totally rewritten
1631 
1632  $title = $this->getTitle();
1633  $context = $this->getContext();
1634  $user = $context->getUser();
1635  $request = $context->getRequest();
1636 
1637  # Check permissions
1638  $permissionErrors = $title->getUserPermissionsErrors( 'delete', $user );
1639  if ( count( $permissionErrors ) ) {
1640  throw new PermissionsError( 'delete', $permissionErrors );
1641  }
1642 
1643  # Read-only check...
1644  if ( wfReadOnly() ) {
1645  throw new ReadOnlyError;
1646  }
1647 
1648  # Better double-check that it hasn't been deleted yet!
1649  $this->mPage->loadPageData(
1650  $request->wasPosted() ? WikiPage::READ_LATEST : WikiPage::READ_NORMAL
1651  );
1652  if ( !$this->mPage->exists() ) {
1653  $deleteLogPage = new LogPage( 'delete' );
1654  $outputPage = $context->getOutput();
1655  $outputPage->setPageTitle( $context->msg( 'cannotdelete-title', $title->getPrefixedText() ) );
1656  $outputPage->wrapWikiMsg( "<div class=\"error mw-error-cannotdelete\">\n$1\n</div>",
1657  [ 'cannotdelete', wfEscapeWikiText( $title->getPrefixedText() ) ]
1658  );
1659  $outputPage->addHTML(
1660  Xml::element( 'h2', null, $deleteLogPage->getName()->text() )
1661  );
1663  $outputPage,
1664  'delete',
1665  $title
1666  );
1667 
1668  return;
1669  }
1670 
1671  $deleteReasonList = $request->getText( 'wpDeleteReasonList', 'other' );
1672  $deleteReason = $request->getText( 'wpReason' );
1673 
1674  if ( $deleteReasonList == 'other' ) {
1675  $reason = $deleteReason;
1676  } elseif ( $deleteReason != '' ) {
1677  // Entry from drop down menu + additional comment
1678  $colonseparator = wfMessage( 'colon-separator' )->inContentLanguage()->text();
1679  $reason = $deleteReasonList . $colonseparator . $deleteReason;
1680  } else {
1681  $reason = $deleteReasonList;
1682  }
1683 
1684  if ( $request->wasPosted() && $user->matchEditToken( $request->getVal( 'wpEditToken' ),
1685  [ 'delete', $this->getTitle()->getPrefixedText() ] )
1686  ) {
1687  # Flag to hide all contents of the archived revisions
1688  $suppress = $request->getVal( 'wpSuppress' ) && $user->isAllowed( 'suppressrevision' );
1689 
1690  $this->doDelete( $reason, $suppress );
1691 
1692  WatchAction::doWatchOrUnwatch( $request->getCheck( 'wpWatch' ), $title, $user );
1693 
1694  return;
1695  }
1696 
1697  // Generate deletion reason
1698  $hasHistory = false;
1699  if ( !$reason ) {
1700  try {
1701  $reason = $this->generateReason( $hasHistory );
1702  } catch ( Exception $e ) {
1703  # if a page is horribly broken, we still want to be able to
1704  # delete it. So be lenient about errors here.
1705  wfDebug( "Error while building auto delete summary: $e" );
1706  $reason = '';
1707  }
1708  }
1709 
1710  // If the page has a history, insert a warning
1711  if ( $hasHistory ) {
1712  $title = $this->getTitle();
1713 
1714  // The following can use the real revision count as this is only being shown for users
1715  // that can delete this page.
1716  // This, as a side-effect, also makes sure that the following query isn't being run for
1717  // pages with a larger history, unless the user has the 'bigdelete' right
1718  // (and is about to delete this page).
1719  $dbr = wfGetDB( DB_REPLICA );
1720  $revisions = $edits = (int)$dbr->selectField(
1721  'revision',
1722  'COUNT(rev_page)',
1723  [ 'rev_page' => $title->getArticleID() ],
1724  __METHOD__
1725  );
1726 
1727  // @todo FIXME: i18n issue/patchwork message
1728  $context->getOutput()->addHTML(
1729  '<strong class="mw-delete-warning-revisions">' .
1730  $context->msg( 'historywarning' )->numParams( $revisions )->parse() .
1731  $context->msg( 'word-separator' )->escaped() . Linker::linkKnown( $title,
1732  $context->msg( 'history' )->escaped(),
1733  [],
1734  [ 'action' => 'history' ] ) .
1735  '</strong>'
1736  );
1737 
1738  if ( $title->isBigDeletion() ) {
1739  global $wgDeleteRevisionsLimit;
1740  $context->getOutput()->wrapWikiMsg( "<div class='error'>\n$1\n</div>\n",
1741  [
1742  'delete-warning-toobig',
1743  $context->getLanguage()->formatNum( $wgDeleteRevisionsLimit )
1744  ]
1745  );
1746  }
1747  }
1748 
1749  $this->confirmDelete( $reason );
1750  }
1751 
1757  public function confirmDelete( $reason ) {
1758  wfDebug( "Article::confirmDelete\n" );
1759 
1760  $title = $this->getTitle();
1761  $ctx = $this->getContext();
1762  $outputPage = $ctx->getOutput();
1763  $useMediaWikiUIEverywhere = $ctx->getConfig()->get( 'UseMediaWikiUIEverywhere' );
1764  $outputPage->setPageTitle( wfMessage( 'delete-confirm', $title->getPrefixedText() ) );
1765  $outputPage->addBacklinkSubtitle( $title );
1766  $outputPage->setRobotPolicy( 'noindex,nofollow' );
1767  $backlinkCache = $title->getBacklinkCache();
1768  if ( $backlinkCache->hasLinks( 'pagelinks' ) || $backlinkCache->hasLinks( 'templatelinks' ) ) {
1769  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1770  'deleting-backlinks-warning' );
1771  }
1772  $outputPage->addWikiMsg( 'confirmdeletetext' );
1773 
1774  Hooks::run( 'ArticleConfirmDelete', [ $this, $outputPage, &$reason ] );
1775 
1776  $user = $this->getContext()->getUser();
1777 
1778  if ( $user->isAllowed( 'suppressrevision' ) ) {
1779  $suppress = Html::openElement( 'div', [ 'id' => 'wpDeleteSuppressRow' ] ) .
1780  Xml::checkLabel( wfMessage( 'revdelete-suppress' )->text(),
1781  'wpSuppress', 'wpSuppress', false, [ 'tabindex' => '4' ] ) .
1782  Html::closeElement( 'div' );
1783  } else {
1784  $suppress = '';
1785  }
1786  $checkWatch = $user->getBoolOption( 'watchdeletion' ) || $user->isWatched( $title );
1787 
1788  $form = Html::openElement( 'form', [ 'method' => 'post',
1789  'action' => $title->getLocalURL( 'action=delete' ), 'id' => 'deleteconfirm' ] ) .
1790  Html::openElement( 'fieldset', [ 'id' => 'mw-delete-table' ] ) .
1791  Html::element( 'legend', null, wfMessage( 'delete-legend' )->text() ) .
1792  Html::openElement( 'div', [ 'id' => 'mw-deleteconfirm-table' ] ) .
1793  Html::openElement( 'div', [ 'id' => 'wpDeleteReasonListRow' ] ) .
1794  Html::label( wfMessage( 'deletecomment' )->text(), 'wpDeleteReasonList' ) .
1795  '&nbsp;' .
1797  'wpDeleteReasonList',
1798  wfMessage( 'deletereason-dropdown' )->inContentLanguage()->text(),
1799  wfMessage( 'deletereasonotherlist' )->inContentLanguage()->text(),
1800  '',
1801  'wpReasonDropDown',
1802  1
1803  ) .
1804  Html::closeElement( 'div' ) .
1805  Html::openElement( 'div', [ 'id' => 'wpDeleteReasonRow' ] ) .
1806  Html::label( wfMessage( 'deleteotherreason' )->text(), 'wpReason' ) .
1807  '&nbsp;' .
1808  Html::input( 'wpReason', $reason, 'text', [
1809  'size' => '60',
1810  'maxlength' => '255',
1811  'tabindex' => '2',
1812  'id' => 'wpReason',
1813  'class' => 'mw-ui-input-inline',
1814  'autofocus'
1815  ] ) .
1816  Html::closeElement( 'div' );
1817 
1818  # Disallow watching if user is not logged in
1819  if ( $user->isLoggedIn() ) {
1820  $form .=
1821  Xml::checkLabel( wfMessage( 'watchthis' )->text(),
1822  'wpWatch', 'wpWatch', $checkWatch, [ 'tabindex' => '3' ] );
1823  }
1824 
1825  $form .=
1826  Html::openElement( 'div' ) .
1827  $suppress .
1828  Xml::submitButton( wfMessage( 'deletepage' )->text(),
1829  [
1830  'name' => 'wpConfirmB',
1831  'id' => 'wpConfirmB',
1832  'tabindex' => '5',
1833  'class' => $useMediaWikiUIEverywhere ? 'mw-ui-button mw-ui-destructive' : '',
1834  ]
1835  ) .
1836  Html::closeElement( 'div' ) .
1837  Html::closeElement( 'div' ) .
1838  Xml::closeElement( 'fieldset' ) .
1839  Html::hidden(
1840  'wpEditToken',
1841  $user->getEditToken( [ 'delete', $title->getPrefixedText() ] )
1842  ) .
1843  Xml::closeElement( 'form' );
1844 
1845  if ( $user->isAllowed( 'editinterface' ) ) {
1847  $ctx->msg( 'deletereason-dropdown' )->inContentLanguage()->getTitle(),
1848  wfMessage( 'delete-edit-reasonlist' )->escaped(),
1849  [],
1850  [ 'action' => 'edit' ]
1851  );
1852  $form .= '<p class="mw-delete-editreasons">' . $link . '</p>';
1853  }
1854 
1855  $outputPage->addHTML( $form );
1856 
1857  $deleteLogPage = new LogPage( 'delete' );
1858  $outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) );
1859  LogEventsList::showLogExtract( $outputPage, 'delete', $title );
1860  }
1861 
1867  public function doDelete( $reason, $suppress = false ) {
1868  $error = '';
1869  $context = $this->getContext();
1870  $outputPage = $context->getOutput();
1871  $user = $context->getUser();
1872  $status = $this->mPage->doDeleteArticleReal( $reason, $suppress, 0, true, $error, $user );
1873 
1874  if ( $status->isGood() ) {
1875  $deleted = $this->getTitle()->getPrefixedText();
1876 
1877  $outputPage->setPageTitle( wfMessage( 'actioncomplete' ) );
1878  $outputPage->setRobotPolicy( 'noindex,nofollow' );
1879 
1880  $loglink = '[[Special:Log/delete|' . wfMessage( 'deletionlog' )->text() . ']]';
1881 
1882  $outputPage->addWikiMsg( 'deletedtext', wfEscapeWikiText( $deleted ), $loglink );
1883 
1884  Hooks::run( 'ArticleDeleteAfterSuccess', [ $this->getTitle(), $outputPage ] );
1885 
1886  $outputPage->returnToMain( false );
1887  } else {
1888  $outputPage->setPageTitle(
1889  wfMessage( 'cannotdelete-title',
1890  $this->getTitle()->getPrefixedText() )
1891  );
1892 
1893  if ( $error == '' ) {
1894  $outputPage->addWikiText(
1895  "<div class=\"error mw-error-cannotdelete\">\n" . $status->getWikiText() . "\n</div>"
1896  );
1897  $deleteLogPage = new LogPage( 'delete' );
1898  $outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) );
1899 
1901  $outputPage,
1902  'delete',
1903  $this->getTitle()
1904  );
1905  } else {
1906  $outputPage->addHTML( $error );
1907  }
1908  }
1909  }
1910 
1911  /* Caching functions */
1912 
1920  protected function tryFileCache() {
1921  static $called = false;
1922 
1923  if ( $called ) {
1924  wfDebug( "Article::tryFileCache(): called twice!?\n" );
1925  return false;
1926  }
1927 
1928  $called = true;
1929  if ( $this->isFileCacheable() ) {
1930  $cache = new HTMLFileCache( $this->getTitle(), 'view' );
1931  if ( $cache->isCacheGood( $this->mPage->getTouched() ) ) {
1932  wfDebug( "Article::tryFileCache(): about to load file\n" );
1933  $cache->loadFromFileCache( $this->getContext() );
1934  return true;
1935  } else {
1936  wfDebug( "Article::tryFileCache(): starting buffer\n" );
1937  ob_start( [ &$cache, 'saveToFileCache' ] );
1938  }
1939  } else {
1940  wfDebug( "Article::tryFileCache(): not cacheable\n" );
1941  }
1942 
1943  return false;
1944  }
1945 
1951  public function isFileCacheable( $mode = HTMLFileCache::MODE_NORMAL ) {
1952  $cacheable = false;
1953 
1954  if ( HTMLFileCache::useFileCache( $this->getContext(), $mode ) ) {
1955  $cacheable = $this->mPage->getId()
1956  && !$this->mRedirectedFrom && !$this->getTitle()->isRedirect();
1957  // Extension may have reason to disable file caching on some pages.
1958  if ( $cacheable ) {
1959  $cacheable = Hooks::run( 'IsFileCacheable', [ &$this ] );
1960  }
1961  }
1962 
1963  return $cacheable;
1964  }
1965 
1979  public function getParserOutput( $oldid = null, User $user = null ) {
1980  // XXX: bypasses mParserOptions and thus setParserOptions()
1981 
1982  if ( $user === null ) {
1983  $parserOptions = $this->getParserOptions();
1984  } else {
1985  $parserOptions = $this->mPage->makeParserOptions( $user );
1986  }
1987 
1988  return $this->mPage->getParserOutput( $parserOptions, $oldid );
1989  }
1990 
1998  if ( $this->mParserOptions ) {
1999  throw new MWException( "can't change parser options after they have already been set" );
2000  }
2001 
2002  // clone, so if $options is modified later, it doesn't confuse the parser cache.
2003  $this->mParserOptions = clone $options;
2004  }
2005 
2010  public function getParserOptions() {
2011  if ( !$this->mParserOptions ) {
2012  $this->mParserOptions = $this->mPage->makeParserOptions( $this->getContext() );
2013  }
2014  // Clone to allow modifications of the return value without affecting cache
2015  return clone $this->mParserOptions;
2016  }
2017 
2024  public function setContext( $context ) {
2025  $this->mContext = $context;
2026  }
2027 
2034  public function getContext() {
2035  if ( $this->mContext instanceof IContextSource ) {
2036  return $this->mContext;
2037  } else {
2038  wfDebug( __METHOD__ . " called and \$mContext is null. " .
2039  "Return RequestContext::getMain(); for sanity\n" );
2040  return RequestContext::getMain();
2041  }
2042  }
2043 
2051  public function __get( $fname ) {
2052  if ( property_exists( $this->mPage, $fname ) ) {
2053  # wfWarn( "Access to raw $fname field " . __CLASS__ );
2054  return $this->mPage->$fname;
2055  }
2056  trigger_error( 'Inaccessible property via __get(): ' . $fname, E_USER_NOTICE );
2057  }
2058 
2066  public function __set( $fname, $fvalue ) {
2067  if ( property_exists( $this->mPage, $fname ) ) {
2068  # wfWarn( "Access to raw $fname field of " . __CLASS__ );
2069  $this->mPage->$fname = $fvalue;
2070  // Note: extensions may want to toss on new fields
2071  } elseif ( !in_array( $fname, [ 'mContext', 'mPage' ] ) ) {
2072  $this->mPage->$fname = $fvalue;
2073  } else {
2074  trigger_error( 'Inaccessible property via __set(): ' . $fname, E_USER_NOTICE );
2075  }
2076  }
2077 
2082  public function checkFlags( $flags ) {
2083  return $this->mPage->checkFlags( $flags );
2084  }
2085 
2090  public function checkTouched() {
2091  return $this->mPage->checkTouched();
2092  }
2093 
2098  public function clearPreparedEdit() {
2099  $this->mPage->clearPreparedEdit();
2100  }
2101 
2106  public function doDeleteArticleReal(
2107  $reason, $suppress = false, $u1 = null, $u2 = null, &$error = '', User $user = null,
2108  $tags = []
2109  ) {
2110  return $this->mPage->doDeleteArticleReal(
2111  $reason, $suppress, $u1, $u2, $error, $user, $tags
2112  );
2113  }
2114 
2119  public function doDeleteUpdates( $id, Content $content = null ) {
2120  return $this->mPage->doDeleteUpdates( $id, $content );
2121  }
2122 
2129  public function doEdit( $text, $summary, $flags = 0, $baseRevId = false, $user = null ) {
2130  wfDeprecated( __METHOD__, '1.21' );
2131  return $this->mPage->doEdit( $text, $summary, $flags, $baseRevId, $user );
2132  }
2133 
2138  public function doEditContent( Content $content, $summary, $flags = 0, $baseRevId = false,
2139  User $user = null, $serialFormat = null
2140  ) {
2141  return $this->mPage->doEditContent( $content, $summary, $flags, $baseRevId,
2142  $user, $serialFormat
2143  );
2144  }
2145 
2150  public function doEditUpdates( Revision $revision, User $user, array $options = [] ) {
2151  return $this->mPage->doEditUpdates( $revision, $user, $options );
2152  }
2153 
2158  public function doPurge( $flags = WikiPage::PURGE_ALL ) {
2159  return $this->mPage->doPurge( $flags );
2160  }
2161 
2166  public function getLastPurgeTimestamp() {
2167  return $this->mPage->getLastPurgeTimestamp();
2168  }
2169 
2174  public function doViewUpdates( User $user, $oldid = 0 ) {
2175  $this->mPage->doViewUpdates( $user, $oldid );
2176  }
2177 
2182  public function exists() {
2183  return $this->mPage->exists();
2184  }
2185 
2190  public function followRedirect() {
2191  return $this->mPage->followRedirect();
2192  }
2193 
2198  public function getActionOverrides() {
2199  return $this->mPage->getActionOverrides();
2200  }
2201 
2206  public function getAutoDeleteReason( &$hasHistory ) {
2207  return $this->mPage->getAutoDeleteReason( $hasHistory );
2208  }
2209 
2214  public function getCategories() {
2215  return $this->mPage->getCategories();
2216  }
2217 
2222  public function getComment( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2223  return $this->mPage->getComment( $audience, $user );
2224  }
2225 
2230  public function getContentHandler() {
2231  return $this->mPage->getContentHandler();
2232  }
2233 
2238  public function getContentModel() {
2239  return $this->mPage->getContentModel();
2240  }
2241 
2246  public function getContributors() {
2247  return $this->mPage->getContributors();
2248  }
2249 
2254  public function getCreator( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2255  return $this->mPage->getCreator( $audience, $user );
2256  }
2257 
2262  public function getDeletionUpdates( Content $content = null ) {
2263  return $this->mPage->getDeletionUpdates( $content );
2264  }
2265 
2270  public function getHiddenCategories() {
2271  return $this->mPage->getHiddenCategories();
2272  }
2273 
2278  public function getId() {
2279  return $this->mPage->getId();
2280  }
2281 
2286  public function getLatest() {
2287  return $this->mPage->getLatest();
2288  }
2289 
2294  public function getLinksTimestamp() {
2295  return $this->mPage->getLinksTimestamp();
2296  }
2297 
2302  public function getMinorEdit() {
2303  return $this->mPage->getMinorEdit();
2304  }
2305 
2310  public function getOldestRevision() {
2311  return $this->mPage->getOldestRevision();
2312  }
2313 
2318  public function getRedirectTarget() {
2319  return $this->mPage->getRedirectTarget();
2320  }
2321 
2326  public function getRedirectURL( $rt ) {
2327  return $this->mPage->getRedirectURL( $rt );
2328  }
2329 
2334  public function getRevision() {
2335  return $this->mPage->getRevision();
2336  }
2337 
2343  public function getText( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2344  wfDeprecated( __METHOD__, '1.21' );
2345  return $this->mPage->getText( $audience, $user );
2346  }
2347 
2352  public function getTimestamp() {
2353  return $this->mPage->getTimestamp();
2354  }
2355 
2360  public function getTouched() {
2361  return $this->mPage->getTouched();
2362  }
2363 
2368  public function getUndoContent( Revision $undo, Revision $undoafter = null ) {
2369  return $this->mPage->getUndoContent( $undo, $undoafter );
2370  }
2371 
2376  public function getUser( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2377  return $this->mPage->getUser( $audience, $user );
2378  }
2379 
2384  public function getUserText( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2385  return $this->mPage->getUserText( $audience, $user );
2386  }
2387 
2392  public function hasViewableContent() {
2393  return $this->mPage->hasViewableContent();
2394  }
2395 
2400  public function insertOn( $dbw, $pageId = null ) {
2401  return $this->mPage->insertOn( $dbw, $pageId );
2402  }
2403 
2408  public function insertProtectNullRevision( $revCommentMsg, array $limit,
2409  array $expiry, $cascade, $reason, $user = null
2410  ) {
2411  return $this->mPage->insertProtectNullRevision( $revCommentMsg, $limit,
2412  $expiry, $cascade, $reason, $user
2413  );
2414  }
2415 
2420  public function insertRedirect() {
2421  return $this->mPage->insertRedirect();
2422  }
2423 
2428  public function insertRedirectEntry( Title $rt, $oldLatest = null ) {
2429  return $this->mPage->insertRedirectEntry( $rt, $oldLatest );
2430  }
2431 
2436  public function isCountable( $editInfo = false ) {
2437  return $this->mPage->isCountable( $editInfo );
2438  }
2439 
2444  public function isRedirect() {
2445  return $this->mPage->isRedirect();
2446  }
2447 
2452  public function loadFromRow( $data, $from ) {
2453  return $this->mPage->loadFromRow( $data, $from );
2454  }
2455 
2460  public function loadPageData( $from = 'fromdb' ) {
2461  $this->mPage->loadPageData( $from );
2462  }
2463 
2468  public function lockAndGetLatest() {
2469  return $this->mPage->lockAndGetLatest();
2470  }
2471 
2476  public function makeParserOptions( $context ) {
2477  return $this->mPage->makeParserOptions( $context );
2478  }
2479 
2484  public function pageDataFromId( $dbr, $id, $options = [] ) {
2485  return $this->mPage->pageDataFromId( $dbr, $id, $options );
2486  }
2487 
2492  public function pageDataFromTitle( $dbr, $title, $options = [] ) {
2493  return $this->mPage->pageDataFromTitle( $dbr, $title, $options );
2494  }
2495 
2500  public function prepareContentForEdit(
2501  Content $content, $revision = null, User $user = null,
2502  $serialFormat = null, $useCache = true
2503  ) {
2504  return $this->mPage->prepareContentForEdit(
2505  $content, $revision, $user,
2506  $serialFormat, $useCache
2507  );
2508  }
2509 
2515  public function prepareTextForEdit( $text, $revid = null, User $user = null ) {
2516  return $this->mPage->prepareTextForEdit( $text, $revid, $user );
2517  }
2518 
2523  public function protectDescription( array $limit, array $expiry ) {
2524  return $this->mPage->protectDescription( $limit, $expiry );
2525  }
2526 
2531  public function protectDescriptionLog( array $limit, array $expiry ) {
2532  return $this->mPage->protectDescriptionLog( $limit, $expiry );
2533  }
2534 
2539  public function replaceSectionAtRev( $sectionId, Content $sectionContent,
2540  $sectionTitle = '', $baseRevId = null
2541  ) {
2542  return $this->mPage->replaceSectionAtRev( $sectionId, $sectionContent,
2543  $sectionTitle, $baseRevId
2544  );
2545  }
2546 
2551  public function replaceSectionContent(
2552  $sectionId, Content $sectionContent, $sectionTitle = '', $edittime = null
2553  ) {
2554  return $this->mPage->replaceSectionContent(
2555  $sectionId, $sectionContent, $sectionTitle, $edittime
2556  );
2557  }
2558 
2563  public function setTimestamp( $ts ) {
2564  return $this->mPage->setTimestamp( $ts );
2565  }
2566 
2571  public function shouldCheckParserCache( ParserOptions $parserOptions, $oldId ) {
2572  return $this->mPage->shouldCheckParserCache( $parserOptions, $oldId );
2573  }
2574 
2579  public function supportsSections() {
2580  return $this->mPage->supportsSections();
2581  }
2582 
2588  return $this->mPage->triggerOpportunisticLinksUpdate( $parserOutput );
2589  }
2590 
2595  public function updateCategoryCounts( array $added, array $deleted, $id = 0 ) {
2596  return $this->mPage->updateCategoryCounts( $added, $deleted, $id );
2597  }
2598 
2603  public function updateIfNewerOn( $dbw, $revision ) {
2604  return $this->mPage->updateIfNewerOn( $dbw, $revision );
2605  }
2606 
2611  public function updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect = null ) {
2612  return $this->mPage->updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect = null );
2613  }
2614 
2619  public function updateRevisionOn( $dbw, $revision, $lastRevision = null,
2620  $lastRevIsRedirect = null
2621  ) {
2622  return $this->mPage->updateRevisionOn( $dbw, $revision, $lastRevision,
2623  $lastRevIsRedirect
2624  );
2625  }
2626 
2635  public function doUpdateRestrictions( array $limit, array $expiry, &$cascade,
2636  $reason, User $user
2637  ) {
2638  return $this->mPage->doUpdateRestrictions( $limit, $expiry, $cascade, $reason, $user );
2639  }
2640 
2648  public function updateRestrictions( $limit = [], $reason = '',
2649  &$cascade = 0, $expiry = []
2650  ) {
2651  return $this->mPage->doUpdateRestrictions(
2652  $limit,
2653  $expiry,
2654  $cascade,
2655  $reason,
2656  $this->getContext()->getUser()
2657  );
2658  }
2659 
2668  public function doDeleteArticle(
2669  $reason, $suppress = false, $u1 = null, $u2 = null, &$error = ''
2670  ) {
2671  return $this->mPage->doDeleteArticle( $reason, $suppress, $u1, $u2, $error );
2672  }
2673 
2683  public function doRollback( $fromP, $summary, $token, $bot, &$resultDetails, User $user = null ) {
2684  $user = is_null( $user ) ? $this->getContext()->getUser() : $user;
2685  return $this->mPage->doRollback( $fromP, $summary, $token, $bot, $resultDetails, $user );
2686  }
2687 
2696  public function commitRollback( $fromP, $summary, $bot, &$resultDetails, User $guser = null ) {
2697  $guser = is_null( $guser ) ? $this->getContext()->getUser() : $guser;
2698  return $this->mPage->commitRollback( $fromP, $summary, $bot, $resultDetails, $guser );
2699  }
2700 
2705  public function generateReason( &$hasHistory ) {
2706  $title = $this->mPage->getTitle();
2708  return $handler->getAutoDeleteReason( $title, $hasHistory );
2709  }
2710 
2716  public static function selectFields() {
2717  wfDeprecated( __METHOD__, '1.24' );
2718  return WikiPage::selectFields();
2719  }
2720 
2726  public static function onArticleCreate( $title ) {
2727  wfDeprecated( __METHOD__, '1.24' );
2729  }
2730 
2736  public static function onArticleDelete( $title ) {
2737  wfDeprecated( __METHOD__, '1.24' );
2739  }
2740 
2746  public static function onArticleEdit( $title ) {
2747  wfDeprecated( __METHOD__, '1.24' );
2749  }
2750 
2758  public static function getAutosummary( $oldtext, $newtext, $flags ) {
2759  return WikiPage::getAutosummary( $oldtext, $newtext, $flags );
2760  }
2761  // ******
2762 }
pageDataFromId($dbr, $id, $options=[])
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2484
__set($fname, $fvalue)
Use PHP's magic __set handler to handle setting of raw WikiPage fields for backwards compatibility...
Definition: Article.php:2066
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:2318
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:1221
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:1530
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:2468
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:2726
getUndoContent(Revision $undo, Revision $undoafter=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2368
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:831
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:2648
doEditContent(Content $content, $summary, $flags=0, $baseRevId=false, User $user=null, $serialFormat=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2138
getText($audience=Revision::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2343
getLatest()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2286
setParserOptions(ParserOptions $options)
Override the ParserOptions used to render the primary article wikitext.
Definition: Article.php:1997
doEditUpdates(Revision $revision, User $user, array $options=[])
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2150
$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:2182
supportsSections()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2579
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:2683
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:2668
protect()
action=protect handler
Definition: Article.php:1614
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:2436
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:1979
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:2595
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:2460
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:1951
doDeleteUpdates($id, Content $content=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2119
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:727
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:2571
doUpdateRestrictions(array $limit, array $expiry, &$cascade, $reason, User $user)
Definition: Article.php:2635
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:2190
confirmDelete($reason)
Output deletion confirmation dialog.
Definition: Article.php:1757
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:2158
triggerOpportunisticLinksUpdate(ParserOutput $parserOutput)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2587
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:2051
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:2262
updateIfNewerOn($dbw, $revision)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2603
clearPreparedEdit()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2098
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:2246
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:2696
showMissingArticle()
Show the error text for a missing article.
Definition: Article.php:1230
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:2523
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:2034
static closeElement($element)
Shortcut to close an XML element.
Definition: Xml.php:118
isRedirect()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2444
__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:2563
protectDescriptionLog(array $limit, array $expiry)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2531
static onArticleDelete($title)
Definition: Article.php:2736
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:2476
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:2500
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:1028
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:460
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:2408
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:940
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:2334
it s the revision text itself In either if gzip is set
Definition: hooks.txt:2703
generateReason(&$hasHistory)
Definition: Article.php:2705
static getRedirectHeaderHtml(Language $lang, $target, $forceKnown=false)
Return the HTML for the top of a redirect page.
Definition: Article.php:1552
insertRedirect()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2420
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:2392
getComment($audience=Revision::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2222
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:2452
$cache
Definition: mcc.php:33
getActionOverrides()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2198
doViewUpdates(User $user, $oldid=0)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2174
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:2106
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:1604
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:2214
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:2539
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:2082
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:2230
showCssOrJsPage($showCacheHint=true)
Show a page view for a page formatted as CSS or JavaScript.
Definition: Article.php:794
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:2254
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:1587
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:2310
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:2166
getAutoDeleteReason(&$hasHistory)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2206
checkTouched()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2090
updateRevisionOn($dbw, $revision, $lastRevision=null, $lastRevIsRedirect=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2619
static selectFields()
Definition: Article.php:2716
prepareTextForEdit($text, $revid=null, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2515
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:2278
getTimestamp()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2352
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:2492
getMinorEdit()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2302
view()
This is the default action of the index.php entry point: just view the page of the given title...
Definition: Article.php:483
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:1867
showNamespaceHeader()
Show a header specific to the namespace currently being viewed, like [[MediaWiki:Talkpagetext]].
Definition: Article.php:1014
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:2611
tryFileCache()
checkLastModified returns true if it has taken care of all output to the client that is necessary for...
Definition: Article.php:1920
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:2428
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:2129
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:1395
isCurrent()
Returns true if the currently-referenced revision is the current edit to this page (and it exists)...
Definition: Article.php:444
unprotect()
action=unprotect handler (alias)
Definition: Article.php:1622
Handles the page protection UI and backend.
getTouched()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2360
static onArticleEdit($title)
Definition: Article.php:2746
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:2376
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:2551
setContext($context)
Sets the context this Article is executed in.
Definition: Article.php:2024
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:471
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:740
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:2326
Definition: Block.php:25
static getAutosummary($oldtext, $newtext, $flags)
Definition: Article.php:2758
showDeletedRevisionHeader()
If the revision requested for view is deleted, check permissions.
Definition: Article.php:1352
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:2238
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:2294
insertOn($dbw, $pageId=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2400
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:2010
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:2384
showPatrolFooter()
If patrol is possible, output a patrol UI box.
Definition: Article.php:1051
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:911
getHiddenCategories()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2270