MediaWiki  master
Article.php
Go to the documentation of this file.
1 <?php
23 
35 class Article implements Page {
37  protected $mContext;
38 
40  protected $mPage;
41 
44 
49  public $mContent;
50 
56 
58  public $mContentLoaded = false;
59 
61  public $mOldId;
62 
64  public $mRedirectedFrom = null;
65 
67  public $mRedirectUrl = false;
68 
70  public $mRevIdFetched = 0;
71 
73  public $mRevision = null;
74 
77 
83  public function __construct( Title $title, $oldId = null ) {
84  $this->mOldId = $oldId;
85  $this->mPage = $this->newPage( $title );
86  }
87 
92  protected function newPage( Title $title ) {
93  return new WikiPage( $title );
94  }
95 
101  public static function newFromID( $id ) {
102  $t = Title::newFromID( $id );
103  return $t == null ? null : new static( $t );
104  }
105 
113  public static function newFromTitle( $title, IContextSource $context ) {
114  if ( NS_MEDIA == $title->getNamespace() ) {
115  // FIXME: where should this go?
116  $title = Title::makeTitle( NS_FILE, $title->getDBkey() );
117  }
118 
119  $page = null;
120  Hooks::run( 'ArticleFromTitle', [ &$title, &$page, $context ] );
121  if ( !$page ) {
122  switch ( $title->getNamespace() ) {
123  case NS_FILE:
124  $page = new ImagePage( $title );
125  break;
126  case NS_CATEGORY:
127  $page = new CategoryPage( $title );
128  break;
129  default:
130  $page = new Article( $title );
131  }
132  }
133  $page->setContext( $context );
134 
135  return $page;
136  }
137 
145  public static function newFromWikiPage( WikiPage $page, IContextSource $context ) {
146  $article = self::newFromTitle( $page->getTitle(), $context );
147  $article->mPage = $page; // override to keep process cached vars
148  return $article;
149  }
150 
156  public function getRedirectedFrom() {
157  return $this->mRedirectedFrom;
158  }
159 
165  public function setRedirectedFrom( Title $from ) {
166  $this->mRedirectedFrom = $from;
167  }
168 
174  public function getTitle() {
175  return $this->mPage->getTitle();
176  }
177 
184  public function getPage() {
185  return $this->mPage;
186  }
187 
191  public function clear() {
192  $this->mContentLoaded = false;
193 
194  $this->mRedirectedFrom = null; # Title object if set
195  $this->mRevIdFetched = 0;
196  $this->mRedirectUrl = false;
197 
198  $this->mPage->clear();
199  }
200 
216  protected function getContentObject() {
217 
218  if ( $this->mPage->getId() === 0 ) {
219  # If this is a MediaWiki:x message, then load the messages
220  # and return the message value for x.
221  if ( $this->getTitle()->getNamespace() == NS_MEDIAWIKI ) {
222  $text = $this->getTitle()->getDefaultMessageText();
223  if ( $text === false ) {
224  $text = '';
225  }
226 
227  $content = ContentHandler::makeContent( $text, $this->getTitle() );
228  } else {
229  $message = $this->getContext()->getUser()->isLoggedIn() ? 'noarticletext' : 'noarticletextanon';
230  $content = new MessageContent( $message, null, 'parsemag' );
231  }
232  } else {
233  $this->fetchContentObject();
235  }
236 
237  return $content;
238  }
239 
243  public function getOldID() {
244  if ( is_null( $this->mOldId ) ) {
245  $this->mOldId = $this->getOldIDFromRequest();
246  }
247 
248  return $this->mOldId;
249  }
250 
256  public function getOldIDFromRequest() {
257  $this->mRedirectUrl = false;
258 
259  $request = $this->getContext()->getRequest();
260  $oldid = $request->getIntOrNull( 'oldid' );
261 
262  if ( $oldid === null ) {
263  return 0;
264  }
265 
266  if ( $oldid !== 0 ) {
267  # Load the given revision and check whether the page is another one.
268  # In that case, update this instance to reflect the change.
269  if ( $oldid === $this->mPage->getLatest() ) {
270  $this->mRevision = $this->mPage->getRevision();
271  } else {
272  $this->mRevision = Revision::newFromId( $oldid );
273  if ( $this->mRevision !== null ) {
274  // Revision title doesn't match the page title given?
275  if ( $this->mPage->getId() != $this->mRevision->getPage() ) {
276  $function = [ get_class( $this->mPage ), 'newFromID' ];
277  $this->mPage = call_user_func( $function, $this->mRevision->getPage() );
278  }
279  }
280  }
281  }
282 
283  if ( $request->getVal( 'direction' ) == 'next' ) {
284  $nextid = $this->getTitle()->getNextRevisionID( $oldid );
285  if ( $nextid ) {
286  $oldid = $nextid;
287  $this->mRevision = null;
288  } else {
289  $this->mRedirectUrl = $this->getTitle()->getFullURL( 'redirect=no' );
290  }
291  } elseif ( $request->getVal( 'direction' ) == 'prev' ) {
292  $previd = $this->getTitle()->getPreviousRevisionID( $oldid );
293  if ( $previd ) {
294  $oldid = $previd;
295  $this->mRevision = null;
296  }
297  }
298 
299  return $oldid;
300  }
301 
314  protected function fetchContentObject() {
315  if ( $this->mContentLoaded ) {
316  return $this->mContentObject;
317  }
318 
319  $this->mContentLoaded = true;
320  $this->mContent = null;
321 
322  $oldid = $this->getOldID();
323 
324  # Pre-fill content with error message so that if something
325  # fails we'll have something telling us what we intended.
326  // XXX: this isn't page content but a UI message. horrible.
327  $this->mContentObject = new MessageContent( 'missing-revision', [ $oldid ] );
328 
329  if ( $oldid ) {
330  # $this->mRevision might already be fetched by getOldIDFromRequest()
331  if ( !$this->mRevision ) {
332  $this->mRevision = Revision::newFromId( $oldid );
333  if ( !$this->mRevision ) {
334  wfDebug( __METHOD__ . " failed to retrieve specified revision, id $oldid\n" );
335  return false;
336  }
337  }
338  } else {
339  $oldid = $this->mPage->getLatest();
340  if ( !$oldid ) {
341  wfDebug( __METHOD__ . " failed to find page data for title " .
342  $this->getTitle()->getPrefixedText() . "\n" );
343  return false;
344  }
345 
346  # Update error message with correct oldid
347  $this->mContentObject = new MessageContent( 'missing-revision', [ $oldid ] );
348 
349  $this->mRevision = $this->mPage->getRevision();
350 
351  if ( !$this->mRevision ) {
352  wfDebug( __METHOD__ . " failed to retrieve current page, rev_id $oldid\n" );
353  return false;
354  }
355  }
356 
357  // @todo FIXME: Horrible, horrible! This content-loading interface just plain sucks.
358  // We should instead work with the Revision object when we need it...
359  // Loads if user is allowed
360  $content = $this->mRevision->getContent(
362  $this->getContext()->getUser()
363  );
364 
365  if ( !$content ) {
366  wfDebug( __METHOD__ . " failed to retrieve content of revision " .
367  $this->mRevision->getId() . "\n" );
368  return false;
369  }
370 
371  $this->mContentObject = $content;
372  $this->mRevIdFetched = $this->mRevision->getId();
373 
374  // Avoid PHP 7.1 warning of passing $this by reference
375  $articlePage = $this;
376 
377  Hooks::run(
378  'ArticleAfterFetchContentObject',
379  [ &$articlePage, &$this->mContentObject ]
380  );
381 
382  return $this->mContentObject;
383  }
384 
390  public function isCurrent() {
391  # If no oldid, this is the current version.
392  if ( $this->getOldID() == 0 ) {
393  return true;
394  }
395 
396  return $this->mPage->exists() && $this->mRevision && $this->mRevision->isCurrent();
397  }
398 
406  public function getRevisionFetched() {
407  $this->fetchContentObject();
408 
409  return $this->mRevision;
410  }
411 
417  public function getRevIdFetched() {
418  if ( $this->mRevIdFetched ) {
419  return $this->mRevIdFetched;
420  } else {
421  return $this->mPage->getLatest();
422  }
423  }
424 
429  public function view() {
430  global $wgUseFileCache, $wgDebugToolbar;
431 
432  # Get variables from query string
433  # As side effect this will load the revision and update the title
434  # in a revision ID is passed in the request, so this should remain
435  # the first call of this method even if $oldid is used way below.
436  $oldid = $this->getOldID();
437 
438  $user = $this->getContext()->getUser();
439  # Another whitelist check in case getOldID() is altering the title
440  $permErrors = $this->getTitle()->getUserPermissionsErrors( 'read', $user );
441  if ( count( $permErrors ) ) {
442  wfDebug( __METHOD__ . ": denied on secondary read check\n" );
443  throw new PermissionsError( 'read', $permErrors );
444  }
445 
446  $outputPage = $this->getContext()->getOutput();
447  # getOldID() may as well want us to redirect somewhere else
448  if ( $this->mRedirectUrl ) {
449  $outputPage->redirect( $this->mRedirectUrl );
450  wfDebug( __METHOD__ . ": redirecting due to oldid\n" );
451 
452  return;
453  }
454 
455  # If we got diff in the query, we want to see a diff page instead of the article.
456  if ( $this->getContext()->getRequest()->getCheck( 'diff' ) ) {
457  wfDebug( __METHOD__ . ": showing diff page\n" );
458  $this->showDiffPage();
459 
460  return;
461  }
462 
463  # Set page title (may be overridden by DISPLAYTITLE)
464  $outputPage->setPageTitle( $this->getTitle()->getPrefixedText() );
465 
466  $outputPage->setArticleFlag( true );
467  # Allow frames by default
468  $outputPage->allowClickjacking();
469 
470  $parserCache = ParserCache::singleton();
471 
472  $parserOptions = $this->getParserOptions();
473  # Render printable version, use printable version cache
474  if ( $outputPage->isPrintable() ) {
475  $parserOptions->setIsPrintable( true );
476  $parserOptions->setEditSection( false );
477  } elseif ( !$this->isCurrent() || !$this->getTitle()->quickUserCan( 'edit', $user ) ) {
478  $parserOptions->setEditSection( false );
479  }
480 
481  # Try client and file cache
482  if ( !$wgDebugToolbar && $oldid === 0 && $this->mPage->checkTouched() ) {
483  # Try to stream the output from file cache
484  if ( $wgUseFileCache && $this->tryFileCache() ) {
485  wfDebug( __METHOD__ . ": done file cache\n" );
486  # tell wgOut that output is taken care of
487  $outputPage->disable();
488  $this->mPage->doViewUpdates( $user, $oldid );
489 
490  return;
491  }
492  }
493 
494  # Should the parser cache be used?
495  $useParserCache = $this->mPage->shouldCheckParserCache( $parserOptions, $oldid );
496  wfDebug( 'Article::view using parser cache: ' . ( $useParserCache ? 'yes' : 'no' ) . "\n" );
497  if ( $user->getStubThreshold() ) {
498  MediaWikiServices::getInstance()->getStatsdDataFactory()->increment( 'pcache_miss_stub' );
499  }
500 
501  $this->showRedirectedFromHeader();
502  $this->showNamespaceHeader();
503 
504  # Iterate through the possible ways of constructing the output text.
505  # Keep going until $outputDone is set, or we run out of things to do.
506  $pass = 0;
507  $outputDone = false;
508  $this->mParserOutput = false;
509 
510  while ( !$outputDone && ++$pass ) {
511  switch ( $pass ) {
512  case 1:
513  // Avoid PHP 7.1 warning of passing $this by reference
514  $articlePage = $this;
515  Hooks::run( 'ArticleViewHeader', [ &$articlePage, &$outputDone, &$useParserCache ] );
516  break;
517  case 2:
518  # Early abort if the page doesn't exist
519  if ( !$this->mPage->exists() ) {
520  wfDebug( __METHOD__ . ": showing missing article\n" );
521  $this->showMissingArticle();
522  $this->mPage->doViewUpdates( $user );
523  return;
524  }
525 
526  # Try the parser cache
527  if ( $useParserCache ) {
528  $this->mParserOutput = $parserCache->get( $this->mPage, $parserOptions );
529 
530  if ( $this->mParserOutput !== false ) {
531  if ( $oldid ) {
532  wfDebug( __METHOD__ . ": showing parser cache contents for current rev permalink\n" );
533  $this->setOldSubtitle( $oldid );
534  } else {
535  wfDebug( __METHOD__ . ": showing parser cache contents\n" );
536  }
537  $outputPage->addParserOutput( $this->mParserOutput );
538  # Ensure that UI elements requiring revision ID have
539  # the correct version information.
540  $outputPage->setRevisionId( $this->mPage->getLatest() );
541  # Preload timestamp to avoid a DB hit
542  $cachedTimestamp = $this->mParserOutput->getTimestamp();
543  if ( $cachedTimestamp !== null ) {
544  $outputPage->setRevisionTimestamp( $cachedTimestamp );
545  $this->mPage->setTimestamp( $cachedTimestamp );
546  }
547  $outputDone = true;
548  }
549  }
550  break;
551  case 3:
552  # This will set $this->mRevision if needed
553  $this->fetchContentObject();
554 
555  # Are we looking at an old revision
556  if ( $oldid && $this->mRevision ) {
557  $this->setOldSubtitle( $oldid );
558 
559  if ( !$this->showDeletedRevisionHeader() ) {
560  wfDebug( __METHOD__ . ": cannot view deleted revision\n" );
561  return;
562  }
563  }
564 
565  # Ensure that UI elements requiring revision ID have
566  # the correct version information.
567  $outputPage->setRevisionId( $this->getRevIdFetched() );
568  # Preload timestamp to avoid a DB hit
569  $outputPage->setRevisionTimestamp( $this->mPage->getTimestamp() );
570 
571  if ( !Hooks::run( 'ArticleContentViewCustom',
572  [ $this->fetchContentObject(), $this->getTitle(), $outputPage ] ) ) {
573 
574  # Allow extensions do their own custom view for certain pages
575  $outputDone = true;
576  }
577  break;
578  case 4:
579  # Run the parse, protected by a pool counter
580  wfDebug( __METHOD__ . ": doing uncached parse\n" );
581 
582  $content = $this->getContentObject();
583  $poolArticleView = new PoolWorkArticleView( $this->getPage(), $parserOptions,
584  $this->getRevIdFetched(), $useParserCache, $content );
585 
586  if ( !$poolArticleView->execute() ) {
587  $error = $poolArticleView->getError();
588  if ( $error ) {
589  $outputPage->clearHTML(); // for release() errors
590  $outputPage->enableClientCache( false );
591  $outputPage->setRobotPolicy( 'noindex,nofollow' );
592 
593  $errortext = $error->getWikiText( false, 'view-pool-error' );
594  $outputPage->addWikiText( '<div class="errorbox">' . $errortext . '</div>' );
595  }
596  # Connection or timeout error
597  return;
598  }
599 
600  $this->mParserOutput = $poolArticleView->getParserOutput();
601  $outputPage->addParserOutput( $this->mParserOutput );
602  if ( $content->getRedirectTarget() ) {
603  $outputPage->addSubtitle( "<span id=\"redirectsub\">" .
604  $this->getContext()->msg( 'redirectpagesub' )->parse() . "</span>" );
605  }
606 
607  # Don't cache a dirty ParserOutput object
608  if ( $poolArticleView->getIsDirty() ) {
609  $outputPage->setCdnMaxage( 0 );
610  $outputPage->addHTML( "<!-- parser cache is expired, " .
611  "sending anyway due to pool overload-->\n" );
612  }
613 
614  $outputDone = true;
615  break;
616  # Should be unreachable, but just in case...
617  default:
618  break 2;
619  }
620  }
621 
622  # Get the ParserOutput actually *displayed* here.
623  # Note that $this->mParserOutput is the *current*/oldid version output.
624  $pOutput = ( $outputDone instanceof ParserOutput )
625  ? $outputDone // object fetched by hook
626  : $this->mParserOutput;
627 
628  # Adjust title for main page & pages with displaytitle
629  if ( $pOutput ) {
630  $this->adjustDisplayTitle( $pOutput );
631  }
632 
633  # For the main page, overwrite the <title> element with the con-
634  # tents of 'pagetitle-view-mainpage' instead of the default (if
635  # that's not empty).
636  # This message always exists because it is in the i18n files
637  if ( $this->getTitle()->isMainPage() ) {
638  $msg = wfMessage( 'pagetitle-view-mainpage' )->inContentLanguage();
639  if ( !$msg->isDisabled() ) {
640  $outputPage->setHTMLTitle( $msg->title( $this->getTitle() )->text() );
641  }
642  }
643 
644  # Use adaptive TTLs for CDN so delayed/failed purges are noticed less often.
645  # This could use getTouched(), but that could be scary for major template edits.
646  $outputPage->adaptCdnTTL( $this->mPage->getTimestamp(), IExpiringStore::TTL_DAY );
647 
648  # Check for any __NOINDEX__ tags on the page using $pOutput
649  $policy = $this->getRobotPolicy( 'view', $pOutput );
650  $outputPage->setIndexPolicy( $policy['index'] );
651  $outputPage->setFollowPolicy( $policy['follow'] );
652 
653  $this->showViewFooter();
654  $this->mPage->doViewUpdates( $user, $oldid );
655 
656  # Load the postEdit module if the user just saved this revision
657  # See also EditPage::setPostEditCookie
658  $request = $this->getContext()->getRequest();
660  $postEdit = $request->getCookie( $cookieKey );
661  if ( $postEdit ) {
662  # Clear the cookie. This also prevents caching of the response.
663  $request->response()->clearCookie( $cookieKey );
664  $outputPage->addJsConfigVars( 'wgPostEdit', $postEdit );
665  $outputPage->addModules( 'mediawiki.action.view.postEdit' );
666  }
667  }
668 
673  public function adjustDisplayTitle( ParserOutput $pOutput ) {
674  # Adjust the title if it was set by displaytitle, -{T|}- or language conversion
675  $titleText = $pOutput->getTitleText();
676  if ( strval( $titleText ) !== '' ) {
677  $this->getContext()->getOutput()->setPageTitle( $titleText );
678  }
679  }
680 
685  protected function showDiffPage() {
686  $request = $this->getContext()->getRequest();
687  $user = $this->getContext()->getUser();
688  $diff = $request->getVal( 'diff' );
689  $rcid = $request->getVal( 'rcid' );
690  $diffOnly = $request->getBool( 'diffonly', $user->getOption( 'diffonly' ) );
691  $purge = $request->getVal( 'action' ) == 'purge';
692  $unhide = $request->getInt( 'unhide' ) == 1;
693  $oldid = $this->getOldID();
694 
695  $rev = $this->getRevisionFetched();
696 
697  if ( !$rev ) {
698  $this->getContext()->getOutput()->setPageTitle( wfMessage( 'errorpagetitle' ) );
699  $msg = $this->getContext()->msg( 'difference-missing-revision' )
700  ->params( $oldid )
701  ->numParams( 1 )
702  ->parseAsBlock();
703  $this->getContext()->getOutput()->addHTML( $msg );
704  return;
705  }
706 
707  $contentHandler = $rev->getContentHandler();
708  $de = $contentHandler->createDifferenceEngine(
709  $this->getContext(),
710  $oldid,
711  $diff,
712  $rcid,
713  $purge,
714  $unhide
715  );
716 
717  // DifferenceEngine directly fetched the revision:
718  $this->mRevIdFetched = $de->mNewid;
719  $de->showDiffPage( $diffOnly );
720 
721  // Run view updates for the newer revision being diffed (and shown
722  // below the diff if not $diffOnly).
723  list( $old, $new ) = $de->mapDiffPrevNext( $oldid, $diff );
724  // New can be false, convert it to 0 - this conveniently means the latest revision
725  $this->mPage->doViewUpdates( $user, (int)$new );
726  }
727 
735  public function getRobotPolicy( $action, $pOutput = null ) {
736  global $wgArticleRobotPolicies, $wgNamespaceRobotPolicies, $wgDefaultRobotPolicy;
737 
738  $ns = $this->getTitle()->getNamespace();
739 
740  # Don't index user and user talk pages for blocked users (T13443)
741  if ( ( $ns == NS_USER || $ns == NS_USER_TALK ) && !$this->getTitle()->isSubpage() ) {
742  $specificTarget = null;
743  $vagueTarget = null;
744  $titleText = $this->getTitle()->getText();
745  if ( IP::isValid( $titleText ) ) {
746  $vagueTarget = $titleText;
747  } else {
748  $specificTarget = $titleText;
749  }
750  if ( Block::newFromTarget( $specificTarget, $vagueTarget ) instanceof Block ) {
751  return [
752  'index' => 'noindex',
753  'follow' => 'nofollow'
754  ];
755  }
756  }
757 
758  if ( $this->mPage->getId() === 0 || $this->getOldID() ) {
759  # Non-articles (special pages etc), and old revisions
760  return [
761  'index' => 'noindex',
762  'follow' => 'nofollow'
763  ];
764  } elseif ( $this->getContext()->getOutput()->isPrintable() ) {
765  # Discourage indexing of printable versions, but encourage following
766  return [
767  'index' => 'noindex',
768  'follow' => 'follow'
769  ];
770  } elseif ( $this->getContext()->getRequest()->getInt( 'curid' ) ) {
771  # For ?curid=x urls, disallow indexing
772  return [
773  'index' => 'noindex',
774  'follow' => 'follow'
775  ];
776  }
777 
778  # Otherwise, construct the policy based on the various config variables.
779  $policy = self::formatRobotPolicy( $wgDefaultRobotPolicy );
780 
781  if ( isset( $wgNamespaceRobotPolicies[$ns] ) ) {
782  # Honour customised robot policies for this namespace
783  $policy = array_merge(
784  $policy,
785  self::formatRobotPolicy( $wgNamespaceRobotPolicies[$ns] )
786  );
787  }
788  if ( $this->getTitle()->canUseNoindex() && is_object( $pOutput ) && $pOutput->getIndexPolicy() ) {
789  # __INDEX__ and __NOINDEX__ magic words, if allowed. Incorporates
790  # a final sanity check that we have really got the parser output.
791  $policy = array_merge(
792  $policy,
793  [ 'index' => $pOutput->getIndexPolicy() ]
794  );
795  }
796 
797  if ( isset( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] ) ) {
798  # (T16900) site config can override user-defined __INDEX__ or __NOINDEX__
799  $policy = array_merge(
800  $policy,
801  self::formatRobotPolicy( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] )
802  );
803  }
804 
805  return $policy;
806  }
807 
815  public static function formatRobotPolicy( $policy ) {
816  if ( is_array( $policy ) ) {
817  return $policy;
818  } elseif ( !$policy ) {
819  return [];
820  }
821 
822  $policy = explode( ',', $policy );
823  $policy = array_map( 'trim', $policy );
824 
825  $arr = [];
826  foreach ( $policy as $var ) {
827  if ( in_array( $var, [ 'index', 'noindex' ] ) ) {
828  $arr['index'] = $var;
829  } elseif ( in_array( $var, [ 'follow', 'nofollow' ] ) ) {
830  $arr['follow'] = $var;
831  }
832  }
833 
834  return $arr;
835  }
836 
844  public function showRedirectedFromHeader() {
846 
847  $context = $this->getContext();
848  $outputPage = $context->getOutput();
849  $request = $context->getRequest();
850  $rdfrom = $request->getVal( 'rdfrom' );
851 
852  // Construct a URL for the current page view, but with the target title
853  $query = $request->getValues();
854  unset( $query['rdfrom'] );
855  unset( $query['title'] );
856  if ( $this->getTitle()->isRedirect() ) {
857  // Prevent double redirects
858  $query['redirect'] = 'no';
859  }
860  $redirectTargetUrl = $this->getTitle()->getLinkURL( $query );
861 
862  if ( isset( $this->mRedirectedFrom ) ) {
863  // Avoid PHP 7.1 warning of passing $this by reference
864  $articlePage = $this;
865 
866  // This is an internally redirected page view.
867  // We'll need a backlink to the source page for navigation.
868  if ( Hooks::run( 'ArticleViewRedirect', [ &$articlePage ] ) ) {
869  $redir = Linker::linkKnown(
870  $this->mRedirectedFrom,
871  null,
872  [],
873  [ 'redirect' => 'no' ]
874  );
875 
876  $outputPage->addSubtitle( "<span class=\"mw-redirectedfrom\">" .
877  $context->msg( 'redirectedfrom' )->rawParams( $redir )->parse()
878  . "</span>" );
879 
880  // Add the script to update the displayed URL and
881  // set the fragment if one was specified in the redirect
882  $outputPage->addJsConfigVars( [
883  'wgInternalRedirectTargetUrl' => $redirectTargetUrl,
884  ] );
885  $outputPage->addModules( 'mediawiki.action.view.redirect' );
886 
887  // Add a <link rel="canonical"> tag
888  $outputPage->setCanonicalUrl( $this->getTitle()->getCanonicalURL() );
889 
890  // Tell the output object that the user arrived at this article through a redirect
891  $outputPage->setRedirectedFrom( $this->mRedirectedFrom );
892 
893  return true;
894  }
895  } elseif ( $rdfrom ) {
896  // This is an externally redirected view, from some other wiki.
897  // If it was reported from a trusted site, supply a backlink.
898  if ( $wgRedirectSources && preg_match( $wgRedirectSources, $rdfrom ) ) {
899  $redir = Linker::makeExternalLink( $rdfrom, $rdfrom );
900  $outputPage->addSubtitle( "<span class=\"mw-redirectedfrom\">" .
901  $context->msg( 'redirectedfrom' )->rawParams( $redir )->parse()
902  . "</span>" );
903 
904  // Add the script to update the displayed URL
905  $outputPage->addJsConfigVars( [
906  'wgInternalRedirectTargetUrl' => $redirectTargetUrl,
907  ] );
908  $outputPage->addModules( 'mediawiki.action.view.redirect' );
909 
910  return true;
911  }
912  }
913 
914  return false;
915  }
916 
921  public function showNamespaceHeader() {
922  if ( $this->getTitle()->isTalkPage() ) {
923  if ( !wfMessage( 'talkpageheader' )->isDisabled() ) {
924  $this->getContext()->getOutput()->wrapWikiMsg(
925  "<div class=\"mw-talkpageheader\">\n$1\n</div>",
926  [ 'talkpageheader' ]
927  );
928  }
929  }
930  }
931 
935  public function showViewFooter() {
936  # check if we're displaying a [[User talk:x.x.x.x]] anonymous talk page
937  if ( $this->getTitle()->getNamespace() == NS_USER_TALK
938  && IP::isValid( $this->getTitle()->getText() )
939  ) {
940  $this->getContext()->getOutput()->addWikiMsg( 'anontalkpagetext' );
941  }
942 
943  // Show a footer allowing the user to patrol the shown revision or page if possible
944  $patrolFooterShown = $this->showPatrolFooter();
945 
946  Hooks::run( 'ArticleViewFooter', [ $this, $patrolFooterShown ] );
947  }
948 
958  public function showPatrolFooter() {
959  global $wgUseNPPatrol, $wgUseRCPatrol, $wgUseFilePatrol, $wgEnableAPI, $wgEnableWriteAPI;
960 
961  $outputPage = $this->getContext()->getOutput();
962  $user = $this->getContext()->getUser();
963  $title = $this->getTitle();
964  $rc = false;
965 
966  if ( !$title->quickUserCan( 'patrol', $user )
967  || !( $wgUseRCPatrol || $wgUseNPPatrol
968  || ( $wgUseFilePatrol && $title->inNamespace( NS_FILE ) ) )
969  ) {
970  // Patrolling is disabled or the user isn't allowed to
971  return false;
972  }
973 
974  if ( $this->mRevision
975  && !RecentChange::isInRCLifespan( $this->mRevision->getTimestamp(), 21600 )
976  ) {
977  // The current revision is already older than what could be in the RC table
978  // 6h tolerance because the RC might not be cleaned out regularly
979  return false;
980  }
981 
982  // Check for cached results
983  $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
984  $key = $cache->makeKey( 'unpatrollable-page', $title->getArticleID() );
985  if ( $cache->get( $key ) ) {
986  return false;
987  }
988 
989  $dbr = wfGetDB( DB_REPLICA );
990  $oldestRevisionTimestamp = $dbr->selectField(
991  'revision',
992  'MIN( rev_timestamp )',
993  [ 'rev_page' => $title->getArticleID() ],
994  __METHOD__
995  );
996 
997  // New page patrol: Get the timestamp of the oldest revison which
998  // the revision table holds for the given page. Then we look
999  // whether it's within the RC lifespan and if it is, we try
1000  // to get the recentchanges row belonging to that entry
1001  // (with rc_new = 1).
1002  $recentPageCreation = false;
1003  if ( $oldestRevisionTimestamp
1004  && RecentChange::isInRCLifespan( $oldestRevisionTimestamp, 21600 )
1005  ) {
1006  // 6h tolerance because the RC might not be cleaned out regularly
1007  $recentPageCreation = true;
1009  [
1010  'rc_new' => 1,
1011  'rc_timestamp' => $oldestRevisionTimestamp,
1012  'rc_namespace' => $title->getNamespace(),
1013  'rc_cur_id' => $title->getArticleID()
1014  ],
1015  __METHOD__
1016  );
1017  if ( $rc ) {
1018  // Use generic patrol message for new pages
1019  $markPatrolledMsg = wfMessage( 'markaspatrolledtext' );
1020  }
1021  }
1022 
1023  // File patrol: Get the timestamp of the latest upload for this page,
1024  // check whether it is within the RC lifespan and if it is, we try
1025  // to get the recentchanges row belonging to that entry
1026  // (with rc_type = RC_LOG, rc_log_type = upload).
1027  $recentFileUpload = false;
1028  if ( ( !$rc || $rc->getAttribute( 'rc_patrolled' ) ) && $wgUseFilePatrol
1029  && $title->getNamespace() === NS_FILE ) {
1030  // Retrieve timestamp of most recent upload
1031  $newestUploadTimestamp = $dbr->selectField(
1032  'image',
1033  'MAX( img_timestamp )',
1034  [ 'img_name' => $title->getDBkey() ],
1035  __METHOD__
1036  );
1037  if ( $newestUploadTimestamp
1038  && RecentChange::isInRCLifespan( $newestUploadTimestamp, 21600 )
1039  ) {
1040  // 6h tolerance because the RC might not be cleaned out regularly
1041  $recentFileUpload = true;
1043  [
1044  'rc_type' => RC_LOG,
1045  'rc_log_type' => 'upload',
1046  'rc_timestamp' => $newestUploadTimestamp,
1047  'rc_namespace' => NS_FILE,
1048  'rc_cur_id' => $title->getArticleID()
1049  ],
1050  __METHOD__,
1051  [ 'USE INDEX' => 'rc_timestamp' ]
1052  );
1053  if ( $rc ) {
1054  // Use patrol message specific to files
1055  $markPatrolledMsg = wfMessage( 'markaspatrolledtext-file' );
1056  }
1057  }
1058  }
1059 
1060  if ( !$recentPageCreation && !$recentFileUpload ) {
1061  // Page creation and latest upload (for files) is too old to be in RC
1062 
1063  // We definitely can't patrol so cache the information
1064  // When a new file version is uploaded, the cache is cleared
1065  $cache->set( $key, '1' );
1066 
1067  return false;
1068  }
1069 
1070  if ( !$rc ) {
1071  // Don't cache: This can be hit if the page gets accessed very fast after
1072  // its creation / latest upload or in case we have high replica DB lag. In case
1073  // the revision is too old, we will already return above.
1074  return false;
1075  }
1076 
1077  if ( $rc->getAttribute( 'rc_patrolled' ) ) {
1078  // Patrolled RC entry around
1079 
1080  // Cache the information we gathered above in case we can't patrol
1081  // Don't cache in case we can patrol as this could change
1082  $cache->set( $key, '1' );
1083 
1084  return false;
1085  }
1086 
1087  if ( $rc->getPerformer()->equals( $user ) ) {
1088  // Don't show a patrol link for own creations/uploads. If the user could
1089  // patrol them, they already would be patrolled
1090  return false;
1091  }
1092 
1093  $outputPage->preventClickjacking();
1094  if ( $wgEnableAPI && $wgEnableWriteAPI && $user->isAllowed( 'writeapi' ) ) {
1095  $outputPage->addModules( 'mediawiki.page.patrol.ajax' );
1096  }
1097 
1099  $title,
1100  $markPatrolledMsg->escaped(),
1101  [],
1102  [
1103  'action' => 'markpatrolled',
1104  'rcid' => $rc->getAttribute( 'rc_id' ),
1105  ]
1106  );
1107 
1108  $outputPage->addHTML(
1109  "<div class='patrollink' data-mw='interface'>" .
1110  wfMessage( 'markaspatrolledlink' )->rawParams( $link )->escaped() .
1111  '</div>'
1112  );
1113 
1114  return true;
1115  }
1116 
1123  public static function purgePatrolFooterCache( $articleID ) {
1124  $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
1125  $cache->delete( $cache->makeKey( 'unpatrollable-page', $articleID ) );
1126  }
1127 
1132  public function showMissingArticle() {
1134 
1135  $outputPage = $this->getContext()->getOutput();
1136  // Whether the page is a root user page of an existing user (but not a subpage)
1137  $validUserPage = false;
1138 
1139  $title = $this->getTitle();
1140 
1141  # Show info in user (talk) namespace. Does the user exist? Is he blocked?
1142  if ( $title->getNamespace() == NS_USER
1143  || $title->getNamespace() == NS_USER_TALK
1144  ) {
1145  $rootPart = explode( '/', $title->getText() )[0];
1146  $user = User::newFromName( $rootPart, false /* allow IP users */ );
1147  $ip = User::isIP( $rootPart );
1148  $block = Block::newFromTarget( $user, $user );
1149 
1150  if ( !( $user && $user->isLoggedIn() ) && !$ip ) { # User does not exist
1151  $outputPage->wrapWikiMsg( "<div class=\"mw-userpage-userdoesnotexist error\">\n\$1\n</div>",
1152  [ 'userpage-userdoesnotexist-view', wfEscapeWikiText( $rootPart ) ] );
1153  } elseif ( !is_null( $block ) && $block->getType() != Block::TYPE_AUTO ) {
1154  # Show log extract if the user is currently blocked
1156  $outputPage,
1157  'block',
1158  MWNamespace::getCanonicalName( NS_USER ) . ':' . $block->getTarget(),
1159  '',
1160  [
1161  'lim' => 1,
1162  'showIfEmpty' => false,
1163  'msgKey' => [
1164  'blocked-notice-logextract',
1165  $user->getName() # Support GENDER in notice
1166  ]
1167  ]
1168  );
1169  $validUserPage = !$title->isSubpage();
1170  } else {
1171  $validUserPage = !$title->isSubpage();
1172  }
1173  }
1174 
1175  Hooks::run( 'ShowMissingArticle', [ $this ] );
1176 
1177  # Show delete and move logs if there were any such events.
1178  # The logging query can DOS the site when bots/crawlers cause 404 floods,
1179  # so be careful showing this. 404 pages must be cheap as they are hard to cache.
1180  $cache = MediaWikiServices::getInstance()->getMainObjectStash();
1181  $key = $cache->makeKey( 'page-recent-delete', md5( $title->getPrefixedText() ) );
1182  $loggedIn = $this->getContext()->getUser()->isLoggedIn();
1183  if ( $loggedIn || $cache->get( $key ) ) {
1184  $logTypes = [ 'delete', 'move' ];
1185 
1186  $dbr = wfGetDB( DB_REPLICA );
1187 
1188  $conds = [ 'log_action != ' . $dbr->addQuotes( 'revision' ) ];
1189  // Give extensions a chance to hide their (unrelated) log entries
1190  Hooks::run( 'Article::MissingArticleConditions', [ &$conds, $logTypes ] );
1192  $outputPage,
1193  $logTypes,
1194  $title,
1195  '',
1196  [
1197  'lim' => 10,
1198  'conds' => $conds,
1199  'showIfEmpty' => false,
1200  'msgKey' => [ $loggedIn
1201  ? 'moveddeleted-notice'
1202  : 'moveddeleted-notice-recent'
1203  ]
1204  ]
1205  );
1206  }
1207 
1208  if ( !$this->mPage->hasViewableContent() && $wgSend404Code && !$validUserPage ) {
1209  // If there's no backing content, send a 404 Not Found
1210  // for better machine handling of broken links.
1211  $this->getContext()->getRequest()->response()->statusHeader( 404 );
1212  }
1213 
1214  // Also apply the robot policy for nonexisting pages (even if a 404 was used for sanity)
1215  $policy = $this->getRobotPolicy( 'view' );
1216  $outputPage->setIndexPolicy( $policy['index'] );
1217  $outputPage->setFollowPolicy( $policy['follow'] );
1218 
1219  $hookResult = Hooks::run( 'BeforeDisplayNoArticleText', [ $this ] );
1220 
1221  if ( !$hookResult ) {
1222  return;
1223  }
1224 
1225  # Show error message
1226  $oldid = $this->getOldID();
1227  if ( !$oldid && $title->getNamespace() === NS_MEDIAWIKI && $title->hasSourceText() ) {
1228  $outputPage->addParserOutput( $this->getContentObject()->getParserOutput( $title ) );
1229  } else {
1230  if ( $oldid ) {
1231  $text = wfMessage( 'missing-revision', $oldid )->plain();
1232  } elseif ( $title->quickUserCan( 'create', $this->getContext()->getUser() )
1233  && $title->quickUserCan( 'edit', $this->getContext()->getUser() )
1234  ) {
1235  $message = $this->getContext()->getUser()->isLoggedIn() ? 'noarticletext' : 'noarticletextanon';
1236  $text = wfMessage( $message )->plain();
1237  } else {
1238  $text = wfMessage( 'noarticletext-nopermission' )->plain();
1239  }
1240 
1241  $dir = $this->getContext()->getLanguage()->getDir();
1242  $lang = $this->getContext()->getLanguage()->getCode();
1243  $outputPage->addWikiText( Xml::openElement( 'div', [
1244  'class' => "noarticletext mw-content-$dir",
1245  'dir' => $dir,
1246  'lang' => $lang,
1247  ] ) . "\n$text\n</div>" );
1248  }
1249  }
1250 
1257  public function showDeletedRevisionHeader() {
1258  if ( !$this->mRevision->isDeleted( Revision::DELETED_TEXT ) ) {
1259  // Not deleted
1260  return true;
1261  }
1262 
1263  $outputPage = $this->getContext()->getOutput();
1264  $user = $this->getContext()->getUser();
1265  // If the user is not allowed to see it...
1266  if ( !$this->mRevision->userCan( Revision::DELETED_TEXT, $user ) ) {
1267  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1268  'rev-deleted-text-permission' );
1269 
1270  return false;
1271  // If the user needs to confirm that they want to see it...
1272  } elseif ( $this->getContext()->getRequest()->getInt( 'unhide' ) != 1 ) {
1273  # Give explanation and add a link to view the revision...
1274  $oldid = intval( $this->getOldID() );
1275  $link = $this->getTitle()->getFullURL( "oldid={$oldid}&unhide=1" );
1276  $msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ?
1277  'rev-suppressed-text-unhide' : 'rev-deleted-text-unhide';
1278  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1279  [ $msg, $link ] );
1280 
1281  return false;
1282  // We are allowed to see...
1283  } else {
1284  $msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ?
1285  'rev-suppressed-text-view' : 'rev-deleted-text-view';
1286  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n", $msg );
1287 
1288  return true;
1289  }
1290  }
1291 
1300  public function setOldSubtitle( $oldid = 0 ) {
1301  // Avoid PHP 7.1 warning of passing $this by reference
1302  $articlePage = $this;
1303 
1304  if ( !Hooks::run( 'DisplayOldSubtitle', [ &$articlePage, &$oldid ] ) ) {
1305  return;
1306  }
1307 
1308  $context = $this->getContext();
1309  $unhide = $context->getRequest()->getInt( 'unhide' ) == 1;
1310 
1311  # Cascade unhide param in links for easy deletion browsing
1312  $extraParams = [];
1313  if ( $unhide ) {
1314  $extraParams['unhide'] = 1;
1315  }
1316 
1317  if ( $this->mRevision && $this->mRevision->getId() === $oldid ) {
1318  $revision = $this->mRevision;
1319  } else {
1320  $revision = Revision::newFromId( $oldid );
1321  }
1322 
1323  $timestamp = $revision->getTimestamp();
1324 
1325  $current = ( $oldid == $this->mPage->getLatest() );
1326  $language = $context->getLanguage();
1327  $user = $context->getUser();
1328 
1329  $td = $language->userTimeAndDate( $timestamp, $user );
1330  $tddate = $language->userDate( $timestamp, $user );
1331  $tdtime = $language->userTime( $timestamp, $user );
1332 
1333  # Show user links if allowed to see them. If hidden, then show them only if requested...
1334  $userlinks = Linker::revUserTools( $revision, !$unhide );
1335 
1336  $infomsg = $current && !$context->msg( 'revision-info-current' )->isDisabled()
1337  ? 'revision-info-current'
1338  : 'revision-info';
1339 
1340  $outputPage = $context->getOutput();
1341  $revisionInfo = "<div id=\"mw-{$infomsg}\">" .
1342  $context->msg( $infomsg, $td )
1343  ->rawParams( $userlinks )
1344  ->params( $revision->getId(), $tddate, $tdtime, $revision->getUserText() )
1345  ->rawParams( Linker::revComment( $revision, true, true ) )
1346  ->parse() .
1347  "</div>";
1348 
1349  $lnk = $current
1350  ? $context->msg( 'currentrevisionlink' )->escaped()
1352  $this->getTitle(),
1353  $context->msg( 'currentrevisionlink' )->escaped(),
1354  [],
1355  $extraParams
1356  );
1357  $curdiff = $current
1358  ? $context->msg( 'diff' )->escaped()
1360  $this->getTitle(),
1361  $context->msg( 'diff' )->escaped(),
1362  [],
1363  [
1364  'diff' => 'cur',
1365  'oldid' => $oldid
1366  ] + $extraParams
1367  );
1368  $prev = $this->getTitle()->getPreviousRevisionID( $oldid );
1369  $prevlink = $prev
1371  $this->getTitle(),
1372  $context->msg( 'previousrevision' )->escaped(),
1373  [],
1374  [
1375  'direction' => 'prev',
1376  'oldid' => $oldid
1377  ] + $extraParams
1378  )
1379  : $context->msg( 'previousrevision' )->escaped();
1380  $prevdiff = $prev
1382  $this->getTitle(),
1383  $context->msg( 'diff' )->escaped(),
1384  [],
1385  [
1386  'diff' => 'prev',
1387  'oldid' => $oldid
1388  ] + $extraParams
1389  )
1390  : $context->msg( 'diff' )->escaped();
1391  $nextlink = $current
1392  ? $context->msg( 'nextrevision' )->escaped()
1394  $this->getTitle(),
1395  $context->msg( 'nextrevision' )->escaped(),
1396  [],
1397  [
1398  'direction' => 'next',
1399  'oldid' => $oldid
1400  ] + $extraParams
1401  );
1402  $nextdiff = $current
1403  ? $context->msg( 'diff' )->escaped()
1405  $this->getTitle(),
1406  $context->msg( 'diff' )->escaped(),
1407  [],
1408  [
1409  'diff' => 'next',
1410  'oldid' => $oldid
1411  ] + $extraParams
1412  );
1413 
1414  $cdel = Linker::getRevDeleteLink( $user, $revision, $this->getTitle() );
1415  if ( $cdel !== '' ) {
1416  $cdel .= ' ';
1417  }
1418 
1419  // the outer div is need for styling the revision info and nav in MobileFrontend
1420  $outputPage->addSubtitle( "<div class=\"mw-revision\">" . $revisionInfo .
1421  "<div id=\"mw-revision-nav\">" . $cdel .
1422  $context->msg( 'revision-nav' )->rawParams(
1423  $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff
1424  )->escaped() . "</div></div>" );
1425  }
1426 
1438  public function viewRedirect( $target, $appendSubtitle = true, $forceKnown = false ) {
1439  $lang = $this->getTitle()->getPageLanguage();
1440  $out = $this->getContext()->getOutput();
1441  if ( $appendSubtitle ) {
1442  $out->addSubtitle( wfMessage( 'redirectpagesub' ) );
1443  }
1444  $out->addModuleStyles( 'mediawiki.action.view.redirectPage' );
1445  return static::getRedirectHeaderHtml( $lang, $target, $forceKnown );
1446  }
1447 
1460  public static function getRedirectHeaderHtml( Language $lang, $target, $forceKnown = false ) {
1461  if ( !is_array( $target ) ) {
1462  $target = [ $target ];
1463  }
1464 
1465  $html = '<ul class="redirectText">';
1467  foreach ( $target as $title ) {
1468  $html .= '<li>' . Linker::link(
1469  $title,
1470  htmlspecialchars( $title->getFullText() ),
1471  [],
1472  // Make sure wiki page redirects are not followed
1473  $title->isRedirect() ? [ 'redirect' => 'no' ] : [],
1474  ( $forceKnown ? [ 'known', 'noclasses' ] : [] )
1475  ) . '</li>';
1476  }
1477  $html .= '</ul>';
1478 
1479  $redirectToText = wfMessage( 'redirectto' )->inLanguage( $lang )->escaped();
1480 
1481  return '<div class="redirectMsg">' .
1482  '<p>' . $redirectToText . '</p>' .
1483  $html .
1484  '</div>';
1485  }
1486 
1495  public function addHelpLink( $to, $overrideBaseUrl = false ) {
1496  $msg = wfMessage(
1497  'namespace-' . $this->getTitle()->getNamespace() . '-helppage'
1498  );
1499 
1500  $out = $this->getContext()->getOutput();
1501  if ( !$msg->isDisabled() ) {
1502  $helpUrl = Skin::makeUrl( $msg->plain() );
1503  $out->addHelpLink( $helpUrl, true );
1504  } else {
1505  $out->addHelpLink( $to, $overrideBaseUrl );
1506  }
1507  }
1508 
1512  public function render() {
1513  $this->getContext()->getRequest()->response()->header( 'X-Robots-Tag: noindex' );
1514  $this->getContext()->getOutput()->setArticleBodyOnly( true );
1515  $this->getContext()->getOutput()->enableSectionEditLinks( false );
1516  $this->view();
1517  }
1518 
1522  public function protect() {
1523  $form = new ProtectionForm( $this );
1524  $form->execute();
1525  }
1526 
1530  public function unprotect() {
1531  $this->protect();
1532  }
1533 
1537  public function delete() {
1538  # This code desperately needs to be totally rewritten
1539 
1540  $title = $this->getTitle();
1541  $context = $this->getContext();
1542  $user = $context->getUser();
1543  $request = $context->getRequest();
1544 
1545  # Check permissions
1546  $permissionErrors = $title->getUserPermissionsErrors( 'delete', $user );
1547  if ( count( $permissionErrors ) ) {
1548  throw new PermissionsError( 'delete', $permissionErrors );
1549  }
1550 
1551  # Read-only check...
1552  if ( wfReadOnly() ) {
1553  throw new ReadOnlyError;
1554  }
1555 
1556  # Better double-check that it hasn't been deleted yet!
1557  $this->mPage->loadPageData(
1558  $request->wasPosted() ? WikiPage::READ_LATEST : WikiPage::READ_NORMAL
1559  );
1560  if ( !$this->mPage->exists() ) {
1561  $deleteLogPage = new LogPage( 'delete' );
1562  $outputPage = $context->getOutput();
1563  $outputPage->setPageTitle( $context->msg( 'cannotdelete-title', $title->getPrefixedText() ) );
1564  $outputPage->wrapWikiMsg( "<div class=\"error mw-error-cannotdelete\">\n$1\n</div>",
1565  [ 'cannotdelete', wfEscapeWikiText( $title->getPrefixedText() ) ]
1566  );
1567  $outputPage->addHTML(
1568  Xml::element( 'h2', null, $deleteLogPage->getName()->text() )
1569  );
1571  $outputPage,
1572  'delete',
1573  $title
1574  );
1575 
1576  return;
1577  }
1578 
1579  $deleteReasonList = $request->getText( 'wpDeleteReasonList', 'other' );
1580  $deleteReason = $request->getText( 'wpReason' );
1581 
1582  if ( $deleteReasonList == 'other' ) {
1583  $reason = $deleteReason;
1584  } elseif ( $deleteReason != '' ) {
1585  // Entry from drop down menu + additional comment
1586  $colonseparator = wfMessage( 'colon-separator' )->inContentLanguage()->text();
1587  $reason = $deleteReasonList . $colonseparator . $deleteReason;
1588  } else {
1589  $reason = $deleteReasonList;
1590  }
1591 
1592  if ( $request->wasPosted() && $user->matchEditToken( $request->getVal( 'wpEditToken' ),
1593  [ 'delete', $this->getTitle()->getPrefixedText() ] )
1594  ) {
1595  # Flag to hide all contents of the archived revisions
1596  $suppress = $request->getVal( 'wpSuppress' ) && $user->isAllowed( 'suppressrevision' );
1597 
1598  $this->doDelete( $reason, $suppress );
1599 
1600  WatchAction::doWatchOrUnwatch( $request->getCheck( 'wpWatch' ), $title, $user );
1601 
1602  return;
1603  }
1604 
1605  // Generate deletion reason
1606  $hasHistory = false;
1607  if ( !$reason ) {
1608  try {
1609  $reason = $this->generateReason( $hasHistory );
1610  } catch ( Exception $e ) {
1611  # if a page is horribly broken, we still want to be able to
1612  # delete it. So be lenient about errors here.
1613  wfDebug( "Error while building auto delete summary: $e" );
1614  $reason = '';
1615  }
1616  }
1617 
1618  // If the page has a history, insert a warning
1619  if ( $hasHistory ) {
1620  $title = $this->getTitle();
1621 
1622  // The following can use the real revision count as this is only being shown for users
1623  // that can delete this page.
1624  // This, as a side-effect, also makes sure that the following query isn't being run for
1625  // pages with a larger history, unless the user has the 'bigdelete' right
1626  // (and is about to delete this page).
1627  $dbr = wfGetDB( DB_REPLICA );
1628  $revisions = $edits = (int)$dbr->selectField(
1629  'revision',
1630  'COUNT(rev_page)',
1631  [ 'rev_page' => $title->getArticleID() ],
1632  __METHOD__
1633  );
1634 
1635  // @todo FIXME: i18n issue/patchwork message
1636  $context->getOutput()->addHTML(
1637  '<strong class="mw-delete-warning-revisions">' .
1638  $context->msg( 'historywarning' )->numParams( $revisions )->parse() .
1639  $context->msg( 'word-separator' )->escaped() . Linker::linkKnown( $title,
1640  $context->msg( 'history' )->escaped(),
1641  [],
1642  [ 'action' => 'history' ] ) .
1643  '</strong>'
1644  );
1645 
1646  if ( $title->isBigDeletion() ) {
1647  global $wgDeleteRevisionsLimit;
1648  $context->getOutput()->wrapWikiMsg( "<div class='error'>\n$1\n</div>\n",
1649  [
1650  'delete-warning-toobig',
1651  $context->getLanguage()->formatNum( $wgDeleteRevisionsLimit )
1652  ]
1653  );
1654  }
1655  }
1656 
1657  $this->confirmDelete( $reason );
1658  }
1659 
1665  public function confirmDelete( $reason ) {
1666  wfDebug( "Article::confirmDelete\n" );
1667 
1668  $title = $this->getTitle();
1669  $ctx = $this->getContext();
1670  $outputPage = $ctx->getOutput();
1671  $useMediaWikiUIEverywhere = $ctx->getConfig()->get( 'UseMediaWikiUIEverywhere' );
1672  $outputPage->setPageTitle( wfMessage( 'delete-confirm', $title->getPrefixedText() ) );
1673  $outputPage->addBacklinkSubtitle( $title );
1674  $outputPage->setRobotPolicy( 'noindex,nofollow' );
1675  $backlinkCache = $title->getBacklinkCache();
1676  if ( $backlinkCache->hasLinks( 'pagelinks' ) || $backlinkCache->hasLinks( 'templatelinks' ) ) {
1677  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1678  'deleting-backlinks-warning' );
1679  }
1680  $outputPage->addWikiMsg( 'confirmdeletetext' );
1681 
1682  Hooks::run( 'ArticleConfirmDelete', [ $this, $outputPage, &$reason ] );
1683 
1684  $user = $this->getContext()->getUser();
1685  if ( $user->isAllowed( 'suppressrevision' ) ) {
1686  $suppress = Html::openElement( 'div', [ 'id' => 'wpDeleteSuppressRow' ] ) .
1687  Xml::checkLabel( wfMessage( 'revdelete-suppress' )->text(),
1688  'wpSuppress', 'wpSuppress', false, [ 'tabindex' => '4' ] ) .
1689  Html::closeElement( 'div' );
1690  } else {
1691  $suppress = '';
1692  }
1693  $checkWatch = $user->getBoolOption( 'watchdeletion' ) || $user->isWatched( $title );
1694  $form = Html::openElement( 'form', [ 'method' => 'post',
1695  'action' => $title->getLocalURL( 'action=delete' ), 'id' => 'deleteconfirm' ] ) .
1696  Html::openElement( 'fieldset', [ 'id' => 'mw-delete-table' ] ) .
1697  Html::element( 'legend', null, wfMessage( 'delete-legend' )->text() ) .
1698  Html::openElement( 'div', [ 'id' => 'mw-deleteconfirm-table' ] ) .
1699  Html::openElement( 'div', [ 'id' => 'wpDeleteReasonListRow' ] ) .
1700  Html::label( wfMessage( 'deletecomment' )->text(), 'wpDeleteReasonList' ) .
1701  '&nbsp;' .
1703  'wpDeleteReasonList',
1704  wfMessage( 'deletereason-dropdown' )->inContentLanguage()->text(),
1705  wfMessage( 'deletereasonotherlist' )->inContentLanguage()->text(),
1706  '',
1707  'wpReasonDropDown',
1708  1
1709  ) .
1710  Html::closeElement( 'div' ) .
1711  Html::openElement( 'div', [ 'id' => 'wpDeleteReasonRow' ] ) .
1712  Html::label( wfMessage( 'deleteotherreason' )->text(), 'wpReason' ) .
1713  '&nbsp;' .
1714  Html::input( 'wpReason', $reason, 'text', [
1715  'size' => '60',
1716  'maxlength' => '255',
1717  'tabindex' => '2',
1718  'id' => 'wpReason',
1719  'class' => 'mw-ui-input-inline',
1720  'autofocus'
1721  ] ) .
1722  Html::closeElement( 'div' );
1723 
1724  # Disallow watching if user is not logged in
1725  if ( $user->isLoggedIn() ) {
1726  $form .=
1727  Xml::checkLabel( wfMessage( 'watchthis' )->text(),
1728  'wpWatch', 'wpWatch', $checkWatch, [ 'tabindex' => '3' ] );
1729  }
1730 
1731  $form .=
1732  Html::openElement( 'div' ) .
1733  $suppress .
1734  Xml::submitButton( wfMessage( 'deletepage' )->text(),
1735  [
1736  'name' => 'wpConfirmB',
1737  'id' => 'wpConfirmB',
1738  'tabindex' => '5',
1739  'class' => $useMediaWikiUIEverywhere ? 'mw-ui-button mw-ui-destructive' : '',
1740  ]
1741  ) .
1742  Html::closeElement( 'div' ) .
1743  Html::closeElement( 'div' ) .
1744  Xml::closeElement( 'fieldset' ) .
1745  Html::hidden(
1746  'wpEditToken',
1747  $user->getEditToken( [ 'delete', $title->getPrefixedText() ] )
1748  ) .
1749  Xml::closeElement( 'form' );
1750 
1751  if ( $user->isAllowed( 'editinterface' ) ) {
1753  $ctx->msg( 'deletereason-dropdown' )->inContentLanguage()->getTitle(),
1754  wfMessage( 'delete-edit-reasonlist' )->escaped(),
1755  [],
1756  [ 'action' => 'edit' ]
1757  );
1758  $form .= '<p class="mw-delete-editreasons">' . $link . '</p>';
1759  }
1760 
1761  $outputPage->addHTML( $form );
1762 
1763  $deleteLogPage = new LogPage( 'delete' );
1764  $outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) );
1765  LogEventsList::showLogExtract( $outputPage, 'delete', $title );
1766  }
1767 
1773  public function doDelete( $reason, $suppress = false ) {
1774  $error = '';
1775  $context = $this->getContext();
1776  $outputPage = $context->getOutput();
1777  $user = $context->getUser();
1778  $status = $this->mPage->doDeleteArticleReal( $reason, $suppress, 0, true, $error, $user );
1779 
1780  if ( $status->isGood() ) {
1781  $deleted = $this->getTitle()->getPrefixedText();
1782 
1783  $outputPage->setPageTitle( wfMessage( 'actioncomplete' ) );
1784  $outputPage->setRobotPolicy( 'noindex,nofollow' );
1785 
1786  $loglink = '[[Special:Log/delete|' . wfMessage( 'deletionlog' )->text() . ']]';
1787 
1788  $outputPage->addWikiMsg( 'deletedtext', wfEscapeWikiText( $deleted ), $loglink );
1789 
1790  Hooks::run( 'ArticleDeleteAfterSuccess', [ $this->getTitle(), $outputPage ] );
1791 
1792  $outputPage->returnToMain( false );
1793  } else {
1794  $outputPage->setPageTitle(
1795  wfMessage( 'cannotdelete-title',
1796  $this->getTitle()->getPrefixedText() )
1797  );
1798 
1799  if ( $error == '' ) {
1800  $outputPage->addWikiText(
1801  "<div class=\"error mw-error-cannotdelete\">\n" . $status->getWikiText() . "\n</div>"
1802  );
1803  $deleteLogPage = new LogPage( 'delete' );
1804  $outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) );
1805 
1807  $outputPage,
1808  'delete',
1809  $this->getTitle()
1810  );
1811  } else {
1812  $outputPage->addHTML( $error );
1813  }
1814  }
1815  }
1816 
1817  /* Caching functions */
1818 
1826  protected function tryFileCache() {
1827  static $called = false;
1828 
1829  if ( $called ) {
1830  wfDebug( "Article::tryFileCache(): called twice!?\n" );
1831  return false;
1832  }
1833 
1834  $called = true;
1835  if ( $this->isFileCacheable() ) {
1836  $cache = new HTMLFileCache( $this->getTitle(), 'view' );
1837  if ( $cache->isCacheGood( $this->mPage->getTouched() ) ) {
1838  wfDebug( "Article::tryFileCache(): about to load file\n" );
1839  $cache->loadFromFileCache( $this->getContext() );
1840  return true;
1841  } else {
1842  wfDebug( "Article::tryFileCache(): starting buffer\n" );
1843  ob_start( [ &$cache, 'saveToFileCache' ] );
1844  }
1845  } else {
1846  wfDebug( "Article::tryFileCache(): not cacheable\n" );
1847  }
1848 
1849  return false;
1850  }
1851 
1857  public function isFileCacheable( $mode = HTMLFileCache::MODE_NORMAL ) {
1858  $cacheable = false;
1859 
1860  if ( HTMLFileCache::useFileCache( $this->getContext(), $mode ) ) {
1861  $cacheable = $this->mPage->getId()
1862  && !$this->mRedirectedFrom && !$this->getTitle()->isRedirect();
1863  // Extension may have reason to disable file caching on some pages.
1864  if ( $cacheable ) {
1865  // Avoid PHP 7.1 warning of passing $this by reference
1866  $articlePage = $this;
1867  $cacheable = Hooks::run( 'IsFileCacheable', [ &$articlePage ] );
1868  }
1869  }
1870 
1871  return $cacheable;
1872  }
1873 
1887  public function getParserOutput( $oldid = null, User $user = null ) {
1888  // XXX: bypasses mParserOptions and thus setParserOptions()
1889 
1890  if ( $user === null ) {
1891  $parserOptions = $this->getParserOptions();
1892  } else {
1893  $parserOptions = $this->mPage->makeParserOptions( $user );
1894  }
1895 
1896  return $this->mPage->getParserOutput( $parserOptions, $oldid );
1897  }
1898 
1906  if ( $this->mParserOptions ) {
1907  throw new MWException( "can't change parser options after they have already been set" );
1908  }
1909 
1910  // clone, so if $options is modified later, it doesn't confuse the parser cache.
1911  $this->mParserOptions = clone $options;
1912  }
1913 
1918  public function getParserOptions() {
1919  if ( !$this->mParserOptions ) {
1920  $this->mParserOptions = $this->mPage->makeParserOptions( $this->getContext() );
1921  }
1922  // Clone to allow modifications of the return value without affecting cache
1923  return clone $this->mParserOptions;
1924  }
1925 
1932  public function setContext( $context ) {
1933  $this->mContext = $context;
1934  }
1935 
1942  public function getContext() {
1943  if ( $this->mContext instanceof IContextSource ) {
1944  return $this->mContext;
1945  } else {
1946  wfDebug( __METHOD__ . " called and \$mContext is null. " .
1947  "Return RequestContext::getMain(); for sanity\n" );
1948  return RequestContext::getMain();
1949  }
1950  }
1951 
1959  public function __get( $fname ) {
1960  if ( property_exists( $this->mPage, $fname ) ) {
1961  # wfWarn( "Access to raw $fname field " . __CLASS__ );
1962  return $this->mPage->$fname;
1963  }
1964  trigger_error( 'Inaccessible property via __get(): ' . $fname, E_USER_NOTICE );
1965  }
1966 
1974  public function __set( $fname, $fvalue ) {
1975  if ( property_exists( $this->mPage, $fname ) ) {
1976  # wfWarn( "Access to raw $fname field of " . __CLASS__ );
1977  $this->mPage->$fname = $fvalue;
1978  // Note: extensions may want to toss on new fields
1979  } elseif ( !in_array( $fname, [ 'mContext', 'mPage' ] ) ) {
1980  $this->mPage->$fname = $fvalue;
1981  } else {
1982  trigger_error( 'Inaccessible property via __set(): ' . $fname, E_USER_NOTICE );
1983  }
1984  }
1985 
1990  public function checkFlags( $flags ) {
1991  return $this->mPage->checkFlags( $flags );
1992  }
1993 
1998  public function checkTouched() {
1999  return $this->mPage->checkTouched();
2000  }
2001 
2006  public function clearPreparedEdit() {
2007  $this->mPage->clearPreparedEdit();
2008  }
2009 
2014  public function doDeleteArticleReal(
2015  $reason, $suppress = false, $u1 = null, $u2 = null, &$error = '', User $user = null,
2016  $tags = []
2017  ) {
2018  return $this->mPage->doDeleteArticleReal(
2019  $reason, $suppress, $u1, $u2, $error, $user, $tags
2020  );
2021  }
2022 
2027  public function doDeleteUpdates( $id, Content $content = null ) {
2028  return $this->mPage->doDeleteUpdates( $id, $content );
2029  }
2030 
2036  public function doEditContent( Content $content, $summary, $flags = 0, $baseRevId = false,
2037  User $user = null, $serialFormat = null
2038  ) {
2039  wfDeprecated( __METHOD__, '1.29' );
2040  return $this->mPage->doEditContent( $content, $summary, $flags, $baseRevId,
2041  $user, $serialFormat
2042  );
2043  }
2044 
2049  public function doEditUpdates( Revision $revision, User $user, array $options = [] ) {
2050  return $this->mPage->doEditUpdates( $revision, $user, $options );
2051  }
2052 
2059  public function doPurge() {
2060  return $this->mPage->doPurge();
2061  }
2062 
2068  public function getLastPurgeTimestamp() {
2069  wfDeprecated( __METHOD__, '1.29' );
2070  return $this->mPage->getLastPurgeTimestamp();
2071  }
2072 
2077  public function doViewUpdates( User $user, $oldid = 0 ) {
2078  $this->mPage->doViewUpdates( $user, $oldid );
2079  }
2080 
2085  public function exists() {
2086  return $this->mPage->exists();
2087  }
2088 
2093  public function followRedirect() {
2094  return $this->mPage->followRedirect();
2095  }
2096 
2101  public function getActionOverrides() {
2102  return $this->mPage->getActionOverrides();
2103  }
2104 
2109  public function getAutoDeleteReason( &$hasHistory ) {
2110  return $this->mPage->getAutoDeleteReason( $hasHistory );
2111  }
2112 
2117  public function getCategories() {
2118  return $this->mPage->getCategories();
2119  }
2120 
2125  public function getComment( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2126  return $this->mPage->getComment( $audience, $user );
2127  }
2128 
2133  public function getContentHandler() {
2134  return $this->mPage->getContentHandler();
2135  }
2136 
2141  public function getContentModel() {
2142  return $this->mPage->getContentModel();
2143  }
2144 
2149  public function getContributors() {
2150  return $this->mPage->getContributors();
2151  }
2152 
2157  public function getCreator( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2158  return $this->mPage->getCreator( $audience, $user );
2159  }
2160 
2165  public function getDeletionUpdates( Content $content = null ) {
2166  return $this->mPage->getDeletionUpdates( $content );
2167  }
2168 
2173  public function getHiddenCategories() {
2174  return $this->mPage->getHiddenCategories();
2175  }
2176 
2181  public function getId() {
2182  return $this->mPage->getId();
2183  }
2184 
2189  public function getLatest() {
2190  return $this->mPage->getLatest();
2191  }
2192 
2197  public function getLinksTimestamp() {
2198  return $this->mPage->getLinksTimestamp();
2199  }
2200 
2205  public function getMinorEdit() {
2206  return $this->mPage->getMinorEdit();
2207  }
2208 
2213  public function getOldestRevision() {
2214  return $this->mPage->getOldestRevision();
2215  }
2216 
2221  public function getRedirectTarget() {
2222  return $this->mPage->getRedirectTarget();
2223  }
2224 
2229  public function getRedirectURL( $rt ) {
2230  return $this->mPage->getRedirectURL( $rt );
2231  }
2232 
2237  public function getRevision() {
2238  return $this->mPage->getRevision();
2239  }
2240 
2245  public function getTimestamp() {
2246  return $this->mPage->getTimestamp();
2247  }
2248 
2253  public function getTouched() {
2254  return $this->mPage->getTouched();
2255  }
2256 
2261  public function getUndoContent( Revision $undo, Revision $undoafter = null ) {
2262  return $this->mPage->getUndoContent( $undo, $undoafter );
2263  }
2264 
2269  public function getUser( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2270  return $this->mPage->getUser( $audience, $user );
2271  }
2272 
2277  public function getUserText( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2278  return $this->mPage->getUserText( $audience, $user );
2279  }
2280 
2285  public function hasViewableContent() {
2286  return $this->mPage->hasViewableContent();
2287  }
2288 
2293  public function insertOn( $dbw, $pageId = null ) {
2294  return $this->mPage->insertOn( $dbw, $pageId );
2295  }
2296 
2301  public function insertProtectNullRevision( $revCommentMsg, array $limit,
2302  array $expiry, $cascade, $reason, $user = null
2303  ) {
2304  return $this->mPage->insertProtectNullRevision( $revCommentMsg, $limit,
2305  $expiry, $cascade, $reason, $user
2306  );
2307  }
2308 
2313  public function insertRedirect() {
2314  return $this->mPage->insertRedirect();
2315  }
2316 
2321  public function insertRedirectEntry( Title $rt, $oldLatest = null ) {
2322  return $this->mPage->insertRedirectEntry( $rt, $oldLatest );
2323  }
2324 
2329  public function isCountable( $editInfo = false ) {
2330  return $this->mPage->isCountable( $editInfo );
2331  }
2332 
2337  public function isRedirect() {
2338  return $this->mPage->isRedirect();
2339  }
2340 
2345  public function loadFromRow( $data, $from ) {
2346  return $this->mPage->loadFromRow( $data, $from );
2347  }
2348 
2353  public function loadPageData( $from = 'fromdb' ) {
2354  $this->mPage->loadPageData( $from );
2355  }
2356 
2361  public function lockAndGetLatest() {
2362  return $this->mPage->lockAndGetLatest();
2363  }
2364 
2369  public function makeParserOptions( $context ) {
2370  return $this->mPage->makeParserOptions( $context );
2371  }
2372 
2377  public function pageDataFromId( $dbr, $id, $options = [] ) {
2378  return $this->mPage->pageDataFromId( $dbr, $id, $options );
2379  }
2380 
2385  public function pageDataFromTitle( $dbr, $title, $options = [] ) {
2386  return $this->mPage->pageDataFromTitle( $dbr, $title, $options );
2387  }
2388 
2393  public function prepareContentForEdit(
2394  Content $content, $revision = null, User $user = null,
2395  $serialFormat = null, $useCache = true
2396  ) {
2397  return $this->mPage->prepareContentForEdit(
2398  $content, $revision, $user,
2399  $serialFormat, $useCache
2400  );
2401  }
2402 
2407  public function protectDescription( array $limit, array $expiry ) {
2408  return $this->mPage->protectDescription( $limit, $expiry );
2409  }
2410 
2415  public function protectDescriptionLog( array $limit, array $expiry ) {
2416  return $this->mPage->protectDescriptionLog( $limit, $expiry );
2417  }
2418 
2423  public function replaceSectionAtRev( $sectionId, Content $sectionContent,
2424  $sectionTitle = '', $baseRevId = null
2425  ) {
2426  return $this->mPage->replaceSectionAtRev( $sectionId, $sectionContent,
2427  $sectionTitle, $baseRevId
2428  );
2429  }
2430 
2435  public function replaceSectionContent(
2436  $sectionId, Content $sectionContent, $sectionTitle = '', $edittime = null
2437  ) {
2438  return $this->mPage->replaceSectionContent(
2439  $sectionId, $sectionContent, $sectionTitle, $edittime
2440  );
2441  }
2442 
2447  public function setTimestamp( $ts ) {
2448  return $this->mPage->setTimestamp( $ts );
2449  }
2450 
2455  public function shouldCheckParserCache( ParserOptions $parserOptions, $oldId ) {
2456  return $this->mPage->shouldCheckParserCache( $parserOptions, $oldId );
2457  }
2458 
2463  public function supportsSections() {
2464  return $this->mPage->supportsSections();
2465  }
2466 
2472  return $this->mPage->triggerOpportunisticLinksUpdate( $parserOutput );
2473  }
2474 
2479  public function updateCategoryCounts( array $added, array $deleted, $id = 0 ) {
2480  return $this->mPage->updateCategoryCounts( $added, $deleted, $id );
2481  }
2482 
2487  public function updateIfNewerOn( $dbw, $revision ) {
2488  return $this->mPage->updateIfNewerOn( $dbw, $revision );
2489  }
2490 
2495  public function updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect = null ) {
2496  return $this->mPage->updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect = null );
2497  }
2498 
2503  public function updateRevisionOn( $dbw, $revision, $lastRevision = null,
2504  $lastRevIsRedirect = null
2505  ) {
2506  return $this->mPage->updateRevisionOn( $dbw, $revision, $lastRevision,
2507  $lastRevIsRedirect
2508  );
2509  }
2510 
2519  public function doUpdateRestrictions( array $limit, array $expiry, &$cascade,
2520  $reason, User $user
2521  ) {
2522  return $this->mPage->doUpdateRestrictions( $limit, $expiry, $cascade, $reason, $user );
2523  }
2524 
2532  public function updateRestrictions( $limit = [], $reason = '',
2533  &$cascade = 0, $expiry = []
2534  ) {
2535  return $this->mPage->doUpdateRestrictions(
2536  $limit,
2537  $expiry,
2538  $cascade,
2539  $reason,
2540  $this->getContext()->getUser()
2541  );
2542  }
2543 
2552  public function doDeleteArticle(
2553  $reason, $suppress = false, $u1 = null, $u2 = null, &$error = ''
2554  ) {
2555  return $this->mPage->doDeleteArticle( $reason, $suppress, $u1, $u2, $error );
2556  }
2557 
2567  public function doRollback( $fromP, $summary, $token, $bot, &$resultDetails, User $user = null ) {
2568  $user = is_null( $user ) ? $this->getContext()->getUser() : $user;
2569  return $this->mPage->doRollback( $fromP, $summary, $token, $bot, $resultDetails, $user );
2570  }
2571 
2580  public function commitRollback( $fromP, $summary, $bot, &$resultDetails, User $guser = null ) {
2581  $guser = is_null( $guser ) ? $this->getContext()->getUser() : $guser;
2582  return $this->mPage->commitRollback( $fromP, $summary, $bot, $resultDetails, $guser );
2583  }
2584 
2589  public function generateReason( &$hasHistory ) {
2590  $title = $this->mPage->getTitle();
2592  return $handler->getAutoDeleteReason( $title, $hasHistory );
2593  }
2594 
2600  public static function selectFields() {
2601  wfDeprecated( __METHOD__, '1.24' );
2602  return WikiPage::selectFields();
2603  }
2604 
2610  public static function onArticleCreate( $title ) {
2611  wfDeprecated( __METHOD__, '1.24' );
2613  }
2614 
2620  public static function onArticleDelete( $title ) {
2621  wfDeprecated( __METHOD__, '1.24' );
2623  }
2624 
2630  public static function onArticleEdit( $title ) {
2631  wfDeprecated( __METHOD__, '1.24' );
2633  }
2634 
2635  // ******
2636 }
pageDataFromId($dbr, $id, $options=[])
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2377
__set($fname, $fvalue)
Use PHP's magic __set handler to handle setting of raw WikiPage fields for backwards compatibility...
Definition: Article.php:1974
static newFromName($name, $validate= 'valid')
Static factory method for creation from username.
Definition: User.php:556
getRedirectTarget()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2221
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
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:1123
const FOR_THIS_USER
Definition: Revision.php:99
viewRedirect($target, $appendSubtitle=true, $forceKnown=false)
Return the HTML for the top of a redirect page.
Definition: Article.php:1438
static newFromID($id, $flags=0)
Create a new Title from an article ID.
Definition: Title.php:405
static closeElement($element)
Returns "".
Definition: Html.php:309
lockAndGetLatest()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2361
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:3250
static onArticleCreate($title)
Definition: Article.php:2610
getUndoContent(Revision $undo, Revision $undoafter=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2261
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:1966
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:1464
WikiPage $mPage
The WikiPage object of this instance.
Definition: Article.php:40
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:784
getRobotPolicy($action, $pOutput=null)
Get the robot policy to be used for the current view.
Definition: Article.php:735
the array() calling protocol came about after MediaWiki 1.4rc1.
getRedirectedFrom()
Get the page this view was redirected from.
Definition: Article.php:156
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:1582
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist Do not use this to implement individual filters if they are compatible with the ChangesListFilter and ChangesListFilterGroup structure use sub classes of those in conjunction with the ChangesListSpecialPageStructuredFilters hook This hook can be used to implement filters that do not implement that or custom behavior that is not an individual filter e g Watchlist and Watchlist you will want to construct new ChangesListBooleanFilter or ChangesListStringOptionsFilter objects When constructing you specify which group they belong to You can reuse existing or create your you must register them with $special registerFilterGroup 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:1050
updateRestrictions($limit=[], $reason= '', &$cascade=0, $expiry=[])
Definition: Article.php:2532
doEditContent(Content $content, $summary, $flags=0, $baseRevId=false, User $user=null, $serialFormat=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2036
getLatest()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2189
setParserOptions(ParserOptions $options)
Override the ParserOptions used to render the primary article wikitext.
Definition: Article.php:1905
magic word the default is to use $key to get the and $key value or $key value text $key value html to format the value $key
Definition: hooks.txt:2556
doEditUpdates(Revision $revision, User $user, array $options=[])
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2049
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:2015
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist Do not use this to implement individual filters if they are compatible with the ChangesListFilter and ChangesListFilterGroup structure use sub classes of those in conjunction with the ChangesListSpecialPageStructuredFilters hook This hook can be used to implement filters that do not implement that or custom behavior that is not an individual filter e g Watchlist and Watchlist you will want to construct new ChangesListBooleanFilter or ChangesListStringOptionsFilter objects When constructing you specify which group they belong to You can reuse existing or create your you must register them with $special registerFilterGroup 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:1050
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:2085
supportsSections()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2463
static makeUrl($name, $urlaction= '')
Definition: Skin.php:1146
doRollback($fromP, $summary, $token, $bot, &$resultDetails, User $user=null)
Definition: Article.php:2567
Show an error when the wiki is locked/read-only and the user tries to do something that requires writ...
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
doDeleteArticle($reason, $suppress=false, $u1=null, $u2=null, &$error= '')
Definition: Article.php:2552
protect()
action=protect handler
Definition: Article.php:1522
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException'returning false will NOT prevent logging $e
Definition: hooks.txt:2142
IContextSource $mContext
The context this Article is executed in.
Definition: Article.php:37
Set options of the Parser.
isCountable($editInfo=false)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2329
Wrapper allowing us to handle a system message as a Content object.
getPage()
Get the WikiPage object of this instance.
Definition: Article.php:184
getParserOutput($oldid=null, User $user=null)
#@-
Definition: Article.php:1887
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:2479
string bool $mRedirectUrl
URL to redirect to or false if none.
Definition: Article.php:67
loadPageData($from= 'fromdb')
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2353
if(!isset($args[0])) $lang
ParserOptions $mParserOptions
ParserOptions object for $wgUser articles.
Definition: Article.php:43
Content $mContentObject
Content of the revision we are working on.
Definition: Article.php:55
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:1857
doDeleteUpdates($id, Content $content=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2027
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:673
static hidden($name, $value, array $attribs=[])
Convenience function to produce an input element with type=hidden.
Definition: Html.php:746
shouldCheckParserCache(ParserOptions $parserOptions, $oldId)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2455
doUpdateRestrictions(array $limit, array $expiry, &$cascade, $reason, User $user)
Definition: Article.php:2519
do that in ParserLimitReportFormat instead use this to modify the parameters of the image all existing parser cache entries will be invalid To avoid you ll need to handle that somehow(e.g.with the RejectParserCacheValue hook) because MediaWiki won't do it for you.&$defaults also a ContextSource you ll probably need to make sure the header is varied on $request
Definition: hooks.txt:2582
Class for viewing MediaWiki article and history.
Definition: Article.php:35
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist Do not use this to implement individual filters if they are compatible with the ChangesListFilter and ChangesListFilterGroup structure use sub classes of those in conjunction with the ChangesListSpecialPageStructuredFilters hook This hook can be used to implement filters that do not implement that or custom behavior that is not an individual filter e g Watchlist and Watchlist you will want to construct new ChangesListBooleanFilter or ChangesListStringOptionsFilter objects When constructing you specify which group they belong to You can reuse existing or create your you must register them with $special registerFilterGroup 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 please use GetContentModels hook to make them known to core 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:1050
null for the local wiki Added in
Definition: hooks.txt:1582
Page view caching in the file system.
followRedirect()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2093
confirmDelete($reason)
Output deletion confirmation dialog.
Definition: Article.php:1665
Class for viewing MediaWiki file description pages.
Definition: ImagePage.php:30
triggerOpportunisticLinksUpdate(ParserOutput $parserOutput)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2471
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 MediaWikiServices
Definition: injection.txt:23
getOldIDFromRequest()
Sets $this->mRedirectUrl to a correct URL if the query parameters are incorrect.
Definition: Article.php:256
__get($fname)
Use PHP's magic __get handler to handle accessing of raw WikiPage fields for backwards compatibility...
Definition: Article.php:1959
it s the revision text itself In either if gzip is the revision text is gzipped $flags
Definition: hooks.txt:2796
getDeletionUpdates(Content $content=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2165
updateIfNewerOn($dbw, $revision)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2487
clearPreparedEdit()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2006
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:2149
static submitButton($value, $attribs=[])
Convenience function to build an HTML submit button When $wgUseMediaWikiUIEverywhere is true it will ...
Definition: Xml.php:459
commitRollback($fromP, $summary, $bot, &$resultDetails, User $guser=null)
Definition: Article.php:2580
showMissingArticle()
Show the error text for a missing article.
Definition: Article.php:1132
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:2407
Class to simplify the use of log pages.
Definition: LogPage.php:31
usually copyright or history_copyright This message must be in HTML not wikitext & $link
Definition: hooks.txt:2976
getContext()
Gets the context this Article is executed in.
Definition: Article.php:1942
static closeElement($element)
Shortcut to close an XML element.
Definition: Xml.php:118
isRedirect()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2337
__construct(Title $title, $oldId=null)
Constructor and clear the article.
Definition: Article.php:83
setTimestamp($ts)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2447
protectDescriptionLog(array $limit, array $expiry)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2415
static onArticleDelete($title)
Definition: Article.php:2620
static openElement($element, $attribs=[])
Identical to rawElement(), but has no third parameter and omits the end tag (and the self-closing '/'...
Definition: Html.php:251
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:2369
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:2393
const FOR_PUBLIC
Definition: Revision.php:98
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:935
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:1113
getRevisionFetched()
Get the fetched Revision object depending on request parameters or null on failure.
Definition: Article.php:406
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist Do not use this to implement individual filters if they are compatible with the ChangesListFilter and ChangesListFilterGroup structure use sub classes of those in conjunction with the ChangesListSpecialPageStructuredFilters hook This hook can be used to implement filters that do not implement that or custom behavior that is not an individual filter e g Watchlist and Watchlist you will want to construct new ChangesListBooleanFilter or ChangesListStringOptionsFilter objects When constructing you specify which group they belong to You can reuse existing or create your you must register them with $special registerFilterGroup 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:1050
insertProtectNullRevision($revCommentMsg, array $limit, array $expiry, $cascade, $reason, $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2301
const NS_MEDIA
Definition: Defines.php:50
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:844
ParserOutput $mParserOutput
Definition: Article.php:76
getRevision()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2237
it s the revision text itself In either if gzip is set
Definition: hooks.txt:2796
generateReason(&$hasHistory)
Definition: Article.php:2589
static getRedirectHeaderHtml(Language $lang, $target, $forceKnown=false)
Return the HTML for the top of a redirect page.
Definition: Article.php:1460
insertRedirect()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2313
int null $mOldId
The oldid of the article that is to be shown, 0 for the current revision.
Definition: Article.php:61
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:2285
getComment($audience=Revision::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2125
Base interface for content objects.
Definition: Content.php:34
getTitle()
Get the title object of the article.
Definition: Article.php:174
loadFromRow($data, $from)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2345
$cache
Definition: mcc.php:33
getActionOverrides()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2101
doViewUpdates(User $user, $oldid=0)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2077
getTitle()
Get the title object of the article.
Definition: WikiPage.php:237
const NS_CATEGORY
Definition: Defines.php:76
doPurge()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2059
doDeleteArticleReal($reason, $suppress=false, $u1=null, $u2=null, &$error= '', User $user=null, $tags=[])
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2014
static newFromTitle($title, IContextSource $context)
Create an Article object of the appropriate class for the given page.
Definition: Article.php:113
do that in ParserLimitReportFormat instead use this to modify the parameters of the image all existing parser cache entries will be invalid To avoid you ll need to handle that somehow(e.g.with the RejectParserCacheValue hook) because MediaWiki won't do it for you.&$defaults also a ContextSource you ll probably need to make sure the header is varied on and they can depend only on the ResourceLoaderContext $context
Definition: hooks.txt:2582
wfDeprecated($function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
render()
Handle action=render.
Definition: Article.php:1512
static isIP($name)
Does the string match an anonymous IP address?
Definition: User.php:831
const DELETED_RESTRICTED
Definition: Revision.php:93
getCategories()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2117
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:935
static linkKnown($target, $html=null, $customAttribs=[], $query=[], $options=[ 'known'])
Identical to link(), except $options defaults to 'known'.
Definition: Linker.php:159
replaceSectionAtRev($sectionId, Content $sectionContent, $sectionTitle= '', $baseRevId=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2423
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:1990
const NS_FILE
Definition: Defines.php:68
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:86
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:1751
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:2133
const NS_MEDIAWIKI
Definition: Defines.php:70
static link($target, $html=null, $customAttribs=[], $query=[], $options=[])
This function returns an HTML link to the given target.
Definition: Linker.php:107
static onArticleEdit(Title $title, Revision $revision=null)
Purge caches on page update etc.
Definition: WikiPage.php:3322
Title $mRedirectedFrom
Title from which we were redirected here.
Definition: Article.php:64
const DELETED_TEXT
Definition: Revision.php:90
getCreator($audience=Revision::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2157
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:1495
newPage(Title $title)
Definition: Article.php:92
Class representing a MediaWiki article and history.
Definition: WikiPage.php:36
static newFromId($id, $flags=0)
Load a page revision from a given revision ID number.
Definition: Revision.php:116
getOldestRevision()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2213
string $mContent
Text of the revision we are working on.
Definition: Article.php:49
static makeExternalLink($url, $text, $escape=true, $linktype= '', $attribs=[], $title=null)
Make an external link.
Definition: Linker.php:838
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 as strings Extensions should add to this list prev or next refreshes the diff cache $unhide
Definition: hooks.txt:1582
clear()
Clear the object.
Definition: Article.php:191
bool $mContentLoaded
Is the content ($mContent) already loaded?
Definition: Article.php:58
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:2068
getAutoDeleteReason(&$hasHistory)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2109
checkTouched()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:1998
updateRevisionOn($dbw, $revision, $lastRevision=null, $lastRevIsRedirect=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2503
static selectFields()
Definition: Article.php:2600
static newFromID($id)
Constructor from a page id.
Definition: Article.php:101
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:2181
getTimestamp()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2245
static input($name, $value= '', $type= 'text', array $attribs=[])
Convenience function to produce an "" element.
Definition: Html.php:663
pageDataFromTitle($dbr, $title, $options=[])
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2385
getMinorEdit()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2205
view()
This is the default action of the index.php entry point: just view the page of the given title...
Definition: Article.php:429
static onArticleDelete(Title $title)
Clears caches when article is deleted.
Definition: WikiPage.php:3279
getContentObject()
Returns a Content object representing the pages effective display content, not necessarily the revisi...
Definition: Article.php:216
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist Do not use this to implement individual filters if they are compatible with the ChangesListFilter and ChangesListFilterGroup structure use sub classes of those in conjunction with the ChangesListSpecialPageStructuredFilters hook This hook can be used to implement filters that do not implement that or custom behavior that is not an individual filter e g Watchlist and Watchlist you will want to construct new ChangesListBooleanFilter or ChangesListStringOptionsFilter objects When constructing you specify which group they belong to You can reuse existing or create your you must register them with $special registerFilterGroup 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:1050
getOldID()
Definition: Article.php:243
doDelete($reason, $suppress=false)
Perform a deletion and output success or failure messages.
Definition: Article.php:1773
showNamespaceHeader()
Show a header specific to the namespace currently being viewed, like [[MediaWiki:Talkpagetext]].
Definition: Article.php:921
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:2495
tryFileCache()
checkLastModified returns true if it has taken care of all output to the client that is necessary for...
Definition: Article.php:1826
insertRedirectEntry(Title $rt, $oldLatest=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2321
const POST_EDIT_COOKIE_KEY_PREFIX
Prefix of key for cookie used to pass post-edit state.
Definition: EditPage.php:189
static checkLabel($label, $name, $id, $checked=false, $attribs=[])
Convenience function to build an HTML checkbox with a label.
Definition: Xml.php:419
int $mRevIdFetched
Revision ID of revision we are working on.
Definition: Article.php:70
fetchContentObject()
Get text content object Does NOT follow redirects.
Definition: Article.php:314
const DB_REPLICA
Definition: defines.php:25
setOldSubtitle($oldid=0)
Generate the navigation links when browsing through an article revisions It shows the information as:...
Definition: Article.php:1300
isCurrent()
Returns true if the currently-referenced revision is the current edit to this page (and it exists)...
Definition: Article.php:390
unprotect()
action=unprotect handler (alias)
Definition: Article.php:1530
Handles the page protection UI and backend.
getTouched()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2253
static onArticleEdit($title)
Definition: Article.php:2630
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:784
getUser($audience=Revision::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2269
static newFromWikiPage(WikiPage $page, IContextSource $context)
Create an Article object of the appropriate class for the given page.
Definition: Article.php:145
static selectFields()
Return the list of revision fields that should be selected to create a new page.
Definition: WikiPage.php:286
static doWatchOrUnwatch($watch, Title $title, User $user)
Watch or unwatch a page.
Definition: WatchAction.php:93
$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:2435
setContext($context)
Sets the context this Article is executed in.
Definition: Article.php:1932
static listDropDown($name= '', $list= '', $other= '', $selected= '', $class= '', $tabindex=null)
Build a drop-down box from a textual list.
Definition: Xml.php:507
getRevIdFetched()
Use this to fetch the rev ID used on page views.
Definition: Article.php:417
const NS_USER_TALK
Definition: Defines.php:65
static revUserTools($rev, $isPublic=false)
Generate a user tool link cluster if the current user is allowed to view it.
Definition: Linker.php:1055
showDiffPage()
Show a diff page according to current request variables.
Definition: Article.php:685
static element($element, $attribs=[], $contents= '')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
Definition: Html.php:231
getRedirectURL($rt)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2229
Definition: Block.php:27
showDeletedRevisionHeader()
If the revision requested for view is deleted, check permissions.
Definition: Article.php:1257
static makeTitle($ns, $title, $fragment= '', $interwiki= '')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:514
setRedirectedFrom(Title $from)
Tell the page view functions that this view was redirected from another page on the wiki...
Definition: Article.php:165
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:246
getContentModel()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2141
getLinksTimestamp()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2197
insertOn($dbw, $pageId=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2293
getParserOptions()
Get parser options suitable for rendering the primary article wikitext.
Definition: Article.php:1918
const RC_LOG
Definition: Defines.php:142
Revision $mRevision
Revision we are working on.
Definition: Article.php:73
getUserText($audience=Revision::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2277
showPatrolFooter()
If patrol is possible, output a patrol UI box.
Definition: Article.php:958
static label($label, $id, array $attribs=[])
Convenience function for generating a label for inputs.
Definition: Html.php:730
static formatRobotPolicy($policy)
Converts a String robot policy into an associative array, to allow merging of several policies using ...
Definition: Article.php:815
getHiddenCategories()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2173