MediaWiki REL1_31
Article.php
Go to the documentation of this file.
1<?php
23
35class 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() {
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 ) {
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(
367 Revision::FOR_THIS_USER,
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
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 ) {
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
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
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;
1026 $rc = RecentChange::newFromConds(
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;
1060 $rc = RecentChange::newFromConds(
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
1726 $options = Xml::listDropDownOptions(
1727 $ctx->msg( 'deletereason-dropdown' )->inContentLanguage()->text(),
1728 [ 'other' => $ctx->msg( 'deletereasonotherlist' )->inContentLanguage()->text() ]
1729 );
1730 $options = Xml::listDropDownOptionsOoui( $options );
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();
2670 $handler = ContentHandler::getForTitle( $title );
2671 return $handler->getAutoDeleteReason( $title, $hasHistory );
2672 }
2673
2674 // ******
2675}
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
$wgArticleRobotPolicies
Robot policies per article.
$wgDefaultRobotPolicy
Default robot policy.
$wgSend404Code
Some web hosts attempt to rewrite all responses with a 404 (not found) status code,...
$wgRedirectSources
If local interwikis are set up which allow redirects, set this regexp to restrict URLs which will be ...
$wgUseFilePatrol
Use file patrolling to check new files on Special:Newfiles.
$wgUseRCPatrol
Use RC Patrolling to check for vandalism (from recent changes and watchlists) New pages and new files...
$wgEnableWriteAPI
Allow the API to be used to perform write operations (page edits, rollback, etc.) when an authorised ...
$wgNamespaceRobotPolicies
Robot policies per namespaces.
$wgUseNPPatrol
Use new page patrolling to check new pages on Special:Newpages.
$wgDeleteRevisionsLimit
Optional to restrict deletion of pages with higher revision counts to users with the 'bigdelete' perm...
$wgDebugToolbar
Display the new debugging toolbar.
$wgEnableAPI
Enable the MediaWiki API for convenient access to machine-readable data via api.php.
$wgUseFileCache
This will cache static pages for non-logged-in users to reduce database traffic on public sites.
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfReadOnly()
Check whether the wiki is in read-only mode.
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
if(defined( 'MW_SETUP_CALLBACK')) $fname
Customization point after all loading (constants, functions, classes, DefaultSettings,...
Definition Setup.php:112
Class for viewing MediaWiki article and history.
Definition Article.php:35
shouldCheckParserCache(ParserOptions $parserOptions, $oldId)
Call to WikiPage function for backwards compatibility.
Definition Article.php:2534
getRevisionFetched()
Get the fetched Revision object depending on request parameters or null on failure.
Definition Article.php:412
ParserOutput $mParserOutput
Definition Article.php:76
Title $mRedirectedFrom
Title from which we were redirected here.
Definition Article.php:64
updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect=null)
Call to WikiPage function for backwards compatibility.
Definition Article.php:2574
generateReason(&$hasHistory)
Definition Article.php:2668
checkFlags( $flags)
Call to WikiPage function for backwards compatibility.
Definition Article.php:2079
doDeleteArticleReal( $reason, $suppress=false, $u1=null, $u2=null, &$error='', User $user=null, $tags=[])
Call to WikiPage function for backwards compatibility.
Definition Article.php:2103
checkTouched()
Call to WikiPage function for backwards compatibility.
Definition Article.php:2087
doDelete( $reason, $suppress=false)
Perform a deletion and output success or failure messages.
Definition Article.php:1862
getHiddenCategories()
Call to WikiPage function for backwards compatibility.
Definition Article.php:2252
static newFromWikiPage(WikiPage $page, IContextSource $context)
Create an Article object of the appropriate class for the given page.
Definition Article.php:152
doViewUpdates(User $user, $oldid=0)
Call to WikiPage function for backwards compatibility.
Definition Article.php:2156
getContext()
Gets the context this Article is executed in.
Definition Article.php:2031
getCategories()
Call to WikiPage function for backwards compatibility.
Definition Article.php:2196
commitRollback( $fromP, $summary, $bot, &$resultDetails, User $guser=null)
Definition Article.php:2659
getOldIDFromRequest()
Sets $this->mRedirectUrl to a correct URL if the query parameters are incorrect.
Definition Article.php:262
doEditContent(Content $content, $summary, $flags=0, $baseRevId=false, User $user=null, $serialFormat=null)
Call to WikiPage function for backwards compatibility.
Definition Article.php:2125
getParserOutput( $oldid=null, User $user=null)
#-
Definition Article.php:1976
insertProtectNullRevision( $revCommentMsg, array $limit, array $expiry, $cascade, $reason, $user=null)
Call to WikiPage function for backwards compatibility.
Definition Article.php:2380
doPurge()
Call to WikiPage function for backwards compatibility.
Definition Article.php:2148
getRedirectedFrom()
Get the page this view was redirected from.
Definition Article.php:163
getUserText( $audience=Revision::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition Article.php:2356
insertOn( $dbw, $pageId=null)
Call to WikiPage function for backwards compatibility.
Definition Article.php:2372
updateRevisionOn( $dbw, $revision, $lastRevision=null, $lastRevIsRedirect=null)
Call to WikiPage function for backwards compatibility.
Definition Article.php:2582
IContextSource $mContext
The context this Article is executed in.
Definition Article.php:37
fetchContentObject()
Get text content object Does NOT follow redirects.
Definition Article.php:320
supportsSections()
Call to WikiPage function for backwards compatibility.
Definition Article.php:2542
string $mContent
Text of the revision we are working on.
Definition Article.php:49
pageDataFromTitle( $dbr, $title, $options=[])
Call to WikiPage function for backwards compatibility.
Definition Article.php:2464
bool $disableSectionEditForRender
Whether render() was called.
Definition Article.php:83
getContentHandler()
Call to WikiPage function for backwards compatibility.
Definition Article.php:2212
updateCategoryCounts(array $added, array $deleted, $id=0)
Call to WikiPage function for backwards compatibility.
Definition Article.php:2558
view()
This is the default action of the index.php entry point: just view the page of the given title.
Definition Article.php:435
loadPageData( $from='fromdb')
Call to WikiPage function for backwards compatibility.
Definition Article.php:2432
getRobotPolicy( $action, $pOutput=null)
Get the robot policy to be used for the current view.
Definition Article.php:753
doUpdateRestrictions(array $limit, array $expiry, &$cascade, $reason, User $user)
Definition Article.php:2598
protectDescriptionLog(array $limit, array $expiry)
Call to WikiPage function for backwards compatibility.
Definition Article.php:2494
clearPreparedEdit()
Call to WikiPage function for backwards compatibility.
Definition Article.php:2095
__construct(Title $title, $oldId=null)
Constructor and clear the article.
Definition Article.php:90
static purgePatrolFooterCache( $articleID)
Purge the cache used to check if it is worth showing the patrol footer For example,...
Definition Article.php:1140
followRedirect()
Call to WikiPage function for backwards compatibility.
Definition Article.php:2172
prepareContentForEdit(Content $content, $revision=null, User $user=null, $serialFormat=null, $useCache=true)
Call to WikiPage function for backwards compatibility.
Definition Article.php:2472
replaceSectionAtRev( $sectionId, Content $sectionContent, $sectionTitle='', $baseRevId=null)
Call to WikiPage function for backwards compatibility.
Definition Article.php:2502
getAutoDeleteReason(&$hasHistory)
Call to WikiPage function for backwards compatibility.
Definition Article.php:2188
getLinksTimestamp()
Call to WikiPage function for backwards compatibility.
Definition Article.php:2276
getOldID()
Definition Article.php:249
protectDescription(array $limit, array $expiry)
Call to WikiPage function for backwards compatibility.
Definition Article.php:2486
getOldestRevision()
Call to WikiPage function for backwards compatibility.
Definition Article.php:2292
setTimestamp( $ts)
Call to WikiPage function for backwards compatibility.
Definition Article.php:2526
getTitle()
Get the title object of the article.
Definition Article.php:181
getActionOverrides()
Call to WikiPage function for backwards compatibility.
Definition Article.php:2180
doEditUpdates(Revision $revision, User $user, array $options=[])
Call to WikiPage function for backwards compatibility.
Definition Article.php:2138
adjustDisplayTitle(ParserOutput $pOutput)
Adjust title for pages with displaytitle, -{T|}- or language conversion.
Definition Article.php:691
updateIfNewerOn( $dbw, $revision)
Call to WikiPage function for backwards compatibility.
Definition Article.php:2566
getLatest()
Call to WikiPage function for backwards compatibility.
Definition Article.php:2268
pageDataFromId( $dbr, $id, $options=[])
Call to WikiPage function for backwards compatibility.
Definition Article.php:2456
insertRedirect()
Call to WikiPage function for backwards compatibility.
Definition Article.php:2392
doDeleteUpdates( $id, Content $content=null)
Call to WikiPage function for backwards compatibility.
Definition Article.php:2116
getContributors()
Call to WikiPage function for backwards compatibility.
Definition Article.php:2228
showDeletedRevisionHeader()
If the revision requested for view is deleted, check permissions.
Definition Article.php:1283
isCountable( $editInfo=false)
Call to WikiPage function for backwards compatibility.
Definition Article.php:2408
getParserOptions()
Get parser options suitable for rendering the primary article wikitext.
Definition Article.php:2007
getCreator( $audience=Revision::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition Article.php:2236
clear()
Clear the object.
Definition Article.php:198
getComment( $audience=Revision::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition Article.php:2204
Content $mContentObject
Content of the revision we are working on.
Definition Article.php:55
getContentModel()
Call to WikiPage function for backwards compatibility.
Definition Article.php:2220
static getRedirectHeaderHtml(Language $lang, $target, $forceKnown=false)
Return the HTML for the top of a redirect page.
Definition Article.php:1488
protect()
action=protect handler
Definition Article.php:1550
lockAndGetLatest()
Call to WikiPage function for backwards compatibility.
Definition Article.php:2440
isCurrent()
Returns true if the currently-referenced revision is the current edit to this page (and it exists).
Definition Article.php:396
updateRestrictions( $limit=[], $reason='', &$cascade=0, $expiry=[])
Definition Article.php:2611
showMissingArticle()
Show the error text for a missing article.
Definition Article.php:1149
getUndoContent(Revision $undo, Revision $undoafter=null)
Call to WikiPage function for backwards compatibility.
Definition Article.php:2340
__set( $fname, $fvalue)
Use PHP's magic __set handler to handle setting of raw WikiPage fields for backwards compatibility.
Definition Article.php:2063
doDeleteArticle( $reason, $suppress=false, $u1=null, $u2=null, &$error='')
Definition Article.php:2631
getId()
Call to WikiPage function for backwards compatibility.
Definition Article.php:2260
unprotect()
action=unprotect handler (alias)
Definition Article.php:1558
int $mRevIdFetched
Revision ID of revision we are working on.
Definition Article.php:70
isRedirect()
Call to WikiPage function for backwards compatibility.
Definition Article.php:2416
newPage(Title $title)
Definition Article.php:99
confirmDelete( $reason)
Output deletion confirmation dialog.
Definition Article.php:1693
getPage()
Get the WikiPage object of this instance.
Definition Article.php:191
addHelpLink( $to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
Definition Article.php:1523
getTouched()
Call to WikiPage function for backwards compatibility.
Definition Article.php:2332
getRevision()
Call to WikiPage function for backwards compatibility.
Definition Article.php:2316
string bool $mRedirectUrl
URL to redirect to or false if none.
Definition Article.php:67
getTimestamp()
Call to WikiPage function for backwards compatibility.
Definition Article.php:2324
static newFromID( $id)
Constructor from a page id.
Definition Article.php:108
getRedirectTarget()
Call to WikiPage function for backwards compatibility.
Definition Article.php:2300
getUser( $audience=Revision::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition Article.php:2348
int null $mOldId
The oldid of the article that is to be shown, 0 for the current revision.
Definition Article.php:61
static formatRobotPolicy( $policy)
Converts a String robot policy into an associative array, to allow merging of several policies using ...
Definition Article.php:833
Revision $mRevision
Revision we are working on.
Definition Article.php:73
viewRedirect( $target, $appendSubtitle=true, $forceKnown=false)
Return the HTML for the top of a redirect page.
Definition Article.php:1466
insertRedirectEntry(Title $rt, $oldLatest=null)
Call to WikiPage function for backwards compatibility.
Definition Article.php:2400
triggerOpportunisticLinksUpdate(ParserOutput $parserOutput)
Call to WikiPage function for backwards compatibility.
Definition Article.php:2550
makeParserOptions( $context)
Call to WikiPage function for backwards compatibility.
Definition Article.php:2448
showPatrolFooter()
If patrol is possible, output a patrol UI box.
Definition Article.php:976
setOldSubtitle( $oldid=0)
Generate the navigation links when browsing through an article revisions It shows the information as:...
Definition Article.php:1326
ParserOptions $mParserOptions
ParserOptions object for $wgUser articles.
Definition Article.php:43
showViewFooter()
Show the footer section of an ordinary page view.
Definition Article.php:953
WikiPage $mPage
The WikiPage object of this instance.
Definition Article.php:40
loadFromRow( $data, $from)
Call to WikiPage function for backwards compatibility.
Definition Article.php:2424
setRedirectedFrom(Title $from)
Tell the page view functions that this view was redirected from another page on the wiki.
Definition Article.php:172
isFileCacheable( $mode=HTMLFileCache::MODE_NORMAL)
Check if the page can be cached.
Definition Article.php:1946
tryFileCache()
checkLastModified returns true if it has taken care of all output to the client that is necessary for...
Definition Article.php:1915
getRevIdFetched()
Use this to fetch the rev ID used on page views.
Definition Article.php:423
replaceSectionContent( $sectionId, Content $sectionContent, $sectionTitle='', $edittime=null)
Call to WikiPage function for backwards compatibility.
Definition Article.php:2514
showNamespaceHeader()
Show a header specific to the namespace currently being viewed, like [[MediaWiki:Talkpagetext]].
Definition Article.php:939
__get( $fname)
Use PHP's magic __get handler to handle accessing of raw WikiPage fields for backwards compatibility.
Definition Article.php:2048
static newFromTitle( $title, IContextSource $context)
Create an Article object of the appropriate class for the given page.
Definition Article.php:120
hasViewableContent()
Call to WikiPage function for backwards compatibility.
Definition Article.php:2364
getContentObject()
Returns a Content object representing the pages effective display content, not necessarily the revisi...
Definition Article.php:223
showDiffPage()
Show a diff page according to current request variables.
Definition Article.php:703
getMinorEdit()
Call to WikiPage function for backwards compatibility.
Definition Article.php:2284
setParserOptions(ParserOptions $options)
Override the ParserOptions used to render the primary article wikitext.
Definition Article.php:1994
render()
Handle action=render.
Definition Article.php:1540
showRedirectedFromHeader()
If this request is a redirect view, send "redirected from" subtitle to the output.
Definition Article.php:862
exists()
Call to WikiPage function for backwards compatibility.
Definition Article.php:2164
setContext( $context)
Sets the context this Article is executed in.
Definition Article.php:2021
getRedirectURL( $rt)
Call to WikiPage function for backwards compatibility.
Definition Article.php:2308
bool $mContentLoaded
Is the content ($mContent) already loaded?
Definition Article.php:58
getDeletionUpdates(Content $content=null)
Call to WikiPage function for backwards compatibility.
Definition Article.php:2244
doRollback( $fromP, $summary, $token, $bot, &$resultDetails, User $user=null)
Definition Article.php:2646
const TYPE_AUTO
Definition Block.php:86
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
Special handling for category description pages, showing pages, subcategories and file that belong to...
const POST_EDIT_COOKIE_KEY_PREFIX
Prefix of key for cookie used to pass post-edit state.
Definition EditPage.php:201
Page view caching in the file system.
static useFileCache(IContextSource $context, $mode=self::MODE_NORMAL)
Check if pages can be cached for this request/user.
Class for viewing MediaWiki file description pages.
Definition ImagePage.php:30
Internationalisation code.
Definition Language.php:35
static link( $target, $html=null, $customAttribs=[], $query=[], $options=[])
This function returns an HTML link to the given target.
Definition Linker.php:107
static linkKnown( $target, $html=null, $customAttribs=[], $query=[], $options=[ 'known'])
Identical to link(), except $options defaults to 'known'.
Definition Linker.php:164
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
static revUserTools( $rev, $isPublic=false)
Generate a user tool link cluster if the current user is allowed to view it.
Definition Linker.php:1070
static makeExternalLink( $url, $text, $escape=true, $linktype='', $attribs=[], $title=null)
Make an external link.
Definition Linker.php:843
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
static showLogExtract(&$out, $types=[], $page='', $user='', $param=[])
Show log extract.
Class to simplify the use of log pages.
Definition LogPage.php:31
MediaWiki exception.
MediaWikiServices is the service locator for the application scope of MediaWiki.
Wrapper allowing us to handle a system message as a Content object.
Set options of the Parser.
Show an error when a user tries to do something they do not have the necessary permissions for.
Handles the page protection UI and backend.
Show an error when the wiki is locked/read-only and the user tries to do something that requires writ...
static getMain()
Get the RequestContext object associated with the main request.
Represents a title within MediaWiki.
Definition Title.php:39
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition User.php:53
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition User.php:591
static isIP( $name)
Does the string match an anonymous IP address?
Definition User.php:943
static doWatchOrUnwatch( $watch, Title $title, User $user)
Watch or unwatch a page.
Class representing a MediaWiki article and history.
Definition WikiPage.php:37
getTitle()
Get the title object of the article.
Definition WikiPage.php:236
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
when a variable name is used in a it is silently declared as a new local masking the global
Definition design.txt:95
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
const NS_USER
Definition Defines.php:76
const NS_FILE
Definition Defines.php:80
const NS_MEDIAWIKI
Definition Defines.php:82
const RC_LOG
Definition Defines.php:154
const NS_MEDIA
Definition Defines.php:62
const NS_USER_TALK
Definition Defines.php:77
const MIGRATION_OLD
Definition Defines.php:302
const NS_CATEGORY
Definition Defines.php:88
the array() calling protocol came about after MediaWiki 1.4rc1.
null for the local wiki Added should default to null in handler for backwards compatibility add a value to it if you want to add a cookie that have to vary cache options can modify as strings Extensions should add to this list prev or next refreshes the diff cache $unhide
Definition hooks.txt:1633
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
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
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
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
namespace and then decline to actually register it file or subcat img or subcat $title
Definition hooks.txt:964
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;div ...>$1&lt;/div>"). - flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException':Called before an exception(or PHP error) is logged. This is meant for integration with external error aggregation services
null for the local wiki Added in
Definition hooks.txt:1591
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
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
usually copyright or history_copyright This message must be in HTML not wikitext & $link
Definition hooks.txt:3021
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
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
null for the local wiki Added should default to null in handler for backwards compatibility add a value to it if you want to add a cookie that have to vary cache options can modify $query
Definition hooks.txt:1620
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
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a local account $user
Definition hooks.txt:247
returning false will NOT prevent logging $e
Definition hooks.txt:2176
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
Base interface for content objects.
Definition Content.php:34
Interface for objects which can provide a MediaWiki context on request.
Interface for type hinting (accepts WikiPage, Article, ImagePage, CategoryPage)
Definition Page.php:24
$cache
Definition mcc.php:33
const DB_REPLICA
Definition defines.php:25
if(!isset( $args[0])) $lang