MediaWiki  REL1_31
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 
84 
90  public function __construct( Title $title, $oldId = null ) {
91  $this->mOldId = $oldId;
92  $this->mPage = $this->newPage( $title );
93  }
94 
99  protected function newPage( Title $title ) {
100  return new WikiPage( $title );
101  }
102 
108  public static function newFromID( $id ) {
109  $t = Title::newFromID( $id );
110  return $t == null ? null : new static( $t );
111  }
112 
120  public static function newFromTitle( $title, IContextSource $context ) {
121  if ( NS_MEDIA == $title->getNamespace() ) {
122  // FIXME: where should this go?
123  $title = Title::makeTitle( NS_FILE, $title->getDBkey() );
124  }
125 
126  $page = null;
127  Hooks::run( 'ArticleFromTitle', [ &$title, &$page, $context ] );
128  if ( !$page ) {
129  switch ( $title->getNamespace() ) {
130  case NS_FILE:
131  $page = new ImagePage( $title );
132  break;
133  case NS_CATEGORY:
134  $page = new CategoryPage( $title );
135  break;
136  default:
137  $page = new Article( $title );
138  }
139  }
140  $page->setContext( $context );
141 
142  return $page;
143  }
144 
152  public static function newFromWikiPage( WikiPage $page, IContextSource $context ) {
154  $article->mPage = $page; // override to keep process cached vars
155  return $article;
156  }
157 
163  public function getRedirectedFrom() {
164  return $this->mRedirectedFrom;
165  }
166 
172  public function setRedirectedFrom( Title $from ) {
173  $this->mRedirectedFrom = $from;
174  }
175 
181  public function getTitle() {
182  return $this->mPage->getTitle();
183  }
184 
191  public function getPage() {
192  return $this->mPage;
193  }
194 
198  public function clear() {
199  $this->mContentLoaded = false;
200 
201  $this->mRedirectedFrom = null; # Title object if set
202  $this->mRevIdFetched = 0;
203  $this->mRedirectUrl = false;
204 
205  $this->mPage->clear();
206  }
207 
223  protected function getContentObject() {
224  if ( $this->mPage->getId() === 0 ) {
225  # If this is a MediaWiki:x message, then load the messages
226  # and return the message value for x.
227  if ( $this->getTitle()->getNamespace() == NS_MEDIAWIKI ) {
228  $text = $this->getTitle()->getDefaultMessageText();
229  if ( $text === false ) {
230  $text = '';
231  }
232 
233  $content = ContentHandler::makeContent( $text, $this->getTitle() );
234  } else {
235  $message = $this->getContext()->getUser()->isLoggedIn() ? 'noarticletext' : 'noarticletextanon';
236  $content = new MessageContent( $message, null, 'parsemag' );
237  }
238  } else {
239  $this->fetchContentObject();
240  $content = $this->mContentObject;
241  }
242 
243  return $content;
244  }
245 
249  public function getOldID() {
250  if ( is_null( $this->mOldId ) ) {
251  $this->mOldId = $this->getOldIDFromRequest();
252  }
253 
254  return $this->mOldId;
255  }
256 
262  public function getOldIDFromRequest() {
263  $this->mRedirectUrl = false;
264 
265  $request = $this->getContext()->getRequest();
266  $oldid = $request->getIntOrNull( 'oldid' );
267 
268  if ( $oldid === null ) {
269  return 0;
270  }
271 
272  if ( $oldid !== 0 ) {
273  # Load the given revision and check whether the page is another one.
274  # In that case, update this instance to reflect the change.
275  if ( $oldid === $this->mPage->getLatest() ) {
276  $this->mRevision = $this->mPage->getRevision();
277  } else {
278  $this->mRevision = Revision::newFromId( $oldid );
279  if ( $this->mRevision !== null ) {
280  // Revision title doesn't match the page title given?
281  if ( $this->mPage->getId() != $this->mRevision->getPage() ) {
282  $function = [ get_class( $this->mPage ), 'newFromID' ];
283  $this->mPage = call_user_func( $function, $this->mRevision->getPage() );
284  }
285  }
286  }
287  }
288 
289  if ( $request->getVal( 'direction' ) == 'next' ) {
290  $nextid = $this->getTitle()->getNextRevisionID( $oldid );
291  if ( $nextid ) {
292  $oldid = $nextid;
293  $this->mRevision = null;
294  } else {
295  $this->mRedirectUrl = $this->getTitle()->getFullURL( 'redirect=no' );
296  }
297  } elseif ( $request->getVal( 'direction' ) == 'prev' ) {
298  $previd = $this->getTitle()->getPreviousRevisionID( $oldid );
299  if ( $previd ) {
300  $oldid = $previd;
301  $this->mRevision = null;
302  }
303  }
304 
305  return $oldid;
306  }
307 
320  protected function fetchContentObject() {
321  if ( $this->mContentLoaded ) {
322  return $this->mContentObject;
323  }
324 
325  $this->mContentLoaded = true;
326  $this->mContent = null;
327 
328  $oldid = $this->getOldID();
329 
330  # Pre-fill content with error message so that if something
331  # fails we'll have something telling us what we intended.
332  // XXX: this isn't page content but a UI message. horrible.
333  $this->mContentObject = new MessageContent( 'missing-revision', [ $oldid ] );
334 
335  if ( $oldid ) {
336  # $this->mRevision might already be fetched by getOldIDFromRequest()
337  if ( !$this->mRevision ) {
338  $this->mRevision = Revision::newFromId( $oldid );
339  if ( !$this->mRevision ) {
340  wfDebug( __METHOD__ . " failed to retrieve specified revision, id $oldid\n" );
341  return false;
342  }
343  }
344  } else {
345  $oldid = $this->mPage->getLatest();
346  if ( !$oldid ) {
347  wfDebug( __METHOD__ . " failed to find page data for title " .
348  $this->getTitle()->getPrefixedText() . "\n" );
349  return false;
350  }
351 
352  # Update error message with correct oldid
353  $this->mContentObject = new MessageContent( 'missing-revision', [ $oldid ] );
354 
355  $this->mRevision = $this->mPage->getRevision();
356 
357  if ( !$this->mRevision ) {
358  wfDebug( __METHOD__ . " failed to retrieve current page, rev_id $oldid\n" );
359  return false;
360  }
361  }
362 
363  // @todo FIXME: Horrible, horrible! This content-loading interface just plain sucks.
364  // We should instead work with the Revision object when we need it...
365  // Loads if user is allowed
366  $content = $this->mRevision->getContent(
368  $this->getContext()->getUser()
369  );
370 
371  if ( !$content ) {
372  wfDebug( __METHOD__ . " failed to retrieve content of revision " .
373  $this->mRevision->getId() . "\n" );
374  return false;
375  }
376 
377  $this->mContentObject = $content;
378  $this->mRevIdFetched = $this->mRevision->getId();
379 
380  // Avoid PHP 7.1 warning of passing $this by reference
381  $articlePage = $this;
382 
383  Hooks::run(
384  'ArticleAfterFetchContentObject',
385  [ &$articlePage, &$this->mContentObject ]
386  );
387 
388  return $this->mContentObject;
389  }
390 
396  public function isCurrent() {
397  # If no oldid, this is the current version.
398  if ( $this->getOldID() == 0 ) {
399  return true;
400  }
401 
402  return $this->mPage->exists() && $this->mRevision && $this->mRevision->isCurrent();
403  }
404 
412  public function getRevisionFetched() {
413  $this->fetchContentObject();
414 
415  return $this->mRevision;
416  }
417 
423  public function getRevIdFetched() {
424  if ( $this->mRevIdFetched ) {
425  return $this->mRevIdFetched;
426  } else {
427  return $this->mPage->getLatest();
428  }
429  }
430 
435  public function view() {
437 
438  # Get variables from query string
439  # As side effect this will load the revision and update the title
440  # in a revision ID is passed in the request, so this should remain
441  # the first call of this method even if $oldid is used way below.
442  $oldid = $this->getOldID();
443 
444  $user = $this->getContext()->getUser();
445  # Another whitelist check in case getOldID() is altering the title
446  $permErrors = $this->getTitle()->getUserPermissionsErrors( 'read', $user );
447  if ( count( $permErrors ) ) {
448  wfDebug( __METHOD__ . ": denied on secondary read check\n" );
449  throw new PermissionsError( 'read', $permErrors );
450  }
451 
452  $outputPage = $this->getContext()->getOutput();
453  # getOldID() may as well want us to redirect somewhere else
454  if ( $this->mRedirectUrl ) {
455  $outputPage->redirect( $this->mRedirectUrl );
456  wfDebug( __METHOD__ . ": redirecting due to oldid\n" );
457 
458  return;
459  }
460 
461  # If we got diff in the query, we want to see a diff page instead of the article.
462  if ( $this->getContext()->getRequest()->getCheck( 'diff' ) ) {
463  wfDebug( __METHOD__ . ": showing diff page\n" );
464  $this->showDiffPage();
465 
466  return;
467  }
468 
469  # Set page title (may be overridden by DISPLAYTITLE)
470  $outputPage->setPageTitle( $this->getTitle()->getPrefixedText() );
471 
472  $outputPage->setArticleFlag( true );
473  # Allow frames by default
474  $outputPage->allowClickjacking();
475 
476  $parserCache = MediaWikiServices::getInstance()->getParserCache();
477 
478  $parserOptions = $this->getParserOptions();
479  $poOptions = [];
480  # Render printable version, use printable version cache
481  if ( $outputPage->isPrintable() ) {
482  $parserOptions->setIsPrintable( true );
483  $poOptions['enableSectionEditLinks'] = false;
484  } elseif ( $this->disableSectionEditForRender
485  || !$this->isCurrent() || !$this->getTitle()->quickUserCan( 'edit', $user )
486  ) {
487  $poOptions['enableSectionEditLinks'] = false;
488  }
489 
490  # Try client and file cache
491  if ( !$wgDebugToolbar && $oldid === 0 && $this->mPage->checkTouched() ) {
492  # Try to stream the output from file cache
493  if ( $wgUseFileCache && $this->tryFileCache() ) {
494  wfDebug( __METHOD__ . ": done file cache\n" );
495  # tell wgOut that output is taken care of
496  $outputPage->disable();
497  $this->mPage->doViewUpdates( $user, $oldid );
498 
499  return;
500  }
501  }
502 
503  # Should the parser cache be used?
504  $useParserCache = $this->mPage->shouldCheckParserCache( $parserOptions, $oldid );
505  wfDebug( 'Article::view using parser cache: ' . ( $useParserCache ? 'yes' : 'no' ) . "\n" );
506  if ( $user->getStubThreshold() ) {
507  MediaWikiServices::getInstance()->getStatsdDataFactory()->increment( 'pcache_miss_stub' );
508  }
509 
510  $this->showRedirectedFromHeader();
511  $this->showNamespaceHeader();
512 
513  # Iterate through the possible ways of constructing the output text.
514  # Keep going until $outputDone is set, or we run out of things to do.
515  $pass = 0;
516  $outputDone = false;
517  $this->mParserOutput = false;
518 
519  while ( !$outputDone && ++$pass ) {
520  switch ( $pass ) {
521  case 1:
522  // Avoid PHP 7.1 warning of passing $this by reference
523  $articlePage = $this;
524  Hooks::run( 'ArticleViewHeader', [ &$articlePage, &$outputDone, &$useParserCache ] );
525  break;
526  case 2:
527  # Early abort if the page doesn't exist
528  if ( !$this->mPage->exists() ) {
529  wfDebug( __METHOD__ . ": showing missing article\n" );
530  $this->showMissingArticle();
531  $this->mPage->doViewUpdates( $user );
532  return;
533  }
534 
535  # Try the parser cache
536  if ( $useParserCache ) {
537  $this->mParserOutput = $parserCache->get( $this->mPage, $parserOptions );
538 
539  if ( $this->mParserOutput !== false ) {
540  if ( $oldid ) {
541  wfDebug( __METHOD__ . ": showing parser cache contents for current rev permalink\n" );
542  $this->setOldSubtitle( $oldid );
543  } else {
544  wfDebug( __METHOD__ . ": showing parser cache contents\n" );
545  }
546  $outputPage->addParserOutput( $this->mParserOutput, $poOptions );
547  # Ensure that UI elements requiring revision ID have
548  # the correct version information.
549  $outputPage->setRevisionId( $this->mPage->getLatest() );
550  # Preload timestamp to avoid a DB hit
551  $cachedTimestamp = $this->mParserOutput->getTimestamp();
552  if ( $cachedTimestamp !== null ) {
553  $outputPage->setRevisionTimestamp( $cachedTimestamp );
554  $this->mPage->setTimestamp( $cachedTimestamp );
555  }
556  $outputDone = true;
557  }
558  }
559  break;
560  case 3:
561  # This will set $this->mRevision if needed
562  $this->fetchContentObject();
563 
564  # Are we looking at an old revision
565  if ( $oldid && $this->mRevision ) {
566  $this->setOldSubtitle( $oldid );
567 
568  if ( !$this->showDeletedRevisionHeader() ) {
569  wfDebug( __METHOD__ . ": cannot view deleted revision\n" );
570  return;
571  }
572  }
573 
574  # Ensure that UI elements requiring revision ID have
575  # the correct version information.
576  $outputPage->setRevisionId( $this->getRevIdFetched() );
577  # Preload timestamp to avoid a DB hit
578  $outputPage->setRevisionTimestamp( $this->mPage->getTimestamp() );
579 
580  # Pages containing custom CSS or JavaScript get special treatment
581  if ( $this->getTitle()->isSiteConfigPage() || $this->getTitle()->isUserConfigPage() ) {
582  $dir = $this->getContext()->getLanguage()->getDir();
583  $lang = $this->getContext()->getLanguage()->getHtmlCode();
584 
585  $outputPage->wrapWikiMsg(
586  "<div id='mw-clearyourcache' lang='$lang' dir='$dir' class='mw-content-$dir'>\n$1\n</div>",
587  'clearyourcache'
588  );
589  } elseif ( !Hooks::run( 'ArticleContentViewCustom',
590  [ $this->fetchContentObject(), $this->getTitle(), $outputPage ] )
591  ) {
592  # Allow extensions do their own custom view for certain pages
593  $outputDone = true;
594  }
595  break;
596  case 4:
597  # Run the parse, protected by a pool counter
598  wfDebug( __METHOD__ . ": doing uncached parse\n" );
599 
600  $content = $this->getContentObject();
601  $poolArticleView = new PoolWorkArticleView( $this->getPage(), $parserOptions,
602  $this->getRevIdFetched(), $useParserCache, $content );
603 
604  if ( !$poolArticleView->execute() ) {
605  $error = $poolArticleView->getError();
606  if ( $error ) {
607  $outputPage->clearHTML(); // for release() errors
608  $outputPage->enableClientCache( false );
609  $outputPage->setRobotPolicy( 'noindex,nofollow' );
610 
611  $errortext = $error->getWikiText( false, 'view-pool-error' );
612  $outputPage->addWikiText( Html::errorBox( $errortext ) );
613  }
614  # Connection or timeout error
615  return;
616  }
617 
618  $this->mParserOutput = $poolArticleView->getParserOutput();
619  $outputPage->addParserOutput( $this->mParserOutput, $poOptions );
620  if ( $content->getRedirectTarget() ) {
621  $outputPage->addSubtitle( "<span id=\"redirectsub\">" .
622  $this->getContext()->msg( 'redirectpagesub' )->parse() . "</span>" );
623  }
624 
625  # Don't cache a dirty ParserOutput object
626  if ( $poolArticleView->getIsDirty() ) {
627  $outputPage->setCdnMaxage( 0 );
628  $outputPage->addHTML( "<!-- parser cache is expired, " .
629  "sending anyway due to pool overload-->\n" );
630  }
631 
632  $outputDone = true;
633  break;
634  # Should be unreachable, but just in case...
635  default:
636  break 2;
637  }
638  }
639 
640  # Get the ParserOutput actually *displayed* here.
641  # Note that $this->mParserOutput is the *current*/oldid version output.
642  $pOutput = ( $outputDone instanceof ParserOutput )
643  ? $outputDone // object fetched by hook
644  : $this->mParserOutput;
645 
646  # Adjust title for main page & pages with displaytitle
647  if ( $pOutput ) {
648  $this->adjustDisplayTitle( $pOutput );
649  }
650 
651  # For the main page, overwrite the <title> element with the con-
652  # tents of 'pagetitle-view-mainpage' instead of the default (if
653  # that's not empty).
654  # This message always exists because it is in the i18n files
655  if ( $this->getTitle()->isMainPage() ) {
656  $msg = wfMessage( 'pagetitle-view-mainpage' )->inContentLanguage();
657  if ( !$msg->isDisabled() ) {
658  $outputPage->setHTMLTitle( $msg->title( $this->getTitle() )->text() );
659  }
660  }
661 
662  # Use adaptive TTLs for CDN so delayed/failed purges are noticed less often.
663  # This could use getTouched(), but that could be scary for major template edits.
664  $outputPage->adaptCdnTTL( $this->mPage->getTimestamp(), IExpiringStore::TTL_DAY );
665 
666  # Check for any __NOINDEX__ tags on the page using $pOutput
667  $policy = $this->getRobotPolicy( 'view', $pOutput );
668  $outputPage->setIndexPolicy( $policy['index'] );
669  $outputPage->setFollowPolicy( $policy['follow'] );
670 
671  $this->showViewFooter();
672  $this->mPage->doViewUpdates( $user, $oldid );
673 
674  # Load the postEdit module if the user just saved this revision
675  # See also EditPage::setPostEditCookie
676  $request = $this->getContext()->getRequest();
678  $postEdit = $request->getCookie( $cookieKey );
679  if ( $postEdit ) {
680  # Clear the cookie. This also prevents caching of the response.
681  $request->response()->clearCookie( $cookieKey );
682  $outputPage->addJsConfigVars( 'wgPostEdit', $postEdit );
683  $outputPage->addModules( 'mediawiki.action.view.postEdit' );
684  }
685  }
686 
691  public function adjustDisplayTitle( ParserOutput $pOutput ) {
692  # Adjust the title if it was set by displaytitle, -{T|}- or language conversion
693  $titleText = $pOutput->getTitleText();
694  if ( strval( $titleText ) !== '' ) {
695  $this->getContext()->getOutput()->setPageTitle( $titleText );
696  }
697  }
698 
703  protected function showDiffPage() {
704  $request = $this->getContext()->getRequest();
705  $user = $this->getContext()->getUser();
706  $diff = $request->getVal( 'diff' );
707  $rcid = $request->getVal( 'rcid' );
708  $diffOnly = $request->getBool( 'diffonly', $user->getOption( 'diffonly' ) );
709  $purge = $request->getVal( 'action' ) == 'purge';
710  $unhide = $request->getInt( 'unhide' ) == 1;
711  $oldid = $this->getOldID();
712 
713  $rev = $this->getRevisionFetched();
714 
715  if ( !$rev ) {
716  $this->getContext()->getOutput()->setPageTitle( wfMessage( 'errorpagetitle' ) );
717  $msg = $this->getContext()->msg( 'difference-missing-revision' )
718  ->params( $oldid )
719  ->numParams( 1 )
720  ->parseAsBlock();
721  $this->getContext()->getOutput()->addHTML( $msg );
722  return;
723  }
724 
725  $contentHandler = $rev->getContentHandler();
726  $de = $contentHandler->createDifferenceEngine(
727  $this->getContext(),
728  $oldid,
729  $diff,
730  $rcid,
731  $purge,
732  $unhide
733  );
734 
735  // DifferenceEngine directly fetched the revision:
736  $this->mRevIdFetched = $de->mNewid;
737  $de->showDiffPage( $diffOnly );
738 
739  // Run view updates for the newer revision being diffed (and shown
740  // below the diff if not $diffOnly).
741  list( $old, $new ) = $de->mapDiffPrevNext( $oldid, $diff );
742  // New can be false, convert it to 0 - this conveniently means the latest revision
743  $this->mPage->doViewUpdates( $user, (int)$new );
744  }
745 
753  public function getRobotPolicy( $action, $pOutput = null ) {
755 
756  $ns = $this->getTitle()->getNamespace();
757 
758  # Don't index user and user talk pages for blocked users (T13443)
759  if ( ( $ns == NS_USER || $ns == NS_USER_TALK ) && !$this->getTitle()->isSubpage() ) {
760  $specificTarget = null;
761  $vagueTarget = null;
762  $titleText = $this->getTitle()->getText();
763  if ( IP::isValid( $titleText ) ) {
764  $vagueTarget = $titleText;
765  } else {
766  $specificTarget = $titleText;
767  }
768  if ( Block::newFromTarget( $specificTarget, $vagueTarget ) instanceof Block ) {
769  return [
770  'index' => 'noindex',
771  'follow' => 'nofollow'
772  ];
773  }
774  }
775 
776  if ( $this->mPage->getId() === 0 || $this->getOldID() ) {
777  # Non-articles (special pages etc), and old revisions
778  return [
779  'index' => 'noindex',
780  'follow' => 'nofollow'
781  ];
782  } elseif ( $this->getContext()->getOutput()->isPrintable() ) {
783  # Discourage indexing of printable versions, but encourage following
784  return [
785  'index' => 'noindex',
786  'follow' => 'follow'
787  ];
788  } elseif ( $this->getContext()->getRequest()->getInt( 'curid' ) ) {
789  # For ?curid=x urls, disallow indexing
790  return [
791  'index' => 'noindex',
792  'follow' => 'follow'
793  ];
794  }
795 
796  # Otherwise, construct the policy based on the various config variables.
798 
799  if ( isset( $wgNamespaceRobotPolicies[$ns] ) ) {
800  # Honour customised robot policies for this namespace
801  $policy = array_merge(
802  $policy,
803  self::formatRobotPolicy( $wgNamespaceRobotPolicies[$ns] )
804  );
805  }
806  if ( $this->getTitle()->canUseNoindex() && is_object( $pOutput ) && $pOutput->getIndexPolicy() ) {
807  # __INDEX__ and __NOINDEX__ magic words, if allowed. Incorporates
808  # a final sanity check that we have really got the parser output.
809  $policy = array_merge(
810  $policy,
811  [ 'index' => $pOutput->getIndexPolicy() ]
812  );
813  }
814 
815  if ( isset( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] ) ) {
816  # (T16900) site config can override user-defined __INDEX__ or __NOINDEX__
817  $policy = array_merge(
818  $policy,
819  self::formatRobotPolicy( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] )
820  );
821  }
822 
823  return $policy;
824  }
825 
833  public static function formatRobotPolicy( $policy ) {
834  if ( is_array( $policy ) ) {
835  return $policy;
836  } elseif ( !$policy ) {
837  return [];
838  }
839 
840  $policy = explode( ',', $policy );
841  $policy = array_map( 'trim', $policy );
842 
843  $arr = [];
844  foreach ( $policy as $var ) {
845  if ( in_array( $var, [ 'index', 'noindex' ] ) ) {
846  $arr['index'] = $var;
847  } elseif ( in_array( $var, [ 'follow', 'nofollow' ] ) ) {
848  $arr['follow'] = $var;
849  }
850  }
851 
852  return $arr;
853  }
854 
862  public function showRedirectedFromHeader() {
864 
865  $context = $this->getContext();
866  $outputPage = $context->getOutput();
867  $request = $context->getRequest();
868  $rdfrom = $request->getVal( 'rdfrom' );
869 
870  // Construct a URL for the current page view, but with the target title
871  $query = $request->getValues();
872  unset( $query['rdfrom'] );
873  unset( $query['title'] );
874  if ( $this->getTitle()->isRedirect() ) {
875  // Prevent double redirects
876  $query['redirect'] = 'no';
877  }
878  $redirectTargetUrl = $this->getTitle()->getLinkURL( $query );
879 
880  if ( isset( $this->mRedirectedFrom ) ) {
881  // Avoid PHP 7.1 warning of passing $this by reference
882  $articlePage = $this;
883 
884  // This is an internally redirected page view.
885  // We'll need a backlink to the source page for navigation.
886  if ( Hooks::run( 'ArticleViewRedirect', [ &$articlePage ] ) ) {
887  $redir = Linker::linkKnown(
888  $this->mRedirectedFrom,
889  null,
890  [],
891  [ 'redirect' => 'no' ]
892  );
893 
894  $outputPage->addSubtitle( "<span class=\"mw-redirectedfrom\">" .
895  $context->msg( 'redirectedfrom' )->rawParams( $redir )->parse()
896  . "</span>" );
897 
898  // Add the script to update the displayed URL and
899  // set the fragment if one was specified in the redirect
900  $outputPage->addJsConfigVars( [
901  'wgInternalRedirectTargetUrl' => $redirectTargetUrl,
902  ] );
903  $outputPage->addModules( 'mediawiki.action.view.redirect' );
904 
905  // Add a <link rel="canonical"> tag
906  $outputPage->setCanonicalUrl( $this->getTitle()->getCanonicalURL() );
907 
908  // Tell the output object that the user arrived at this article through a redirect
909  $outputPage->setRedirectedFrom( $this->mRedirectedFrom );
910 
911  return true;
912  }
913  } elseif ( $rdfrom ) {
914  // This is an externally redirected view, from some other wiki.
915  // If it was reported from a trusted site, supply a backlink.
916  if ( $wgRedirectSources && preg_match( $wgRedirectSources, $rdfrom ) ) {
917  $redir = Linker::makeExternalLink( $rdfrom, $rdfrom );
918  $outputPage->addSubtitle( "<span class=\"mw-redirectedfrom\">" .
919  $context->msg( 'redirectedfrom' )->rawParams( $redir )->parse()
920  . "</span>" );
921 
922  // Add the script to update the displayed URL
923  $outputPage->addJsConfigVars( [
924  'wgInternalRedirectTargetUrl' => $redirectTargetUrl,
925  ] );
926  $outputPage->addModules( 'mediawiki.action.view.redirect' );
927 
928  return true;
929  }
930  }
931 
932  return false;
933  }
934 
939  public function showNamespaceHeader() {
940  if ( $this->getTitle()->isTalkPage() ) {
941  if ( !wfMessage( 'talkpageheader' )->isDisabled() ) {
942  $this->getContext()->getOutput()->wrapWikiMsg(
943  "<div class=\"mw-talkpageheader\">\n$1\n</div>",
944  [ 'talkpageheader' ]
945  );
946  }
947  }
948  }
949 
953  public function showViewFooter() {
954  # check if we're displaying a [[User talk:x.x.x.x]] anonymous talk page
955  if ( $this->getTitle()->getNamespace() == NS_USER_TALK
956  && IP::isValid( $this->getTitle()->getText() )
957  ) {
958  $this->getContext()->getOutput()->addWikiMsg( 'anontalkpagetext' );
959  }
960 
961  // Show a footer allowing the user to patrol the shown revision or page if possible
962  $patrolFooterShown = $this->showPatrolFooter();
963 
964  Hooks::run( 'ArticleViewFooter', [ $this, $patrolFooterShown ] );
965  }
966 
976  public function showPatrolFooter() {
978 
979  $outputPage = $this->getContext()->getOutput();
980  $user = $this->getContext()->getUser();
981  $title = $this->getTitle();
982  $rc = false;
983 
984  if ( !$title->quickUserCan( 'patrol', $user )
986  || ( $wgUseFilePatrol && $title->inNamespace( NS_FILE ) ) )
987  ) {
988  // Patrolling is disabled or the user isn't allowed to
989  return false;
990  }
991 
992  if ( $this->mRevision
993  && !RecentChange::isInRCLifespan( $this->mRevision->getTimestamp(), 21600 )
994  ) {
995  // The current revision is already older than what could be in the RC table
996  // 6h tolerance because the RC might not be cleaned out regularly
997  return false;
998  }
999 
1000  // Check for cached results
1001  $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
1002  $key = $cache->makeKey( 'unpatrollable-page', $title->getArticleID() );
1003  if ( $cache->get( $key ) ) {
1004  return false;
1005  }
1006 
1007  $dbr = wfGetDB( DB_REPLICA );
1008  $oldestRevisionTimestamp = $dbr->selectField(
1009  'revision',
1010  'MIN( rev_timestamp )',
1011  [ 'rev_page' => $title->getArticleID() ],
1012  __METHOD__
1013  );
1014 
1015  // New page patrol: Get the timestamp of the oldest revison which
1016  // the revision table holds for the given page. Then we look
1017  // whether it's within the RC lifespan and if it is, we try
1018  // to get the recentchanges row belonging to that entry
1019  // (with rc_new = 1).
1020  $recentPageCreation = false;
1021  if ( $oldestRevisionTimestamp
1022  && RecentChange::isInRCLifespan( $oldestRevisionTimestamp, 21600 )
1023  ) {
1024  // 6h tolerance because the RC might not be cleaned out regularly
1025  $recentPageCreation = true;
1027  [
1028  'rc_new' => 1,
1029  'rc_timestamp' => $oldestRevisionTimestamp,
1030  'rc_namespace' => $title->getNamespace(),
1031  'rc_cur_id' => $title->getArticleID()
1032  ],
1033  __METHOD__
1034  );
1035  if ( $rc ) {
1036  // Use generic patrol message for new pages
1037  $markPatrolledMsg = wfMessage( 'markaspatrolledtext' );
1038  }
1039  }
1040 
1041  // File patrol: Get the timestamp of the latest upload for this page,
1042  // check whether it is within the RC lifespan and if it is, we try
1043  // to get the recentchanges row belonging to that entry
1044  // (with rc_type = RC_LOG, rc_log_type = upload).
1045  $recentFileUpload = false;
1046  if ( ( !$rc || $rc->getAttribute( 'rc_patrolled' ) ) && $wgUseFilePatrol
1047  && $title->getNamespace() === NS_FILE ) {
1048  // Retrieve timestamp of most recent upload
1049  $newestUploadTimestamp = $dbr->selectField(
1050  'image',
1051  'MAX( img_timestamp )',
1052  [ 'img_name' => $title->getDBkey() ],
1053  __METHOD__
1054  );
1055  if ( $newestUploadTimestamp
1056  && RecentChange::isInRCLifespan( $newestUploadTimestamp, 21600 )
1057  ) {
1058  // 6h tolerance because the RC might not be cleaned out regularly
1059  $recentFileUpload = true;
1061  [
1062  'rc_type' => RC_LOG,
1063  'rc_log_type' => 'upload',
1064  'rc_timestamp' => $newestUploadTimestamp,
1065  'rc_namespace' => NS_FILE,
1066  'rc_cur_id' => $title->getArticleID()
1067  ],
1068  __METHOD__
1069  );
1070  if ( $rc ) {
1071  // Use patrol message specific to files
1072  $markPatrolledMsg = wfMessage( 'markaspatrolledtext-file' );
1073  }
1074  }
1075  }
1076 
1077  if ( !$recentPageCreation && !$recentFileUpload ) {
1078  // Page creation and latest upload (for files) is too old to be in RC
1079 
1080  // We definitely can't patrol so cache the information
1081  // When a new file version is uploaded, the cache is cleared
1082  $cache->set( $key, '1' );
1083 
1084  return false;
1085  }
1086 
1087  if ( !$rc ) {
1088  // Don't cache: This can be hit if the page gets accessed very fast after
1089  // its creation / latest upload or in case we have high replica DB lag. In case
1090  // the revision is too old, we will already return above.
1091  return false;
1092  }
1093 
1094  if ( $rc->getAttribute( 'rc_patrolled' ) ) {
1095  // Patrolled RC entry around
1096 
1097  // Cache the information we gathered above in case we can't patrol
1098  // Don't cache in case we can patrol as this could change
1099  $cache->set( $key, '1' );
1100 
1101  return false;
1102  }
1103 
1104  if ( $rc->getPerformer()->equals( $user ) ) {
1105  // Don't show a patrol link for own creations/uploads. If the user could
1106  // patrol them, they already would be patrolled
1107  return false;
1108  }
1109 
1110  $outputPage->preventClickjacking();
1111  if ( $wgEnableAPI && $wgEnableWriteAPI && $user->isAllowed( 'writeapi' ) ) {
1112  $outputPage->addModules( 'mediawiki.page.patrol.ajax' );
1113  }
1114 
1116  $title,
1117  $markPatrolledMsg->escaped(),
1118  [],
1119  [
1120  'action' => 'markpatrolled',
1121  'rcid' => $rc->getAttribute( 'rc_id' ),
1122  ]
1123  );
1124 
1125  $outputPage->addHTML(
1126  "<div class='patrollink' data-mw='interface'>" .
1127  wfMessage( 'markaspatrolledlink' )->rawParams( $link )->escaped() .
1128  '</div>'
1129  );
1130 
1131  return true;
1132  }
1133 
1140  public static function purgePatrolFooterCache( $articleID ) {
1141  $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
1142  $cache->delete( $cache->makeKey( 'unpatrollable-page', $articleID ) );
1143  }
1144 
1149  public function showMissingArticle() {
1151 
1152  $outputPage = $this->getContext()->getOutput();
1153  // Whether the page is a root user page of an existing user (but not a subpage)
1154  $validUserPage = false;
1155 
1156  $title = $this->getTitle();
1157 
1158  # Show info in user (talk) namespace. Does the user exist? Is he blocked?
1159  if ( $title->getNamespace() == NS_USER
1160  || $title->getNamespace() == NS_USER_TALK
1161  ) {
1162  $rootPart = explode( '/', $title->getText() )[0];
1163  $user = User::newFromName( $rootPart, false /* allow IP users */ );
1164  $ip = User::isIP( $rootPart );
1165  $block = Block::newFromTarget( $user, $user );
1166 
1167  if ( $user && $user->isLoggedIn() && $user->isHidden() &&
1168  !$this->getContext()->getUser()->isAllowed( 'hideuser' )
1169  ) {
1170  // T120883 if the user is hidden and the viewer cannot see hidden
1171  // users, pretend like it does not exist at all.
1172  $user = false;
1173  }
1174 
1175  if ( !( $user && $user->isLoggedIn() ) && !$ip ) { # User does not exist
1176  $outputPage->wrapWikiMsg( "<div class=\"mw-userpage-userdoesnotexist error\">\n\$1\n</div>",
1177  [ 'userpage-userdoesnotexist-view', wfEscapeWikiText( $rootPart ) ] );
1178  } elseif ( !is_null( $block ) && $block->getType() != Block::TYPE_AUTO ) {
1179  # Show log extract if the user is currently blocked
1181  $outputPage,
1182  'block',
1183  MWNamespace::getCanonicalName( NS_USER ) . ':' . $block->getTarget(),
1184  '',
1185  [
1186  'lim' => 1,
1187  'showIfEmpty' => false,
1188  'msgKey' => [
1189  'blocked-notice-logextract',
1190  $user->getName() # Support GENDER in notice
1191  ]
1192  ]
1193  );
1194  $validUserPage = !$title->isSubpage();
1195  } else {
1196  $validUserPage = !$title->isSubpage();
1197  }
1198  }
1199 
1200  Hooks::run( 'ShowMissingArticle', [ $this ] );
1201 
1202  # Show delete and move logs if there were any such events.
1203  # The logging query can DOS the site when bots/crawlers cause 404 floods,
1204  # so be careful showing this. 404 pages must be cheap as they are hard to cache.
1205  $cache = MediaWikiServices::getInstance()->getMainObjectStash();
1206  $key = $cache->makeKey( 'page-recent-delete', md5( $title->getPrefixedText() ) );
1207  $loggedIn = $this->getContext()->getUser()->isLoggedIn();
1208  $sessionExists = $this->getContext()->getRequest()->getSession()->isPersistent();
1209  if ( $loggedIn || $cache->get( $key ) || $sessionExists ) {
1210  $logTypes = [ 'delete', 'move', 'protect' ];
1211 
1212  $dbr = wfGetDB( DB_REPLICA );
1213 
1214  $conds = [ 'log_action != ' . $dbr->addQuotes( 'revision' ) ];
1215  // Give extensions a chance to hide their (unrelated) log entries
1216  Hooks::run( 'Article::MissingArticleConditions', [ &$conds, $logTypes ] );
1218  $outputPage,
1219  $logTypes,
1220  $title,
1221  '',
1222  [
1223  'lim' => 10,
1224  'conds' => $conds,
1225  'showIfEmpty' => false,
1226  'msgKey' => [ $loggedIn || $sessionExists
1227  ? 'moveddeleted-notice'
1228  : 'moveddeleted-notice-recent'
1229  ]
1230  ]
1231  );
1232  }
1233 
1234  if ( !$this->mPage->hasViewableContent() && $wgSend404Code && !$validUserPage ) {
1235  // If there's no backing content, send a 404 Not Found
1236  // for better machine handling of broken links.
1237  $this->getContext()->getRequest()->response()->statusHeader( 404 );
1238  }
1239 
1240  // Also apply the robot policy for nonexisting pages (even if a 404 was used for sanity)
1241  $policy = $this->getRobotPolicy( 'view' );
1242  $outputPage->setIndexPolicy( $policy['index'] );
1243  $outputPage->setFollowPolicy( $policy['follow'] );
1244 
1245  $hookResult = Hooks::run( 'BeforeDisplayNoArticleText', [ $this ] );
1246 
1247  if ( !$hookResult ) {
1248  return;
1249  }
1250 
1251  # Show error message
1252  $oldid = $this->getOldID();
1253  if ( !$oldid && $title->getNamespace() === NS_MEDIAWIKI && $title->hasSourceText() ) {
1254  $outputPage->addParserOutput( $this->getContentObject()->getParserOutput( $title ) );
1255  } else {
1256  if ( $oldid ) {
1257  $text = wfMessage( 'missing-revision', $oldid )->plain();
1258  } elseif ( $title->quickUserCan( 'create', $this->getContext()->getUser() )
1259  && $title->quickUserCan( 'edit', $this->getContext()->getUser() )
1260  ) {
1261  $message = $this->getContext()->getUser()->isLoggedIn() ? 'noarticletext' : 'noarticletextanon';
1262  $text = wfMessage( $message )->plain();
1263  } else {
1264  $text = wfMessage( 'noarticletext-nopermission' )->plain();
1265  }
1266 
1267  $dir = $this->getContext()->getLanguage()->getDir();
1268  $lang = $this->getContext()->getLanguage()->getHtmlCode();
1269  $outputPage->addWikiText( Xml::openElement( 'div', [
1270  'class' => "noarticletext mw-content-$dir",
1271  'dir' => $dir,
1272  'lang' => $lang,
1273  ] ) . "\n$text\n</div>" );
1274  }
1275  }
1276 
1283  public function showDeletedRevisionHeader() {
1284  if ( !$this->mRevision->isDeleted( Revision::DELETED_TEXT ) ) {
1285  // Not deleted
1286  return true;
1287  }
1288 
1289  $outputPage = $this->getContext()->getOutput();
1290  $user = $this->getContext()->getUser();
1291  // If the user is not allowed to see it...
1292  if ( !$this->mRevision->userCan( Revision::DELETED_TEXT, $user ) ) {
1293  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1294  'rev-deleted-text-permission' );
1295 
1296  return false;
1297  // If the user needs to confirm that they want to see it...
1298  } elseif ( $this->getContext()->getRequest()->getInt( 'unhide' ) != 1 ) {
1299  # Give explanation and add a link to view the revision...
1300  $oldid = intval( $this->getOldID() );
1301  $link = $this->getTitle()->getFullURL( "oldid={$oldid}&unhide=1" );
1302  $msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ?
1303  'rev-suppressed-text-unhide' : 'rev-deleted-text-unhide';
1304  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1305  [ $msg, $link ] );
1306 
1307  return false;
1308  // We are allowed to see...
1309  } else {
1310  $msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ?
1311  'rev-suppressed-text-view' : 'rev-deleted-text-view';
1312  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n", $msg );
1313 
1314  return true;
1315  }
1316  }
1317 
1326  public function setOldSubtitle( $oldid = 0 ) {
1327  // Avoid PHP 7.1 warning of passing $this by reference
1328  $articlePage = $this;
1329 
1330  if ( !Hooks::run( 'DisplayOldSubtitle', [ &$articlePage, &$oldid ] ) ) {
1331  return;
1332  }
1333 
1334  $context = $this->getContext();
1335  $unhide = $context->getRequest()->getInt( 'unhide' ) == 1;
1336 
1337  # Cascade unhide param in links for easy deletion browsing
1338  $extraParams = [];
1339  if ( $unhide ) {
1340  $extraParams['unhide'] = 1;
1341  }
1342 
1343  if ( $this->mRevision && $this->mRevision->getId() === $oldid ) {
1344  $revision = $this->mRevision;
1345  } else {
1346  $revision = Revision::newFromId( $oldid );
1347  }
1348 
1349  $timestamp = $revision->getTimestamp();
1350 
1351  $current = ( $oldid == $this->mPage->getLatest() );
1352  $language = $context->getLanguage();
1353  $user = $context->getUser();
1354 
1355  $td = $language->userTimeAndDate( $timestamp, $user );
1356  $tddate = $language->userDate( $timestamp, $user );
1357  $tdtime = $language->userTime( $timestamp, $user );
1358 
1359  # Show user links if allowed to see them. If hidden, then show them only if requested...
1360  $userlinks = Linker::revUserTools( $revision, !$unhide );
1361 
1362  $infomsg = $current && !$context->msg( 'revision-info-current' )->isDisabled()
1363  ? 'revision-info-current'
1364  : 'revision-info';
1365 
1366  $outputPage = $context->getOutput();
1367  $revisionInfo = "<div id=\"mw-{$infomsg}\">" .
1368  $context->msg( $infomsg, $td )
1369  ->rawParams( $userlinks )
1370  ->params( $revision->getId(), $tddate, $tdtime, $revision->getUserText() )
1371  ->rawParams( Linker::revComment( $revision, true, true ) )
1372  ->parse() .
1373  "</div>";
1374 
1375  $lnk = $current
1376  ? $context->msg( 'currentrevisionlink' )->escaped()
1378  $this->getTitle(),
1379  $context->msg( 'currentrevisionlink' )->escaped(),
1380  [],
1381  $extraParams
1382  );
1383  $curdiff = $current
1384  ? $context->msg( 'diff' )->escaped()
1386  $this->getTitle(),
1387  $context->msg( 'diff' )->escaped(),
1388  [],
1389  [
1390  'diff' => 'cur',
1391  'oldid' => $oldid
1392  ] + $extraParams
1393  );
1394  $prev = $this->getTitle()->getPreviousRevisionID( $oldid );
1395  $prevlink = $prev
1397  $this->getTitle(),
1398  $context->msg( 'previousrevision' )->escaped(),
1399  [],
1400  [
1401  'direction' => 'prev',
1402  'oldid' => $oldid
1403  ] + $extraParams
1404  )
1405  : $context->msg( 'previousrevision' )->escaped();
1406  $prevdiff = $prev
1408  $this->getTitle(),
1409  $context->msg( 'diff' )->escaped(),
1410  [],
1411  [
1412  'diff' => 'prev',
1413  'oldid' => $oldid
1414  ] + $extraParams
1415  )
1416  : $context->msg( 'diff' )->escaped();
1417  $nextlink = $current
1418  ? $context->msg( 'nextrevision' )->escaped()
1420  $this->getTitle(),
1421  $context->msg( 'nextrevision' )->escaped(),
1422  [],
1423  [
1424  'direction' => 'next',
1425  'oldid' => $oldid
1426  ] + $extraParams
1427  );
1428  $nextdiff = $current
1429  ? $context->msg( 'diff' )->escaped()
1431  $this->getTitle(),
1432  $context->msg( 'diff' )->escaped(),
1433  [],
1434  [
1435  'diff' => 'next',
1436  'oldid' => $oldid
1437  ] + $extraParams
1438  );
1439 
1440  $cdel = Linker::getRevDeleteLink( $user, $revision, $this->getTitle() );
1441  if ( $cdel !== '' ) {
1442  $cdel .= ' ';
1443  }
1444 
1445  // the outer div is need for styling the revision info and nav in MobileFrontend
1446  $outputPage->addSubtitle( "<div class=\"mw-revision\">" . $revisionInfo .
1447  "<div id=\"mw-revision-nav\">" . $cdel .
1448  $context->msg( 'revision-nav' )->rawParams(
1449  $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff
1450  )->escaped() . "</div></div>" );
1451  }
1452 
1466  public function viewRedirect( $target, $appendSubtitle = true, $forceKnown = false ) {
1467  $lang = $this->getTitle()->getPageLanguage();
1468  $out = $this->getContext()->getOutput();
1469  if ( $appendSubtitle ) {
1470  $out->addSubtitle( wfMessage( 'redirectpagesub' ) );
1471  }
1472  $out->addModuleStyles( 'mediawiki.action.view.redirectPage' );
1473  return static::getRedirectHeaderHtml( $lang, $target, $forceKnown );
1474  }
1475 
1488  public static function getRedirectHeaderHtml( Language $lang, $target, $forceKnown = false ) {
1489  if ( !is_array( $target ) ) {
1490  $target = [ $target ];
1491  }
1492 
1493  $html = '<ul class="redirectText">';
1495  foreach ( $target as $title ) {
1496  $html .= '<li>' . Linker::link(
1497  $title,
1498  htmlspecialchars( $title->getFullText() ),
1499  [],
1500  // Make sure wiki page redirects are not followed
1501  $title->isRedirect() ? [ 'redirect' => 'no' ] : [],
1502  ( $forceKnown ? [ 'known', 'noclasses' ] : [] )
1503  ) . '</li>';
1504  }
1505  $html .= '</ul>';
1506 
1507  $redirectToText = wfMessage( 'redirectto' )->inLanguage( $lang )->escaped();
1508 
1509  return '<div class="redirectMsg">' .
1510  '<p>' . $redirectToText . '</p>' .
1511  $html .
1512  '</div>';
1513  }
1514 
1523  public function addHelpLink( $to, $overrideBaseUrl = false ) {
1524  $msg = wfMessage(
1525  'namespace-' . $this->getTitle()->getNamespace() . '-helppage'
1526  );
1527 
1528  $out = $this->getContext()->getOutput();
1529  if ( !$msg->isDisabled() ) {
1530  $helpUrl = Skin::makeUrl( $msg->plain() );
1531  $out->addHelpLink( $helpUrl, true );
1532  } else {
1533  $out->addHelpLink( $to, $overrideBaseUrl );
1534  }
1535  }
1536 
1540  public function render() {
1541  $this->getContext()->getRequest()->response()->header( 'X-Robots-Tag: noindex' );
1542  $this->getContext()->getOutput()->setArticleBodyOnly( true );
1543  $this->disableSectionEditForRender = true;
1544  $this->view();
1545  }
1546 
1550  public function protect() {
1551  $form = new ProtectionForm( $this );
1552  $form->execute();
1553  }
1554 
1558  public function unprotect() {
1559  $this->protect();
1560  }
1561 
1565  public function delete() {
1566  # This code desperately needs to be totally rewritten
1567 
1568  $title = $this->getTitle();
1569  $context = $this->getContext();
1570  $user = $context->getUser();
1571  $request = $context->getRequest();
1572 
1573  # Check permissions
1574  $permissionErrors = $title->getUserPermissionsErrors( 'delete', $user );
1575  if ( count( $permissionErrors ) ) {
1576  throw new PermissionsError( 'delete', $permissionErrors );
1577  }
1578 
1579  # Read-only check...
1580  if ( wfReadOnly() ) {
1581  throw new ReadOnlyError;
1582  }
1583 
1584  # Better double-check that it hasn't been deleted yet!
1585  $this->mPage->loadPageData(
1586  $request->wasPosted() ? WikiPage::READ_LATEST : WikiPage::READ_NORMAL
1587  );
1588  if ( !$this->mPage->exists() ) {
1589  $deleteLogPage = new LogPage( 'delete' );
1590  $outputPage = $context->getOutput();
1591  $outputPage->setPageTitle( $context->msg( 'cannotdelete-title', $title->getPrefixedText() ) );
1592  $outputPage->wrapWikiMsg( "<div class=\"error mw-error-cannotdelete\">\n$1\n</div>",
1593  [ 'cannotdelete', wfEscapeWikiText( $title->getPrefixedText() ) ]
1594  );
1595  $outputPage->addHTML(
1596  Xml::element( 'h2', null, $deleteLogPage->getName()->text() )
1597  );
1599  $outputPage,
1600  'delete',
1601  $title
1602  );
1603 
1604  return;
1605  }
1606 
1607  $deleteReasonList = $request->getText( 'wpDeleteReasonList', 'other' );
1608  $deleteReason = $request->getText( 'wpReason' );
1609 
1610  if ( $deleteReasonList == 'other' ) {
1611  $reason = $deleteReason;
1612  } elseif ( $deleteReason != '' ) {
1613  // Entry from drop down menu + additional comment
1614  $colonseparator = wfMessage( 'colon-separator' )->inContentLanguage()->text();
1615  $reason = $deleteReasonList . $colonseparator . $deleteReason;
1616  } else {
1617  $reason = $deleteReasonList;
1618  }
1619 
1620  if ( $request->wasPosted() && $user->matchEditToken( $request->getVal( 'wpEditToken' ),
1621  [ 'delete', $this->getTitle()->getPrefixedText() ] )
1622  ) {
1623  # Flag to hide all contents of the archived revisions
1624  $suppress = $request->getCheck( 'wpSuppress' ) && $user->isAllowed( 'suppressrevision' );
1625 
1626  $this->doDelete( $reason, $suppress );
1627 
1628  WatchAction::doWatchOrUnwatch( $request->getCheck( 'wpWatch' ), $title, $user );
1629 
1630  return;
1631  }
1632 
1633  // Generate deletion reason
1634  $hasHistory = false;
1635  if ( !$reason ) {
1636  try {
1637  $reason = $this->generateReason( $hasHistory );
1638  } catch ( Exception $e ) {
1639  # if a page is horribly broken, we still want to be able to
1640  # delete it. So be lenient about errors here.
1641  wfDebug( "Error while building auto delete summary: $e" );
1642  $reason = '';
1643  }
1644  }
1645 
1646  // If the page has a history, insert a warning
1647  if ( $hasHistory ) {
1648  $title = $this->getTitle();
1649 
1650  // The following can use the real revision count as this is only being shown for users
1651  // that can delete this page.
1652  // This, as a side-effect, also makes sure that the following query isn't being run for
1653  // pages with a larger history, unless the user has the 'bigdelete' right
1654  // (and is about to delete this page).
1655  $dbr = wfGetDB( DB_REPLICA );
1656  $revisions = $edits = (int)$dbr->selectField(
1657  'revision',
1658  'COUNT(rev_page)',
1659  [ 'rev_page' => $title->getArticleID() ],
1660  __METHOD__
1661  );
1662 
1663  // @todo FIXME: i18n issue/patchwork message
1664  $context->getOutput()->addHTML(
1665  '<strong class="mw-delete-warning-revisions">' .
1666  $context->msg( 'historywarning' )->numParams( $revisions )->parse() .
1667  $context->msg( 'word-separator' )->escaped() . Linker::linkKnown( $title,
1668  $context->msg( 'history' )->escaped(),
1669  [],
1670  [ 'action' => 'history' ] ) .
1671  '</strong>'
1672  );
1673 
1674  if ( $title->isBigDeletion() ) {
1676  $context->getOutput()->wrapWikiMsg( "<div class='error'>\n$1\n</div>\n",
1677  [
1678  'delete-warning-toobig',
1679  $context->getLanguage()->formatNum( $wgDeleteRevisionsLimit )
1680  ]
1681  );
1682  }
1683  }
1684 
1685  $this->confirmDelete( $reason );
1686  }
1687 
1693  public function confirmDelete( $reason ) {
1694  wfDebug( "Article::confirmDelete\n" );
1695 
1696  $title = $this->getTitle();
1697  $ctx = $this->getContext();
1698  $outputPage = $ctx->getOutput();
1699  $outputPage->setPageTitle( wfMessage( 'delete-confirm', $title->getPrefixedText() ) );
1700  $outputPage->addBacklinkSubtitle( $title );
1701  $outputPage->setRobotPolicy( 'noindex,nofollow' );
1702  $outputPage->addModules( 'mediawiki.action.delete' );
1703 
1704  $backlinkCache = $title->getBacklinkCache();
1705  if ( $backlinkCache->hasLinks( 'pagelinks' ) || $backlinkCache->hasLinks( 'templatelinks' ) ) {
1706  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1707  'deleting-backlinks-warning' );
1708  }
1709 
1710  $subpageQueryLimit = 51;
1711  $subpages = $title->getSubpages( $subpageQueryLimit );
1712  $subpageCount = count( $subpages );
1713  if ( $subpageCount > 0 ) {
1714  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1715  [ 'deleting-subpages-warning', Message::numParam( $subpageCount ) ] );
1716  }
1717  $outputPage->addWikiMsg( 'confirmdeletetext' );
1718 
1719  Hooks::run( 'ArticleConfirmDelete', [ $this, $outputPage, &$reason ] );
1720 
1721  $user = $this->getContext()->getUser();
1722  $checkWatch = $user->getBoolOption( 'watchdeletion' ) || $user->isWatched( $title );
1723 
1724  $outputPage->enableOOUI();
1725 
1727  $ctx->msg( 'deletereason-dropdown' )->inContentLanguage()->text(),
1728  [ 'other' => $ctx->msg( 'deletereasonotherlist' )->inContentLanguage()->text() ]
1729  );
1731 
1732  $fields[] = new OOUI\FieldLayout(
1733  new OOUI\DropdownInputWidget( [
1734  'name' => 'wpDeleteReasonList',
1735  'inputId' => 'wpDeleteReasonList',
1736  'tabIndex' => 1,
1737  'infusable' => true,
1738  'value' => '',
1739  'options' => $options
1740  ] ),
1741  [
1742  'label' => $ctx->msg( 'deletecomment' )->text(),
1743  'align' => 'top',
1744  ]
1745  );
1746 
1747  // HTML maxlength uses "UTF-16 code units", which means that characters outside BMP
1748  // (e.g. emojis) count for two each. This limit is overridden in JS to instead count
1749  // Unicode codepoints (or 255 UTF-8 bytes for old schema).
1750  $conf = $this->getContext()->getConfig();
1751  $oldCommentSchema = $conf->get( 'CommentTableSchemaMigrationStage' ) === MIGRATION_OLD;
1752  $fields[] = new OOUI\FieldLayout(
1753  new OOUI\TextInputWidget( [
1754  'name' => 'wpReason',
1755  'inputId' => 'wpReason',
1756  'tabIndex' => 2,
1757  'maxLength' => $oldCommentSchema ? 255 : CommentStore::COMMENT_CHARACTER_LIMIT,
1758  'infusable' => true,
1759  'value' => $reason,
1760  'autofocus' => true,
1761  ] ),
1762  [
1763  'label' => $ctx->msg( 'deleteotherreason' )->text(),
1764  'align' => 'top',
1765  ]
1766  );
1767 
1768  if ( $user->isLoggedIn() ) {
1769  $fields[] = new OOUI\FieldLayout(
1770  new OOUI\CheckboxInputWidget( [
1771  'name' => 'wpWatch',
1772  'inputId' => 'wpWatch',
1773  'tabIndex' => 3,
1774  'selected' => $checkWatch,
1775  ] ),
1776  [
1777  'label' => $ctx->msg( 'watchthis' )->text(),
1778  'align' => 'inline',
1779  'infusable' => true,
1780  ]
1781  );
1782  }
1783 
1784  if ( $user->isAllowed( 'suppressrevision' ) ) {
1785  $fields[] = new OOUI\FieldLayout(
1786  new OOUI\CheckboxInputWidget( [
1787  'name' => 'wpSuppress',
1788  'inputId' => 'wpSuppress',
1789  'tabIndex' => 4,
1790  ] ),
1791  [
1792  'label' => $ctx->msg( 'revdelete-suppress' )->text(),
1793  'align' => 'inline',
1794  'infusable' => true,
1795  ]
1796  );
1797  }
1798 
1799  $fields[] = new OOUI\FieldLayout(
1800  new OOUI\ButtonInputWidget( [
1801  'name' => 'wpConfirmB',
1802  'inputId' => 'wpConfirmB',
1803  'tabIndex' => 5,
1804  'value' => $ctx->msg( 'deletepage' )->text(),
1805  'label' => $ctx->msg( 'deletepage' )->text(),
1806  'flags' => [ 'primary', 'destructive' ],
1807  'type' => 'submit',
1808  ] ),
1809  [
1810  'align' => 'top',
1811  ]
1812  );
1813 
1814  $fieldset = new OOUI\FieldsetLayout( [
1815  'label' => $ctx->msg( 'delete-legend' )->text(),
1816  'id' => 'mw-delete-table',
1817  'items' => $fields,
1818  ] );
1819 
1820  $form = new OOUI\FormLayout( [
1821  'method' => 'post',
1822  'action' => $title->getLocalURL( 'action=delete' ),
1823  'id' => 'deleteconfirm',
1824  ] );
1825  $form->appendContent(
1826  $fieldset,
1827  new OOUI\HtmlSnippet(
1828  Html::hidden( 'wpEditToken', $user->getEditToken( [ 'delete', $title->getPrefixedText() ] ) )
1829  )
1830  );
1831 
1832  $outputPage->addHTML(
1833  new OOUI\PanelLayout( [
1834  'classes' => [ 'deletepage-wrapper' ],
1835  'expanded' => false,
1836  'padded' => true,
1837  'framed' => true,
1838  'content' => $form,
1839  ] )
1840  );
1841 
1842  if ( $user->isAllowed( 'editinterface' ) ) {
1844  $ctx->msg( 'deletereason-dropdown' )->inContentLanguage()->getTitle(),
1845  wfMessage( 'delete-edit-reasonlist' )->escaped(),
1846  [],
1847  [ 'action' => 'edit' ]
1848  );
1849  $outputPage->addHTML( '<p class="mw-delete-editreasons">' . $link . '</p>' );
1850  }
1851 
1852  $deleteLogPage = new LogPage( 'delete' );
1853  $outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) );
1854  LogEventsList::showLogExtract( $outputPage, 'delete', $title );
1855  }
1856 
1862  public function doDelete( $reason, $suppress = false ) {
1863  $error = '';
1864  $context = $this->getContext();
1865  $outputPage = $context->getOutput();
1866  $user = $context->getUser();
1867  $status = $this->mPage->doDeleteArticleReal( $reason, $suppress, 0, true, $error, $user );
1868 
1869  if ( $status->isGood() ) {
1870  $deleted = $this->getTitle()->getPrefixedText();
1871 
1872  $outputPage->setPageTitle( wfMessage( 'actioncomplete' ) );
1873  $outputPage->setRobotPolicy( 'noindex,nofollow' );
1874 
1875  $loglink = '[[Special:Log/delete|' . wfMessage( 'deletionlog' )->text() . ']]';
1876 
1877  $outputPage->addWikiMsg( 'deletedtext', wfEscapeWikiText( $deleted ), $loglink );
1878 
1879  Hooks::run( 'ArticleDeleteAfterSuccess', [ $this->getTitle(), $outputPage ] );
1880 
1881  $outputPage->returnToMain( false );
1882  } else {
1883  $outputPage->setPageTitle(
1884  wfMessage( 'cannotdelete-title',
1885  $this->getTitle()->getPrefixedText() )
1886  );
1887 
1888  if ( $error == '' ) {
1889  $outputPage->addWikiText(
1890  "<div class=\"error mw-error-cannotdelete\">\n" . $status->getWikiText() . "\n</div>"
1891  );
1892  $deleteLogPage = new LogPage( 'delete' );
1893  $outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) );
1894 
1896  $outputPage,
1897  'delete',
1898  $this->getTitle()
1899  );
1900  } else {
1901  $outputPage->addHTML( $error );
1902  }
1903  }
1904  }
1905 
1906  /* Caching functions */
1907 
1915  protected function tryFileCache() {
1916  static $called = false;
1917 
1918  if ( $called ) {
1919  wfDebug( "Article::tryFileCache(): called twice!?\n" );
1920  return false;
1921  }
1922 
1923  $called = true;
1924  if ( $this->isFileCacheable() ) {
1925  $cache = new HTMLFileCache( $this->getTitle(), 'view' );
1926  if ( $cache->isCacheGood( $this->mPage->getTouched() ) ) {
1927  wfDebug( "Article::tryFileCache(): about to load file\n" );
1928  $cache->loadFromFileCache( $this->getContext() );
1929  return true;
1930  } else {
1931  wfDebug( "Article::tryFileCache(): starting buffer\n" );
1932  ob_start( [ &$cache, 'saveToFileCache' ] );
1933  }
1934  } else {
1935  wfDebug( "Article::tryFileCache(): not cacheable\n" );
1936  }
1937 
1938  return false;
1939  }
1940 
1946  public function isFileCacheable( $mode = HTMLFileCache::MODE_NORMAL ) {
1947  $cacheable = false;
1948 
1949  if ( HTMLFileCache::useFileCache( $this->getContext(), $mode ) ) {
1950  $cacheable = $this->mPage->getId()
1951  && !$this->mRedirectedFrom && !$this->getTitle()->isRedirect();
1952  // Extension may have reason to disable file caching on some pages.
1953  if ( $cacheable ) {
1954  // Avoid PHP 7.1 warning of passing $this by reference
1955  $articlePage = $this;
1956  $cacheable = Hooks::run( 'IsFileCacheable', [ &$articlePage ] );
1957  }
1958  }
1959 
1960  return $cacheable;
1961  }
1962 
1976  public function getParserOutput( $oldid = null, User $user = null ) {
1977  // XXX: bypasses mParserOptions and thus setParserOptions()
1978 
1979  if ( $user === null ) {
1980  $parserOptions = $this->getParserOptions();
1981  } else {
1982  $parserOptions = $this->mPage->makeParserOptions( $user );
1983  }
1984 
1985  return $this->mPage->getParserOutput( $parserOptions, $oldid );
1986  }
1987 
1995  if ( $this->mParserOptions ) {
1996  throw new MWException( "can't change parser options after they have already been set" );
1997  }
1998 
1999  // clone, so if $options is modified later, it doesn't confuse the parser cache.
2000  $this->mParserOptions = clone $options;
2001  }
2002 
2007  public function getParserOptions() {
2008  if ( !$this->mParserOptions ) {
2009  $this->mParserOptions = $this->mPage->makeParserOptions( $this->getContext() );
2010  }
2011  // Clone to allow modifications of the return value without affecting cache
2012  return clone $this->mParserOptions;
2013  }
2014 
2021  public function setContext( $context ) {
2022  $this->mContext = $context;
2023  }
2024 
2031  public function getContext() {
2032  if ( $this->mContext instanceof IContextSource ) {
2033  return $this->mContext;
2034  } else {
2035  wfDebug( __METHOD__ . " called and \$mContext is null. " .
2036  "Return RequestContext::getMain(); for sanity\n" );
2037  return RequestContext::getMain();
2038  }
2039  }
2040 
2048  public function __get( $fname ) {
2049  if ( property_exists( $this->mPage, $fname ) ) {
2050  # wfWarn( "Access to raw $fname field " . __CLASS__ );
2051  return $this->mPage->$fname;
2052  }
2053  trigger_error( 'Inaccessible property via __get(): ' . $fname, E_USER_NOTICE );
2054  }
2055 
2063  public function __set( $fname, $fvalue ) {
2064  if ( property_exists( $this->mPage, $fname ) ) {
2065  # wfWarn( "Access to raw $fname field of " . __CLASS__ );
2066  $this->mPage->$fname = $fvalue;
2067  // Note: extensions may want to toss on new fields
2068  } elseif ( !in_array( $fname, [ 'mContext', 'mPage' ] ) ) {
2069  $this->mPage->$fname = $fvalue;
2070  } else {
2071  trigger_error( 'Inaccessible property via __set(): ' . $fname, E_USER_NOTICE );
2072  }
2073  }
2074 
2079  public function checkFlags( $flags ) {
2080  return $this->mPage->checkFlags( $flags );
2081  }
2082 
2087  public function checkTouched() {
2088  return $this->mPage->checkTouched();
2089  }
2090 
2095  public function clearPreparedEdit() {
2096  $this->mPage->clearPreparedEdit();
2097  }
2098 
2103  public function doDeleteArticleReal(
2104  $reason, $suppress = false, $u1 = null, $u2 = null, &$error = '', User $user = null,
2105  $tags = []
2106  ) {
2107  return $this->mPage->doDeleteArticleReal(
2108  $reason, $suppress, $u1, $u2, $error, $user, $tags
2109  );
2110  }
2111 
2116  public function doDeleteUpdates( $id, Content $content = null ) {
2117  return $this->mPage->doDeleteUpdates( $id, $content );
2118  }
2119 
2125  public function doEditContent( Content $content, $summary, $flags = 0, $baseRevId = false,
2126  User $user = null, $serialFormat = null
2127  ) {
2128  wfDeprecated( __METHOD__, '1.29' );
2129  return $this->mPage->doEditContent( $content, $summary, $flags, $baseRevId,
2130  $user, $serialFormat
2131  );
2132  }
2133 
2138  public function doEditUpdates( Revision $revision, User $user, array $options = [] ) {
2139  return $this->mPage->doEditUpdates( $revision, $user, $options );
2140  }
2141 
2148  public function doPurge() {
2149  return $this->mPage->doPurge();
2150  }
2151 
2156  public function doViewUpdates( User $user, $oldid = 0 ) {
2157  $this->mPage->doViewUpdates( $user, $oldid );
2158  }
2159 
2164  public function exists() {
2165  return $this->mPage->exists();
2166  }
2167 
2172  public function followRedirect() {
2173  return $this->mPage->followRedirect();
2174  }
2175 
2180  public function getActionOverrides() {
2181  return $this->mPage->getActionOverrides();
2182  }
2183 
2188  public function getAutoDeleteReason( &$hasHistory ) {
2189  return $this->mPage->getAutoDeleteReason( $hasHistory );
2190  }
2191 
2196  public function getCategories() {
2197  return $this->mPage->getCategories();
2198  }
2199 
2204  public function getComment( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2205  return $this->mPage->getComment( $audience, $user );
2206  }
2207 
2212  public function getContentHandler() {
2213  return $this->mPage->getContentHandler();
2214  }
2215 
2220  public function getContentModel() {
2221  return $this->mPage->getContentModel();
2222  }
2223 
2228  public function getContributors() {
2229  return $this->mPage->getContributors();
2230  }
2231 
2236  public function getCreator( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2237  return $this->mPage->getCreator( $audience, $user );
2238  }
2239 
2244  public function getDeletionUpdates( Content $content = null ) {
2245  return $this->mPage->getDeletionUpdates( $content );
2246  }
2247 
2252  public function getHiddenCategories() {
2253  return $this->mPage->getHiddenCategories();
2254  }
2255 
2260  public function getId() {
2261  return $this->mPage->getId();
2262  }
2263 
2268  public function getLatest() {
2269  return $this->mPage->getLatest();
2270  }
2271 
2276  public function getLinksTimestamp() {
2277  return $this->mPage->getLinksTimestamp();
2278  }
2279 
2284  public function getMinorEdit() {
2285  return $this->mPage->getMinorEdit();
2286  }
2287 
2292  public function getOldestRevision() {
2293  return $this->mPage->getOldestRevision();
2294  }
2295 
2300  public function getRedirectTarget() {
2301  return $this->mPage->getRedirectTarget();
2302  }
2303 
2308  public function getRedirectURL( $rt ) {
2309  return $this->mPage->getRedirectURL( $rt );
2310  }
2311 
2316  public function getRevision() {
2317  return $this->mPage->getRevision();
2318  }
2319 
2324  public function getTimestamp() {
2325  return $this->mPage->getTimestamp();
2326  }
2327 
2332  public function getTouched() {
2333  return $this->mPage->getTouched();
2334  }
2335 
2340  public function getUndoContent( Revision $undo, Revision $undoafter = null ) {
2341  return $this->mPage->getUndoContent( $undo, $undoafter );
2342  }
2343 
2348  public function getUser( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2349  return $this->mPage->getUser( $audience, $user );
2350  }
2351 
2356  public function getUserText( $audience = Revision::FOR_PUBLIC, User $user = null ) {
2357  return $this->mPage->getUserText( $audience, $user );
2358  }
2359 
2364  public function hasViewableContent() {
2365  return $this->mPage->hasViewableContent();
2366  }
2367 
2372  public function insertOn( $dbw, $pageId = null ) {
2373  return $this->mPage->insertOn( $dbw, $pageId );
2374  }
2375 
2380  public function insertProtectNullRevision( $revCommentMsg, array $limit,
2381  array $expiry, $cascade, $reason, $user = null
2382  ) {
2383  return $this->mPage->insertProtectNullRevision( $revCommentMsg, $limit,
2384  $expiry, $cascade, $reason, $user
2385  );
2386  }
2387 
2392  public function insertRedirect() {
2393  return $this->mPage->insertRedirect();
2394  }
2395 
2400  public function insertRedirectEntry( Title $rt, $oldLatest = null ) {
2401  return $this->mPage->insertRedirectEntry( $rt, $oldLatest );
2402  }
2403 
2408  public function isCountable( $editInfo = false ) {
2409  return $this->mPage->isCountable( $editInfo );
2410  }
2411 
2416  public function isRedirect() {
2417  return $this->mPage->isRedirect();
2418  }
2419 
2424  public function loadFromRow( $data, $from ) {
2425  return $this->mPage->loadFromRow( $data, $from );
2426  }
2427 
2432  public function loadPageData( $from = 'fromdb' ) {
2433  $this->mPage->loadPageData( $from );
2434  }
2435 
2440  public function lockAndGetLatest() {
2441  return $this->mPage->lockAndGetLatest();
2442  }
2443 
2448  public function makeParserOptions( $context ) {
2449  return $this->mPage->makeParserOptions( $context );
2450  }
2451 
2456  public function pageDataFromId( $dbr, $id, $options = [] ) {
2457  return $this->mPage->pageDataFromId( $dbr, $id, $options );
2458  }
2459 
2464  public function pageDataFromTitle( $dbr, $title, $options = [] ) {
2465  return $this->mPage->pageDataFromTitle( $dbr, $title, $options );
2466  }
2467 
2472  public function prepareContentForEdit(
2473  Content $content, $revision = null, User $user = null,
2474  $serialFormat = null, $useCache = true
2475  ) {
2476  return $this->mPage->prepareContentForEdit(
2477  $content, $revision, $user,
2478  $serialFormat, $useCache
2479  );
2480  }
2481 
2486  public function protectDescription( array $limit, array $expiry ) {
2487  return $this->mPage->protectDescription( $limit, $expiry );
2488  }
2489 
2494  public function protectDescriptionLog( array $limit, array $expiry ) {
2495  return $this->mPage->protectDescriptionLog( $limit, $expiry );
2496  }
2497 
2502  public function replaceSectionAtRev( $sectionId, Content $sectionContent,
2503  $sectionTitle = '', $baseRevId = null
2504  ) {
2505  return $this->mPage->replaceSectionAtRev( $sectionId, $sectionContent,
2506  $sectionTitle, $baseRevId
2507  );
2508  }
2509 
2514  public function replaceSectionContent(
2515  $sectionId, Content $sectionContent, $sectionTitle = '', $edittime = null
2516  ) {
2517  return $this->mPage->replaceSectionContent(
2518  $sectionId, $sectionContent, $sectionTitle, $edittime
2519  );
2520  }
2521 
2526  public function setTimestamp( $ts ) {
2527  return $this->mPage->setTimestamp( $ts );
2528  }
2529 
2534  public function shouldCheckParserCache( ParserOptions $parserOptions, $oldId ) {
2535  return $this->mPage->shouldCheckParserCache( $parserOptions, $oldId );
2536  }
2537 
2542  public function supportsSections() {
2543  return $this->mPage->supportsSections();
2544  }
2545 
2550  public function triggerOpportunisticLinksUpdate( ParserOutput $parserOutput ) {
2551  return $this->mPage->triggerOpportunisticLinksUpdate( $parserOutput );
2552  }
2553 
2558  public function updateCategoryCounts( array $added, array $deleted, $id = 0 ) {
2559  return $this->mPage->updateCategoryCounts( $added, $deleted, $id );
2560  }
2561 
2566  public function updateIfNewerOn( $dbw, $revision ) {
2567  return $this->mPage->updateIfNewerOn( $dbw, $revision );
2568  }
2569 
2574  public function updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect = null ) {
2575  return $this->mPage->updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect );
2576  }
2577 
2582  public function updateRevisionOn( $dbw, $revision, $lastRevision = null,
2583  $lastRevIsRedirect = null
2584  ) {
2585  return $this->mPage->updateRevisionOn( $dbw, $revision, $lastRevision,
2586  $lastRevIsRedirect
2587  );
2588  }
2589 
2598  public function doUpdateRestrictions( array $limit, array $expiry, &$cascade,
2599  $reason, User $user
2600  ) {
2601  return $this->mPage->doUpdateRestrictions( $limit, $expiry, $cascade, $reason, $user );
2602  }
2603 
2611  public function updateRestrictions( $limit = [], $reason = '',
2612  &$cascade = 0, $expiry = []
2613  ) {
2614  return $this->mPage->doUpdateRestrictions(
2615  $limit,
2616  $expiry,
2617  $cascade,
2618  $reason,
2619  $this->getContext()->getUser()
2620  );
2621  }
2622 
2631  public function doDeleteArticle(
2632  $reason, $suppress = false, $u1 = null, $u2 = null, &$error = ''
2633  ) {
2634  return $this->mPage->doDeleteArticle( $reason, $suppress, $u1, $u2, $error );
2635  }
2636 
2646  public function doRollback( $fromP, $summary, $token, $bot, &$resultDetails, User $user = null ) {
2647  $user = is_null( $user ) ? $this->getContext()->getUser() : $user;
2648  return $this->mPage->doRollback( $fromP, $summary, $token, $bot, $resultDetails, $user );
2649  }
2650 
2659  public function commitRollback( $fromP, $summary, $bot, &$resultDetails, User $guser = null ) {
2660  $guser = is_null( $guser ) ? $this->getContext()->getUser() : $guser;
2661  return $this->mPage->commitRollback( $fromP, $summary, $bot, $resultDetails, $guser );
2662  }
2663 
2668  public function generateReason( &$hasHistory ) {
2669  $title = $this->mPage->getTitle();
2671  return $handler->getAutoDeleteReason( $title, $hasHistory );
2672  }
2673 
2674  // ******
2675 }
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:3497
Revision\FOR_PUBLIC
const FOR_PUBLIC
Definition: Revision.php:55
Article\showMissingArticle
showMissingArticle()
Show the error text for a missing article.
Definition: Article.php:1149
Article\checkFlags
checkFlags( $flags)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2079
$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:247
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
Html\errorBox
static errorBox( $html, $heading='')
Return an error box.
Definition: Html.php:713
$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:903
Message\numParam
static numParam( $num)
Definition: Message.php:1028
Page
Interface for type hinting (accepts WikiPage, Article, ImagePage, CategoryPage)
Definition: Page.php:24
Revision\DELETED_RESTRICTED
const DELETED_RESTRICTED
Definition: Revision.php:50
Article\isRedirect
isRedirect()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2416
Article\getCategories
getCategories()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2196
ParserOutput
Definition: ParserOutput.php:25
Article\formatRobotPolicy
static formatRobotPolicy( $policy)
Converts a String robot policy into an associative array, to allow merging of several policies using ...
Definition: Article.php:833
Article\getRedirectTarget
getRedirectTarget()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2300
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:435
use
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
Definition: APACHE-LICENSE-2.0.txt:10
Revision\newFromId
static newFromId( $id, $flags=0)
Load a page revision from a given revision ID number.
Definition: Revision.php:114
Article\doDeleteUpdates
doDeleteUpdates( $id, Content $content=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2116
$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:2013
Xml\listDropDownOptionsOoui
static listDropDownOptionsOoui( $options)
Convert options for a drop-down box into a format accepted by OOUI\DropdownInputWidget etc.
Definition: Xml.php:582
Article\getContentModel
getContentModel()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2220
Article\getUserText
getUserText( $audience=Revision::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2356
Article\getLinksTimestamp
getLinksTimestamp()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2276
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\$disableSectionEditForRender
bool $disableSectionEditForRender
Whether render() was called.
Definition: Article.php:83
array
the array() calling protocol came about after MediaWiki 1.4rc1.
Article\getOldIDFromRequest
getOldIDFromRequest()
Sets $this->mRedirectUrl to a correct URL if the query parameters are incorrect.
Definition: Article.php:262
Article\showPatrolFooter
showPatrolFooter()
If patrol is possible, output a patrol UI box.
Definition: Article.php:976
$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:194
Article\tryFileCache
tryFileCache()
checkLastModified returns true if it has taken care of all output to the client that is necessary for...
Definition: Article.php:1915
Article\getContentHandler
getContentHandler()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2212
Article\clearPreparedEdit
clearPreparedEdit()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2095
HTMLFileCache
Page view caching in the file system.
Definition: HTMLFileCache.php:33
Article\lockAndGetLatest
lockAndGetLatest()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2440
Article\$mParserOutput
ParserOutput $mParserOutput
Definition: Article.php:76
Article\supportsSections
supportsSections()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2542
Article\getOldestRevision
getOldestRevision()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2292
Article\getDeletionUpdates
getDeletionUpdates(Content $content=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2244
$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:1633
Article\checkTouched
checkTouched()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2087
Article\getRevision
getRevision()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2316
RC_LOG
const RC_LOG
Definition: Defines.php:154
CategoryPage
Special handling for category description pages, showing pages, subcategories and file that belong to...
Definition: CategoryPage.php:28
$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:864
WikiPage
Class representing a MediaWiki article and history.
Definition: WikiPage.php:37
Article\getRobotPolicy
getRobotPolicy( $action, $pOutput=null)
Get the robot policy to be used for the current view.
Definition: Article.php:753
PoolWorkArticleView
Definition: PoolWorkArticleView.php:22
NS_FILE
const NS_FILE
Definition: Defines.php:80
Article\doDeleteArticle
doDeleteArticle( $reason, $suppress=false, $u1=null, $u2=null, &$error='')
Definition: Article.php:2631
Article\getCreator
getCreator( $audience=Revision::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2236
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:1173
wfReadOnly
wfReadOnly()
Check whether the wiki is in read-only mode.
Definition: GlobalFunctions.php:1262
User\newFromName
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition: User.php:591
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:703
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:8006
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:1693
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:2558
Article\adjustDisplayTitle
adjustDisplayTitle(ParserOutput $pOutput)
Adjust title for pages with displaytitle, -{T|}- or language conversion.
Definition: Article.php:691
Article\showNamespaceHeader
showNamespaceHeader()
Show a header specific to the namespace currently being viewed, like [[MediaWiki:Talkpagetext]].
Definition: Article.php:939
Article\shouldCheckParserCache
shouldCheckParserCache(ParserOptions $parserOptions, $oldId)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2534
$wgUseRCPatrol
$wgUseRCPatrol
Use RC Patrolling to check for vandalism (from recent changes and watchlists) New pages and new files...
Definition: DefaultSettings.php:6860
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:6895
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:37
Article\protectDescriptionLog
protectDescriptionLog(array $limit, array $expiry)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2494
$dbr
$dbr
Definition: testCompression.php:50
Revision\FOR_THIS_USER
const FOR_THIS_USER
Definition: Revision.php:56
Revision
Definition: Revision.php:41
Article\unprotect
unprotect()
action=unprotect handler (alias)
Definition: Article.php:1558
Article\insertRedirect
insertRedirect()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2392
Article\getRedirectedFrom
getRedirectedFrom()
Get the page this view was redirected from.
Definition: Article.php:163
Article\fetchContentObject
fetchContentObject()
Get text content object Does NOT follow redirects.
Definition: Article.php:320
Article\clear
clear()
Clear the object.
Definition: Article.php:198
Article\updateRedirectOn
updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2574
Article\newFromWikiPage
static newFromWikiPage(WikiPage $page, IContextSource $context)
Create an Article object of the appropriate class for the given page.
Definition: Article.php:152
Article\getTouched
getTouched()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2332
MWException
MediaWiki exception.
Definition: MWException.php:26
Article\getTitle
getTitle()
Get the title object of the article.
Definition: Article.php:181
Article\updateRevisionOn
updateRevisionOn( $dbw, $revision, $lastRevision=null, $lastRevIsRedirect=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2582
Article\render
render()
Handle action=render.
Definition: Article.php:1540
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
Definition: GlobalFunctions.php:1123
Article\insertProtectNullRevision
insertProtectNullRevision( $revCommentMsg, array $limit, array $expiry, $cascade, $reason, $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2380
Article\addHelpLink
addHelpLink( $to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
Definition: Article.php:1523
Article\exists
exists()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2164
Article\setRedirectedFrom
setRedirectedFrom(Title $from)
Tell the page view functions that this view was redirected from another page on the wiki.
Definition: Article.php:172
$wgUseFileCache
$wgUseFileCache
This will cache static pages for non-logged-in users to reduce database traffic on public sites.
Definition: DefaultSettings.php:2580
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2812
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:1070
Article\__construct
__construct(Title $title, $oldId=null)
Constructor and clear the article.
Definition: Article.php:90
Article\showDeletedRevisionHeader
showDeletedRevisionHeader()
If the revision requested for view is deleted, check permissions.
Definition: Article.php:1283
IExpiringStore\TTL_DAY
const TTL_DAY
Definition: IExpiringStore.php:36
Article\isFileCacheable
isFileCacheable( $mode=HTMLFileCache::MODE_NORMAL)
Check if the page can be cached.
Definition: Article.php:1946
Article\doRollback
doRollback( $fromP, $summary, $token, $bot, &$resultDetails, User $user=null)
Definition: Article.php:2646
WikiPage\getTitle
getTitle()
Get the title object of the article.
Definition: WikiPage.php:236
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:1326
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:191
Article\getContext
getContext()
Gets the context this Article is executed in.
Definition: Article.php:2031
User\isIP
static isIP( $name)
Does the string match an anonymous IP address?
Definition: User.php:943
WatchAction\doWatchOrUnwatch
static doWatchOrUnwatch( $watch, Title $title, User $user)
Watch or unwatch a page.
Definition: WatchAction.php:91
Article\getComment
getComment( $audience=Revision::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2204
$link
usually copyright or history_copyright This message must be in HTML not wikitext & $link
Definition: hooks.txt:3021
Article\pageDataFromId
pageDataFromId( $dbr, $id, $options=[])
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2456
$title
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:964
Article\insertOn
insertOn( $dbw, $pageId=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2372
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:534
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:95
Article\getRedirectURL
getRedirectURL( $rt)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2308
LogEventsList\showLogExtract
static showLogExtract(&$out, $types=[], $page='', $user='', $param=[])
Show log extract.
Definition: LogEventsList.php:616
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
Article\newFromID
static newFromID( $id)
Constructor from a page id.
Definition: Article.php:108
Article\newPage
newPage(Title $title)
Definition: Article.php:99
NS_CATEGORY
const NS_CATEGORY
Definition: Defines.php:88
Article\getUndoContent
getUndoContent(Revision $undo, Revision $undoafter=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2340
Article\prepareContentForEdit
prepareContentForEdit(Content $content, $revision=null, User $user=null, $serialFormat=null, $useCache=true)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2472
$wgDeleteRevisionsLimit
$wgDeleteRevisionsLimit
Optional to restrict deletion of pages with higher revision counts to users with the 'bigdelete' perm...
Definition: DefaultSettings.php:5530
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:2125
$wgUseFilePatrol
$wgUseFilePatrol
Use file patrolling to check new files on Special:Newfiles.
Definition: DefaultSettings.php:6906
Article\getLatest
getLatest()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2268
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:994
Article\isCurrent
isCurrent()
Returns true if the currently-referenced revision is the current edit to this page (and it exists).
Definition: Article.php:396
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
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:2188
Article\$mContentLoaded
bool $mContentLoaded
Is the content ($mContent) already loaded?
Definition: Article.php:58
MIGRATION_OLD
const MIGRATION_OLD
Definition: Defines.php:302
Article\setParserOptions
setParserOptions(ParserOptions $options)
Override the ParserOptions used to render the primary article wikitext.
Definition: Article.php:1994
Article\isCountable
isCountable( $editInfo=false)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2408
$fname
if(defined( 'MW_SETUP_CALLBACK')) $fname
Customization point after all loading (constants, functions, classes, DefaultSettings,...
Definition: Setup.php:112
Article\$mRedirectedFrom
Title $mRedirectedFrom
Title from which we were redirected here.
Definition: Article.php:64
$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:2001
Article\doDelete
doDelete( $reason, $suppress=false)
Perform a deletion and output success or failure messages.
Definition: Article.php:1862
Html\hidden
static hidden( $name, $value, array $attribs=[])
Convenience function to produce an input element with type=hidden.
Definition: Html.php:774
ParserOutput\getTitleText
getTitleText()
Definition: ParserOutput.php:408
Article\doPurge
doPurge()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2148
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:1480
NS_USER_TALK
const NS_USER_TALK
Definition: Defines.php:77
Article\hasViewableContent
hasViewableContent()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2364
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:62
Article\getContributors
getContributors()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2228
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:2424
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:2053
Article\protect
protect()
action=protect handler
Definition: Article.php:1550
Article\getTimestamp
getTimestamp()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2324
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:1643
Article\getMinorEdit
getMinorEdit()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2284
Article\commitRollback
commitRollback( $fromP, $summary, $bot, &$resultDetails, User $guser=null)
Definition: Article.php:2659
$wgNamespaceRobotPolicies
$wgNamespaceRobotPolicies
Robot policies per namespaces.
Definition: DefaultSettings.php:7978
Article\getParserOutput
getParserOutput( $oldid=null, User $user=null)
#-
Definition: Article.php:1976
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:2348
Article\getContentObject
getContentObject()
Returns a Content object representing the pages effective display content, not necessarily the revisi...
Definition: Article.php:223
$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). 'DeleteUnknownPreferences':Called by the cleanupPreferences.php maintenance script to build a WHERE clause with which to delete preferences that are not known about. This hook is used by extensions that have dynamically-named preferences that should not be deleted in the usual cleanup process. For example, the Gadgets extension creates preferences prefixed with 'gadget-', and so anything with that prefix is excluded from the deletion. &where:An array that will be passed as the $cond parameter to IDatabase::select() to determine what will be deleted from the user_properties table. $db:The IDatabase object, useful for accessing $db->buildLike() etc. '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:1255
Article\followRedirect
followRedirect()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2172
Block\TYPE_AUTO
const TYPE_AUTO
Definition: Block.php:86
Article\getHiddenCategories
getHiddenCategories()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2252
RequestContext\getMain
static getMain()
Get the RequestContext object associated with the main request.
Definition: RequestContext.php:434
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:2526
IContextSource
Interface for objects which can provide a MediaWiki context on request.
Definition: IContextSource.php:53
Article\__get
__get( $fname)
Use PHP's magic __get handler to handle accessing of raw WikiPage fields for backwards compatibility.
Definition: Article.php:2048
Content
Base interface for content objects.
Definition: Content.php:34
CommentStore\COMMENT_CHARACTER_LIMIT
const COMMENT_CHARACTER_LIMIT
Maximum length of a comment in UTF-8 characters.
Definition: CommentStore.php:37
Article\replaceSectionAtRev
replaceSectionAtRev( $sectionId, Content $sectionContent, $sectionTitle='', $baseRevId=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2502
$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:1777
Title
Represents a title within MediaWiki.
Definition: Title.php:39
Skin\makeUrl
static makeUrl( $name, $urlaction='')
Definition: Skin.php:1153
$cache
$cache
Definition: mcc.php:33
Article\replaceSectionContent
replaceSectionContent( $sectionId, Content $sectionContent, $sectionTitle='', $edittime=null)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2514
Article\getRevIdFetched
getRevIdFetched()
Use this to fetch the rev ID used on page views.
Definition: Article.php:423
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:8047
Article\makeParserOptions
makeParserOptions( $context)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2448
Article\showRedirectedFromHeader
showRedirectedFromHeader()
If this request is a redirect view, send "redirected from" subtitle to the output.
Definition: Article.php:862
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:22
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:1153
NS_USER
const NS_USER
Definition: Defines.php:76
Article\getId
getId()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2260
Article\getActionOverrides
getActionOverrides()
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2180
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:2598
Article\updateRestrictions
updateRestrictions( $limit=[], $reason='', &$cascade=0, $expiry=[])
Definition: Article.php:2611
in
null for the wiki Added in
Definition: hooks.txt:1591
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:2486
Article\getParserOptions
getParserOptions()
Get parser options suitable for rendering the primary article wikitext.
Definition: Article.php:2007
$wgRedirectSources
$wgRedirectSources
If local interwikis are set up which allow redirects, set this regexp to restrict URLs which will be ...
Definition: DefaultSettings.php:4033
Article\updateIfNewerOn
updateIfNewerOn( $dbw, $revision)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2566
NS_MEDIAWIKI
const NS_MEDIAWIKI
Definition: Defines.php:82
$t
$t
Definition: testCompression.php:69
Article
Class for viewing MediaWiki article and history.
Definition: Article.php:35
$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:2806
Article\pageDataFromTitle
pageDataFromTitle( $dbr, $title, $options=[])
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2464
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:25
$wgDebugToolbar
$wgDebugToolbar
Display the new debugging toolbar.
Definition: DefaultSettings.php:6445
Article\triggerOpportunisticLinksUpdate
triggerOpportunisticLinksUpdate(ParserOutput $parserOutput)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2550
$wgEnableAPI
$wgEnableAPI
Enable the MediaWiki API for convenient access to machine-readable data via api.php.
Definition: DefaultSettings.php:8038
$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:1620
Article\getOldID
getOldID()
Definition: Article.php:249
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:201
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:53
Article\setContext
setContext( $context)
Sets the context this Article is executed in.
Definition: Article.php:2021
Title\newFromID
static newFromID( $id, $flags=0)
Create a new Title from an article ID.
Definition: Title.php:416
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:2400
$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:2811
$wgDefaultRobotPolicy
$wgDefaultRobotPolicy
Default robot policy.
Definition: DefaultSettings.php:7962
Article\getRevisionFetched
getRevisionFetched()
Get the fetched Revision object depending on request parameters or null on failure.
Definition: Article.php:412
MWNamespace\getCanonicalName
static getCanonicalName( $index)
Returns the canonical (English) name for a given index.
Definition: MWNamespace.php:255
Article\viewRedirect
viewRedirect( $target, $appendSubtitle=true, $forceKnown=false)
Return the HTML for the top of a redirect page.
Definition: Article.php:1466
Revision\DELETED_TEXT
const DELETED_TEXT
Definition: Revision.php:47
$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\loadPageData
loadPageData( $from='fromdb')
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2432
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:2138
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:2063
Article\showViewFooter
showViewFooter()
Show the footer section of an ordinary page view.
Definition: Article.php:953
Article\$mPage
WikiPage $mPage
The WikiPage object of this instance.
Definition: Article.php:40
Article\getRedirectHeaderHtml
static getRedirectHeaderHtml(Language $lang, $target, $forceKnown=false)
Return the HTML for the top of a redirect page.
Definition: Article.php:1488
$e
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException' returning false will NOT prevent logging $e
Definition: hooks.txt:2171
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:2103
Xml\listDropDownOptions
static listDropDownOptions( $list, $params=[])
Build options for a drop-down box from a textual list.
Definition: Xml.php:540
Article\newFromTitle
static newFromTitle( $title, IContextSource $context)
Create an Article object of the appropriate class for the given page.
Definition: Article.php:120
Article\generateReason
generateReason(&$hasHistory)
Definition: Article.php:2668
Article\purgePatrolFooterCache
static purgePatrolFooterCache( $articleID)
Purge the cache used to check if it is worth showing the patrol footer For example,...
Definition: Article.php:1140
Article\doViewUpdates
doViewUpdates(User $user, $oldid=0)
Call to WikiPage function for backwards compatibility.
Definition: Article.php:2156