MediaWiki  1.30.1
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 ) {
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  if ( $this->mPage->getId() === 0 ) {
218  # If this is a MediaWiki:x message, then load the messages
219  # and return the message value for x.
220  if ( $this->getTitle()->getNamespace() == NS_MEDIAWIKI ) {
221  $text = $this->getTitle()->getDefaultMessageText();
222  if ( $text === false ) {
223  $text = '';
224  }
225 
226  $content = ContentHandler::makeContent( $text, $this->getTitle() );
227  } else {
228  $message = $this->getContext()->getUser()->isLoggedIn() ? 'noarticletext' : 'noarticletextanon';
229  $content = new MessageContent( $message, null, 'parsemag' );
230  }
231  } else {
232  $this->fetchContentObject();
233  $content = $this->mContentObject;
234  }
235 
236  return $content;
237  }
238 
242  public function getOldID() {
243  if ( is_null( $this->mOldId ) ) {
244  $this->mOldId = $this->getOldIDFromRequest();
245  }
246 
247  return $this->mOldId;
248  }
249 
255  public function getOldIDFromRequest() {
256  $this->mRedirectUrl = false;
257 
258  $request = $this->getContext()->getRequest();
259  $oldid = $request->getIntOrNull( 'oldid' );
260 
261  if ( $oldid === null ) {
262  return 0;
263  }
264 
265  if ( $oldid !== 0 ) {
266  # Load the given revision and check whether the page is another one.
267  # In that case, update this instance to reflect the change.
268  if ( $oldid === $this->mPage->getLatest() ) {
269  $this->mRevision = $this->mPage->getRevision();
270  } else {
271  $this->mRevision = Revision::newFromId( $oldid );
272  if ( $this->mRevision !== null ) {
273  // Revision title doesn't match the page title given?
274  if ( $this->mPage->getId() != $this->mRevision->getPage() ) {
275  $function = [ get_class( $this->mPage ), 'newFromID' ];
276  $this->mPage = call_user_func( $function, $this->mRevision->getPage() );
277  }
278  }
279  }
280  }
281 
282  if ( $request->getVal( 'direction' ) == 'next' ) {
283  $nextid = $this->getTitle()->getNextRevisionID( $oldid );
284  if ( $nextid ) {
285  $oldid = $nextid;
286  $this->mRevision = null;
287  } else {
288  $this->mRedirectUrl = $this->getTitle()->getFullURL( 'redirect=no' );
289  }
290  } elseif ( $request->getVal( 'direction' ) == 'prev' ) {
291  $previd = $this->getTitle()->getPreviousRevisionID( $oldid );
292  if ( $previd ) {
293  $oldid = $previd;
294  $this->mRevision = null;
295  }
296  }
297 
298  return $oldid;
299  }
300 
313  protected function fetchContentObject() {
314  if ( $this->mContentLoaded ) {
315  return $this->mContentObject;
316  }
317 
318  $this->mContentLoaded = true;
319  $this->mContent = null;
320 
321  $oldid = $this->getOldID();
322 
323  # Pre-fill content with error message so that if something
324  # fails we'll have something telling us what we intended.
325  // XXX: this isn't page content but a UI message. horrible.
326  $this->mContentObject = new MessageContent( 'missing-revision', [ $oldid ] );
327 
328  if ( $oldid ) {
329  # $this->mRevision might already be fetched by getOldIDFromRequest()
330  if ( !$this->mRevision ) {
331  $this->mRevision = Revision::newFromId( $oldid );
332  if ( !$this->mRevision ) {
333  wfDebug( __METHOD__ . " failed to retrieve specified revision, id $oldid\n" );
334  return false;
335  }
336  }
337  } else {
338  $oldid = $this->mPage->getLatest();
339  if ( !$oldid ) {
340  wfDebug( __METHOD__ . " failed to find page data for title " .
341  $this->getTitle()->getPrefixedText() . "\n" );
342  return false;
343  }
344 
345  # Update error message with correct oldid
346  $this->mContentObject = new MessageContent( 'missing-revision', [ $oldid ] );
347 
348  $this->mRevision = $this->mPage->getRevision();
349 
350  if ( !$this->mRevision ) {
351  wfDebug( __METHOD__ . " failed to retrieve current page, rev_id $oldid\n" );
352  return false;
353  }
354  }
355 
356  // @todo FIXME: Horrible, horrible! This content-loading interface just plain sucks.
357  // We should instead work with the Revision object when we need it...
358  // Loads if user is allowed
359  $content = $this->mRevision->getContent(
361  $this->getContext()->getUser()
362  );
363 
364  if ( !$content ) {
365  wfDebug( __METHOD__ . " failed to retrieve content of revision " .
366  $this->mRevision->getId() . "\n" );
367  return false;
368  }
369 
370  $this->mContentObject = $content;
371  $this->mRevIdFetched = $this->mRevision->getId();
372 
373  // Avoid PHP 7.1 warning of passing $this by reference
374  $articlePage = $this;
375 
376  Hooks::run(
377  'ArticleAfterFetchContentObject',
378  [ &$articlePage, &$this->mContentObject ]
379  );
380 
381  return $this->mContentObject;
382  }
383 
389  public function isCurrent() {
390  # If no oldid, this is the current version.
391  if ( $this->getOldID() == 0 ) {
392  return true;
393  }
394 
395  return $this->mPage->exists() && $this->mRevision && $this->mRevision->isCurrent();
396  }
397 
405  public function getRevisionFetched() {
406  $this->fetchContentObject();
407 
408  return $this->mRevision;
409  }
410 
416  public function getRevIdFetched() {
417  if ( $this->mRevIdFetched ) {
418  return $this->mRevIdFetched;
419  } else {
420  return $this->mPage->getLatest();
421  }
422  }
423 
428  public function view() {
430 
431  # Get variables from query string
432  # As side effect this will load the revision and update the title
433  # in a revision ID is passed in the request, so this should remain
434  # the first call of this method even if $oldid is used way below.
435  $oldid = $this->getOldID();
436 
437  $user = $this->getContext()->getUser();
438  # Another whitelist check in case getOldID() is altering the title
439  $permErrors = $this->getTitle()->getUserPermissionsErrors( 'read', $user );
440  if ( count( $permErrors ) ) {
441  wfDebug( __METHOD__ . ": denied on secondary read check\n" );
442  throw new PermissionsError( 'read', $permErrors );
443  }
444 
445  $outputPage = $this->getContext()->getOutput();
446  # getOldID() may as well want us to redirect somewhere else
447  if ( $this->mRedirectUrl ) {
448  $outputPage->redirect( $this->mRedirectUrl );
449  wfDebug( __METHOD__ . ": redirecting due to oldid\n" );
450 
451  return;
452  }
453 
454  # If we got diff in the query, we want to see a diff page instead of the article.
455  if ( $this->getContext()->getRequest()->getCheck( 'diff' ) ) {
456  wfDebug( __METHOD__ . ": showing diff page\n" );
457  $this->showDiffPage();
458 
459  return;
460  }
461 
462  # Set page title (may be overridden by DISPLAYTITLE)
463  $outputPage->setPageTitle( $this->getTitle()->getPrefixedText() );
464 
465  $outputPage->setArticleFlag( true );
466  # Allow frames by default
467  $outputPage->allowClickjacking();
468 
469  $parserCache = MediaWikiServices::getInstance()->getParserCache();
470 
471  $parserOptions = $this->getParserOptions();
472  # Render printable version, use printable version cache
473  if ( $outputPage->isPrintable() ) {
474  $parserOptions->setIsPrintable( true );
475  $parserOptions->setEditSection( false );
476  } elseif ( !$this->isCurrent() || !$this->getTitle()->quickUserCan( 'edit', $user ) ) {
477  $parserOptions->setEditSection( false );
478  }
479 
480  # Try client and file cache
481  if ( !$wgDebugToolbar && $oldid === 0 && $this->mPage->checkTouched() ) {
482  # Try to stream the output from file cache
483  if ( $wgUseFileCache && $this->tryFileCache() ) {
484  wfDebug( __METHOD__ . ": done file cache\n" );
485  # tell wgOut that output is taken care of
486  $outputPage->disable();
487  $this->mPage->doViewUpdates( $user, $oldid );
488 
489  return;
490  }
491  }
492 
493  # Should the parser cache be used?
494  $useParserCache = $this->mPage->shouldCheckParserCache( $parserOptions, $oldid );
495  wfDebug( 'Article::view using parser cache: ' . ( $useParserCache ? 'yes' : 'no' ) . "\n" );
496  if ( $user->getStubThreshold() ) {
497  MediaWikiServices::getInstance()->getStatsdDataFactory()->increment( 'pcache_miss_stub' );
498  }
499 
500  $this->showRedirectedFromHeader();
501  $this->showNamespaceHeader();
502 
503  # Iterate through the possible ways of constructing the output text.
504  # Keep going until $outputDone is set, or we run out of things to do.
505  $pass = 0;
506  $outputDone = false;
507  $this->mParserOutput = false;
508 
509  while ( !$outputDone && ++$pass ) {
510  switch ( $pass ) {
511  case 1:
512  // Avoid PHP 7.1 warning of passing $this by reference
513  $articlePage = $this;
514  Hooks::run( 'ArticleViewHeader', [ &$articlePage, &$outputDone, &$useParserCache ] );
515  break;
516  case 2:
517  # Early abort if the page doesn't exist
518  if ( !$this->mPage->exists() ) {
519  wfDebug( __METHOD__ . ": showing missing article\n" );
520  $this->showMissingArticle();
521  $this->mPage->doViewUpdates( $user );
522  return;
523  }
524 
525  # Try the parser cache
526  if ( $useParserCache ) {
527  $this->mParserOutput = $parserCache->get( $this->mPage, $parserOptions );
528 
529  if ( $this->mParserOutput !== false ) {
530  if ( $oldid ) {
531  wfDebug( __METHOD__ . ": showing parser cache contents for current rev permalink\n" );
532  $this->setOldSubtitle( $oldid );
533  } else {
534  wfDebug( __METHOD__ . ": showing parser cache contents\n" );
535  }
536  $outputPage->addParserOutput( $this->mParserOutput );
537  # Ensure that UI elements requiring revision ID have
538  # the correct version information.
539  $outputPage->setRevisionId( $this->mPage->getLatest() );
540  # Preload timestamp to avoid a DB hit
541  $cachedTimestamp = $this->mParserOutput->getTimestamp();
542  if ( $cachedTimestamp !== null ) {
543  $outputPage->setRevisionTimestamp( $cachedTimestamp );
544  $this->mPage->setTimestamp( $cachedTimestamp );
545  }
546  $outputDone = true;
547  }
548  }
549  break;
550  case 3:
551  # This will set $this->mRevision if needed
552  $this->fetchContentObject();
553 
554  # Are we looking at an old revision
555  if ( $oldid && $this->mRevision ) {
556  $this->setOldSubtitle( $oldid );
557 
558  if ( !$this->showDeletedRevisionHeader() ) {
559  wfDebug( __METHOD__ . ": cannot view deleted revision\n" );
560  return;
561  }
562  }
563 
564  # Ensure that UI elements requiring revision ID have
565  # the correct version information.
566  $outputPage->setRevisionId( $this->getRevIdFetched() );
567  # Preload timestamp to avoid a DB hit
568  $outputPage->setRevisionTimestamp( $this->mPage->getTimestamp() );
569 
570  # Pages containing custom CSS or JavaScript get special treatment
571  if ( $this->getTitle()->isCssOrJsPage() || $this->getTitle()->isCssJsSubpage() ) {
572  $dir = $this->getContext()->getLanguage()->getDir();
573  $lang = $this->getContext()->getLanguage()->getHtmlCode();
574 
575  $outputPage->wrapWikiMsg(
576  "<div id='mw-clearyourcache' lang='$lang' dir='$dir' class='mw-content-$dir'>\n$1\n</div>",
577  'clearyourcache'
578  );
579  } elseif ( !Hooks::run( 'ArticleContentViewCustom',
580  [ $this->fetchContentObject(), $this->getTitle(), $outputPage ] )
581  ) {
582  # Allow extensions do their own custom view for certain pages
583  $outputDone = true;
584  }
585  break;
586  case 4:
587  # Run the parse, protected by a pool counter
588  wfDebug( __METHOD__ . ": doing uncached parse\n" );
589 
590  $content = $this->getContentObject();
591  $poolArticleView = new PoolWorkArticleView( $this->getPage(), $parserOptions,
592  $this->getRevIdFetched(), $useParserCache, $content );
593 
594  if ( !$poolArticleView->execute() ) {
595  $error = $poolArticleView->getError();
596  if ( $error ) {
597  $outputPage->clearHTML(); // for release() errors
598  $outputPage->enableClientCache( false );
599  $outputPage->setRobotPolicy( 'noindex,nofollow' );
600 
601  $errortext = $error->getWikiText( false, 'view-pool-error' );
602  $outputPage->addWikiText( '<div class="errorbox">' . $errortext . '</div>' );
603  }
604  # Connection or timeout error
605  return;
606  }
607 
608  $this->mParserOutput = $poolArticleView->getParserOutput();
609  $outputPage->addParserOutput( $this->mParserOutput );
610  if ( $content->getRedirectTarget() ) {
611  $outputPage->addSubtitle( "<span id=\"redirectsub\">" .
612  $this->getContext()->msg( 'redirectpagesub' )->parse() . "</span>" );
613  }
614 
615  # Don't cache a dirty ParserOutput object
616  if ( $poolArticleView->getIsDirty() ) {
617  $outputPage->setCdnMaxage( 0 );
618  $outputPage->addHTML( "<!-- parser cache is expired, " .
619  "sending anyway due to pool overload-->\n" );
620  }
621 
622  $outputDone = true;
623  break;
624  # Should be unreachable, but just in case...
625  default:
626  break 2;
627  }
628  }
629 
630  # Get the ParserOutput actually *displayed* here.
631  # Note that $this->mParserOutput is the *current*/oldid version output.
632  $pOutput = ( $outputDone instanceof ParserOutput )
633  ? $outputDone // object fetched by hook
634  : $this->mParserOutput;
635 
636  # Adjust title for main page & pages with displaytitle
637  if ( $pOutput ) {
638  $this->adjustDisplayTitle( $pOutput );
639  }
640 
641  # For the main page, overwrite the <title> element with the con-
642  # tents of 'pagetitle-view-mainpage' instead of the default (if
643  # that's not empty).
644  # This message always exists because it is in the i18n files
645  if ( $this->getTitle()->isMainPage() ) {
646  $msg = wfMessage( 'pagetitle-view-mainpage' )->inContentLanguage();
647  if ( !$msg->isDisabled() ) {
648  $outputPage->setHTMLTitle( $msg->title( $this->getTitle() )->text() );
649  }
650  }
651 
652  # Use adaptive TTLs for CDN so delayed/failed purges are noticed less often.
653  # This could use getTouched(), but that could be scary for major template edits.
654  $outputPage->adaptCdnTTL( $this->mPage->getTimestamp(), IExpiringStore::TTL_DAY );
655 
656  # Check for any __NOINDEX__ tags on the page using $pOutput
657  $policy = $this->getRobotPolicy( 'view', $pOutput );
658  $outputPage->setIndexPolicy( $policy['index'] );
659  $outputPage->setFollowPolicy( $policy['follow'] );
660 
661  $this->showViewFooter();
662  $this->mPage->doViewUpdates( $user, $oldid );
663 
664  # Load the postEdit module if the user just saved this revision
665  # See also EditPage::setPostEditCookie
666  $request = $this->getContext()->getRequest();
668  $postEdit = $request->getCookie( $cookieKey );
669  if ( $postEdit ) {
670  # Clear the cookie. This also prevents caching of the response.
671  $request->response()->clearCookie( $cookieKey );
672  $outputPage->addJsConfigVars( 'wgPostEdit', $postEdit );
673  $outputPage->addModules( 'mediawiki.action.view.postEdit' );
674  }
675  }
676 
681  public function adjustDisplayTitle( ParserOutput $pOutput ) {
682  # Adjust the title if it was set by displaytitle, -{T|}- or language conversion
683  $titleText = $pOutput->getTitleText();
684  if ( strval( $titleText ) !== '' ) {
685  $this->getContext()->getOutput()->setPageTitle( $titleText );
686  }
687  }
688 
693  protected function showDiffPage() {
694  $request = $this->getContext()->getRequest();
695  $user = $this->getContext()->getUser();
696  $diff = $request->getVal( 'diff' );
697  $rcid = $request->getVal( 'rcid' );
698  $diffOnly = $request->getBool( 'diffonly', $user->getOption( 'diffonly' ) );
699  $purge = $request->getVal( 'action' ) == 'purge';
700  $unhide = $request->getInt( 'unhide' ) == 1;
701  $oldid = $this->getOldID();
702 
703  $rev = $this->getRevisionFetched();
704 
705  if ( !$rev ) {
706  $this->getContext()->getOutput()->setPageTitle( wfMessage( 'errorpagetitle' ) );
707  $msg = $this->getContext()->msg( 'difference-missing-revision' )
708  ->params( $oldid )
709  ->numParams( 1 )
710  ->parseAsBlock();
711  $this->getContext()->getOutput()->addHTML( $msg );
712  return;
713  }
714 
715  $contentHandler = $rev->getContentHandler();
716  $de = $contentHandler->createDifferenceEngine(
717  $this->getContext(),
718  $oldid,
719  $diff,
720  $rcid,
721  $purge,
722  $unhide
723  );
724 
725  // DifferenceEngine directly fetched the revision:
726  $this->mRevIdFetched = $de->mNewid;
727  $de->showDiffPage( $diffOnly );
728 
729  // Run view updates for the newer revision being diffed (and shown
730  // below the diff if not $diffOnly).
731  list( $old, $new ) = $de->mapDiffPrevNext( $oldid, $diff );
732  // New can be false, convert it to 0 - this conveniently means the latest revision
733  $this->mPage->doViewUpdates( $user, (int)$new );
734  }
735 
743  public function getRobotPolicy( $action, $pOutput = null ) {
745 
746  $ns = $this->getTitle()->getNamespace();
747 
748  # Don't index user and user talk pages for blocked users (T13443)
749  if ( ( $ns == NS_USER || $ns == NS_USER_TALK ) && !$this->getTitle()->isSubpage() ) {
750  $specificTarget = null;
751  $vagueTarget = null;
752  $titleText = $this->getTitle()->getText();
753  if ( IP::isValid( $titleText ) ) {
754  $vagueTarget = $titleText;
755  } else {
756  $specificTarget = $titleText;
757  }
758  if ( Block::newFromTarget( $specificTarget, $vagueTarget ) instanceof Block ) {
759  return [
760  'index' => 'noindex',
761  'follow' => 'nofollow'
762  ];
763  }
764  }
765 
766  if ( $this->mPage->getId() === 0 || $this->getOldID() ) {
767  # Non-articles (special pages etc), and old revisions
768  return [
769  'index' => 'noindex',
770  'follow' => 'nofollow'
771  ];
772  } elseif ( $this->getContext()->getOutput()->isPrintable() ) {
773  # Discourage indexing of printable versions, but encourage following
774  return [
775  'index' => 'noindex',
776  'follow' => 'follow'
777  ];
778  } elseif ( $this->getContext()->getRequest()->getInt( 'curid' ) ) {
779  # For ?curid=x urls, disallow indexing
780  return [
781  'index' => 'noindex',
782  'follow' => 'follow'
783  ];
784  }
785 
786  # Otherwise, construct the policy based on the various config variables.
788 
789  if ( isset( $wgNamespaceRobotPolicies[$ns] ) ) {
790  # Honour customised robot policies for this namespace
791  $policy = array_merge(
792  $policy,
793  self::formatRobotPolicy( $wgNamespaceRobotPolicies[$ns] )
794  );
795  }
796  if ( $this->getTitle()->canUseNoindex() && is_object( $pOutput ) && $pOutput->getIndexPolicy() ) {
797  # __INDEX__ and __NOINDEX__ magic words, if allowed. Incorporates
798  # a final sanity check that we have really got the parser output.
799  $policy = array_merge(
800  $policy,
801  [ 'index' => $pOutput->getIndexPolicy() ]
802  );
803  }
804 
805  if ( isset( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] ) ) {
806  # (T16900) site config can override user-defined __INDEX__ or __NOINDEX__
807  $policy = array_merge(
808  $policy,
809  self::formatRobotPolicy( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] )
810  );
811  }
812 
813  return $policy;
814  }
815 
823  public static function formatRobotPolicy( $policy ) {
824  if ( is_array( $policy ) ) {
825  return $policy;
826  } elseif ( !$policy ) {
827  return [];
828  }
829 
830  $policy = explode( ',', $policy );
831  $policy = array_map( 'trim', $policy );
832 
833  $arr = [];
834  foreach ( $policy as $var ) {
835  if ( in_array( $var, [ 'index', 'noindex' ] ) ) {
836  $arr['index'] = $var;
837  } elseif ( in_array( $var, [ 'follow', 'nofollow' ] ) ) {
838  $arr['follow'] = $var;
839  }
840  }
841 
842  return $arr;
843  }
844 
852  public function showRedirectedFromHeader() {
854 
855  $context = $this->getContext();
856  $outputPage = $context->getOutput();
857  $request = $context->getRequest();
858  $rdfrom = $request->getVal( 'rdfrom' );
859 
860  // Construct a URL for the current page view, but with the target title
861  $query = $request->getValues();
862  unset( $query['rdfrom'] );
863  unset( $query['title'] );
864  if ( $this->getTitle()->isRedirect() ) {
865  // Prevent double redirects
866  $query['redirect'] = 'no';
867  }
868  $redirectTargetUrl = $this->getTitle()->getLinkURL( $query );
869 
870  if ( isset( $this->mRedirectedFrom ) ) {
871  // Avoid PHP 7.1 warning of passing $this by reference
872  $articlePage = $this;
873 
874  // This is an internally redirected page view.
875  // We'll need a backlink to the source page for navigation.
876  if ( Hooks::run( 'ArticleViewRedirect', [ &$articlePage ] ) ) {
877  $redir = Linker::linkKnown(
878  $this->mRedirectedFrom,
879  null,
880  [],
881  [ 'redirect' => 'no' ]
882  );
883 
884  $outputPage->addSubtitle( "<span class=\"mw-redirectedfrom\">" .
885  $context->msg( 'redirectedfrom' )->rawParams( $redir )->parse()
886  . "</span>" );
887 
888  // Add the script to update the displayed URL and
889  // set the fragment if one was specified in the redirect
890  $outputPage->addJsConfigVars( [
891  'wgInternalRedirectTargetUrl' => $redirectTargetUrl,
892  ] );
893  $outputPage->addModules( 'mediawiki.action.view.redirect' );
894 
895  // Add a <link rel="canonical"> tag
896  $outputPage->setCanonicalUrl( $this->getTitle()->getCanonicalURL() );
897 
898  // Tell the output object that the user arrived at this article through a redirect
899  $outputPage->setRedirectedFrom( $this->mRedirectedFrom );
900 
901  return true;
902  }
903  } elseif ( $rdfrom ) {
904  // This is an externally redirected view, from some other wiki.
905  // If it was reported from a trusted site, supply a backlink.
906  if ( $wgRedirectSources && preg_match( $wgRedirectSources, $rdfrom ) ) {
907  $redir = Linker::makeExternalLink( $rdfrom, $rdfrom );
908  $outputPage->addSubtitle( "<span class=\"mw-redirectedfrom\">" .
909  $context->msg( 'redirectedfrom' )->rawParams( $redir )->parse()
910  . "</span>" );
911 
912  // Add the script to update the displayed URL
913  $outputPage->addJsConfigVars( [
914  'wgInternalRedirectTargetUrl' => $redirectTargetUrl,
915  ] );
916  $outputPage->addModules( 'mediawiki.action.view.redirect' );
917 
918  return true;
919  }
920  }
921 
922  return false;
923  }
924 
929  public function showNamespaceHeader() {
930  if ( $this->getTitle()->isTalkPage() ) {
931  if ( !wfMessage( 'talkpageheader' )->isDisabled() ) {
932  $this->getContext()->getOutput()->wrapWikiMsg(
933  "<div class=\"mw-talkpageheader\">\n$1\n</div>",
934  [ 'talkpageheader' ]
935  );
936  }
937  }
938  }
939 
943  public function showViewFooter() {
944  # check if we're displaying a [[User talk:x.x.x.x]] anonymous talk page
945  if ( $this->getTitle()->getNamespace() == NS_USER_TALK
946  && IP::isValid( $this->getTitle()->getText() )
947  ) {
948  $this->getContext()->getOutput()->addWikiMsg( 'anontalkpagetext' );
949  }
950 
951  // Show a footer allowing the user to patrol the shown revision or page if possible
952  $patrolFooterShown = $this->showPatrolFooter();
953 
954  Hooks::run( 'ArticleViewFooter', [ $this, $patrolFooterShown ] );
955  }
956 
966  public function showPatrolFooter() {
968 
969  $outputPage = $this->getContext()->getOutput();
970  $user = $this->getContext()->getUser();
971  $title = $this->getTitle();
972  $rc = false;
973 
974  if ( !$title->quickUserCan( 'patrol', $user )
976  || ( $wgUseFilePatrol && $title->inNamespace( NS_FILE ) ) )
977  ) {
978  // Patrolling is disabled or the user isn't allowed to
979  return false;
980  }
981 
982  if ( $this->mRevision
983  && !RecentChange::isInRCLifespan( $this->mRevision->getTimestamp(), 21600 )
984  ) {
985  // The current revision is already older than what could be in the RC table
986  // 6h tolerance because the RC might not be cleaned out regularly
987  return false;
988  }
989 
990  // Check for cached results
991  $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
992  $key = $cache->makeKey( 'unpatrollable-page', $title->getArticleID() );
993  if ( $cache->get( $key ) ) {
994  return false;
995  }
996 
997  $dbr = wfGetDB( DB_REPLICA );
998  $oldestRevisionTimestamp = $dbr->selectField(
999  'revision',
1000  'MIN( rev_timestamp )',
1001  [ 'rev_page' => $title->getArticleID() ],
1002  __METHOD__
1003  );
1004 
1005  // New page patrol: Get the timestamp of the oldest revison which
1006  // the revision table holds for the given page. Then we look
1007  // whether it's within the RC lifespan and if it is, we try
1008  // to get the recentchanges row belonging to that entry
1009  // (with rc_new = 1).
1010  $recentPageCreation = false;
1011  if ( $oldestRevisionTimestamp
1012  && RecentChange::isInRCLifespan( $oldestRevisionTimestamp, 21600 )
1013  ) {
1014  // 6h tolerance because the RC might not be cleaned out regularly
1015  $recentPageCreation = true;
1017  [
1018  'rc_new' => 1,
1019  'rc_timestamp' => $oldestRevisionTimestamp,
1020  'rc_namespace' => $title->getNamespace(),
1021  'rc_cur_id' => $title->getArticleID()
1022  ],
1023  __METHOD__
1024  );
1025  if ( $rc ) {
1026  // Use generic patrol message for new pages
1027  $markPatrolledMsg = wfMessage( 'markaspatrolledtext' );
1028  }
1029  }
1030 
1031  // File patrol: Get the timestamp of the latest upload for this page,
1032  // check whether it is within the RC lifespan and if it is, we try
1033  // to get the recentchanges row belonging to that entry
1034  // (with rc_type = RC_LOG, rc_log_type = upload).
1035  $recentFileUpload = false;
1036  if ( ( !$rc || $rc->getAttribute( 'rc_patrolled' ) ) && $wgUseFilePatrol
1037  && $title->getNamespace() === NS_FILE ) {
1038  // Retrieve timestamp of most recent upload
1039  $newestUploadTimestamp = $dbr->selectField(
1040  'image',
1041  'MAX( img_timestamp )',
1042  [ 'img_name' => $title->getDBkey() ],
1043  __METHOD__
1044  );
1045  if ( $newestUploadTimestamp
1046  && RecentChange::isInRCLifespan( $newestUploadTimestamp, 21600 )
1047  ) {
1048  // 6h tolerance because the RC might not be cleaned out regularly
1049  $recentFileUpload = true;
1051  [
1052  'rc_type' => RC_LOG,
1053  'rc_log_type' => 'upload',
1054  'rc_timestamp' => $newestUploadTimestamp,
1055  'rc_namespace' => NS_FILE,
1056  'rc_cur_id' => $title->getArticleID()
1057  ],
1058  __METHOD__
1059  );
1060  if ( $rc ) {
1061  // Use patrol message specific to files
1062  $markPatrolledMsg = wfMessage( 'markaspatrolledtext-file' );
1063  }
1064  }
1065  }
1066 
1067  if ( !$recentPageCreation && !$recentFileUpload ) {
1068  // Page creation and latest upload (for files) is too old to be in RC
1069 
1070  // We definitely can't patrol so cache the information
1071  // When a new file version is uploaded, the cache is cleared
1072  $cache->set( $key, '1' );
1073 
1074  return false;
1075  }
1076 
1077  if ( !$rc ) {
1078  // Don't cache: This can be hit if the page gets accessed very fast after
1079  // its creation / latest upload or in case we have high replica DB lag. In case
1080  // the revision is too old, we will already return above.
1081  return false;
1082  }
1083 
1084  if ( $rc->getAttribute( 'rc_patrolled' ) ) {
1085  // Patrolled RC entry around
1086 
1087  // Cache the information we gathered above in case we can't patrol
1088  // Don't cache in case we can patrol as this could change
1089  $cache->set( $key, '1' );
1090 
1091  return false;
1092  }
1093 
1094  if ( $rc->getPerformer()->equals( $user ) ) {
1095  // Don't show a patrol link for own creations/uploads. If the user could
1096  // patrol them, they already would be patrolled
1097  return false;
1098  }
1099 
1100  $outputPage->preventClickjacking();
1101  if ( $wgEnableAPI && $wgEnableWriteAPI && $user->isAllowed( 'writeapi' ) ) {
1102  $outputPage->addModules( 'mediawiki.page.patrol.ajax' );
1103  }
1104 
1106  $title,
1107  $markPatrolledMsg->escaped(),
1108  [],
1109  [
1110  'action' => 'markpatrolled',
1111  'rcid' => $rc->getAttribute( 'rc_id' ),
1112  ]
1113  );
1114 
1115  $outputPage->addHTML(
1116  "<div class='patrollink' data-mw='interface'>" .
1117  wfMessage( 'markaspatrolledlink' )->rawParams( $link )->escaped() .
1118  '</div>'
1119  );
1120 
1121  return true;
1122  }
1123 
1130  public static function purgePatrolFooterCache( $articleID ) {
1131  $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
1132  $cache->delete( $cache->makeKey( 'unpatrollable-page', $articleID ) );
1133  }
1134 
1139  public function showMissingArticle() {
1141 
1142  $outputPage = $this->getContext()->getOutput();
1143  // Whether the page is a root user page of an existing user (but not a subpage)
1144  $validUserPage = false;
1145 
1146  $title = $this->getTitle();
1147 
1148  # Show info in user (talk) namespace. Does the user exist? Is he blocked?
1149  if ( $title->getNamespace() == NS_USER
1150  || $title->getNamespace() == NS_USER_TALK
1151  ) {
1152  $rootPart = explode( '/', $title->getText() )[0];
1153  $user = User::newFromName( $rootPart, false /* allow IP users */ );
1154  $ip = User::isIP( $rootPart );
1155  $block = Block::newFromTarget( $user, $user );
1156 
1157  if ( !( $user && $user->isLoggedIn() ) && !$ip ) { # User does not exist
1158  $outputPage->wrapWikiMsg( "<div class=\"mw-userpage-userdoesnotexist error\">\n\$1\n</div>",
1159  [ 'userpage-userdoesnotexist-view', wfEscapeWikiText( $rootPart ) ] );
1160  } elseif ( !is_null( $block ) && $block->getType() != Block::TYPE_AUTO ) {
1161  # Show log extract if the user is currently blocked
1163  $outputPage,
1164  'block',
1165  MWNamespace::getCanonicalName( NS_USER ) . ':' . $block->getTarget(),
1166  '',
1167  [
1168  'lim' => 1,
1169  'showIfEmpty' => false,
1170  'msgKey' => [
1171  'blocked-notice-logextract',
1172  $user->getName() # Support GENDER in notice
1173  ]
1174  ]
1175  );
1176  $validUserPage = !$title->isSubpage();
1177  } else {
1178  $validUserPage = !$title->isSubpage();
1179  }
1180  }
1181 
1182  Hooks::run( 'ShowMissingArticle', [ $this ] );
1183 
1184  # Show delete and move logs if there were any such events.
1185  # The logging query can DOS the site when bots/crawlers cause 404 floods,
1186  # so be careful showing this. 404 pages must be cheap as they are hard to cache.
1187  $cache = MediaWikiServices::getInstance()->getMainObjectStash();
1188  $key = $cache->makeKey( 'page-recent-delete', md5( $title->getPrefixedText() ) );
1189  $loggedIn = $this->getContext()->getUser()->isLoggedIn();
1190  if ( $loggedIn || $cache->get( $key ) ) {
1191  $logTypes = [ 'delete', 'move', 'protect' ];
1192 
1193  $dbr = wfGetDB( DB_REPLICA );
1194 
1195  $conds = [ 'log_action != ' . $dbr->addQuotes( 'revision' ) ];
1196  // Give extensions a chance to hide their (unrelated) log entries
1197  Hooks::run( 'Article::MissingArticleConditions', [ &$conds, $logTypes ] );
1199  $outputPage,
1200  $logTypes,
1201  $title,
1202  '',
1203  [
1204  'lim' => 10,
1205  'conds' => $conds,
1206  'showIfEmpty' => false,
1207  'msgKey' => [ $loggedIn
1208  ? 'moveddeleted-notice'
1209  : 'moveddeleted-notice-recent'
1210  ]
1211  ]
1212  );
1213  }
1214 
1215  if ( !$this->mPage->hasViewableContent() && $wgSend404Code && !$validUserPage ) {
1216  // If there's no backing content, send a 404 Not Found
1217  // for better machine handling of broken links.
1218  $this->getContext()->getRequest()->response()->statusHeader( 404 );
1219  }
1220 
1221  // Also apply the robot policy for nonexisting pages (even if a 404 was used for sanity)
1222  $policy = $this->getRobotPolicy( 'view' );
1223  $outputPage->setIndexPolicy( $policy['index'] );
1224  $outputPage->setFollowPolicy( $policy['follow'] );
1225 
1226  $hookResult = Hooks::run( 'BeforeDisplayNoArticleText', [ $this ] );
1227 
1228  if ( !$hookResult ) {
1229  return;
1230  }
1231 
1232  # Show error message
1233  $oldid = $this->getOldID();
1234  if ( !$oldid && $title->getNamespace() === NS_MEDIAWIKI && $title->hasSourceText() ) {
1235  $outputPage->addParserOutput( $this->getContentObject()->getParserOutput( $title ) );
1236  } else {
1237  if ( $oldid ) {
1238  $text = wfMessage( 'missing-revision', $oldid )->plain();
1239  } elseif ( $title->quickUserCan( 'create', $this->getContext()->getUser() )
1240  && $title->quickUserCan( 'edit', $this->getContext()->getUser() )
1241  ) {
1242  $message = $this->getContext()->getUser()->isLoggedIn() ? 'noarticletext' : 'noarticletextanon';
1243  $text = wfMessage( $message )->plain();
1244  } else {
1245  $text = wfMessage( 'noarticletext-nopermission' )->plain();
1246  }
1247 
1248  $dir = $this->getContext()->getLanguage()->getDir();
1249  $lang = $this->getContext()->getLanguage()->getCode();
1250  $outputPage->addWikiText( Xml::openElement( 'div', [
1251  'class' => "noarticletext mw-content-$dir",
1252  'dir' => $dir,
1253  'lang' => $lang,
1254  ] ) . "\n$text\n</div>" );
1255  }
1256  }
1257 
1264  public function showDeletedRevisionHeader() {
1265  if ( !$this->mRevision->isDeleted( Revision::DELETED_TEXT ) ) {
1266  // Not deleted
1267  return true;
1268  }
1269 
1270  $outputPage = $this->getContext()->getOutput();
1271  $user = $this->getContext()->getUser();
1272  // If the user is not allowed to see it...
1273  if ( !$this->mRevision->userCan( Revision::DELETED_TEXT, $user ) ) {
1274  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1275  'rev-deleted-text-permission' );
1276 
1277  return false;
1278  // If the user needs to confirm that they want to see it...
1279  } elseif ( $this->getContext()->getRequest()->getInt( 'unhide' ) != 1 ) {
1280  # Give explanation and add a link to view the revision...
1281  $oldid = intval( $this->getOldID() );
1282  $link = $this->getTitle()->getFullURL( "oldid={$oldid}&unhide=1" );
1283  $msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ?
1284  'rev-suppressed-text-unhide' : 'rev-deleted-text-unhide';
1285  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1286  [ $msg, $link ] );
1287 
1288  return false;
1289  // We are allowed to see...
1290  } else {
1291  $msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ?
1292  'rev-suppressed-text-view' : 'rev-deleted-text-view';
1293  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n", $msg );
1294 
1295  return true;
1296  }
1297  }
1298 
1307  public function setOldSubtitle( $oldid = 0 ) {
1308  // Avoid PHP 7.1 warning of passing $this by reference
1309  $articlePage = $this;
1310 
1311  if ( !Hooks::run( 'DisplayOldSubtitle', [ &$articlePage, &$oldid ] ) ) {
1312  return;
1313  }
1314 
1315  $context = $this->getContext();
1316  $unhide = $context->getRequest()->getInt( 'unhide' ) == 1;
1317 
1318  # Cascade unhide param in links for easy deletion browsing
1319  $extraParams = [];
1320  if ( $unhide ) {
1321  $extraParams['unhide'] = 1;
1322  }
1323 
1324  if ( $this->mRevision && $this->mRevision->getId() === $oldid ) {
1325  $revision = $this->mRevision;
1326  } else {
1327  $revision = Revision::newFromId( $oldid );
1328  }
1329 
1330  $timestamp = $revision->getTimestamp();
1331 
1332  $current = ( $oldid == $this->mPage->getLatest() );
1333  $language = $context->getLanguage();
1334  $user = $context->getUser();
1335 
1336  $td = $language->userTimeAndDate( $timestamp, $user );
1337  $tddate = $language->userDate( $timestamp, $user );
1338  $tdtime = $language->userTime( $timestamp, $user );
1339 
1340  # Show user links if allowed to see them. If hidden, then show them only if requested...
1341  $userlinks = Linker::revUserTools( $revision, !$unhide );
1342 
1343  $infomsg = $current && !$context->msg( 'revision-info-current' )->isDisabled()
1344  ? 'revision-info-current'
1345  : 'revision-info';
1346 
1347  $outputPage = $context->getOutput();
1348  $revisionInfo = "<div id=\"mw-{$infomsg}\">" .
1349  $context->msg( $infomsg, $td )
1350  ->rawParams( $userlinks )
1351  ->params( $revision->getId(), $tddate, $tdtime, $revision->getUserText() )
1352  ->rawParams( Linker::revComment( $revision, true, true ) )
1353  ->parse() .
1354  "</div>";
1355 
1356  $lnk = $current
1357  ? $context->msg( 'currentrevisionlink' )->escaped()
1359  $this->getTitle(),
1360  $context->msg( 'currentrevisionlink' )->escaped(),
1361  [],
1362  $extraParams
1363  );
1364  $curdiff = $current
1365  ? $context->msg( 'diff' )->escaped()
1367  $this->getTitle(),
1368  $context->msg( 'diff' )->escaped(),
1369  [],
1370  [
1371  'diff' => 'cur',
1372  'oldid' => $oldid
1373  ] + $extraParams
1374  );
1375  $prev = $this->getTitle()->getPreviousRevisionID( $oldid );
1376  $prevlink = $prev
1378  $this->getTitle(),
1379  $context->msg( 'previousrevision' )->escaped(),
1380  [],
1381  [
1382  'direction' => 'prev',
1383  'oldid' => $oldid
1384  ] + $extraParams
1385  )
1386  : $context->msg( 'previousrevision' )->escaped();
1387  $prevdiff = $prev
1389  $this->getTitle(),
1390  $context->msg( 'diff' )->escaped(),
1391  [],
1392  [
1393  'diff' => 'prev',
1394  'oldid' => $oldid
1395  ] + $extraParams
1396  )
1397  : $context->msg( 'diff' )->escaped();
1398  $nextlink = $current
1399  ? $context->msg( 'nextrevision' )->escaped()
1401  $this->getTitle(),
1402  $context->msg( 'nextrevision' )->escaped(),
1403  [],
1404  [
1405  'direction' => 'next',
1406  'oldid' => $oldid
1407  ] + $extraParams
1408  );
1409  $nextdiff = $current
1410  ? $context->msg( 'diff' )->escaped()
1412  $this->getTitle(),
1413  $context->msg( 'diff' )->escaped(),
1414  [],
1415  [
1416  'diff' => 'next',
1417  'oldid' => $oldid
1418  ] + $extraParams
1419  );
1420 
1421  $cdel = Linker::getRevDeleteLink( $user, $revision, $this->getTitle() );
1422  if ( $cdel !== '' ) {
1423  $cdel .= ' ';
1424  }
1425 
1426  // the outer div is need for styling the revision info and nav in MobileFrontend
1427  $outputPage->addSubtitle( "<div class=\"mw-revision\">" . $revisionInfo .
1428  "<div id=\"mw-revision-nav\">" . $cdel .
1429  $context->msg( 'revision-nav' )->rawParams(
1430  $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff
1431  )->escaped() . "</div></div>" );
1432  }
1433 
1447  public function viewRedirect( $target, $appendSubtitle = true, $forceKnown = false ) {
1448  $lang = $this->getTitle()->getPageLanguage();
1449  $out = $this->getContext()->getOutput();
1450  if ( $appendSubtitle ) {
1451  $out->addSubtitle( wfMessage( 'redirectpagesub' ) );
1452  }
1453  $out->addModuleStyles( 'mediawiki.action.view.redirectPage' );
1454  return static::getRedirectHeaderHtml( $lang, $target, $forceKnown );
1455  }
1456 
1469  public static function getRedirectHeaderHtml( Language $lang, $target, $forceKnown = false ) {
1470  if ( !is_array( $target ) ) {
1471  $target = [ $target ];
1472  }
1473 
1474  $html = '<ul class="redirectText">';
1476  foreach ( $target as $title ) {
1477  $html .= '<li>' . Linker::link(
1478  $title,
1479  htmlspecialchars( $title->getFullText() ),
1480  [],
1481  // Make sure wiki page redirects are not followed
1482  $title->isRedirect() ? [ 'redirect' => 'no' ] : [],
1483  ( $forceKnown ? [ 'known', 'noclasses' ] : [] )
1484  ) . '</li>';
1485  }
1486  $html .= '</ul>';
1487 
1488  $redirectToText = wfMessage( 'redirectto' )->inLanguage( $lang )->escaped();
1489 
1490  return '<div class="redirectMsg">' .
1491  '<p>' . $redirectToText . '</p>' .
1492  $html .
1493  '</div>';
1494  }
1495 
1504  public function addHelpLink( $to, $overrideBaseUrl = false ) {
1505  $msg = wfMessage(
1506  'namespace-' . $this->getTitle()->getNamespace() . '-helppage'
1507  );
1508 
1509  $out = $this->getContext()->getOutput();
1510  if ( !$msg->isDisabled() ) {
1511  $helpUrl = Skin::makeUrl( $msg->plain() );
1512  $out->addHelpLink( $helpUrl, true );
1513  } else {
1514  $out->addHelpLink( $to, $overrideBaseUrl );
1515  }
1516  }
1517 
1521  public function render() {
1522  $this->getContext()->getRequest()->response()->header( 'X-Robots-Tag: noindex' );
1523  $this->getContext()->getOutput()->setArticleBodyOnly( true );
1524  $this->getContext()->getOutput()->enableSectionEditLinks( false );
1525  $this->view();
1526  }
1527 
1531  public function protect() {
1532  $form = new ProtectionForm( $this );
1533  $form->execute();
1534  }
1535 
1539  public function unprotect() {
1540  $this->protect();
1541  }
1542 
1546  public function delete() {
1547  # This code desperately needs to be totally rewritten
1548 
1549  $title = $this->getTitle();
1550  $context = $this->getContext();
1551  $user = $context->getUser();
1552  $request = $context->getRequest();
1553 
1554  # Check permissions
1555  $permissionErrors = $title->getUserPermissionsErrors( 'delete', $user );
1556  if ( count( $permissionErrors ) ) {
1557  throw new PermissionsError( 'delete', $permissionErrors );
1558  }
1559 
1560  # Read-only check...
1561  if ( wfReadOnly() ) {
1562  throw new ReadOnlyError;
1563  }
1564 
1565  # Better double-check that it hasn't been deleted yet!
1566  $this->mPage->loadPageData(
1567  $request->wasPosted() ? WikiPage::READ_LATEST : WikiPage::READ_NORMAL
1568  );
1569  if ( !$this->mPage->exists() ) {
1570  $deleteLogPage = new LogPage( 'delete' );
1571  $outputPage = $context->getOutput();
1572  $outputPage->setPageTitle( $context->msg( 'cannotdelete-title', $title->getPrefixedText() ) );
1573  $outputPage->wrapWikiMsg( "<div class=\"error mw-error-cannotdelete\">\n$1\n</div>",
1574  [ 'cannotdelete', wfEscapeWikiText( $title->getPrefixedText() ) ]
1575  );
1576  $outputPage->addHTML(
1577  Xml::element( 'h2', null, $deleteLogPage->getName()->text() )
1578  );
1580  $outputPage,
1581  'delete',
1582  $title
1583  );
1584 
1585  return;
1586  }
1587 
1588  $deleteReasonList = $request->getText( 'wpDeleteReasonList', 'other' );
1589  $deleteReason = $request->getText( 'wpReason' );
1590 
1591  if ( $deleteReasonList == 'other' ) {
1592  $reason = $deleteReason;
1593  } elseif ( $deleteReason != '' ) {
1594  // Entry from drop down menu + additional comment
1595  $colonseparator = wfMessage( 'colon-separator' )->inContentLanguage()->text();
1596  $reason = $deleteReasonList . $colonseparator . $deleteReason;
1597  } else {
1598  $reason = $deleteReasonList;
1599  }
1600 
1601  if ( $request->wasPosted() && $user->matchEditToken( $request->getVal( 'wpEditToken' ),
1602  [ 'delete', $this->getTitle()->getPrefixedText() ] )
1603  ) {
1604  # Flag to hide all contents of the archived revisions
1605  $suppress = $request->getCheck( 'wpSuppress' ) && $user->isAllowed( 'suppressrevision' );
1606 
1607  $this->doDelete( $reason, $suppress );
1608 
1609  WatchAction::doWatchOrUnwatch( $request->getCheck( 'wpWatch' ), $title, $user );
1610 
1611  return;
1612  }
1613 
1614  // Generate deletion reason
1615  $hasHistory = false;
1616  if ( !$reason ) {
1617  try {
1618  $reason = $this->generateReason( $hasHistory );
1619  } catch ( Exception $e ) {
1620  # if a page is horribly broken, we still want to be able to
1621  # delete it. So be lenient about errors here.
1622  wfDebug( "Error while building auto delete summary: $e" );
1623  $reason = '';
1624  }
1625  }
1626 
1627  // If the page has a history, insert a warning
1628  if ( $hasHistory ) {
1629  $title = $this->getTitle();
1630 
1631  // The following can use the real revision count as this is only being shown for users
1632  // that can delete this page.
1633  // This, as a side-effect, also makes sure that the following query isn't being run for
1634  // pages with a larger history, unless the user has the 'bigdelete' right
1635  // (and is about to delete this page).
1636  $dbr = wfGetDB( DB_REPLICA );
1637  $revisions = $edits = (int)$dbr->selectField(
1638  'revision',
1639  'COUNT(rev_page)',
1640  [ 'rev_page' => $title->getArticleID() ],
1641  __METHOD__
1642  );
1643 
1644  // @todo FIXME: i18n issue/patchwork message
1645  $context->getOutput()->addHTML(
1646  '<strong class="mw-delete-warning-revisions">' .
1647  $context->msg( 'historywarning' )->numParams( $revisions )->parse() .
1648  $context->msg( 'word-separator' )->escaped() . Linker::linkKnown( $title,
1649  $context->msg( 'history' )->escaped(),
1650  [],
1651  [ 'action' => 'history' ] ) .
1652  '</strong>'
1653  );
1654 
1655  if ( $title->isBigDeletion() ) {
1657  $context->getOutput()->wrapWikiMsg( "<div class='error'>\n$1\n</div>\n",
1658  [
1659  'delete-warning-toobig',
1660  $context->getLanguage()->formatNum( $wgDeleteRevisionsLimit )
1661  ]
1662  );
1663  }
1664  }
1665 
1666  $this->confirmDelete( $reason );
1667  }
1668 
1674  public function confirmDelete( $reason ) {
1675  wfDebug( "Article::confirmDelete\n" );
1676 
1677  $title = $this->getTitle();
1678  $ctx = $this->getContext();
1679  $outputPage = $ctx->getOutput();
1680  $outputPage->setPageTitle( wfMessage( 'delete-confirm', $title->getPrefixedText() ) );
1681  $outputPage->addBacklinkSubtitle( $title );
1682  $outputPage->setRobotPolicy( 'noindex,nofollow' );
1683 
1684  $backlinkCache = $title->getBacklinkCache();
1685  if ( $backlinkCache->hasLinks( 'pagelinks' ) || $backlinkCache->hasLinks( 'templatelinks' ) ) {
1686  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1687  'deleting-backlinks-warning' );
1688  }
1689 
1690  $subpageQueryLimit = 51;
1691  $subpages = $title->getSubpages( $subpageQueryLimit );
1692  $subpageCount = count( $subpages );
1693  if ( $subpageCount > 0 ) {
1694  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1695  [ 'deleting-subpages-warning', Message::numParam( $subpageCount ) ] );
1696  }
1697  $outputPage->addWikiMsg( 'confirmdeletetext' );
1698 
1699  Hooks::run( 'ArticleConfirmDelete', [ $this, $outputPage, &$reason ] );
1700 
1701  $user = $this->getContext()->getUser();
1702  $checkWatch = $user->getBoolOption( 'watchdeletion' ) || $user->isWatched( $title );
1703 
1704  $outputPage->enableOOUI();
1705 
1707  $ctx->msg( 'deletereason-dropdown' )->inContentLanguage()->text(),
1708  [ 'other' => $ctx->msg( 'deletereasonotherlist' )->inContentLanguage()->text() ]
1709  );
1711 
1712  $fields[] = new OOUI\FieldLayout(
1713  new OOUI\DropdownInputWidget( [
1714  'name' => 'wpDeleteReasonList',
1715  'inputId' => 'wpDeleteReasonList',
1716  'tabIndex' => 1,
1717  'infusable' => true,
1718  'value' => '',
1719  'options' => $options
1720  ] ),
1721  [
1722  'label' => $ctx->msg( 'deletecomment' )->text(),
1723  'align' => 'top',
1724  ]
1725  );
1726 
1727  $fields[] = new OOUI\FieldLayout(
1728  new OOUI\TextInputWidget( [
1729  'name' => 'wpReason',
1730  'inputId' => 'wpReason',
1731  'tabIndex' => 2,
1732  'maxLength' => 255,
1733  'infusable' => true,
1734  'value' => $reason,
1735  'autofocus' => true,
1736  ] ),
1737  [
1738  'label' => $ctx->msg( 'deleteotherreason' )->text(),
1739  'align' => 'top',
1740  ]
1741  );
1742 
1743  if ( $user->isLoggedIn() ) {
1744  $fields[] = new OOUI\FieldLayout(
1745  new OOUI\CheckboxInputWidget( [
1746  'name' => 'wpWatch',
1747  'inputId' => 'wpWatch',
1748  'tabIndex' => 3,
1749  'selected' => $checkWatch,
1750  ] ),
1751  [
1752  'label' => $ctx->msg( 'watchthis' )->text(),
1753  'align' => 'inline',
1754  'infusable' => true,
1755  ]
1756  );
1757  }
1758 
1759  if ( $user->isAllowed( 'suppressrevision' ) ) {
1760  $fields[] = new OOUI\FieldLayout(
1761  new OOUI\CheckboxInputWidget( [
1762  'name' => 'wpSuppress',
1763  'inputId' => 'wpSuppress',
1764  'tabIndex' => 4,
1765  ] ),
1766  [
1767  'label' => $ctx->msg( 'revdelete-suppress' )->text(),
1768  'align' => 'inline',
1769  'infusable' => true,
1770  ]
1771  );
1772  }
1773 
1774  $fields[] = new OOUI\FieldLayout(
1775  new OOUI\ButtonInputWidget( [
1776  'name' => 'wpConfirmB',
1777  'inputId' => 'wpConfirmB',
1778  'tabIndex' => 5,
1779  'value' => $ctx->msg( 'deletepage' )->text(),
1780  'label' => $ctx->msg( 'deletepage' )->text(),
1781  'flags' => [ 'primary', 'destructive' ],
1782  'type' => 'submit',
1783  ] ),
1784  [
1785  'align' => 'top',
1786  ]
1787  );
1788 
1789  $fieldset = new OOUI\FieldsetLayout( [
1790  'label' => $ctx->msg( 'delete-legend' )->text(),
1791  'id' => 'mw-delete-table',
1792  'items' => $fields,
1793  ] );
1794 
1795  $form = new OOUI\FormLayout( [
1796  'method' => 'post',
1797  'action' => $title->getLocalURL( 'action=delete' ),
1798  'id' => 'deleteconfirm',
1799  ] );
1800  $form->appendContent(
1801  $fieldset,
1802  new OOUI\HtmlSnippet(
1803  Html::hidden( 'wpEditToken', $user->getEditToken( [ 'delete', $title->getPrefixedText() ] ) )
1804  )
1805  );
1806 
1807  $outputPage->addHTML(
1808  new OOUI\PanelLayout( [
1809  'classes' => [ 'deletepage-wrapper' ],
1810  'expanded' => false,
1811  'padded' => true,
1812  'framed' => true,
1813  'content' => $form,
1814  ] )
1815  );
1816 
1817  if ( $user->isAllowed( 'editinterface' ) ) {
1819  $ctx->msg( 'deletereason-dropdown' )->inContentLanguage()->getTitle(),
1820  wfMessage( 'delete-edit-reasonlist' )->escaped(),
1821  [],
1822  [ 'action' => 'edit' ]
1823  );
1824  $outputPage->addHTML( '<p class="mw-delete-editreasons">' . $link . '</p>' );
1825  }
1826 
1827  $deleteLogPage = new LogPage( 'delete' );
1828  $outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) );
1829  LogEventsList::showLogExtract( $outputPage, 'delete', $title );
1830  }
1831 
1837  public function doDelete( $reason, $suppress = false ) {
1838  $error = '';
1839  $context = $this->getContext();
1840  $outputPage = $context->getOutput();
1841  $user = $context->getUser();
1842  $status = $this->mPage->doDeleteArticleReal( $reason, $suppress, 0, true, $error, $user );
1843 
1844  if ( $status->isGood() ) {
1845  $deleted = $this->getTitle()->getPrefixedText();
1846 
1847  $outputPage->setPageTitle( wfMessage( 'actioncomplete' ) );
1848  $outputPage->setRobotPolicy( 'noindex,nofollow' );
1849 
1850  $loglink = '[[Special:Log/delete|' . wfMessage( 'deletionlog' )->text() . ']]';
1851 
1852  $outputPage->addWikiMsg( 'deletedtext', wfEscapeWikiText( $deleted ), $loglink );
1853 
1854  Hooks::run( 'ArticleDeleteAfterSuccess', [ $this->getTitle(), $outputPage ] );
1855 
1856  $outputPage->returnToMain( false );
1857  } else {
1858  $outputPage->setPageTitle(
1859  wfMessage( 'cannotdelete-title',
1860  $this->getTitle()->getPrefixedText() )
1861  );
1862 
1863  if ( $error == '' ) {
1864  $outputPage->addWikiText(
1865  "<div class=\"error mw-error-cannotdelete\">\n" . $status->getWikiText() . "\n</div>"
1866  );
1867  $deleteLogPage = new LogPage( 'delete' );
1868  $outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) );
1869 
1871  $outputPage,
1872  'delete',
1873  $this->getTitle()
1874  );
1875  } else {
1876  $outputPage->addHTML( $error );
1877  }
1878  }
1879  }
1880 
1881  /* Caching functions */
1882 
1890  protected function tryFileCache() {
1891  static $called = false;
1892 
1893  if ( $called ) {
1894  wfDebug( "Article::tryFileCache(): called twice!?\n" );
1895  return false;
1896  }
1897 
1898  $called = true;
1899  if ( $this->isFileCacheable() ) {
1900  $cache = new HTMLFileCache( $this->getTitle(), 'view' );
1901  if ( $cache->isCacheGood( $this->mPage->getTouched() ) ) {
1902  wfDebug( "Article::tryFileCache(): about to load file\n" );
1903  $cache->loadFromFileCache( $this->getContext() );
1904  return true;
1905  } else {
1906  wfDebug( "Article::tryFileCache(): starting buffer\n" );
1907  ob_start( [ &$cache, 'saveToFileCache' ] );
1908  }
1909  } else {
1910  wfDebug( "Article::tryFileCache(): not cacheable\n" );
1911  }
1912 
1913  return false;
1914  }
1915 
1921  public function isFileCacheable( $mode = HTMLFileCache::MODE_NORMAL ) {
1922  $cacheable = false;
1923 
1924  if ( HTMLFileCache::useFileCache( $this->getContext(), $mode ) ) {
1925  $cacheable = $this->mPage->getId()
1926  && !$this->mRedirectedFrom && !$this->getTitle()->isRedirect();
1927  // Extension may have reason to disable file caching on some pages.
1928  if ( $cacheable ) {
1929  // Avoid PHP 7.1 warning of passing $this by reference
1930  $articlePage = $this;
1931  $cacheable = Hooks::run( 'IsFileCacheable', [ &$articlePage ] );
1932  }
1933  }
1934 
1935  return $cacheable;
1936  }
1937 
1951  public function getParserOutput( $oldid = null, User $user = null ) {
1952  // XXX: bypasses mParserOptions and thus setParserOptions()
1953 
1954  if ( $user === null ) {
1955  $parserOptions = $this->getParserOptions();
1956  } else {
1957  $parserOptions = $this->mPage->makeParserOptions( $user );
1958  }
1959 
1960  return $this->mPage->getParserOutput( $parserOptions, $oldid );
1961  }
1962 
1970  if ( $this->mParserOptions ) {
1971  throw new MWException( "can't change parser options after they have already been set" );
1972  }
1973 
1974  // clone, so if $options is modified later, it doesn't confuse the parser cache.
1975  $this->mParserOptions = clone $options;
1976  }
1977 
1982  public function getParserOptions() {
1983  if ( !$this->mParserOptions ) {
1984  $this->mParserOptions = $this->mPage->makeParserOptions( $this->getContext() );
1985  }
1986  // Clone to allow modifications of the return value without affecting cache
1987  return clone $this->mParserOptions;
1988  }
1989 
1996  public function setContext( $context ) {
1997  $this->mContext = $context;
1998  }
1999 
2006  public function getContext() {
2007  if ( $this->mContext instanceof IContextSource ) {
2008  return $this->mContext;
2009  } else {
2010  wfDebug( __METHOD__ . " called and \$mContext is null. " .
2011  "Return RequestContext::getMain(); for sanity\n" );
2012  return RequestContext::getMain();
2013  }
2014  }
2015 
2023  public function __get( $fname ) {
2024  if ( property_exists( $this->mPage, $fname ) ) {
2025  # wfWarn( "Access to raw $fname field " . __CLASS__ );
2026  return $this->mPage->$fname;
2027  }
2028  trigger_error( 'Inaccessible property via __get(): ' . $fname, E_USER_NOTICE );
2029  }
2030 
2038  public function __set( $fname, $fvalue ) {
2039  if ( property_exists( $this->mPage, $fname ) ) {
2040  # wfWarn( "Access to raw $fname field of " . __CLASS__ );
2041  $this->mPage->$fname = $fvalue;
2042  // Note: extensions may want to toss on new fields
2043  } elseif ( !in_array( $fname, [ 'mContext', 'mPage' ] ) ) {
2044  $this->mPage->$fname = $fvalue;
2045  } else {
2046  trigger_error( 'Inaccessible property via __set(): ' . $fname, E_USER_NOTICE );
2047  }
2048  }
2049 
2054  public function checkFlags( $flags ) {
2055  return $this->mPage->checkFlags( $flags );
2056  }
2057 
2062  public function checkTouched() {
2063  return $this->mPage->checkTouched();
2064  }
2065 
2070  public function clearPreparedEdit() {
2071  $this->mPage->clearPreparedEdit();
2072  }
2073 
2078  public function doDeleteArticleReal(
2079  $reason, $suppress = false, $u1 = null, $u2 = null, &$error = '', User $user = null,
2080  $tags = []
2081  ) {
2082  return $this->mPage->doDeleteArticleReal(
2083  $reason, $suppress, $u1, $u2, $error, $user, $tags
2084  );
2085  }
2086 
2091  public function doDeleteUpdates( $id, Content $content = null ) {
2092  return $this->mPage->doDeleteUpdates( $id, $content );
2093  }
2094 
2100  public function doEditContent( Content $content, $summary, $flags = 0, $baseRevId = false,
2101  User $user = null, $serialFormat = null
2102  ) {
2103  wfDeprecated( __METHOD__, '1.29' );
2104  return $this->mPage->doEditContent( $content, $summary, $flags, $baseRevId,
2105  $user, $serialFormat
2106  );
2107  }
2108 
2113  public function doEditUpdates( Revision $revision, User $user, array $options = [] ) {
2114  return $this->mPage->doEditUpdates( $revision, $user, $options );
2115  }
2116 
2123  public function doPurge() {
2124  return $this->mPage->doPurge();
2125  }
2126 
2132  public function getLastPurgeTimestamp() {
2133  wfDeprecated( __METHOD__, '1.29' );
2134  return $this->mPage->getLastPurgeTimestamp();
2135  }
2136 
2141  public function doViewUpdates( User $user, $oldid = 0 ) {
2142  $this->mPage->doViewUpdates( $user, $oldid );
2143  }
2144 
2149  public function exists() {
2150  return $this->mPage->exists();
2151  }
2152 
2157  public function followRedirect() {
2158  return $this->mPage->followRedirect();
2159  }
2160 
2165  public function getActionOverrides() {
2166  return $this->mPage->getActionOverrides();
2167  }
2168 
2173  public function getAutoDeleteReason( &$hasHistory ) {
2174  return $this->mPage->getAutoDeleteReason( $hasHistory );
2175  }
2176 
2181  public function getCategories() {
2182  return $this->mPage->getCategories();
2183  }
2184 
2189  public function getComment( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2190  return $this->mPage->getComment( $audience, $user );
2191  }
2192 
2197  public function getContentHandler() {
2198  return $this->mPage->getContentHandler();
2199  }
2200 
2205  public function getContentModel() {
2206  return $this->mPage->getContentModel();
2207  }
2208 
2213  public function getContributors() {
2214  return $this->mPage->getContributors();
2215  }
2216 
2221  public function getCreator( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2222  return $this->mPage->getCreator( $audience, $user );
2223  }
2224 
2229  public function getDeletionUpdates( Content $content = null ) {
2230  return $this->mPage->getDeletionUpdates( $content );
2231  }
2232 
2237  public function getHiddenCategories() {
2238  return $this->mPage->getHiddenCategories();
2239  }
2240 
2245  public function getId() {
2246  return $this->mPage->getId();
2247  }
2248 
2253  public function getLatest() {
2254  return $this->mPage->getLatest();
2255  }
2256 
2261  public function getLinksTimestamp() {
2262  return $this->mPage->getLinksTimestamp();
2263  }
2264 
2269  public function getMinorEdit() {
2270  return $this->mPage->getMinorEdit();
2271  }
2272 
2277  public function getOldestRevision() {
2278  return $this->mPage->getOldestRevision();
2279  }
2280 
2285  public function getRedirectTarget() {
2286  return $this->mPage->getRedirectTarget();
2287  }
2288 
2293  public function getRedirectURL( $rt ) {
2294  return $this->mPage->getRedirectURL( $rt );
2295  }
2296 
2301  public function getRevision() {
2302  return $this->mPage->getRevision();
2303  }
2304 
2309  public function getTimestamp() {
2310  return $this->mPage->getTimestamp();
2311  }
2312 
2317  public function getTouched() {
2318  return $this->mPage->getTouched();
2319  }
2320 
2325  public function getUndoContent( Revision $undo, Revision $undoafter = null ) {
2326  return $this->mPage->getUndoContent( $undo, $undoafter );
2327  }
2328 
2333  public function getUser( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2334  return $this->mPage->getUser( $audience, $user );
2335  }
2336 
2341  public function getUserText( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2342  return $this->mPage->getUserText( $audience, $user );
2343  }
2344 
2349  public function hasViewableContent() {
2350  return $this->mPage->hasViewableContent();
2351  }
2352 
2357  public function insertOn( $dbw, $pageId = null ) {
2358  return $this->mPage->insertOn( $dbw, $pageId );
2359  }
2360 
2365  public function insertProtectNullRevision( $revCommentMsg, array $limit,
2366  array $expiry, $cascade, $reason, $user = null
2367  ) {
2368  return $this->mPage->insertProtectNullRevision( $revCommentMsg, $limit,
2369  $expiry, $cascade, $reason, $user
2370  );
2371  }
2372 
2377  public function insertRedirect() {
2378  return $this->mPage->insertRedirect();
2379  }
2380 
2385  public function insertRedirectEntry( Title $rt, $oldLatest = null ) {
2386  return $this->mPage->insertRedirectEntry( $rt, $oldLatest );
2387  }
2388 
2393  public function isCountable( $editInfo = false ) {
2394  return $this->mPage->isCountable( $editInfo );
2395  }
2396 
2401  public function isRedirect() {
2402  return $this->mPage->isRedirect();
2403  }
2404 
2409  public function loadFromRow( $data, $from ) {
2410  return $this->mPage->loadFromRow( $data, $from );
2411  }
2412 
2417  public function loadPageData( $from = 'fromdb' ) {
2418  $this->mPage->loadPageData( $from );
2419  }
2420 
2425  public function lockAndGetLatest() {
2426  return $this->mPage->lockAndGetLatest();
2427  }
2428 
2433  public function makeParserOptions( $context ) {
2434  return $this->mPage->makeParserOptions( $context );
2435  }
2436 
2441  public function pageDataFromId( $dbr, $id, $options = [] ) {
2442  return $this->mPage->pageDataFromId( $dbr, $id, $options );
2443  }
2444 
2449  public function pageDataFromTitle( $dbr, $title, $options = [] ) {
2450  return $this->mPage->pageDataFromTitle( $dbr, $title, $options );
2451  }
2452 
2457  public function prepareContentForEdit(
2458  Content $content, $revision = null, User $user = null,
2459  $serialFormat = null, $useCache = true
2460  ) {
2461  return $this->mPage->prepareContentForEdit(
2462  $content, $revision, $user,
2463  $serialFormat, $useCache
2464  );
2465  }
2466 
2471  public function protectDescription( array $limit, array $expiry ) {
2472  return $this->mPage->protectDescription( $limit, $expiry );
2473  }
2474 
2479  public function protectDescriptionLog( array $limit, array $expiry ) {
2480  return $this->mPage->protectDescriptionLog( $limit, $expiry );
2481  }
2482 
2487  public function replaceSectionAtRev( $sectionId, Content $sectionContent,
2488  $sectionTitle = '', $baseRevId = null
2489  ) {
2490  return $this->mPage->replaceSectionAtRev( $sectionId, $sectionContent,
2491  $sectionTitle, $baseRevId
2492  );
2493  }
2494 
2499  public function replaceSectionContent(
2500  $sectionId, Content $sectionContent, $sectionTitle = '', $edittime = null
2501  ) {
2502  return $this->mPage->replaceSectionContent(
2503  $sectionId, $sectionContent, $sectionTitle, $edittime
2504  );
2505  }
2506 
2511  public function setTimestamp( $ts ) {
2512  return $this->mPage->setTimestamp( $ts );
2513  }
2514 
2519  public function shouldCheckParserCache( ParserOptions $parserOptions, $oldId ) {
2520  return $this->mPage->shouldCheckParserCache( $parserOptions, $oldId );
2521  }
2522 
2527  public function supportsSections() {
2528  return $this->mPage->supportsSections();
2529  }
2530 
2535  public function triggerOpportunisticLinksUpdate( ParserOutput $parserOutput ) {
2536  return $this->mPage->triggerOpportunisticLinksUpdate( $parserOutput );
2537  }
2538 
2543  public function updateCategoryCounts( array $added, array $deleted, $id = 0 ) {
2544  return $this->mPage->updateCategoryCounts( $added, $deleted, $id );
2545  }
2546 
2551  public function updateIfNewerOn( $dbw, $revision ) {
2552  return $this->mPage->updateIfNewerOn( $dbw, $revision );
2553  }
2554 
2559  public function updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect = null ) {
2560  return $this->mPage->updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect = null );
2561  }
2562 
2567  public function updateRevisionOn( $dbw, $revision, $lastRevision = null,
2568  $lastRevIsRedirect = null
2569  ) {
2570  return $this->mPage->updateRevisionOn( $dbw, $revision, $lastRevision,
2571  $lastRevIsRedirect
2572  );
2573  }
2574 
2583  public function doUpdateRestrictions( array $limit, array $expiry, &$cascade,
2584  $reason, User $user
2585  ) {
2586  return $this->mPage->doUpdateRestrictions( $limit, $expiry, $cascade, $reason, $user );
2587  }
2588 
2596  public function updateRestrictions( $limit = [], $reason = '',
2597  &$cascade = 0, $expiry = []
2598  ) {
2599  return $this->mPage->doUpdateRestrictions(
2600  $limit,
2601  $expiry,
2602  $cascade,
2603  $reason,
2604  $this->getContext()->getUser()
2605  );
2606  }
2607 
2616  public function doDeleteArticle(
2617  $reason, $suppress = false, $u1 = null, $u2 = null, &$error = ''
2618  ) {
2619  return $this->mPage->doDeleteArticle( $reason, $suppress, $u1, $u2, $error );
2620  }
2621 
2631  public function doRollback( $fromP, $summary, $token, $bot, &$resultDetails, User $user = null ) {
2632  $user = is_null( $user ) ? $this->getContext()->getUser() : $user;
2633  return $this->mPage->doRollback( $fromP, $summary, $token, $bot, $resultDetails, $user );
2634  }
2635 
2644  public function commitRollback( $fromP, $summary, $bot, &$resultDetails, User $guser = null ) {
2645  $guser = is_null( $guser ) ? $this->getContext()->getUser() : $guser;
2646  return $this->mPage->commitRollback( $fromP, $summary, $bot, $resultDetails, $guser );
2647  }
2648 
2653  public function generateReason( &$hasHistory ) {
2654  $title = $this->mPage->getTitle();
2656  return $handler->getAutoDeleteReason( $title, $hasHistory );
2657  }
2658 
2664  public static function selectFields() {
2665  wfDeprecated( __METHOD__, '1.24' );
2666  return WikiPage::selectFields();
2667  }
2668 
2674  public static function onArticleCreate( $title ) {
2675  wfDeprecated( __METHOD__, '1.24' );
2677  }
2678 
2684  public static function onArticleDelete( $title ) {
2685  wfDeprecated( __METHOD__, '1.24' );
2687  }
2688 
2694  public static function onArticleEdit( $title ) {
2695  wfDeprecated( __METHOD__, '1.24' );
2697  }
2698 
2699  // ******
2700 }
ReadOnlyError
Show an error when the wiki is locked/read-only and the user tries to do something that requires writ...
Definition: ReadOnlyError.php:28
$wgSend404Code
$wgSend404Code
Some web hosts attempt to rewrite all responses with a 404 (not found) status code,...
Definition: DefaultSettings.php:3476
Revision\FOR_PUBLIC
const FOR_PUBLIC
Definition: Revision.php:98
Article\showMissingArticle
showMissingArticle()
Show the error text for a missing article.
Definition: Article.php:1139
Article\checkFlags
checkFlags( $flags)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2054
ParserOptions
Set options of the Parser.
Definition: ParserOptions.php:40
HTMLFileCache\useFileCache
static useFileCache(IContextSource $context, $mode=self::MODE_NORMAL)
Check if pages can be cached for this request/user.
Definition: HTMLFileCache.php:93
$user
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 account $user
Definition: hooks.txt:244
Page
Interface for type hinting (accepts WikiPage, Article, ImagePage, CategoryPage)
Definition: Page.php:24
Revision\DELETED_RESTRICTED
const DELETED_RESTRICTED
Definition: Revision.php:93
WikiPage\onArticleCreate
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:3272
Article\isRedirect
isRedirect()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2401
Article\getCategories
getCategories()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2181
ParserOutput
Definition: ParserOutput.php:24
Article\formatRobotPolicy
static formatRobotPolicy( $policy)
Converts a String robot policy into an associative array, to allow merging of several policies using ...
Definition: Article.php:823
Article\getRedirectTarget
getRedirectTarget()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2285
Article\view
view()
This is the default action of the index.php entry point: just view the page of the given title.
Definition: Article.php:428
Revision\newFromId
static newFromId( $id, $flags=0)
Load a page revision from a given revision ID number.
Definition: Revision.php:116
Article\doDeleteUpdates
doDeleteUpdates( $id, Content $content=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2091
$context
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 after deleting those rows but within the same transaction you ll probably need to make sure the header is varied on and they can depend only on the ResourceLoaderContext $context
Definition: hooks.txt:2581
Xml\listDropDownOptionsOoui
static listDropDownOptionsOoui( $options)
Convert options for a drop-down box into a format accepted by OOUI\DropdownInputWidget etc.
Definition: Xml.php:581
Article\getContentModel
getContentModel()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2205
Article\getUserText
getUserText( $audience=Revision::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2341
Article\getLinksTimestamp
getLinksTimestamp()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2261
Article\onArticleCreate
static onArticleCreate( $title)
Definition: Article.php:2674
Article\getOldIDFromRequest
getOldIDFromRequest()
Sets $this->mRedirectUrl to a correct URL if the query parameters are incorrect.
Definition: Article.php:255
Article\showPatrolFooter
showPatrolFooter()
If patrol is possible, output a patrol UI box.
Definition: Article.php:966
$lang
if(!isset( $args[0])) $lang
Definition: testCompression.php:33
RecentChange\newFromConds
static newFromConds( $conds, $fname=__METHOD__, $dbType=DB_REPLICA)
Find the first recent change matching some specific conditions.
Definition: RecentChange.php:189
Article\tryFileCache
tryFileCache()
checkLastModified returns true if it has taken care of all output to the client that is necessary for...
Definition: Article.php:1890
Article\getContentHandler
getContentHandler()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2197
Article\clearPreparedEdit
clearPreparedEdit()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2070
HTMLFileCache
Page view caching in the file system.
Definition: HTMLFileCache.php:33
captcha-old.count
count
Definition: captcha-old.py:249
text
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
Article\lockAndGetLatest
lockAndGetLatest()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2425
Article\$mParserOutput
ParserOutput $mParserOutput
Definition: Article.php:76
Article\supportsSections
supportsSections()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2527
Article\getOldestRevision
getOldestRevision()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2277
Article\getDeletionUpdates
getDeletionUpdates(Content $content=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2229
Article\checkTouched
checkTouched()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2062
$status
Status::newGood()` to allow deletion, and then `return false` from the hook function. Ensure you consume the 'ChangeTagAfterDelete' hook to carry out custom deletion actions. $tag:name of the tag $user:user initiating the action & $status:Status object. See above. 'ChangeTagsListActive':Allows you to nominate which of the tags your extension uses are in active use. & $tags:list of all active tags. Append to this array. 'ChangeTagsAfterUpdateTags':Called after tags have been updated with the ChangeTags::updateTags function. Params:$addedTags:tags effectively added in the update $removedTags:tags effectively removed in the update $prevTags:tags that were present prior to the update $rc_id:recentchanges table id $rev_id:revision table id $log_id:logging table id $params:tag params $rc:RecentChange being tagged when the tagging accompanies the action or null $user:User who performed the tagging when the tagging is subsequent to the action or null 'ChangeTagsAllowedAdd':Called when checking if a user can add tags to a change. & $allowedTags:List of all the tags the user is allowed to add. Any tags the user wants to add( $addTags) that are not in this array will cause it to fail. You may add or remove tags to this array as required. $addTags:List of tags user intends to add. $user:User who is adding the tags. 'ChangeUserGroups':Called before user groups are changed. $performer:The User who will perform the change $user:The User whose groups will be changed & $add:The groups that will be added & $remove:The groups that will be removed 'Collation::factory':Called if $wgCategoryCollation is an unknown collation. $collationName:Name of the collation in question & $collationObject:Null. Replace with a subclass of the Collation class that implements the collation given in $collationName. 'ConfirmEmailComplete':Called after a user 's email has been confirmed successfully. $user:user(object) whose email is being confirmed 'ContentAlterParserOutput':Modify parser output for a given content object. Called by Content::getParserOutput after parsing has finished. Can be used for changes that depend on the result of the parsing but have to be done before LinksUpdate is called(such as adding tracking categories based on the rendered HTML). $content:The Content to render $title:Title of the page, as context $parserOutput:ParserOutput to manipulate 'ContentGetParserOutput':Customize parser output for a given content object, called by AbstractContent::getParserOutput. May be used to override the normal model-specific rendering of page content. $content:The Content to render $title:Title of the page, as context $revId:The revision ID, as context $options:ParserOptions for rendering. To avoid confusing the parser cache, the output can only depend on parameters provided to this hook function, not on global state. $generateHtml:boolean, indicating whether full HTML should be generated. If false, generation of HTML may be skipped, but other information should still be present in the ParserOutput object. & $output:ParserOutput, to manipulate or replace 'ContentHandlerDefaultModelFor':Called when the default content model is determined for a given title. May be used to assign a different model for that title. $title:the Title in question & $model:the model name. Use with CONTENT_MODEL_XXX constants. 'ContentHandlerForModelID':Called when a ContentHandler is requested for a given content model name, but no entry for that model exists in $wgContentHandlers. Note:if your extension implements additional models via this hook, please use GetContentModels hook to make them known to core. $modeName:the requested content model name & $handler:set this to a ContentHandler object, if desired. 'ContentModelCanBeUsedOn':Called to determine whether that content model can be used on a given page. This is especially useful to prevent some content models to be used in some special location. $contentModel:ID of the content model in question $title:the Title in question. & $ok:Output parameter, 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. 'ContribsPager::getQueryInfo':Before the contributions query is about to run & $pager:Pager object for contributions & $queryInfo:The query for the contribs Pager 'ContribsPager::reallyDoQuery':Called before really executing the query for My Contributions & $data:an array of results of all contribs queries $pager:The ContribsPager object hooked into $offset:Index offset, inclusive $limit:Exact query limit $descending:Query direction, false for ascending, true for descending 'ContributionsLineEnding':Called before a contributions HTML line is finished $page:SpecialPage object for contributions & $ret:the HTML line $row:the DB row for this line & $classes:the classes to add to the surrounding< li > & $attribs:associative array of other HTML attributes for the< li > element. Currently only data attributes reserved to MediaWiki are allowed(see Sanitizer::isReservedDataAttribute). 'ContributionsToolLinks':Change tool links above Special:Contributions $id:User identifier $title:User page title & $tools:Array of tool links $specialPage:SpecialPage instance for context and services. Can be either SpecialContributions or DeletedContributionsPage. Extensions should type hint against a generic SpecialPage though. 'ConvertContent':Called by AbstractContent::convert when a conversion to another content model is requested. Handler functions that modify $result should generally return false to disable further attempts at conversion. $content:The Content object to be converted. $toModel:The ID of the content model to convert to. $lossy:boolean indicating whether lossy conversion is allowed. & $result:Output parameter, in case the handler function wants to provide a converted Content object. Note that $result->getContentModel() must return $toModel. 'CustomEditor':When invoking the page editor Return true to allow the normal editor to be used, or false if implementing a custom editor, e.g. for a special namespace, etc. $article:Article being edited $user:User performing the edit 'DatabaseOraclePostInit':Called after initialising an Oracle database $db:the DatabaseOracle object 'DeletedContribsPager::reallyDoQuery':Called before really executing the query for Special:DeletedContributions Similar to ContribsPager::reallyDoQuery & $data:an array of results of all contribs queries $pager:The DeletedContribsPager object hooked into $offset:Index offset, inclusive $limit:Exact query limit $descending:Query direction, false for ascending, true for descending 'DeletedContributionsLineEnding':Called before a DeletedContributions HTML line is finished. Similar to ContributionsLineEnding $page:SpecialPage object for DeletedContributions & $ret:the HTML line $row:the DB row for this line & $classes:the classes to add to the surrounding< li > & $attribs:associative array of other HTML attributes for the< li > element. Currently only data attributes reserved to MediaWiki are allowed(see Sanitizer::isReservedDataAttribute). 'DifferenceEngineAfterLoadNewText':called in DifferenceEngine::loadNewText() after the new revision 's content has been loaded into the class member variable $differenceEngine->mNewContent but before returning true from this function. $differenceEngine:DifferenceEngine object 'DifferenceEngineLoadTextAfterNewContentIsLoaded':called in DifferenceEngine::loadText() after the new revision 's content has been loaded into the class member variable $differenceEngine->mNewContent but before checking if the variable 's value is null. This hook can be used to inject content into said class member variable. $differenceEngine:DifferenceEngine object 'DifferenceEngineMarkPatrolledLink':Allows extensions to change the "mark as patrolled" link which is shown both on the diff header as well as on the bottom of a page, usually wrapped in a span element which has class="patrollink". $differenceEngine:DifferenceEngine object & $markAsPatrolledLink:The "mark as patrolled" link HTML(string) $rcid:Recent change ID(rc_id) for this change(int) 'DifferenceEngineMarkPatrolledRCID':Allows extensions to possibly change the rcid parameter. For example the rcid might be set to zero due to the user being the same as the performer of the change but an extension might still want to show it under certain conditions. & $rcid:rc_id(int) of the change or 0 $differenceEngine:DifferenceEngine object $change:RecentChange object $user:User object representing the current user 'DifferenceEngineNewHeader':Allows extensions to change the $newHeader variable, which contains information about the new revision, such as the revision 's author, whether the revision was marked as a minor edit or not, etc. $differenceEngine:DifferenceEngine object & $newHeader:The string containing the various #mw-diff-otitle[1-5] divs, which include things like revision author info, revision comment, RevisionDelete link and more $formattedRevisionTools:Array containing revision tools, some of which may have been injected with the DiffRevisionTools hook $nextlink:String containing the link to the next revision(if any) $status
Definition: hooks.txt:1245
Article\getRevision
getRevision()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2301
RC_LOG
const RC_LOG
Definition: Defines.php:145
CategoryPage
Special handling for category description pages, showing pages, subcategories and file that belong to...
Definition: CategoryPage.php:28
use
as see the revision history and available at free of to any person obtaining a copy of this software and associated documentation to deal in the Software without including without limitation the rights to use
Definition: MIT-LICENSE.txt:10
WikiPage
Class representing a MediaWiki article and history.
Definition: WikiPage.php:37
$fname
if(!defined( 'MEDIAWIKI')) $fname
This file is not a valid entry point, perform no further processing unless MEDIAWIKI is defined.
Definition: Setup.php:36
Article\getRobotPolicy
getRobotPolicy( $action, $pOutput=null)
Get the robot policy to be used for the current view.
Definition: Article.php:743
PoolWorkArticleView
Definition: PoolWorkArticleView.php:22
NS_FILE
const NS_FILE
Definition: Defines.php:71
Article\doDeleteArticle
doDeleteArticle( $reason, $suppress=false, $u1=null, $u2=null, &$error='')
Definition: Article.php:2616
Article\getCreator
getCreator( $audience=Revision::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2221
Block\newFromTarget
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:1112
wfReadOnly
wfReadOnly()
Check whether the wiki is in read-only mode.
Definition: GlobalFunctions.php:1324
User\newFromName
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition: User.php:550
Linker\linkKnown
static linkKnown( $target, $html=null, $customAttribs=[], $query=[], $options=[ 'known'])
Identical to link(), except $options defaults to 'known'.
Definition: Linker.php:164
Article\showDiffPage
showDiffPage()
Show a diff page according to current request variables.
Definition: Article.php:693
ContentHandler\getForTitle
static getForTitle(Title $title)
Returns the appropriate ContentHandler singleton for the given title.
Definition: ContentHandler.php:240
$wgArticleRobotPolicies
$wgArticleRobotPolicies
Robot policies per article.
Definition: DefaultSettings.php:7923
ImagePage
Class for viewing MediaWiki file description pages.
Definition: ImagePage.php:30
PermissionsError
Show an error when a user tries to do something they do not have the necessary permissions for.
Definition: PermissionsError.php:28
Article\confirmDelete
confirmDelete( $reason)
Output deletion confirmation dialog.
Definition: Article.php:1674
Article\$mContext
IContextSource $mContext
The context this Article is executed in.
Definition: Article.php:37
Article\updateCategoryCounts
updateCategoryCounts(array $added, array $deleted, $id=0)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2543
WikiPage\onArticleEdit
static onArticleEdit(Title $title, Revision $revision=null)
Purge caches on page update etc.
Definition: WikiPage.php:3344
Article\adjustDisplayTitle
adjustDisplayTitle(ParserOutput $pOutput)
Adjust title for pages with displaytitle, -{T|}- or language conversion.
Definition: Article.php:681
Article\showNamespaceHeader
showNamespaceHeader()
Show a header specific to the namespace currently being viewed, like [[MediaWiki:Talkpagetext]].
Definition: Article.php:929
Article\shouldCheckParserCache
shouldCheckParserCache(ParserOptions $parserOptions, $oldId)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2519
$wgUseRCPatrol
$wgUseRCPatrol
Use RC Patrolling to check for vandalism (from recent changes and watchlists) New pages and new files...
Definition: DefaultSettings.php:6802
Xml\openElement
static openElement( $element, $attribs=null)
This opens an XML element.
Definition: Xml.php:109
Article\$mContentObject
Content $mContentObject
Content of the revision we are working on.
Definition: Article.php:55
$wgUseNPPatrol
$wgUseNPPatrol
Use new page patrolling to check new pages on Special:Newpages.
Definition: DefaultSettings.php:6834
ProtectionForm
Handles the page protection UI and backend.
Definition: ProtectionForm.php:30
php
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
Article\protectDescriptionLog
protectDescriptionLog(array $limit, array $expiry)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2479
Revision\FOR_THIS_USER
const FOR_THIS_USER
Definition: Revision.php:99
Revision
Definition: Revision.php:33
Article\unprotect
unprotect()
action=unprotect handler (alias)
Definition: Article.php:1539
Article\insertRedirect
insertRedirect()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2377
$query
null for the 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:1581
Article\getRedirectedFrom
getRedirectedFrom()
Get the page this view was redirected from.
Definition: Article.php:156
Article\fetchContentObject
fetchContentObject()
Get text content object Does NOT follow redirects.
Definition: Article.php:313
Article\getLastPurgeTimestamp
getLastPurgeTimestamp()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2132
Article\clear
clear()
Clear the object.
Definition: Article.php:191
Article\updateRedirectOn
updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2559
Article\newFromWikiPage
static newFromWikiPage(WikiPage $page, IContextSource $context)
Create an Article object of the appropriate class for the given page.
Definition: Article.php:145
Article\getTouched
getTouched()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2317
$html
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:1965
Article\onArticleDelete
static onArticleDelete( $title)
Definition: Article.php:2684
MWException
MediaWiki exception.
Definition: MWException.php:26
$title
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:932
Article\getTitle
getTitle()
Get the title object of the article.
Definition: Article.php:174
Article\updateRevisionOn
updateRevisionOn( $dbw, $revision, $lastRevision=null, $lastRevIsRedirect=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2567
Article\render
render()
Handle action=render.
Definition: Article.php:1521
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
Definition: GlobalFunctions.php:1176
Article\insertProtectNullRevision
insertProtectNullRevision( $revCommentMsg, array $limit, array $expiry, $cascade, $reason, $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2365
Article\addHelpLink
addHelpLink( $to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
Definition: Article.php:1504
Article\exists
exists()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2149
Article\setRedirectedFrom
setRedirectedFrom(Title $from)
Tell the page view functions that this view was redirected from another page on the wiki.
Definition: Article.php:165
$wgUseFileCache
$wgUseFileCache
This will cache static pages for non-logged-in users to reduce database traffic on public sites.
Definition: DefaultSettings.php:2554
WikiPage\selectFields
static selectFields()
Return the list of revision fields that should be selected to create a new page.
Definition: WikiPage.php:288
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2856
Linker\revUserTools
static revUserTools( $rev, $isPublic=false)
Generate a user tool link cluster if the current user is allowed to view it.
Definition: Linker.php:1060
Article\__construct
__construct(Title $title, $oldId=null)
Constructor and clear the article.
Definition: Article.php:83
in
null for the wiki Added in
Definition: hooks.txt:1581
$article
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:77
Article\showDeletedRevisionHeader
showDeletedRevisionHeader()
If the revision requested for view is deleted, check permissions.
Definition: Article.php:1264
IExpiringStore\TTL_DAY
const TTL_DAY
Definition: IExpiringStore.php:35
Article\isFileCacheable
isFileCacheable( $mode=HTMLFileCache::MODE_NORMAL)
Check if the page can be cached.
Definition: Article.php:1921
Article\doRollback
doRollback( $fromP, $summary, $token, $bot, &$resultDetails, User $user=null)
Definition: Article.php:2631
not
if not
Definition: COPYING.txt:307
WikiPage\getTitle
getTitle()
Get the title object of the article.
Definition: WikiPage.php:239
LogPage
Class to simplify the use of log pages.
Definition: LogPage.php:31
Article\setOldSubtitle
setOldSubtitle( $oldid=0)
Generate the navigation links when browsing through an article revisions It shows the information as:...
Definition: Article.php:1307
WikiPage\onArticleDelete
static onArticleDelete(Title $title)
Clears caches when article is deleted.
Definition: WikiPage.php:3301
Xml\element
static element( $element, $attribs=null, $contents='', $allowShortTag=true)
Format an XML element with given attributes and, optionally, text content.
Definition: Xml.php:39
Article\getPage
getPage()
Get the WikiPage object of this instance.
Definition: Article.php:184
Article\getContext
getContext()
Gets the context this Article is executed in.
Definition: Article.php:2006
User\isIP
static isIP( $name)
Does the string match an anonymous IP address?
Definition: User.php:825
WatchAction\doWatchOrUnwatch
static doWatchOrUnwatch( $watch, Title $title, User $user)
Watch or unwatch a page.
Definition: WatchAction.php:93
Article\getComment
getComment( $audience=Revision::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2189
Article\pageDataFromId
pageDataFromId( $dbr, $id, $options=[])
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2441
Article\insertOn
insertOn( $dbw, $pageId=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2357
Linker\makeExternalLink
static makeExternalLink( $url, $text, $escape=true, $linktype='', $attribs=[], $title=null)
Make an external link.
Definition: Linker.php:843
Title\makeTitle
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:529
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:93
Article\getRedirectURL
getRedirectURL( $rt)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2293
LogEventsList\showLogExtract
static showLogExtract(&$out, $types=[], $page='', $user='', $param=[])
Show log extract.
Definition: LogEventsList.php:594
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
Article\newFromID
static newFromID( $id)
Constructor from a page id.
Definition: Article.php:101
Article\newPage
newPage(Title $title)
Definition: Article.php:92
NS_CATEGORY
const NS_CATEGORY
Definition: Defines.php:79
Article\getUndoContent
getUndoContent(Revision $undo, Revision $undoafter=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2325
Article\prepareContentForEdit
prepareContentForEdit(Content $content, $revision=null, User $user=null, $serialFormat=null, $useCache=true)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2457
$wgDeleteRevisionsLimit
$wgDeleteRevisionsLimit
Optional to restrict deletion of pages with higher revision counts to users with the 'bigdelete' perm...
Definition: DefaultSettings.php:5534
Article\$mRevIdFetched
int $mRevIdFetched
Revision ID of revision we are working on.
Definition: Article.php:70
Article\doEditContent
doEditContent(Content $content, $summary, $flags=0, $baseRevId=false, User $user=null, $serialFormat=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2100
$wgUseFilePatrol
$wgUseFilePatrol
Use file patrolling to check new files on Special:Newfiles.
Definition: DefaultSettings.php:6841
Article\getLatest
getLatest()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2253
wfDebug
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Definition: GlobalFunctions.php:1047
Article\isCurrent
isCurrent()
Returns true if the currently-referenced revision is the current edit to this page (and it exists).
Definition: Article.php:389
list
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
$dir
$dir
Definition: Autoload.php:8
ContentHandler\makeContent
static makeContent( $text, Title $title=null, $modelId=null, $format=null)
Convenience function for creating a Content object from a given textual representation.
Definition: ContentHandler.php:129
Article\getAutoDeleteReason
getAutoDeleteReason(&$hasHistory)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2173
Article\$mContentLoaded
bool $mContentLoaded
Is the content ($mContent) already loaded?
Definition: Article.php:58
Article\setParserOptions
setParserOptions(ParserOptions $options)
Override the ParserOptions used to render the primary article wikitext.
Definition: Article.php:1969
Article\isCountable
isCountable( $editInfo=false)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2393
Article\$mRedirectedFrom
Title $mRedirectedFrom
Title from which we were redirected here.
Definition: Article.php:64
$request
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 after deleting those rows but within the same transaction you ll probably need to make sure the header is varied on $request
Definition: hooks.txt:2581
Article\doDelete
doDelete( $reason, $suppress=false)
Perform a deletion and output success or failure messages.
Definition: Article.php:1837
Html\hidden
static hidden( $name, $value, array $attribs=[])
Convenience function to produce an input element with type=hidden.
Definition: Html.php:725
ParserOutput\getTitleText
getTitleText()
Definition: ParserOutput.php:334
Article\doPurge
doPurge()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2123
Linker\revComment
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:1470
NS_USER_TALK
const NS_USER_TALK
Definition: Defines.php:68
$e
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException' returning false will NOT prevent logging $e
Definition: hooks.txt:2141
Article\hasViewableContent
hasViewableContent()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2349
Article\$mOldId
int null $mOldId
The oldid of the article that is to be shown, 0 for the current revision.
Definition: Article.php:61
NS_MEDIA
const NS_MEDIA
Definition: Defines.php:53
Article\getContributors
getContributors()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2213
HTMLFileCache\MODE_NORMAL
const MODE_NORMAL
Definition: HTMLFileCache.php:34
Article\loadFromRow
loadFromRow( $data, $from)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2409
Linker\getRevDeleteLink
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:2030
Article\onArticleEdit
static onArticleEdit( $title)
Definition: Article.php:2694
Article\protect
protect()
action=protect handler
Definition: Article.php:1531
Article\getTimestamp
getTimestamp()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2309
Linker\link
static link( $target, $html=null, $customAttribs=[], $query=[], $options=[])
This function returns an HTML link to the given target.
Definition: Linker.php:107
wfEscapeWikiText
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
Definition: GlobalFunctions.php:1703
Article\getMinorEdit
getMinorEdit()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2269
Article\commitRollback
commitRollback( $fromP, $summary, $bot, &$resultDetails, User $guser=null)
Definition: Article.php:2644
$wgNamespaceRobotPolicies
$wgNamespaceRobotPolicies
Robot policies per namespaces.
Definition: DefaultSettings.php:7895
Article\getParserOutput
getParserOutput( $oldid=null, User $user=null)
#-
Definition: Article.php:1951
Article\$mParserOptions
ParserOptions $mParserOptions
ParserOptions object for $wgUser articles.
Definition: Article.php:43
Article\getUser
getUser( $audience=Revision::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2333
Article\getContentObject
getContentObject()
Returns a Content object representing the pages effective display content, not necessarily the revisi...
Definition: Article.php:216
Article\followRedirect
followRedirect()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2157
Block\TYPE_AUTO
const TYPE_AUTO
Definition: Block.php:86
Article\getHiddenCategories
getHiddenCategories()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2237
$handler
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:781
RequestContext\getMain
static getMain()
Static methods.
Definition: RequestContext.php:470
IP\isValid
static isValid( $ip)
Validate an IP address.
Definition: IP.php:111
Article\setTimestamp
setTimestamp( $ts)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2511
IContextSource
Interface for objects which can provide a MediaWiki context on request.
Definition: IContextSource.php:55
Article\__get
__get( $fname)
Use PHP's magic __get handler to handle accessing of raw WikiPage fields for backwards compatibility.
Definition: Article.php:2023
Content
Base interface for content objects.
Definition: Content.php:34
Article\replaceSectionAtRev
replaceSectionAtRev( $sectionId, Content $sectionContent, $sectionTitle='', $baseRevId=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2487
$unhide
null for the 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:1581
Title
Represents a title within MediaWiki.
Definition: Title.php:39
Article\selectFields
static selectFields()
Definition: Article.php:2664
Skin\makeUrl
static makeUrl( $name, $urlaction='')
Definition: Skin.php:1148
$dbr
if(! $regexes) $dbr
Definition: cleanup.php:94
$cache
$cache
Definition: mcc.php:33
$options
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 & $options
Definition: hooks.txt:1965
Article\replaceSectionContent
replaceSectionContent( $sectionId, Content $sectionContent, $sectionTitle='', $edittime=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2499
Article\getRevIdFetched
getRevIdFetched()
Use this to fetch the rev ID used on page views.
Definition: Article.php:416
MessageContent
Wrapper allowing us to handle a system message as a Content object.
Definition: MessageContent.php:36
$wgEnableWriteAPI
$wgEnableWriteAPI
Allow the API to be used to perform write operations (page edits, rollback, etc.) when an authorised ...
Definition: DefaultSettings.php:7960
Article\makeParserOptions
makeParserOptions( $context)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2433
Article\showRedirectedFromHeader
showRedirectedFromHeader()
If this request is a redirect view, send "redirected from" subtitle to the output.
Definition: Article.php:852
$rev
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:1750
as
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
Block
Definition: Block.php:27
RecentChange\isInRCLifespan
static isInRCLifespan( $timestamp, $tolerance=0)
Check whether the given timestamp is new enough to have a RC row with a given tolerance as the recent...
Definition: RecentChange.php:1050
NS_USER
const NS_USER
Definition: Defines.php:67
$link
usually copyright or history_copyright This message must be in HTML not wikitext & $link
Definition: hooks.txt:2981
Article\getId
getId()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2245
Article\getActionOverrides
getActionOverrides()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2165
Article\$mRedirectUrl
string bool $mRedirectUrl
URL to redirect to or false if none.
Definition: Article.php:67
Article\doUpdateRestrictions
doUpdateRestrictions(array $limit, array $expiry, &$cascade, $reason, User $user)
Definition: Article.php:2583
Article\updateRestrictions
updateRestrictions( $limit=[], $reason='', &$cascade=0, $expiry=[])
Definition: Article.php:2596
Article\$mRevision
Revision $mRevision
Revision we are working on.
Definition: Article.php:73
Article\protectDescription
protectDescription(array $limit, array $expiry)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2471
Article\getParserOptions
getParserOptions()
Get parser options suitable for rendering the primary article wikitext.
Definition: Article.php:1982
$wgRedirectSources
$wgRedirectSources
If local interwikis are set up which allow redirects, set this regexp to restrict URLs which will be ...
Definition: DefaultSettings.php:4041
wfMessage
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 unset offset - wrap String Wrap the message in html(usually something like "&lt
Article\updateIfNewerOn
updateIfNewerOn( $dbw, $revision)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2551
NS_MEDIAWIKI
const NS_MEDIAWIKI
Definition: Defines.php:73
$t
$t
Definition: testCompression.php:67
Article
Class for viewing MediaWiki article and history.
Definition: Article.php:35
Article\pageDataFromTitle
pageDataFromTitle( $dbr, $title, $options=[])
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2449
MediaWikiServices
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
$wgDebugToolbar
$wgDebugToolbar
Display the new debugging toolbar.
Definition: DefaultSettings.php:6398
Article\triggerOpportunisticLinksUpdate
triggerOpportunisticLinksUpdate(ParserOutput $parserOutput)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2535
$wgEnableAPI
$wgEnableAPI
Enable the MediaWiki API for convenient access to machine-readable data via api.php.
Definition: DefaultSettings.php:7953
Article\getOldID
getOldID()
Definition: Article.php:242
EditPage\POST_EDIT_COOKIE_KEY_PREFIX
const POST_EDIT_COOKIE_KEY_PREFIX
Prefix of key for cookie used to pass post-edit state.
Definition: EditPage.php:199
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:51
Article\setContext
setContext( $context)
Sets the context this Article is executed in.
Definition: Article.php:1996
Title\newFromID
static newFromID( $id, $flags=0)
Create a new Title from an article ID.
Definition: Title.php:411
Hooks\run
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:203
Article\insertRedirectEntry
insertRedirectEntry(Title $rt, $oldLatest=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2385
$wgDefaultRobotPolicy
$wgDefaultRobotPolicy
Default robot policy.
Definition: DefaultSettings.php:7879
Article\getRevisionFetched
getRevisionFetched()
Get the fetched Revision object depending on request parameters or null on failure.
Definition: Article.php:405
MWNamespace\getCanonicalName
static getCanonicalName( $index)
Returns the canonical (English) name for a given index.
Definition: MWNamespace.php:228
Article\viewRedirect
viewRedirect( $target, $appendSubtitle=true, $forceKnown=false)
Return the HTML for the top of a redirect page.
Definition: Article.php:1447
Revision\DELETED_TEXT
const DELETED_TEXT
Definition: Revision.php:90
Article\loadPageData
loadPageData( $from='fromdb')
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2417
Language
Internationalisation code.
Definition: Language.php:35
Article\doEditUpdates
doEditUpdates(Revision $revision, User $user, array $options=[])
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2113
$flags
it s the revision text itself In either if gzip is the revision text is gzipped $flags
Definition: hooks.txt:2801
Article\$mContent
string $mContent
Text of the revision we are working on.
Definition: Article.php:49
Article\__set
__set( $fname, $fvalue)
Use PHP's magic __set handler to handle setting of raw WikiPage fields for backwards compatibility.
Definition: Article.php:2038
Article\showViewFooter
showViewFooter()
Show the footer section of an ordinary page view.
Definition: Article.php:943
Article\$mPage
WikiPage $mPage
The WikiPage object of this instance.
Definition: Article.php:40
array
the array() calling protocol came about after MediaWiki 1.4rc1.
Article\getRedirectHeaderHtml
static getRedirectHeaderHtml(Language $lang, $target, $forceKnown=false)
Return the HTML for the top of a redirect page.
Definition: Article.php:1469
Article\doDeleteArticleReal
doDeleteArticleReal( $reason, $suppress=false, $u1=null, $u2=null, &$error='', User $user=null, $tags=[])
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2078
Xml\listDropDownOptions
static listDropDownOptions( $list, $params=[])
Build options for a drop-down box from a textual list.
Definition: Xml.php:539
Article\newFromTitle
static newFromTitle( $title, IContextSource $context)
Create an Article object of the appropriate class for the given page.
Definition: Article.php:113
Article\generateReason
generateReason(&$hasHistory)
Definition: Article.php:2653
Article\purgePatrolFooterCache
static purgePatrolFooterCache( $articleID)
Purge the cache used to check if it is worth showing the patrol footer For example,...
Definition: Article.php:1130
Article\doViewUpdates
doViewUpdates(User $user, $oldid=0)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2141
$out
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:781