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