MediaWiki fundraising/REL1_35
Article.php
Go to the documentation of this file.
1<?php
25use MediaWiki\HookContainer\ProtectedHookAccessorTrait;
33use Wikimedia\IPUtils;
35
46class Article implements Page {
47 use ProtectedHookAccessorTrait;
48
54 protected $mContext;
55
57 protected $mPage;
58
64
74
81 public $mContentLoaded = false;
82
88 public $mOldId;
89
91 public $mRedirectedFrom = null;
92
94 public $mRedirectUrl = false;
95
101 public $mRevIdFetched = 0;
102
111 private $fetchResult = null;
112
118 public $mParserOutput = null;
119
125 protected $viewIsRenderAction = false;
126
130 protected $linkRenderer;
131
136
141
142 /*
143 * @var RevisionRecord|null Revision to be shown
144 *
145 * Initialized by getOldIDFromRequest() or fetchRevisionRecord(). Normally loaded from the
146 * database, but may be replaced by an extension, or be a fake representing an error message
147 * or some such. While the output of Article::view is typically based on this revision,
148 * it may be overwritten by error messages or replaced by extensions.
149 *
150 * Replaced $mRevision, which was public and is provided in a deprecated manner via
151 * __get and __set
152 */
153 private $mRevisionRecord = null;
154
159 public function __construct( Title $title, $oldId = null ) {
160 $this->mOldId = $oldId;
161 $this->mPage = $this->newPage( $title );
162
163 $services = MediaWikiServices::getInstance();
164 $this->linkRenderer = $services->getLinkRenderer();
165 $this->permManager = $services->getPermissionManager();
166 $this->revisionStore = $services->getRevisionStore();
167 }
168
173 protected function newPage( Title $title ) {
174 return new WikiPage( $title );
175 }
176
182 public static function newFromID( $id ) {
183 $t = Title::newFromID( $id );
184 return $t == null ? null : new static( $t );
185 }
186
194 public static function newFromTitle( $title, IContextSource $context ) {
195 if ( $title->getNamespace() == NS_MEDIA ) {
196 // XXX: This should not be here, but where should it go?
197 $title = Title::makeTitle( NS_FILE, $title->getDBkey() );
198 }
199
200 $page = null;
201 Hooks::runner()->onArticleFromTitle( $title, $page, $context );
202 if ( !$page ) {
203 switch ( $title->getNamespace() ) {
204 case NS_FILE:
205 $page = new ImagePage( $title );
206 break;
207 case NS_CATEGORY:
208 $page = new CategoryPage( $title );
209 break;
210 default:
211 $page = new Article( $title );
212 }
213 }
214 $page->setContext( $context );
215
216 return $page;
217 }
218
226 public static function newFromWikiPage( WikiPage $page, IContextSource $context ) {
227 $article = self::newFromTitle( $page->getTitle(), $context );
228 $article->mPage = $page; // override to keep process cached vars
229 return $article;
230 }
231
237 public function getRedirectedFrom() {
238 return $this->mRedirectedFrom;
239 }
240
246 public function setRedirectedFrom( Title $from ) {
247 $this->mRedirectedFrom = $from;
248 }
249
255 public function getTitle() {
256 return $this->mPage->getTitle();
257 }
258
265 public function getPage() {
266 return $this->mPage;
267 }
268
272 public function clear() {
273 $this->mContentLoaded = false;
274
275 $this->mRedirectedFrom = null; # Title object if set
276 $this->mRevIdFetched = 0;
277 $this->mRedirectUrl = false;
278 $this->mRevisionRecord = null;
279 $this->mContentObject = null;
280 $this->fetchResult = null;
281
282 // TODO hard-deprecate direct access to public fields
283
284 $this->mPage->clear();
285 }
286
304 protected function getContentObject() {
305 if ( $this->mPage->getId() === 0 ) {
307 } else {
308 $this->fetchContentObject();
309 $content = $this->mContentObject;
310 }
311
312 return $content;
313 }
314
320 private function getSubstituteContent() {
321 # If this is a MediaWiki:x message, then load the messages
322 # and return the message value for x.
323 if ( $this->getTitle()->getNamespace() == NS_MEDIAWIKI ) {
324 $text = $this->getTitle()->getDefaultMessageText();
325 if ( $text === false ) {
326 $text = '';
327 }
328
329 $content = ContentHandler::makeContent( $text, $this->getTitle() );
330 } else {
331 $message = $this->getContext()->getUser()->isLoggedIn() ? 'noarticletext' : 'noarticletextanon';
332 $content = new MessageContent( $message, null );
333 }
334
335 return $content;
336 }
337
347 protected function getEmptyPageParserOutput( ParserOptions $options ) {
349
350 return $content->getParserOutput( $this->getTitle(), 0, $options );
351 }
352
360 public function getOldID() {
361 if ( $this->mOldId === null ) {
362 $this->mOldId = $this->getOldIDFromRequest();
363 }
364
365 return $this->mOldId;
366 }
367
373 public function getOldIDFromRequest() {
374 $this->mRedirectUrl = false;
375
376 $request = $this->getContext()->getRequest();
377 $oldid = $request->getIntOrNull( 'oldid' );
378
379 if ( $oldid === null ) {
380 return 0;
381 }
382
383 if ( $oldid !== 0 ) {
384 # Load the given revision and check whether the page is another one.
385 # In that case, update this instance to reflect the change.
386 if ( $oldid === $this->mPage->getLatest() ) {
387 $this->mRevisionRecord = $this->mPage->getRevisionRecord();
388 } else {
389 $this->mRevisionRecord = $this->revisionStore->getRevisionById( $oldid );
390 if ( $this->mRevisionRecord !== null ) {
391 $revPageId = $this->mRevisionRecord->getPageId();
392 // Revision title doesn't match the page title given?
393 if ( $this->mPage->getId() != $revPageId ) {
394 $function = get_class( $this->mPage ) . '::newFromID';
395 $this->mPage = $function( $revPageId );
396 }
397 }
398 }
399 }
400
401 $oldRev = $this->mRevisionRecord;
402 if ( $request->getVal( 'direction' ) == 'next' ) {
403 $nextid = 0;
404 if ( $oldRev ) {
405 $nextRev = $this->revisionStore->getNextRevision( $oldRev );
406 if ( $nextRev ) {
407 $nextid = $nextRev->getId();
408 }
409 }
410 if ( $nextid ) {
411 $oldid = $nextid;
412 $this->mRevisionRecord = null;
413 } else {
414 $this->mRedirectUrl = $this->getTitle()->getFullURL( 'redirect=no' );
415 }
416 } elseif ( $request->getVal( 'direction' ) == 'prev' ) {
417 $previd = 0;
418 if ( $oldRev ) {
419 $prevRev = $this->revisionStore->getPreviousRevision( $oldRev );
420 if ( $prevRev ) {
421 $previd = $prevRev->getId();
422 }
423 }
424 if ( $previd ) {
425 $oldid = $previd;
426 $this->mRevisionRecord = null;
427 }
428 }
429
430 $this->mRevIdFetched = $this->mRevisionRecord ? $this->mRevisionRecord->getId() : 0;
431
432 return $oldid;
433 }
434
448 protected function fetchContentObject() {
449 if ( !$this->mContentLoaded ) {
450 $this->fetchRevisionRecord();
451 }
452
453 return $this->mContentObject;
454 }
455
467 public function fetchRevisionRecord() {
468 if ( $this->fetchResult ) {
469 return $this->mRevisionRecord;
470 }
471
472 $this->mContentLoaded = true;
473 $this->mContentObject = null;
474
475 $oldid = $this->getOldID();
476
477 // $this->mRevisionRecord might already be fetched by getOldIDFromRequest()
478 if ( !$this->mRevisionRecord ) {
479 if ( !$oldid ) {
480 $this->mRevisionRecord = $this->mPage->getRevisionRecord();
481
482 if ( !$this->mRevisionRecord ) {
483 wfDebug( __METHOD__ . " failed to find page data for title " .
484 $this->getTitle()->getPrefixedText() );
485
486 // Just for sanity, output for this case is done by showMissingArticle().
487 $this->fetchResult = Status::newFatal( 'noarticletext' );
489 return null;
490 }
491 } else {
492 $this->mRevisionRecord = $this->revisionStore->getRevisionById( $oldid );
493
494 if ( !$this->mRevisionRecord ) {
495 wfDebug( __METHOD__ . " failed to load revision, rev_id $oldid" );
496
497 $this->fetchResult = Status::newFatal( 'missing-revision', $oldid );
499 return null;
500 }
501 }
502 }
503
504 $this->mRevIdFetched = $this->mRevisionRecord->getId();
505 $this->fetchResult = Status::newGood( $this->mRevisionRecord );
506
507 if ( !RevisionRecord::userCanBitfield(
508 $this->mRevisionRecord->getVisibility(),
509 RevisionRecord::DELETED_TEXT,
510 $this->getContext()->getUser()
511 ) ) {
512 wfDebug( __METHOD__ . " failed to retrieve content of revision " .
513 $this->mRevisionRecord->getId() );
514
515 // Just for sanity, output for this case is done by showDeletedRevisionHeader().
516 $this->fetchResult = Status::newFatal( 'rev-deleted-text-permission' );
518 return null;
519 }
520
521 // For B/C only
522 $this->mContentObject = $this->mRevisionRecord->getContent(
523 SlotRecord::MAIN,
524 RevisionRecord::FOR_THIS_USER,
525 $this->getContext()->getUser()
526 );
527
528 return $this->mRevisionRecord;
529 }
530
537 private function makeFetchErrorContent() {
538 if ( !$this->fetchResult || $this->fetchResult->isOK() ) {
539 return null;
540 }
541
542 return new MessageContent( $this->fetchResult->getMessage() );
543 }
544
558 private function applyContentOverride( Content $override ) {
559 // Construct a fake revision
560 $rev = new MutableRevisionRecord( $this->getTitle() );
561 $rev->setContent( SlotRecord::MAIN, $override );
562
563 $this->mRevisionRecord = $rev;
564
565 // For B/C only
566 $this->mContentObject = $override;
567 }
568
574 public function isCurrent() {
575 # If no oldid, this is the current version.
576 if ( $this->getOldID() == 0 ) {
577 return true;
578 }
579
580 return $this->mPage->exists() &&
581 $this->mRevisionRecord &&
582 $this->mRevisionRecord->isCurrent();
583 }
584
596 public function getRevisionFetched() {
597 wfDeprecated( __METHOD__, '1.35' );
598 $revRecord = $this->fetchRevisionRecord();
599
600 return $revRecord ? new Revision( $revRecord ) : null;
601 }
602
611 public function getRevIdFetched() {
612 if ( $this->fetchResult && $this->fetchResult->isOK() ) {
613 return $this->fetchResult->value->getId();
614 } else {
615 return $this->mPage->getLatest();
616 }
617 }
618
623 public function view() {
625
626 # Get variables from query string
627 # As side effect this will load the revision and update the title
628 # in a revision ID is passed in the request, so this should remain
629 # the first call of this method even if $oldid is used way below.
630 $oldid = $this->getOldID();
631
632 $user = $this->getContext()->getUser();
633 # Another whitelist check in case getOldID() is altering the title
634 $permErrors = $this->permManager->getPermissionErrors(
635 'read',
636 $user,
637 $this->getTitle()
638 );
639 if ( count( $permErrors ) ) {
640 wfDebug( __METHOD__ . ": denied on secondary read check" );
641 throw new PermissionsError( 'read', $permErrors );
642 }
643
644 $outputPage = $this->getContext()->getOutput();
645 # getOldID() may as well want us to redirect somewhere else
646 if ( $this->mRedirectUrl ) {
647 $outputPage->redirect( $this->mRedirectUrl );
648 wfDebug( __METHOD__ . ": redirecting due to oldid" );
649
650 return;
651 }
652
653 # If we got diff in the query, we want to see a diff page instead of the article.
654 if ( $this->getContext()->getRequest()->getCheck( 'diff' ) ) {
655 wfDebug( __METHOD__ . ": showing diff page" );
656 $this->showDiffPage();
657
658 return;
659 }
660
661 # Set page title (may be overridden by DISPLAYTITLE)
662 $outputPage->setPageTitle( $this->getTitle()->getPrefixedText() );
663
664 $outputPage->setArticleFlag( true );
665 # Allow frames by default
666 $outputPage->allowClickjacking();
667
668 $parserCache = MediaWikiServices::getInstance()->getParserCache();
669
670 $parserOptions = $this->getParserOptions();
671 $poOptions = [];
672 # Render printable version, use printable version cache
673 if ( $outputPage->isPrintable() ) {
674 $parserOptions->setIsPrintable( true );
675 $poOptions['enableSectionEditLinks'] = false;
676 $outputPage->prependHTML(
677 Html::warningBox(
678 $outputPage->msg( 'printableversion-deprecated-warning' )->escaped()
679 )
680 );
681 } elseif ( $this->viewIsRenderAction || !$this->isCurrent() ||
682 !$this->permManager->quickUserCan( 'edit', $user, $this->getTitle() )
683 ) {
684 $poOptions['enableSectionEditLinks'] = false;
685 }
686
687 # Try client and file cache
688 if ( $oldid === 0 && $this->mPage->checkTouched() ) {
689 # Try to stream the output from file cache
690 if ( $wgUseFileCache && $this->tryFileCache() ) {
691 wfDebug( __METHOD__ . ": done file cache" );
692 # tell wgOut that output is taken care of
693 $outputPage->disable();
694 $this->mPage->doViewUpdates( $user, $oldid );
695
696 return;
697 }
698 }
699
700 # Should the parser cache be used?
701 $useParserCache = $this->mPage->shouldCheckParserCache( $parserOptions, $oldid );
702 wfDebug( 'Article::view using parser cache: ' . ( $useParserCache ? 'yes' : 'no' ) );
703 if ( $user->getStubThreshold() ) {
704 MediaWikiServices::getInstance()->getStatsdDataFactory()->increment( 'pcache_miss_stub' );
705 }
706
708 $this->showNamespaceHeader();
709
710 # Iterate through the possible ways of constructing the output text.
711 # Keep going until $outputDone is set, or we run out of things to do.
712 $pass = 0;
713 $outputDone = false;
714 $this->mParserOutput = false;
715
716 while ( !$outputDone && ++$pass ) {
717 switch ( $pass ) {
718 case 1:
719 $this->getHookRunner()->onArticleViewHeader( $this, $outputDone, $useParserCache );
720 break;
721 case 2:
722 # Early abort if the page doesn't exist
723 if ( !$this->mPage->exists() ) {
724 wfDebug( __METHOD__ . ": showing missing article" );
725 $this->showMissingArticle();
726 $this->mPage->doViewUpdates( $user );
727 return;
728 }
729
730 # Try the parser cache
731 if ( $useParserCache ) {
732 $this->mParserOutput = $parserCache->get( $this->getPage(), $parserOptions );
733
734 if ( $this->mParserOutput !== false ) {
735 if ( $oldid ) {
736 wfDebug( __METHOD__ . ": showing parser cache contents for current rev permalink" );
737 $this->setOldSubtitle( $oldid );
738 } else {
739 wfDebug( __METHOD__ . ": showing parser cache contents" );
740 }
741 $outputPage->addParserOutput( $this->mParserOutput, $poOptions );
742 # Ensure that UI elements requiring revision ID have
743 # the correct version information.
744 $outputPage->setRevisionId( $this->mPage->getLatest() );
745 # Preload timestamp to avoid a DB hit
746 $cachedTimestamp = $this->mParserOutput->getTimestamp();
747 if ( $cachedTimestamp !== null ) {
748 $outputPage->setRevisionTimestamp( $cachedTimestamp );
749 $this->mPage->setTimestamp( $cachedTimestamp );
750 }
751 $outputDone = true;
752 }
753 }
754 break;
755 case 3:
756 # Are we looking at an old revision
757 $rev = $this->fetchRevisionRecord();
758 if ( $oldid && $this->fetchResult->isOK() ) {
759 $this->setOldSubtitle( $oldid );
760
761 if ( !$this->showDeletedRevisionHeader() ) {
762 wfDebug( __METHOD__ . ": cannot view deleted revision" );
763 return;
764 }
765 }
766
767 # Ensure that UI elements requiring revision ID have
768 # the correct version information.
769 $outputPage->setRevisionId( $this->getRevIdFetched() );
770 # Preload timestamp to avoid a DB hit
771 $outputPage->setRevisionTimestamp( $this->mPage->getTimestamp() );
772
773 # Pages containing custom CSS or JavaScript get special treatment
774 if ( $this->getTitle()->isSiteConfigPage() || $this->getTitle()->isUserConfigPage() ) {
775 $dir = $this->getContext()->getLanguage()->getDir();
776 $lang = $this->getContext()->getLanguage()->getHtmlCode();
777
778 $outputPage->wrapWikiMsg(
779 "<div id='mw-clearyourcache' lang='$lang' dir='$dir' class='mw-content-$dir'>\n$1\n</div>",
780 'clearyourcache'
781 );
782 } elseif ( !$this->getHookRunner()->onArticleRevisionViewCustom(
783 $rev,
784 $this->getTitle(),
785 $oldid,
786 $outputPage )
787 ) {
788 // NOTE: sync with hooks called in DifferenceEngine::renderNewRevision()
789 // Allow extensions do their own custom view for certain pages
790 $outputDone = true;
791 }
792 break;
793 case 4:
794 # Run the parse, protected by a pool counter
795 wfDebug( __METHOD__ . ": doing uncached parse" );
796
797 $rev = $this->fetchRevisionRecord();
798 $error = null;
799
800 if ( $rev ) {
801 $poolArticleView = new PoolWorkArticleView(
802 $this->getPage(),
803 $parserOptions,
804 $this->getRevIdFetched(),
805 $useParserCache,
806 $rev,
807 // permission checking was done earlier via showDeletedRevisionHeader()
808 RevisionRecord::RAW
809 );
810 $ok = $poolArticleView->execute();
811 $error = $poolArticleView->getError();
812 $this->mParserOutput = $poolArticleView->getParserOutput() ?: null;
813
814 # Cache stale ParserOutput object with a short expiry
815 if ( $poolArticleView->getIsDirty() ) {
816 $outputPage->setCdnMaxage( $wgCdnMaxageStale );
817 $outputPage->setLastModified( $this->mParserOutput->getCacheTime() );
818 $staleReason = $poolArticleView->getIsFastStale()
819 ? 'pool contention' : 'pool overload';
820 $outputPage->addHTML( "<!-- parser cache is expired, " .
821 "sending anyway due to $staleReason-->\n" );
822 }
823 } else {
824 $ok = false;
825 }
826
827 if ( !$ok ) {
828 if ( $error ) {
829 $outputPage->clearHTML(); // for release() errors
830 $outputPage->enableClientCache( false );
831 $outputPage->setRobotPolicy( 'noindex,nofollow' );
832
833 $errortext = $error->getWikiText(
834 false, 'view-pool-error', $this->getContext()->getLanguage()
835 );
836 $outputPage->wrapWikiTextAsInterface( 'errorbox', $errortext );
837 }
838 # Connection or timeout error
839 return;
840 }
841
842 if ( $this->mParserOutput ) {
843 $outputPage->addParserOutput( $this->mParserOutput, $poOptions );
844 }
845
846 if ( $rev && $this->getRevisionRedirectTarget( $rev ) ) {
847 $outputPage->addSubtitle( "<span id=\"redirectsub\">" .
848 $this->getContext()->msg( 'redirectpagesub' )->parse() . "</span>" );
849 }
850
851 $outputDone = true;
852 break;
853 # Should be unreachable, but just in case...
854 default:
855 break 2;
856 }
857 }
858
859 // Get the ParserOutput actually *displayed* here.
860 // Note that $this->mParserOutput is the *current*/oldid version output.
861 // Note that the ArticleViewHeader hook is allowed to set $outputDone to a
862 // ParserOutput instance.
863 $pOutput = ( $outputDone instanceof ParserOutput )
864 ? $outputDone // object fetched by hook
865 : ( $this->mParserOutput ?: null ); // ParserOutput or null, avoid false
866
867 # Adjust title for main page & pages with displaytitle
868 if ( $pOutput ) {
869 $this->adjustDisplayTitle( $pOutput );
870 }
871
872 # For the main page, overwrite the <title> element with the con-
873 # tents of 'pagetitle-view-mainpage' instead of the default (if
874 # that's not empty).
875 # This message always exists because it is in the i18n files
876 if ( $this->getTitle()->isMainPage() ) {
877 $msg = wfMessage( 'pagetitle-view-mainpage' )->inContentLanguage();
878 if ( !$msg->isDisabled() ) {
879 $outputPage->setHTMLTitle( $msg->title( $this->getTitle() )->text() );
880 }
881 }
882
883 # Use adaptive TTLs for CDN so delayed/failed purges are noticed less often.
884 # This could use getTouched(), but that could be scary for major template edits.
885 $outputPage->adaptCdnTTL( $this->mPage->getTimestamp(), IExpiringStore::TTL_DAY );
886
887 # Check for any __NOINDEX__ tags on the page using $pOutput
888 $policy = $this->getRobotPolicy( 'view', $pOutput ?: null );
889 $outputPage->setIndexPolicy( $policy['index'] );
890 $outputPage->setFollowPolicy( $policy['follow'] ); // FIXME: test this
891
892 $this->showViewFooter();
893 $this->mPage->doViewUpdates( $user, $oldid ); // FIXME: test this
894
895 # Load the postEdit module if the user just saved this revision
896 # See also EditPage::setPostEditCookie
897 $request = $this->getContext()->getRequest();
899 $postEdit = $request->getCookie( $cookieKey );
900 if ( $postEdit ) {
901 # Clear the cookie. This also prevents caching of the response.
902 $request->response()->clearCookie( $cookieKey );
903 $outputPage->addJsConfigVars( 'wgPostEdit', $postEdit );
904 $outputPage->addModules( 'mediawiki.action.view.postEdit' ); // FIXME: test this
905 }
906 }
907
912 private function getRevisionRedirectTarget( RevisionRecord $revision ) {
913 // TODO: find a *good* place for the code that determines the redirect target for
914 // a given revision!
915 // NOTE: Use main slot content. Compare code in DerivedPageDataUpdater::revisionIsRedirect.
916 $content = $revision->getContent( SlotRecord::MAIN );
917 return $content ? $content->getRedirectTarget() : null;
918 }
919
924 public function adjustDisplayTitle( ParserOutput $pOutput ) {
925 $out = $this->getContext()->getOutput();
926
927 # Adjust the title if it was set by displaytitle, -{T|}- or language conversion
928 $titleText = $pOutput->getTitleText();
929 if ( strval( $titleText ) !== '' ) {
930 $out->setPageTitle( $titleText );
931 $out->setDisplayTitle( $titleText );
932 }
933 }
934
939 protected function showDiffPage() {
940 $request = $this->getContext()->getRequest();
941 $user = $this->getContext()->getUser();
942 $diff = $request->getVal( 'diff' );
943 $rcid = $request->getVal( 'rcid' );
944 $diffOnly = $request->getBool( 'diffonly', $user->getOption( 'diffonly' ) );
945 $purge = $request->getVal( 'action' ) == 'purge';
946 $unhide = $request->getInt( 'unhide' ) == 1;
947 $oldid = $this->getOldID();
948
949 $rev = $this->fetchRevisionRecord();
950
951 if ( !$rev ) {
952 $this->getContext()->getOutput()->setPageTitle( wfMessage( 'errorpagetitle' ) );
953 $msg = $this->getContext()->msg( 'difference-missing-revision' )
954 ->params( $oldid )
955 ->numParams( 1 )
956 ->parseAsBlock();
957 $this->getContext()->getOutput()->addHTML( $msg );
958 return;
959 }
960
961 $contentHandler = MediaWikiServices::getInstance()
962 ->getContentHandlerFactory()
963 ->getContentHandler(
964 $rev->getSlot( SlotRecord::MAIN, RevisionRecord::RAW )->getModel()
965 );
966 $de = $contentHandler->createDifferenceEngine(
967 $this->getContext(),
968 $oldid,
969 $diff,
970 $rcid,
971 $purge,
972 $unhide
973 );
974 $de->setSlotDiffOptions( [
975 'diff-type' => $request->getVal( 'diff-type' )
976 ] );
977
978 // DifferenceEngine directly fetched the revision:
979 $this->mRevIdFetched = $de->getNewid();
980 $de->showDiffPage( $diffOnly );
981
982 // Run view updates for the newer revision being diffed (and shown
983 // below the diff if not $diffOnly).
984 list( $old, $new ) = $de->mapDiffPrevNext( $oldid, $diff );
985 // New can be false, convert it to 0 - this conveniently means the latest revision
986 $this->mPage->doViewUpdates( $user, (int)$new );
987 }
988
996 public function getRobotPolicy( $action, ParserOutput $pOutput = null ) {
998
999 $ns = $this->getTitle()->getNamespace();
1000
1001 # Don't index user and user talk pages for blocked users (T13443)
1002 if ( ( $ns == NS_USER || $ns == NS_USER_TALK ) && !$this->getTitle()->isSubpage() ) {
1003 $specificTarget = null;
1004 $vagueTarget = null;
1005 $titleText = $this->getTitle()->getText();
1006 if ( IPUtils::isValid( $titleText ) ) {
1007 $vagueTarget = $titleText;
1008 } else {
1009 $specificTarget = $titleText;
1010 }
1011 if ( DatabaseBlock::newFromTarget( $specificTarget, $vagueTarget ) instanceof DatabaseBlock ) {
1012 return [
1013 'index' => 'noindex',
1014 'follow' => 'nofollow'
1015 ];
1016 }
1017 }
1018
1019 if ( $this->mPage->getId() === 0 || $this->getOldID() ) {
1020 # Non-articles (special pages etc), and old revisions
1021 return [
1022 'index' => 'noindex',
1023 'follow' => 'nofollow'
1024 ];
1025 } elseif ( $this->getContext()->getOutput()->isPrintable() ) {
1026 # Discourage indexing of printable versions, but encourage following
1027 return [
1028 'index' => 'noindex',
1029 'follow' => 'follow'
1030 ];
1031 } elseif ( $this->getContext()->getRequest()->getInt( 'curid' ) ) {
1032 # For ?curid=x urls, disallow indexing
1033 return [
1034 'index' => 'noindex',
1035 'follow' => 'follow'
1036 ];
1037 }
1038
1039 # Otherwise, construct the policy based on the various config variables.
1040 $policy = self::formatRobotPolicy( $wgDefaultRobotPolicy );
1041
1042 if ( isset( $wgNamespaceRobotPolicies[$ns] ) ) {
1043 # Honour customised robot policies for this namespace
1044 $policy = array_merge(
1045 $policy,
1046 self::formatRobotPolicy( $wgNamespaceRobotPolicies[$ns] )
1047 );
1048 }
1049 if ( $this->getTitle()->canUseNoindex() && is_object( $pOutput ) && $pOutput->getIndexPolicy() ) {
1050 # __INDEX__ and __NOINDEX__ magic words, if allowed. Incorporates
1051 # a final sanity check that we have really got the parser output.
1052 $policy = array_merge(
1053 $policy,
1054 [ 'index' => $pOutput->getIndexPolicy() ]
1055 );
1056 }
1057
1058 if ( isset( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] ) ) {
1059 # (T16900) site config can override user-defined __INDEX__ or __NOINDEX__
1060 $policy = array_merge(
1061 $policy,
1062 self::formatRobotPolicy( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] )
1063 );
1064 }
1065
1066 return $policy;
1067 }
1068
1076 public static function formatRobotPolicy( $policy ) {
1077 if ( is_array( $policy ) ) {
1078 return $policy;
1079 } elseif ( !$policy ) {
1080 return [];
1081 }
1082
1083 $policy = explode( ',', $policy );
1084 $policy = array_map( 'trim', $policy );
1085
1086 $arr = [];
1087 foreach ( $policy as $var ) {
1088 if ( in_array( $var, [ 'index', 'noindex' ] ) ) {
1089 $arr['index'] = $var;
1090 } elseif ( in_array( $var, [ 'follow', 'nofollow' ] ) ) {
1091 $arr['follow'] = $var;
1092 }
1093 }
1094
1095 return $arr;
1096 }
1097
1105 public function showRedirectedFromHeader() {
1106 global $wgRedirectSources;
1107
1108 $context = $this->getContext();
1109 $outputPage = $context->getOutput();
1110 $request = $context->getRequest();
1111 $rdfrom = $request->getVal( 'rdfrom' );
1112
1113 // Construct a URL for the current page view, but with the target title
1114 $query = $request->getValues();
1115 unset( $query['rdfrom'] );
1116 unset( $query['title'] );
1117 if ( $this->getTitle()->isRedirect() ) {
1118 // Prevent double redirects
1119 $query['redirect'] = 'no';
1120 }
1121 $redirectTargetUrl = $this->getTitle()->getLinkURL( $query );
1122
1123 if ( isset( $this->mRedirectedFrom ) ) {
1124 // This is an internally redirected page view.
1125 // We'll need a backlink to the source page for navigation.
1126 if ( $this->getHookRunner()->onArticleViewRedirect( $this ) ) {
1127 $redir = $this->linkRenderer->makeKnownLink(
1128 $this->mRedirectedFrom,
1129 null,
1130 [],
1131 [ 'redirect' => 'no' ]
1132 );
1133
1134 $outputPage->addSubtitle( "<span class=\"mw-redirectedfrom\">" .
1135 $context->msg( 'redirectedfrom' )->rawParams( $redir )->parse()
1136 . "</span>" );
1137
1138 // Add the script to update the displayed URL and
1139 // set the fragment if one was specified in the redirect
1140 $outputPage->addJsConfigVars( [
1141 'wgInternalRedirectTargetUrl' => $redirectTargetUrl,
1142 ] );
1143 $outputPage->addModules( 'mediawiki.action.view.redirect' );
1144
1145 // Add a <link rel="canonical"> tag
1146 $outputPage->setCanonicalUrl( $this->getTitle()->getCanonicalURL() );
1147
1148 // Tell the output object that the user arrived at this article through a redirect
1149 $outputPage->setRedirectedFrom( $this->mRedirectedFrom );
1150
1151 return true;
1152 }
1153 } elseif ( $rdfrom ) {
1154 // This is an externally redirected view, from some other wiki.
1155 // If it was reported from a trusted site, supply a backlink.
1156 if ( $wgRedirectSources && preg_match( $wgRedirectSources, $rdfrom ) ) {
1157 $redir = Linker::makeExternalLink( $rdfrom, $rdfrom );
1158 $outputPage->addSubtitle( "<span class=\"mw-redirectedfrom\">" .
1159 $context->msg( 'redirectedfrom' )->rawParams( $redir )->parse()
1160 . "</span>" );
1161
1162 // Add the script to update the displayed URL
1163 $outputPage->addJsConfigVars( [
1164 'wgInternalRedirectTargetUrl' => $redirectTargetUrl,
1165 ] );
1166 $outputPage->addModules( 'mediawiki.action.view.redirect' );
1167
1168 return true;
1169 }
1170 }
1171
1172 return false;
1173 }
1174
1179 public function showNamespaceHeader() {
1180 if ( $this->getTitle()->isTalkPage() && !wfMessage( 'talkpageheader' )->isDisabled() ) {
1181 $this->getContext()->getOutput()->wrapWikiMsg(
1182 "<div class=\"mw-talkpageheader\">\n$1\n</div>",
1183 [ 'talkpageheader' ]
1184 );
1185 }
1186 }
1187
1191 public function showViewFooter() {
1192 # check if we're displaying a [[User talk:x.x.x.x]] anonymous talk page
1193 if ( $this->getTitle()->getNamespace() == NS_USER_TALK
1194 && IPUtils::isValid( $this->getTitle()->getText() )
1195 ) {
1196 $this->getContext()->getOutput()->addWikiMsg( 'anontalkpagetext' );
1197 }
1198
1199 // Show a footer allowing the user to patrol the shown revision or page if possible
1200 $patrolFooterShown = $this->showPatrolFooter();
1201
1202 $this->getHookRunner()->onArticleViewFooter( $this, $patrolFooterShown );
1203 }
1204
1215 public function showPatrolFooter() {
1217
1218 // Allow hooks to decide whether to not output this at all
1219 if ( !$this->getHookRunner()->onArticleShowPatrolFooter( $this ) ) {
1220 return false;
1221 }
1222
1223 $outputPage = $this->getContext()->getOutput();
1224 $user = $this->getContext()->getUser();
1225 $title = $this->getTitle();
1226 $rc = false;
1227
1228 if ( !$this->permManager->quickUserCan( 'patrol', $user, $title )
1230 || ( $wgUseFilePatrol && $title->inNamespace( NS_FILE ) ) )
1231 ) {
1232 // Patrolling is disabled or the user isn't allowed to
1233 return false;
1234 }
1235
1236 if ( $this->mRevisionRecord
1237 && !RecentChange::isInRCLifespan( $this->mRevisionRecord->getTimestamp(), 21600 )
1238 ) {
1239 // The current revision is already older than what could be in the RC table
1240 // 6h tolerance because the RC might not be cleaned out regularly
1241 return false;
1242 }
1243
1244 // Check for cached results
1245 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
1246 $key = $cache->makeKey( 'unpatrollable-page', $title->getArticleID() );
1247 if ( $cache->get( $key ) ) {
1248 return false;
1249 }
1250
1251 $dbr = wfGetDB( DB_REPLICA );
1252 $oldestRevisionTimestamp = $dbr->selectField(
1253 'revision',
1254 'MIN( rev_timestamp )',
1255 [ 'rev_page' => $title->getArticleID() ],
1256 __METHOD__
1257 );
1258
1259 // New page patrol: Get the timestamp of the oldest revison which
1260 // the revision table holds for the given page. Then we look
1261 // whether it's within the RC lifespan and if it is, we try
1262 // to get the recentchanges row belonging to that entry
1263 // (with rc_new = 1).
1264 $recentPageCreation = false;
1265 if ( $oldestRevisionTimestamp
1266 && RecentChange::isInRCLifespan( $oldestRevisionTimestamp, 21600 )
1267 ) {
1268 // 6h tolerance because the RC might not be cleaned out regularly
1269 $recentPageCreation = true;
1270 $rc = RecentChange::newFromConds(
1271 [
1272 'rc_new' => 1,
1273 'rc_timestamp' => $oldestRevisionTimestamp,
1274 'rc_namespace' => $title->getNamespace(),
1275 'rc_cur_id' => $title->getArticleID()
1276 ],
1277 __METHOD__
1278 );
1279 if ( $rc ) {
1280 // Use generic patrol message for new pages
1281 $markPatrolledMsg = wfMessage( 'markaspatrolledtext' );
1282 }
1283 }
1284
1285 // File patrol: Get the timestamp of the latest upload for this page,
1286 // check whether it is within the RC lifespan and if it is, we try
1287 // to get the recentchanges row belonging to that entry
1288 // (with rc_type = RC_LOG, rc_log_type = upload).
1289 $recentFileUpload = false;
1290 if ( ( !$rc || $rc->getAttribute( 'rc_patrolled' ) ) && $wgUseFilePatrol
1291 && $title->getNamespace() === NS_FILE ) {
1292 // Retrieve timestamp of most recent upload
1293 $newestUploadTimestamp = $dbr->selectField(
1294 'image',
1295 'MAX( img_timestamp )',
1296 [ 'img_name' => $title->getDBkey() ],
1297 __METHOD__
1298 );
1299 if ( $newestUploadTimestamp
1300 && RecentChange::isInRCLifespan( $newestUploadTimestamp, 21600 )
1301 ) {
1302 // 6h tolerance because the RC might not be cleaned out regularly
1303 $recentFileUpload = true;
1304 $rc = RecentChange::newFromConds(
1305 [
1306 'rc_type' => RC_LOG,
1307 'rc_log_type' => 'upload',
1308 'rc_timestamp' => $newestUploadTimestamp,
1309 'rc_namespace' => NS_FILE,
1310 'rc_cur_id' => $title->getArticleID()
1311 ],
1312 __METHOD__
1313 );
1314 if ( $rc ) {
1315 // Use patrol message specific to files
1316 $markPatrolledMsg = wfMessage( 'markaspatrolledtext-file' );
1317 }
1318 }
1319 }
1320
1321 if ( !$recentPageCreation && !$recentFileUpload ) {
1322 // Page creation and latest upload (for files) is too old to be in RC
1323
1324 // We definitely can't patrol so cache the information
1325 // When a new file version is uploaded, the cache is cleared
1326 $cache->set( $key, '1' );
1327
1328 return false;
1329 }
1330
1331 if ( !$rc ) {
1332 // Don't cache: This can be hit if the page gets accessed very fast after
1333 // its creation / latest upload or in case we have high replica DB lag. In case
1334 // the revision is too old, we will already return above.
1335 return false;
1336 }
1337
1338 if ( $rc->getAttribute( 'rc_patrolled' ) ) {
1339 // Patrolled RC entry around
1340
1341 // Cache the information we gathered above in case we can't patrol
1342 // Don't cache in case we can patrol as this could change
1343 $cache->set( $key, '1' );
1344
1345 return false;
1346 }
1347
1348 if ( $rc->getPerformer()->equals( $user ) ) {
1349 // Don't show a patrol link for own creations/uploads. If the user could
1350 // patrol them, they already would be patrolled
1351 return false;
1352 }
1353
1354 $outputPage->preventClickjacking();
1355 if ( $this->permManager->userHasRight( $user, 'writeapi' ) ) {
1356 $outputPage->addModules( 'mediawiki.misc-authed-curate' );
1357 }
1358
1359 $link = $this->linkRenderer->makeKnownLink(
1360 $title,
1361 $markPatrolledMsg->text(),
1362 [],
1363 [
1364 'action' => 'markpatrolled',
1365 'rcid' => $rc->getAttribute( 'rc_id' ),
1366 ]
1367 );
1368
1369 $outputPage->addHTML(
1370 "<div class='patrollink' data-mw='interface'>" .
1371 wfMessage( 'markaspatrolledlink' )->rawParams( $link )->escaped() .
1372 '</div>'
1373 );
1374
1375 return true;
1376 }
1377
1384 public static function purgePatrolFooterCache( $articleID ) {
1385 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
1386 $cache->delete( $cache->makeKey( 'unpatrollable-page', $articleID ) );
1387 }
1388
1393 public function showMissingArticle() {
1394 global $wgSend404Code;
1395
1396 $outputPage = $this->getContext()->getOutput();
1397 // Whether the page is a root user page of an existing user (but not a subpage)
1398 $validUserPage = false;
1399
1400 $title = $this->getTitle();
1401
1402 $services = MediaWikiServices::getInstance();
1403
1404 $contextUser = $this->getContext()->getUser();
1405
1406 # Show info in user (talk) namespace. Does the user exist? Is he blocked?
1407 if ( $title->getNamespace() == NS_USER
1408 || $title->getNamespace() == NS_USER_TALK
1409 ) {
1410 $rootPart = explode( '/', $title->getText() )[0];
1411 $user = User::newFromName( $rootPart, false /* allow IP users */ );
1412 $ip = User::isIP( $rootPart );
1413 $block = DatabaseBlock::newFromTarget( $user, $user );
1414
1415 if ( $user && $user->isLoggedIn() && $user->isHidden() &&
1416 !$services->getPermissionManager()
1417 ->userHasRight( $contextUser, 'hideuser' )
1418 ) {
1419 // T120883 if the user is hidden and the viewer cannot see hidden
1420 // users, pretend like it does not exist at all.
1421 $user = false;
1422 }
1423 if ( !( $user && $user->isLoggedIn() ) && !$ip ) { # User does not exist
1424 $outputPage->wrapWikiMsg( "<div class=\"mw-userpage-userdoesnotexist error\">\n\$1\n</div>",
1425 [ 'userpage-userdoesnotexist-view', wfEscapeWikiText( $rootPart ) ] );
1426 } elseif (
1427 $block !== null &&
1428 $block->getType() != DatabaseBlock::TYPE_AUTO &&
1429 ( $block->isSitewide() || $user->isBlockedFrom( $title ) )
1430 ) {
1431 // Show log extract if the user is sitewide blocked or is partially
1432 // blocked and not allowed to edit their user page or user talk page
1433 LogEventsList::showLogExtract(
1434 $outputPage,
1435 'block',
1436 $services->getNamespaceInfo()->getCanonicalName( NS_USER ) . ':' .
1437 $block->getTarget(),
1438 '',
1439 [
1440 'lim' => 1,
1441 'showIfEmpty' => false,
1442 'msgKey' => [
1443 'blocked-notice-logextract',
1444 $user->getName() # Support GENDER in notice
1445 ]
1446 ]
1447 );
1448 $validUserPage = !$title->isSubpage();
1449 } else {
1450 $validUserPage = !$title->isSubpage();
1451 }
1452 }
1453
1454 $this->getHookRunner()->onShowMissingArticle( $this );
1455
1456 # Show delete and move logs if there were any such events.
1457 # The logging query can DOS the site when bots/crawlers cause 404 floods,
1458 # so be careful showing this. 404 pages must be cheap as they are hard to cache.
1459 $dbCache = ObjectCache::getInstance( 'db-replicated' );
1460 $key = $dbCache->makeKey( 'page-recent-delete', md5( $title->getPrefixedText() ) );
1461 $loggedIn = $contextUser->isLoggedIn();
1462 $sessionExists = $this->getContext()->getRequest()->getSession()->isPersistent();
1463
1464 if ( $loggedIn || $dbCache->get( $key ) || $sessionExists ) {
1465 $logTypes = [ 'delete', 'move', 'protect' ];
1466
1467 $dbr = wfGetDB( DB_REPLICA );
1468
1469 $conds = [ 'log_action != ' . $dbr->addQuotes( 'revision' ) ];
1470 // Give extensions a chance to hide their (unrelated) log entries
1471 $this->getHookRunner()->onArticle__MissingArticleConditions( $conds, $logTypes );
1472 LogEventsList::showLogExtract(
1473 $outputPage,
1474 $logTypes,
1475 $title,
1476 '',
1477 [
1478 'lim' => 10,
1479 'conds' => $conds,
1480 'showIfEmpty' => false,
1481 'msgKey' => [ $loggedIn || $sessionExists
1482 ? 'moveddeleted-notice'
1483 : 'moveddeleted-notice-recent'
1484 ]
1485 ]
1486 );
1487 }
1488
1489 if ( !$this->mPage->hasViewableContent() && $wgSend404Code && !$validUserPage ) {
1490 // If there's no backing content, send a 404 Not Found
1491 // for better machine handling of broken links.
1492 $this->getContext()->getRequest()->response()->statusHeader( 404 );
1493 }
1494
1495 // Also apply the robot policy for nonexisting pages (even if a 404 was used for sanity)
1496 $policy = $this->getRobotPolicy( 'view' );
1497 $outputPage->setIndexPolicy( $policy['index'] );
1498 $outputPage->setFollowPolicy( $policy['follow'] );
1499
1500 $hookResult = $this->getHookRunner()->onBeforeDisplayNoArticleText( $this );
1501
1502 if ( !$hookResult ) {
1503 return;
1504 }
1505
1506 # Show error message
1507 $oldid = $this->getOldID();
1508 $pm = $this->permManager;
1509 if ( !$oldid && $title->getNamespace() === NS_MEDIAWIKI && $title->hasSourceText() ) {
1510 // use fake Content object for system message
1511 $parserOptions = ParserOptions::newCanonical( 'canonical' );
1512 $outputPage->addParserOutput( $this->getEmptyPageParserOutput( $parserOptions ) );
1513 } else {
1514 if ( $oldid ) {
1515 $text = wfMessage( 'missing-revision', $oldid )->plain();
1516 } elseif ( $pm->quickUserCan( 'create', $contextUser, $title ) &&
1517 $pm->quickUserCan( 'edit', $contextUser, $title )
1518 ) {
1519 $message = $loggedIn ? 'noarticletext' : 'noarticletextanon';
1520 $text = wfMessage( $message )->plain();
1521 } else {
1522 $text = wfMessage( 'noarticletext-nopermission' )->plain();
1523 }
1524
1525 $dir = $this->getContext()->getLanguage()->getDir();
1526 $lang = $this->getContext()->getLanguage()->getHtmlCode();
1527 $outputPage->addWikiTextAsInterface( Xml::openElement( 'div', [
1528 'class' => "noarticletext mw-content-$dir",
1529 'dir' => $dir,
1530 'lang' => $lang,
1531 ] ) . "\n$text\n</div>" );
1532 }
1533 }
1534
1541 public function showDeletedRevisionHeader() {
1542 if ( !$this->mRevisionRecord->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
1543 // Not deleted
1544 return true;
1545 }
1546
1547 $outputPage = $this->getContext()->getOutput();
1548 $user = $this->getContext()->getUser();
1549 // If the user is not allowed to see it...
1550 if ( !RevisionRecord::userCanBitfield(
1551 $this->mRevisionRecord->getVisibility(),
1552 RevisionRecord::DELETED_TEXT,
1553 $user
1554 ) ) {
1555 $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1556 'rev-deleted-text-permission' );
1557
1558 return false;
1559 // If the user needs to confirm that they want to see it...
1560 } elseif ( $this->getContext()->getRequest()->getInt( 'unhide' ) != 1 ) {
1561 # Give explanation and add a link to view the revision...
1562 $oldid = intval( $this->getOldID() );
1563 $link = $this->getTitle()->getFullURL( "oldid={$oldid}&unhide=1" );
1564 $msg = $this->mRevisionRecord->isDeleted( RevisionRecord::DELETED_RESTRICTED ) ?
1565 'rev-suppressed-text-unhide' : 'rev-deleted-text-unhide';
1566 $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1567 [ $msg, $link ] );
1568
1569 return false;
1570 // We are allowed to see...
1571 } else {
1572 $msg = $this->mRevisionRecord->isDeleted( RevisionRecord::DELETED_RESTRICTED ) ?
1573 'rev-suppressed-text-view' : 'rev-deleted-text-view';
1574 $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n", $msg );
1575
1576 return true;
1577 }
1578 }
1579
1588 public function setOldSubtitle( $oldid = 0 ) {
1589 if ( !$this->getHookRunner()->onDisplayOldSubtitle( $this, $oldid ) ) {
1590 return;
1591 }
1592
1593 $context = $this->getContext();
1594 $unhide = $context->getRequest()->getInt( 'unhide' ) == 1;
1595
1596 # Cascade unhide param in links for easy deletion browsing
1597 $extraParams = [];
1598 if ( $unhide ) {
1599 $extraParams['unhide'] = 1;
1600 }
1601
1602 if ( $this->mRevisionRecord && $this->mRevisionRecord->getId() === $oldid ) {
1603 $revisionRecord = $this->mRevisionRecord;
1604 } else {
1605 $revisionRecord = $this->revisionStore->getRevisionById( $oldid );
1606 }
1607
1608 $timestamp = $revisionRecord->getTimestamp();
1609
1610 $current = ( $oldid == $this->mPage->getLatest() );
1611 $language = $context->getLanguage();
1612 $user = $context->getUser();
1613
1614 $td = $language->userTimeAndDate( $timestamp, $user );
1615 $tddate = $language->userDate( $timestamp, $user );
1616 $tdtime = $language->userTime( $timestamp, $user );
1617
1618 # Show user links if allowed to see them. If hidden, then show them only if requested...
1619 $userlinks = Linker::revUserTools( $revisionRecord, !$unhide );
1620
1621 $infomsg = $current && !$context->msg( 'revision-info-current' )->isDisabled()
1622 ? 'revision-info-current'
1623 : 'revision-info';
1624
1625 $outputPage = $context->getOutput();
1626 $revisionUser = $revisionRecord->getUser();
1627 $revisionInfo = "<div id=\"mw-{$infomsg}\">" .
1628 $context->msg( $infomsg, $td )
1629 ->rawParams( $userlinks )
1630 ->params(
1631 $revisionRecord->getId(),
1632 $tddate,
1633 $tdtime,
1634 $revisionUser ? $revisionUser->getName() : ''
1635 )
1636 ->rawParams( Linker::revComment(
1637 $revisionRecord,
1638 true,
1639 true
1640 ) )
1641 ->parse() .
1642 "</div>";
1643
1644 $lnk = $current
1645 ? $context->msg( 'currentrevisionlink' )->escaped()
1646 : $this->linkRenderer->makeKnownLink(
1647 $this->getTitle(),
1648 $context->msg( 'currentrevisionlink' )->text(),
1649 [],
1650 $extraParams
1651 );
1652 $curdiff = $current
1653 ? $context->msg( 'diff' )->escaped()
1654 : $this->linkRenderer->makeKnownLink(
1655 $this->getTitle(),
1656 $context->msg( 'diff' )->text(),
1657 [],
1658 [
1659 'diff' => 'cur',
1660 'oldid' => $oldid
1661 ] + $extraParams
1662 );
1663 $prevExist = (bool)$this->revisionStore->getPreviousRevision( $revisionRecord );
1664 $prevlink = $prevExist
1665 ? $this->linkRenderer->makeKnownLink(
1666 $this->getTitle(),
1667 $context->msg( 'previousrevision' )->text(),
1668 [],
1669 [
1670 'direction' => 'prev',
1671 'oldid' => $oldid
1672 ] + $extraParams
1673 )
1674 : $context->msg( 'previousrevision' )->escaped();
1675 $prevdiff = $prevExist
1676 ? $this->linkRenderer->makeKnownLink(
1677 $this->getTitle(),
1678 $context->msg( 'diff' )->text(),
1679 [],
1680 [
1681 'diff' => 'prev',
1682 'oldid' => $oldid
1683 ] + $extraParams
1684 )
1685 : $context->msg( 'diff' )->escaped();
1686 $nextlink = $current
1687 ? $context->msg( 'nextrevision' )->escaped()
1688 : $this->linkRenderer->makeKnownLink(
1689 $this->getTitle(),
1690 $context->msg( 'nextrevision' )->text(),
1691 [],
1692 [
1693 'direction' => 'next',
1694 'oldid' => $oldid
1695 ] + $extraParams
1696 );
1697 $nextdiff = $current
1698 ? $context->msg( 'diff' )->escaped()
1699 : $this->linkRenderer->makeKnownLink(
1700 $this->getTitle(),
1701 $context->msg( 'diff' )->text(),
1702 [],
1703 [
1704 'diff' => 'next',
1705 'oldid' => $oldid
1706 ] + $extraParams
1707 );
1708
1710 $user,
1711 $revisionRecord,
1712 $this->getTitle()
1713 );
1714 if ( $cdel !== '' ) {
1715 $cdel .= ' ';
1716 }
1717
1718 // the outer div is need for styling the revision info and nav in MobileFrontend
1719 $outputPage->addSubtitle( "<div class=\"mw-revision warningbox\">" . $revisionInfo .
1720 "<div id=\"mw-revision-nav\">" . $cdel .
1721 $context->msg( 'revision-nav' )->rawParams(
1722 $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff
1723 )->escaped() . "</div></div>" );
1724 }
1725
1739 public function viewRedirect( $target, $appendSubtitle = true, $forceKnown = false ) {
1740 $lang = $this->getTitle()->getPageLanguage();
1741 $out = $this->getContext()->getOutput();
1742 if ( $appendSubtitle ) {
1743 $out->addSubtitle( wfMessage( 'redirectpagesub' ) );
1744 }
1745 $out->addModuleStyles( 'mediawiki.action.view.redirectPage' );
1746 return static::getRedirectHeaderHtml( $lang, $target, $forceKnown );
1747 }
1748
1761 public static function getRedirectHeaderHtml( Language $lang, $target, $forceKnown = false ) {
1762 if ( !is_array( $target ) ) {
1763 $target = [ $target ];
1764 }
1765
1766 $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
1767
1768 $html = '<ul class="redirectText">';
1770 foreach ( $target as $title ) {
1771 if ( $forceKnown ) {
1773 $title,
1774 $title->getFullText(),
1775 [],
1776 // Make sure wiki page redirects are not followed
1777 $title->isRedirect() ? [ 'redirect' => 'no' ] : []
1778 );
1779 } else {
1780 $link = $linkRenderer->makeLink(
1781 $title,
1782 $title->getFullText(),
1783 [],
1784 // Make sure wiki page redirects are not followed
1785 $title->isRedirect() ? [ 'redirect' => 'no' ] : []
1786 );
1787 }
1788 $html .= '<li>' . $link . '</li>';
1789 }
1790 $html .= '</ul>';
1791
1792 $redirectToText = wfMessage( 'redirectto' )->inLanguage( $lang )->escaped();
1793
1794 return '<div class="redirectMsg">' .
1795 '<p>' . $redirectToText . '</p>' .
1796 $html .
1797 '</div>';
1798 }
1799
1808 public function addHelpLink( $to, $overrideBaseUrl = false ) {
1809 $msg = wfMessage(
1810 'namespace-' . $this->getTitle()->getNamespace() . '-helppage'
1811 );
1812
1813 $out = $this->getContext()->getOutput();
1814 if ( !$msg->isDisabled() ) {
1815 $helpUrl = Skin::makeUrl( $msg->plain() );
1816 $out->addHelpLink( $helpUrl, true );
1817 } else {
1818 $out->addHelpLink( $to, $overrideBaseUrl );
1819 }
1820 }
1821
1825 public function render() {
1826 $this->getContext()->getRequest()->response()->header( 'X-Robots-Tag: noindex' );
1827 $this->getContext()->getOutput()->setArticleBodyOnly( true );
1828 // We later set 'enableSectionEditLinks=false' based on this; also used by ImagePage
1829 $this->viewIsRenderAction = true;
1830 $this->view();
1831 }
1832
1836 public function protect() {
1837 $form = new ProtectionForm( $this );
1838 $form->execute();
1839 }
1840
1844 public function unprotect() {
1845 $this->protect();
1846 }
1847
1851 public function delete() {
1852 # This code desperately needs to be totally rewritten
1853
1854 $title = $this->getTitle();
1855 $context = $this->getContext();
1856 $user = $context->getUser();
1857 $request = $context->getRequest();
1858
1859 # Check permissions
1860 $permissionErrors = $this->permManager->getPermissionErrors( 'delete', $user, $title );
1861 if ( count( $permissionErrors ) ) {
1862 throw new PermissionsError( 'delete', $permissionErrors );
1863 }
1864
1865 # Read-only check...
1866 if ( wfReadOnly() ) {
1867 throw new ReadOnlyError;
1868 }
1869
1870 # Better double-check that it hasn't been deleted yet!
1871 $this->mPage->loadPageData(
1872 $request->wasPosted() ? WikiPage::READ_LATEST : WikiPage::READ_NORMAL
1873 );
1874 if ( !$this->mPage->exists() ) {
1875 $deleteLogPage = new LogPage( 'delete' );
1876 $outputPage = $context->getOutput();
1877 $outputPage->setPageTitle( $context->msg( 'cannotdelete-title', $title->getPrefixedText() ) );
1878 $outputPage->wrapWikiMsg( "<div class=\"error mw-error-cannotdelete\">\n$1\n</div>",
1879 [ 'cannotdelete', wfEscapeWikiText( $title->getPrefixedText() ) ]
1880 );
1881 $outputPage->addHTML(
1882 Xml::element( 'h2', null, $deleteLogPage->getName()->text() )
1883 );
1884 LogEventsList::showLogExtract(
1885 $outputPage,
1886 'delete',
1887 $title
1888 );
1889
1890 return;
1891 }
1892
1893 $deleteReasonList = $request->getText( 'wpDeleteReasonList', 'other' );
1894 $deleteReason = $request->getText( 'wpReason' );
1895
1896 if ( $deleteReasonList == 'other' ) {
1897 $reason = $deleteReason;
1898 } elseif ( $deleteReason != '' ) {
1899 // Entry from drop down menu + additional comment
1900 $colonseparator = wfMessage( 'colon-separator' )->inContentLanguage()->text();
1901 $reason = $deleteReasonList . $colonseparator . $deleteReason;
1902 } else {
1903 $reason = $deleteReasonList;
1904 }
1905
1906 if ( $request->wasPosted() && $user->matchEditToken( $request->getVal( 'wpEditToken' ),
1907 [ 'delete', $this->getTitle()->getPrefixedText() ] )
1908 ) {
1909 # Flag to hide all contents of the archived revisions
1910
1911 $suppress = $request->getCheck( 'wpSuppress' ) &&
1912 $this->permManager->userHasRight( $user, 'suppressrevision' );
1913
1914 $this->doDelete( $reason, $suppress );
1915
1916 WatchAction::doWatchOrUnwatch( $request->getCheck( 'wpWatch' ), $title, $user );
1917
1918 return;
1919 }
1920
1921 // Generate deletion reason
1922 $hasHistory = false;
1923 if ( !$reason ) {
1924 try {
1925 $reason = $this->getPage()
1926 ->getAutoDeleteReason( $hasHistory );
1927 } catch ( Exception $e ) {
1928 # if a page is horribly broken, we still want to be able to
1929 # delete it. So be lenient about errors here.
1930 wfDebug( "Error while building auto delete summary: $e" );
1931 $reason = '';
1932 }
1933 }
1934
1935 // If the page has a history, insert a warning
1936 if ( $hasHistory ) {
1937 $title = $this->getTitle();
1938
1939 // The following can use the real revision count as this is only being shown for users
1940 // that can delete this page.
1941 // This, as a side-effect, also makes sure that the following query isn't being run for
1942 // pages with a larger history, unless the user has the 'bigdelete' right
1943 // (and is about to delete this page).
1944 $dbr = wfGetDB( DB_REPLICA );
1945 $revisions = $edits = (int)$dbr->selectField(
1946 'revision',
1947 'COUNT(rev_page)',
1948 [ 'rev_page' => $title->getArticleID() ],
1949 __METHOD__
1950 );
1951
1952 // @todo i18n issue/patchwork message
1953 $context->getOutput()->addHTML(
1954 '<strong class="mw-delete-warning-revisions">' .
1955 $context->msg( 'historywarning' )->numParams( $revisions )->parse() .
1956 $context->msg( 'word-separator' )->escaped() . $this->linkRenderer->makeKnownLink(
1957 $title,
1958 $context->msg( 'history' )->text(),
1959 [],
1960 [ 'action' => 'history' ] ) .
1961 '</strong>'
1962 );
1963
1964 if ( $title->isBigDeletion() ) {
1966 $context->getOutput()->wrapWikiMsg( "<div class='error'>\n$1\n</div>\n",
1967 [
1968 'delete-warning-toobig',
1969 $context->getLanguage()->formatNum( $wgDeleteRevisionsLimit )
1970 ]
1971 );
1972 }
1973 }
1974
1975 $this->confirmDelete( $reason );
1976 }
1977
1983 public function confirmDelete( $reason ) {
1984 wfDebug( "Article::confirmDelete" );
1985
1986 $title = $this->getTitle();
1987 $ctx = $this->getContext();
1988 $outputPage = $ctx->getOutput();
1989 $outputPage->setPageTitle( wfMessage( 'delete-confirm', $title->getPrefixedText() ) );
1990 $outputPage->addBacklinkSubtitle( $title );
1991 $outputPage->setRobotPolicy( 'noindex,nofollow' );
1992 $outputPage->addModules( 'mediawiki.action.delete' );
1993
1994 $backlinkCache = $title->getBacklinkCache();
1995 if ( $backlinkCache->hasLinks( 'pagelinks' ) || $backlinkCache->hasLinks( 'templatelinks' ) ) {
1996 $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1997 'deleting-backlinks-warning' );
1998 }
1999
2000 $subpageQueryLimit = 51;
2001 $subpages = $title->getSubpages( $subpageQueryLimit );
2002 $subpageCount = count( $subpages );
2003 if ( $subpageCount > 0 ) {
2004 $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
2005 [ 'deleting-subpages-warning', Message::numParam( $subpageCount ) ] );
2006 }
2007 $outputPage->addWikiMsg( 'confirmdeletetext' );
2008
2009 $this->getHookRunner()->onArticleConfirmDelete( $this, $outputPage, $reason );
2010
2011 $user = $this->getContext()->getUser();
2012 $checkWatch = $user->getBoolOption( 'watchdeletion' ) || $user->isWatched( $title );
2013
2014 $outputPage->enableOOUI();
2015
2016 $fields = [];
2017
2018 $suppressAllowed = $this->permManager->userHasRight( $user, 'suppressrevision' );
2019 $dropDownReason = $ctx->msg( 'deletereason-dropdown' )->inContentLanguage()->text();
2020 // Add additional specific reasons for suppress
2021 if ( $suppressAllowed ) {
2022 $dropDownReason .= "\n" . $ctx->msg( 'deletereason-dropdown-suppress' )
2023 ->inContentLanguage()->text();
2024 }
2025
2026 $options = Xml::listDropDownOptions(
2027 $dropDownReason,
2028 [ 'other' => $ctx->msg( 'deletereasonotherlist' )->inContentLanguage()->text() ]
2029 );
2030 $options = Xml::listDropDownOptionsOoui( $options );
2031
2032 $fields[] = new OOUI\FieldLayout(
2033 new OOUI\DropdownInputWidget( [
2034 'name' => 'wpDeleteReasonList',
2035 'inputId' => 'wpDeleteReasonList',
2036 'tabIndex' => 1,
2037 'infusable' => true,
2038 'value' => '',
2039 'options' => $options
2040 ] ),
2041 [
2042 'label' => $ctx->msg( 'deletecomment' )->text(),
2043 'align' => 'top',
2044 ]
2045 );
2046
2047 // HTML maxlength uses "UTF-16 code units", which means that characters outside BMP
2048 // (e.g. emojis) count for two each. This limit is overridden in JS to instead count
2049 // Unicode codepoints.
2050 $fields[] = new OOUI\FieldLayout(
2051 new OOUI\TextInputWidget( [
2052 'name' => 'wpReason',
2053 'inputId' => 'wpReason',
2054 'tabIndex' => 2,
2055 'maxLength' => CommentStore::COMMENT_CHARACTER_LIMIT,
2056 'infusable' => true,
2057 'value' => $reason,
2058 'autofocus' => true,
2059 ] ),
2060 [
2061 'label' => $ctx->msg( 'deleteotherreason' )->text(),
2062 'align' => 'top',
2063 ]
2064 );
2065
2066 if ( $user->isLoggedIn() ) {
2067 $fields[] = new OOUI\FieldLayout(
2068 new OOUI\CheckboxInputWidget( [
2069 'name' => 'wpWatch',
2070 'inputId' => 'wpWatch',
2071 'tabIndex' => 3,
2072 'selected' => $checkWatch,
2073 ] ),
2074 [
2075 'label' => $ctx->msg( 'watchthis' )->text(),
2076 'align' => 'inline',
2077 'infusable' => true,
2078 ]
2079 );
2080 }
2081 if ( $suppressAllowed ) {
2082 $fields[] = new OOUI\FieldLayout(
2083 new OOUI\CheckboxInputWidget( [
2084 'name' => 'wpSuppress',
2085 'inputId' => 'wpSuppress',
2086 'tabIndex' => 4,
2087 ] ),
2088 [
2089 'label' => $ctx->msg( 'revdelete-suppress' )->text(),
2090 'align' => 'inline',
2091 'infusable' => true,
2092 ]
2093 );
2094 }
2095
2096 $fields[] = new OOUI\FieldLayout(
2097 new OOUI\ButtonInputWidget( [
2098 'name' => 'wpConfirmB',
2099 'inputId' => 'wpConfirmB',
2100 'tabIndex' => 5,
2101 'value' => $ctx->msg( 'deletepage' )->text(),
2102 'label' => $ctx->msg( 'deletepage' )->text(),
2103 'flags' => [ 'primary', 'destructive' ],
2104 'type' => 'submit',
2105 ] ),
2106 [
2107 'align' => 'top',
2108 ]
2109 );
2110
2111 $fieldset = new OOUI\FieldsetLayout( [
2112 'label' => $ctx->msg( 'delete-legend' )->text(),
2113 'id' => 'mw-delete-table',
2114 'items' => $fields,
2115 ] );
2116
2117 $form = new OOUI\FormLayout( [
2118 'method' => 'post',
2119 'action' => $title->getLocalURL( 'action=delete' ),
2120 'id' => 'deleteconfirm',
2121 ] );
2122 $form->appendContent(
2123 $fieldset,
2124 new OOUI\HtmlSnippet(
2125 Html::hidden( 'wpEditToken', $user->getEditToken( [ 'delete', $title->getPrefixedText() ] ) )
2126 )
2127 );
2128
2129 $outputPage->addHTML(
2130 new OOUI\PanelLayout( [
2131 'classes' => [ 'deletepage-wrapper' ],
2132 'expanded' => false,
2133 'padded' => true,
2134 'framed' => true,
2135 'content' => $form,
2136 ] )
2137 );
2138
2139 if ( $this->permManager->userHasRight( $user, 'editinterface' ) ) {
2140 $link = '';
2141 if ( $suppressAllowed ) {
2142 $link .= $this->linkRenderer->makeKnownLink(
2143 $ctx->msg( 'deletereason-dropdown-suppress' )->inContentLanguage()->getTitle(),
2144 $ctx->msg( 'delete-edit-reasonlist-suppress' )->text(),
2145 [],
2146 [ 'action' => 'edit' ]
2147 );
2148 $link .= $ctx->msg( 'pipe-separator' )->escaped();
2149 }
2150 $link .= $this->linkRenderer->makeKnownLink(
2151 $ctx->msg( 'deletereason-dropdown' )->inContentLanguage()->getTitle(),
2152 $ctx->msg( 'delete-edit-reasonlist' )->text(),
2153 [],
2154 [ 'action' => 'edit' ]
2155 );
2156 $outputPage->addHTML( '<p class="mw-delete-editreasons">' . $link . '</p>' );
2157 }
2158
2159 $deleteLogPage = new LogPage( 'delete' );
2160 $outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) );
2161 LogEventsList::showLogExtract( $outputPage, 'delete', $title );
2162 }
2163
2172 public function doDelete( $reason, $suppress = false, $immediate = false ) {
2173 $error = '';
2174 $context = $this->getContext();
2175 $outputPage = $context->getOutput();
2176 $user = $context->getUser();
2177 $status = $this->mPage->doDeleteArticleReal(
2178 $reason, $user, $suppress, null, $error,
2179 null, [], 'delete', $immediate
2180 );
2181
2182 if ( $status->isOK() ) {
2183 $deleted = $this->getTitle()->getPrefixedText();
2184
2185 $outputPage->setPageTitle( wfMessage( 'actioncomplete' ) );
2186 $outputPage->setRobotPolicy( 'noindex,nofollow' );
2187
2188 if ( $status->isGood() ) {
2189 $loglink = '[[Special:Log/delete|' . wfMessage( 'deletionlog' )->text() . ']]';
2190 $outputPage->addWikiMsg( 'deletedtext', wfEscapeWikiText( $deleted ), $loglink );
2191 $this->getHookRunner()->onArticleDeleteAfterSuccess( $this->getTitle(), $outputPage );
2192 } else {
2193 $outputPage->addWikiMsg( 'delete-scheduled', wfEscapeWikiText( $deleted ) );
2194 }
2195
2196 $outputPage->returnToMain( false );
2197 } else {
2198 $outputPage->setPageTitle(
2199 wfMessage( 'cannotdelete-title',
2200 $this->getTitle()->getPrefixedText() )
2201 );
2202
2203 if ( $error == '' ) {
2204 $outputPage->wrapWikiTextAsInterface(
2205 'error mw-error-cannotdelete',
2206 $status->getWikiText( false, false, $context->getLanguage() )
2207 );
2208 $deleteLogPage = new LogPage( 'delete' );
2209 $outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) );
2210
2211 LogEventsList::showLogExtract(
2212 $outputPage,
2213 'delete',
2214 $this->getTitle()
2215 );
2216 } else {
2217 $outputPage->addHTML( $error );
2218 }
2219 }
2220 }
2221
2222 /* Caching functions */
2223
2231 protected function tryFileCache() {
2232 static $called = false;
2233
2234 if ( $called ) {
2235 wfDebug( "Article::tryFileCache(): called twice!?" );
2236 return false;
2237 }
2238
2239 $called = true;
2240 if ( $this->isFileCacheable() ) {
2241 $cache = new HTMLFileCache( $this->getTitle(), 'view' );
2242 if ( $cache->isCacheGood( $this->mPage->getTouched() ) ) {
2243 wfDebug( "Article::tryFileCache(): about to load file" );
2244 $cache->loadFromFileCache( $this->getContext() );
2245 return true;
2246 } else {
2247 wfDebug( "Article::tryFileCache(): starting buffer" );
2248 ob_start( [ &$cache, 'saveToFileCache' ] );
2249 }
2250 } else {
2251 wfDebug( "Article::tryFileCache(): not cacheable" );
2252 }
2253
2254 return false;
2255 }
2256
2262 public function isFileCacheable( $mode = HTMLFileCache::MODE_NORMAL ) {
2263 $cacheable = false;
2264
2265 if ( HTMLFileCache::useFileCache( $this->getContext(), $mode ) ) {
2266 $cacheable = $this->mPage->getId()
2267 && !$this->mRedirectedFrom && !$this->getTitle()->isRedirect();
2268 // Extension may have reason to disable file caching on some pages.
2269 if ( $cacheable ) {
2270 $cacheable = $this->getHookRunner()->onIsFileCacheable( $this );
2271 }
2272 }
2273
2274 return $cacheable;
2275 }
2276
2290 public function getParserOutput( $oldid = null, User $user = null ) {
2291 // XXX: bypasses mParserOptions and thus setParserOptions()
2292
2293 if ( $user === null ) {
2294 $parserOptions = $this->getParserOptions();
2295 } else {
2296 $parserOptions = $this->mPage->makeParserOptions( $user );
2297 }
2298
2299 return $this->mPage->getParserOutput( $parserOptions, $oldid );
2300 }
2301
2308 public function setParserOptions( ParserOptions $options ) {
2309 if ( $this->mParserOptions ) {
2310 throw new MWException( "can't change parser options after they have already been set" );
2311 }
2312
2313 // clone, so if $options is modified later, it doesn't confuse the parser cache.
2314 $this->mParserOptions = clone $options;
2315 }
2316
2321 public function getParserOptions() {
2322 if ( !$this->mParserOptions ) {
2323 $this->mParserOptions = $this->mPage->makeParserOptions( $this->getContext() );
2324 }
2325 // Clone to allow modifications of the return value without affecting cache
2326 return clone $this->mParserOptions;
2327 }
2328
2335 public function setContext( $context ) {
2336 $this->mContext = $context;
2337 }
2338
2345 public function getContext() {
2346 if ( $this->mContext instanceof IContextSource ) {
2347 return $this->mContext;
2348 } else {
2349 wfDebug( __METHOD__ . " called and \$mContext is null. " .
2350 "Return RequestContext::getMain(); for sanity" );
2351 return RequestContext::getMain();
2352 }
2353 }
2354
2364 public function __get( $fname ) {
2365 wfDeprecatedMsg( "Accessing Article::\$$fname is deprecated since MediaWiki 1.35",
2366 '1.35' );
2367
2368 if ( $fname === 'mRevision' ) {
2369 $record = $this->fetchRevisionRecord(); // Ensure that it is loaded
2370 return $record ? new Revision( $record ) : null;
2371 }
2372
2373 if ( property_exists( $this->mPage, $fname ) ) {
2374 return $this->mPage->$fname;
2375 }
2376 trigger_error( 'Inaccessible property via __get(): ' . $fname, E_USER_NOTICE );
2377 }
2378
2388 public function __set( $fname, $fvalue ) {
2389 wfDeprecatedMsg( "Setting Article::\$$fname is deprecated since MediaWiki 1.35",
2390 '1.35' );
2391
2392 if ( $fname === 'mRevision' ) {
2393 $this->mRevisionRecord = $fvalue ?
2394 $fvalue->getRevisionRecord() :
2395 null;
2396 return;
2397 }
2398
2399 if ( property_exists( $this->mPage, $fname ) ) {
2400 $this->mPage->$fname = $fvalue;
2401 // Note: extensions may want to toss on new fields
2402 } elseif ( !in_array( $fname, [ 'mContext', 'mPage' ] ) ) {
2403 $this->mPage->$fname = $fvalue;
2404 } else {
2405 trigger_error( 'Inaccessible property via __set(): ' . $fname, E_USER_NOTICE );
2406 }
2407 }
2408
2420 public function checkFlags( $flags ) {
2421 wfDeprecated( __METHOD__, '1.35' );
2422 return $this->mPage->checkFlags( $flags );
2423 }
2424
2430 public function checkTouched() {
2431 wfDeprecated( __METHOD__, '1.35' );
2432 return $this->mPage->checkTouched();
2433 }
2434
2440 public function clearPreparedEdit() {
2441 wfDeprecated( __METHOD__, '1.35' );
2442 $this->mPage->clearPreparedEdit();
2443 }
2444
2459 public function doDeleteArticleReal(
2460 $reason, $suppress = false, $u1 = null, $u2 = null, &$error = '', User $user = null,
2461 $tags = [], $immediate = false
2462 ) {
2463 wfDeprecated( __METHOD__, '1.35' );
2464 return $this->mPage->doDeleteArticleReal(
2465 $reason, $suppress, $u1, $u2, $error, $user, $tags, 'delete', $immediate
2466 );
2467 }
2468
2477 public function doDeleteUpdates(
2478 $id,
2479 Content $content = null,
2480 $revision = null,
2481 User $user = null
2482 ) {
2483 wfDeprecated( __METHOD__, '1.35' );
2484 $this->mPage->doDeleteUpdates( $id, $content, $revision, $user );
2485 }
2486
2496 public function doEditUpdates( Revision $revision, User $user, array $options = [] ) {
2497 wfDeprecated( __METHOD__, '1.35' );
2498 $this->mPage->doEditUpdates( $revision, $user, $options );
2499 }
2500
2508 public function doPurge() {
2509 wfDeprecated( __METHOD__, '1.35' );
2510 return $this->mPage->doPurge();
2511 }
2512
2519 public function doViewUpdates( User $user, $oldid = 0 ) {
2520 wfDeprecated( __METHOD__, '1.35' );
2521 $this->mPage->doViewUpdates( $user, $oldid );
2522 }
2523
2529 public function exists() {
2530 wfDeprecated( __METHOD__, '1.35' );
2531 return $this->mPage->exists();
2532 }
2533
2539 public function followRedirect() {
2540 wfDeprecated( __METHOD__, '1.35' );
2541 return $this->mPage->followRedirect();
2542 }
2543
2549 public function getActionOverrides() {
2550 return $this->mPage->getActionOverrides();
2551 }
2552
2559 public function getAutoDeleteReason( &$hasHistory ) {
2560 wfDeprecated( __METHOD__, '1.35' );
2561 return $this->mPage->getAutoDeleteReason( $hasHistory );
2562 }
2563
2569 public function getCategories() {
2570 wfDeprecated( __METHOD__, '1.35' );
2571 return $this->mPage->getCategories();
2572 }
2573
2582 public function getComment( $audience = RevisionRecord::FOR_PUBLIC, User $user = null ) {
2583 wfDeprecated( __METHOD__, '1.35' );
2584 return $this->mPage->getComment( $audience, $user );
2585 }
2586
2592 public function getContentHandler() {
2593 wfDeprecated( __METHOD__, '1.35' );
2594 return $this->mPage->getContentHandler();
2595 }
2596
2602 public function getContentModel() {
2603 wfDeprecated( __METHOD__, '1.35' );
2604 return $this->mPage->getContentModel();
2605 }
2606
2612 public function getContributors() {
2613 wfDeprecated( __METHOD__, '1.35' );
2614 return $this->mPage->getContributors();
2615 }
2616
2625 public function getCreator( $audience = RevisionRecord::FOR_PUBLIC, User $user = null ) {
2626 wfDeprecated( __METHOD__, '1.35' );
2627 return $this->mPage->getCreator( $audience, $user );
2628 }
2629
2636 public function getDeletionUpdates( Content $content = null ) {
2637 wfDeprecated( __METHOD__, '1.35' );
2638 return $this->mPage->getDeletionUpdates( $content );
2639 }
2640
2646 public function getHiddenCategories() {
2647 wfDeprecated( __METHOD__, '1.35' );
2648 return $this->mPage->getHiddenCategories();
2649 }
2650
2656 public function getId() {
2657 wfDeprecated( __METHOD__, '1.35' );
2658 return $this->mPage->getId();
2659 }
2660
2666 public function getLatest() {
2667 wfDeprecated( __METHOD__, '1.35' );
2668 return $this->mPage->getLatest();
2669 }
2670
2676 public function getLinksTimestamp() {
2677 wfDeprecated( __METHOD__, '1.35' );
2678 return $this->mPage->getLinksTimestamp();
2679 }
2680
2686 public function getMinorEdit() {
2687 wfDeprecated( __METHOD__, '1.35' );
2688 return $this->mPage->getMinorEdit();
2689 }
2690
2695 public function getOldestRevision() {
2696 wfDeprecated( __METHOD__, '1.35' );
2697 return $this->mPage->getOldestRevision();
2698 }
2699
2705 public function getRedirectTarget() {
2706 wfDeprecated( __METHOD__, '1.35' );
2707 return $this->mPage->getRedirectTarget();
2708 }
2709
2716 public function getRedirectURL( $rt ) {
2717 wfDeprecated( __METHOD__, '1.35' );
2718 return $this->mPage->getRedirectURL( $rt );
2719 }
2720
2727 public function getRevision() {
2728 wfDeprecated( __METHOD__, '1.35' );
2729 return $this->mPage->getRevision();
2730 }
2731
2737 public function getTimestamp() {
2738 wfDeprecated( __METHOD__, '1.35' );
2739 return $this->mPage->getTimestamp();
2740 }
2741
2747 public function getTouched() {
2748 wfDeprecated( __METHOD__, '1.35' );
2749 return $this->mPage->getTouched();
2750 }
2751
2760 public function getUndoContent( Revision $undo, Revision $undoafter = null ) {
2761 wfDeprecated( __METHOD__, '1.35' );
2762 return $this->mPage->getUndoContent( $undo, $undoafter );
2763 }
2764
2772 public function getUser( $audience = RevisionRecord::FOR_PUBLIC, User $user = null ) {
2773 wfDeprecated( __METHOD__, '1.35' );
2774 return $this->mPage->getUser( $audience, $user );
2775 }
2776
2784 public function getUserText( $audience = RevisionRecord::FOR_PUBLIC, User $user = null ) {
2785 wfDeprecated( __METHOD__, '1.35' );
2786 return $this->mPage->getUserText( $audience, $user );
2787 }
2788
2794 public function hasViewableContent() {
2795 wfDeprecated( __METHOD__, '1.35' );
2796 return $this->mPage->hasViewableContent();
2797 }
2798
2806 public function insertOn( $dbw, $pageId = null ) {
2807 wfDeprecated( __METHOD__, '1.35' );
2808 return $this->mPage->insertOn( $dbw, $pageId );
2809 }
2810
2822 public function insertProtectNullRevision( $revCommentMsg, array $limit,
2823 array $expiry, $cascade, $reason, $user = null
2824 ) {
2825 wfDeprecated( __METHOD__, '1.35' );
2826 return $this->mPage->insertProtectNullRevision( $revCommentMsg, $limit,
2827 $expiry, $cascade, $reason, $user
2828 );
2829 }
2830
2836 public function insertRedirect() {
2837 wfDeprecated( __METHOD__, '1.35' );
2838 return $this->mPage->insertRedirect();
2839 }
2840
2848 public function insertRedirectEntry( Title $rt, $oldLatest = null ) {
2849 wfDeprecated( __METHOD__, '1.35' );
2850 return $this->mPage->insertRedirectEntry( $rt, $oldLatest );
2851 }
2852
2859 public function isCountable( $editInfo = false ) {
2860 wfDeprecated( __METHOD__, '1.35' );
2861 return $this->mPage->isCountable( $editInfo );
2862 }
2863
2869 public function isRedirect() {
2870 wfDeprecated( __METHOD__, '1.35' );
2871 return $this->mPage->isRedirect();
2872 }
2873
2880 public function loadFromRow( $data, $from ) {
2881 wfDeprecated( __METHOD__, '1.35' );
2882 $this->mPage->loadFromRow( $data, $from );
2883 }
2884
2890 public function loadPageData( $from = 'fromdb' ) {
2891 wfDeprecated( __METHOD__, '1.35' );
2892 $this->mPage->loadPageData( $from );
2893 }
2894
2900 public function lockAndGetLatest() {
2901 wfDeprecated( __METHOD__, '1.35' );
2902 return $this->mPage->lockAndGetLatest();
2903 }
2904
2911 public function makeParserOptions( $context ) {
2912 wfDeprecated( __METHOD__, '1.35' );
2913 return $this->mPage->makeParserOptions( $context );
2914 }
2915
2924 public function pageDataFromId( $dbr, $id, $options = [] ) {
2925 wfDeprecated( __METHOD__, '1.35' );
2926 return $this->mPage->pageDataFromId( $dbr, $id, $options );
2927 }
2928
2937 public function pageDataFromTitle( $dbr, $title, $options = [] ) {
2938 wfDeprecated( __METHOD__, '1.35' );
2939 return $this->mPage->pageDataFromTitle( $dbr, $title, $options );
2940 }
2941
2955 public function prepareContentForEdit(
2956 Content $content, $revision = null, User $user = null,
2957 $serialFormat = null, $useCache = true
2958 ) {
2959 wfDeprecated( __METHOD__, '1.35' );
2960 return $this->mPage->prepareContentForEdit(
2961 $content, $revision, $user,
2962 $serialFormat, $useCache
2963 );
2964 }
2965
2973 public function protectDescription( array $limit, array $expiry ) {
2974 wfDeprecated( __METHOD__, '1.35' );
2975 return $this->mPage->protectDescription( $limit, $expiry );
2976 }
2977
2985 public function protectDescriptionLog( array $limit, array $expiry ) {
2986 wfDeprecated( __METHOD__, '1.35' );
2987 return $this->mPage->protectDescriptionLog( $limit, $expiry );
2988 }
2989
2999 public function replaceSectionAtRev( $sectionId, Content $sectionContent,
3000 $sectionTitle = '', $baseRevId = null
3001 ) {
3002 wfDeprecated( __METHOD__, '1.35' );
3003 return $this->mPage->replaceSectionAtRev( $sectionId, $sectionContent,
3004 $sectionTitle, $baseRevId
3005 );
3006 }
3007
3019 public function replaceSectionContent(
3020 $sectionId, Content $sectionContent, $sectionTitle = '', $edittime = null
3021 ) {
3022 wfDeprecated( __METHOD__, '1.35' );
3023 return $this->mPage->replaceSectionContent(
3024 $sectionId, $sectionContent, $sectionTitle, $edittime
3025 );
3026 }
3027
3033 public function setTimestamp( $ts ) {
3034 wfDeprecated( __METHOD__, '1.35' );
3035 $this->mPage->setTimestamp( $ts );
3036 }
3037
3045 public function shouldCheckParserCache( ParserOptions $parserOptions, $oldId ) {
3046 wfDeprecated( __METHOD__, '1.35' );
3047 return $this->mPage->shouldCheckParserCache( $parserOptions, $oldId );
3048 }
3049
3055 public function supportsSections() {
3056 wfDeprecated( __METHOD__, '1.35' );
3057 return $this->mPage->supportsSections();
3058 }
3059
3065 public function triggerOpportunisticLinksUpdate( ParserOutput $parserOutput ) {
3066 wfDeprecated( __METHOD__, '1.35' );
3067 $this->mPage->triggerOpportunisticLinksUpdate( $parserOutput );
3068 }
3069
3077 public function updateCategoryCounts( array $added, array $deleted, $id = 0 ) {
3078 wfDeprecated( __METHOD__, '1.35' );
3079 $this->mPage->updateCategoryCounts( $added, $deleted, $id );
3080 }
3081
3090 public function updateIfNewerOn( $dbw, $revision ) {
3091 wfDeprecated( __METHOD__, '1.35' );
3092 return $this->mPage->updateIfNewerOn( $dbw, $revision );
3093 }
3094
3103 public function updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect = null ) {
3104 wfDeprecated( __METHOD__, '1.35' );
3105 return $this->mPage->updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect );
3106 }
3107
3117 public function updateRevisionOn( $dbw, $revision, $lastRevision = null,
3118 $lastRevIsRedirect = null
3119 ) {
3120 wfDeprecated( __METHOD__, '1.35' );
3121 return $this->mPage->updateRevisionOn( $dbw, $revision, $lastRevision,
3122 $lastRevIsRedirect
3123 );
3124 }
3125
3135 public function doUpdateRestrictions( array $limit, array $expiry, &$cascade,
3136 $reason, User $user
3137 ) {
3138 wfDeprecated( __METHOD__, '1.35' );
3139 return $this->mPage->doUpdateRestrictions( $limit, $expiry, $cascade, $reason, $user );
3140 }
3141
3150 public function updateRestrictions( $limit = [], $reason = '',
3151 &$cascade = 0, $expiry = []
3152 ) {
3153 wfDeprecated( __METHOD__, '1.35' );
3154 return $this->mPage->doUpdateRestrictions(
3155 $limit,
3156 $expiry,
3157 $cascade,
3158 $reason,
3159 $this->getContext()->getUser()
3160 );
3161 }
3162
3175 public function doDeleteArticle(
3176 $reason, $suppress = false, $u1 = null, $u2 = null, &$error = '', $immediate = false
3177 ) {
3178 wfDeprecated( __METHOD__, '1.35' );
3179 return $this->mPage->doDeleteArticle( $reason, $suppress, $u1, $u2, $error,
3180 null, $immediate );
3181 }
3182
3193 public function doRollback(
3194 $fromP,
3195 $summary,
3196 $token,
3197 $bot,
3198 &$resultDetails,
3199 User $user = null
3200 ) {
3201 wfDeprecated( __METHOD__, '1.35' );
3202 if ( !$user ) {
3203 $user = $this->getContext()->getUser();
3204 }
3205
3206 return $this->mPage->doRollback( $fromP, $summary, $token, $bot, $resultDetails, $user );
3207 }
3208
3219 public function commitRollback( $fromP, $summary, $bot, &$resultDetails, User $guser = null ) {
3220 wfDeprecated( __METHOD__, '1.35' );
3221 if ( !$guser ) {
3222 $guser = $this->getContext()->getUser();
3223 }
3224
3225 return $this->mPage->commitRollback( $fromP, $summary, $bot, $resultDetails, $guser );
3226 }
3227
3234 public function generateReason( &$hasHistory ) {
3235 wfDeprecated( __METHOD__, '1.35' );
3236 return $this->getPage()->getAutoDeleteReason( $hasHistory );
3237 }
3238}
getUser()
$wgArticleRobotPolicies
Robot policies per article.
$wgCdnMaxageStale
Cache timeout when delivering a stale ParserCache response due to PoolCounter contention.
$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...
$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...
$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.
wfDeprecatedMsg( $msg, $version=false, $component=false, $callerOffset=2)
Log a deprecation warning with arbitrary message text.
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
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)
Logs a warning that $function is deprecated.
getContext()
Class for viewing MediaWiki article and history.
Definition Article.php:46
shouldCheckParserCache(ParserOptions $parserOptions, $oldId)
Definition Article.php:3045
getRevisionFetched()
Get the fetched Revision object depending on request parameters or null on failure.
Definition Article.php:596
doDeleteUpdates( $id, Content $content=null, $revision=null, User $user=null)
Definition Article.php:2477
updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect=null)
Definition Article.php:3103
generateReason(&$hasHistory)
Definition Article.php:3234
getSubstituteContent()
Returns Content object to use when the page does not exist.
Definition Article.php:320
checkFlags( $flags)
Call to WikiPage function for backwards compatibility.
Definition Article.php:2420
checkTouched()
Definition Article.php:2430
getHiddenCategories()
Definition Article.php:2646
static newFromWikiPage(WikiPage $page, IContextSource $context)
Create an Article object of the appropriate class for the given page.
Definition Article.php:226
$mRevisionRecord
Definition Article.php:153
doViewUpdates(User $user, $oldid=0)
Definition Article.php:2519
getContext()
Gets the context this Article is executed in.
Definition Article.php:2345
getCategories()
Definition Article.php:2569
commitRollback( $fromP, $summary, $bot, &$resultDetails, User $guser=null)
Definition Article.php:3219
getOldIDFromRequest()
Sets $this->mRedirectUrl to a correct URL if the query parameters are incorrect.
Definition Article.php:373
getParserOutput( $oldid=null, User $user=null)
#-
Definition Article.php:2290
insertProtectNullRevision( $revCommentMsg, array $limit, array $expiry, $cascade, $reason, $user=null)
Definition Article.php:2822
getRedirectedFrom()
Get the page this view was redirected from.
Definition Article.php:237
Title null $mRedirectedFrom
Title from which we were redirected here, if any.
Definition Article.php:91
insertOn( $dbw, $pageId=null)
Definition Article.php:2806
updateRevisionOn( $dbw, $revision, $lastRevision=null, $lastRevIsRedirect=null)
Definition Article.php:3117
bool $viewIsRenderAction
Whether render() was called.
Definition Article.php:125
fetchContentObject()
Get text content object Does NOT follow redirects.
Definition Article.php:448
supportsSections()
Definition Article.php:3055
pageDataFromTitle( $dbr, $title, $options=[])
Definition Article.php:2937
getContentHandler()
Definition Article.php:2592
RevisionStore $revisionStore
Definition Article.php:140
updateCategoryCounts(array $added, array $deleted, $id=0)
Definition Article.php:3077
view()
This is the default action of the index.php entry point: just view the page of the given title.
Definition Article.php:623
loadPageData( $from='fromdb')
Definition Article.php:2890
doDeleteArticleReal( $reason, $suppress=false, $u1=null, $u2=null, &$error='', User $user=null, $tags=[], $immediate=false)
Call to WikiPage function for backwards compatibility.
Definition Article.php:2459
doUpdateRestrictions(array $limit, array $expiry, &$cascade, $reason, User $user)
Definition Article.php:3135
protectDescriptionLog(array $limit, array $expiry)
Definition Article.php:2985
getComment( $audience=RevisionRecord::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition Article.php:2582
clearPreparedEdit()
Call to WikiPage function for backwards compatibility.
Definition Article.php:2440
__construct(Title $title, $oldId=null)
Definition Article.php:159
getRobotPolicy( $action, ParserOutput $pOutput=null)
Get the robot policy to be used for the current view.
Definition Article.php:996
static purgePatrolFooterCache( $articleID)
Purge the cache used to check if it is worth showing the patrol footer For example,...
Definition Article.php:1384
followRedirect()
Definition Article.php:2539
prepareContentForEdit(Content $content, $revision=null, User $user=null, $serialFormat=null, $useCache=true)
Call to WikiPage function for backwards compatibility.
Definition Article.php:2955
doDelete( $reason, $suppress=false, $immediate=false)
Perform a deletion and output success or failure messages.
Definition Article.php:2172
replaceSectionAtRev( $sectionId, Content $sectionContent, $sectionTitle='', $baseRevId=null)
Definition Article.php:2999
ParserOutput null false $mParserOutput
The ParserOutput generated for viewing the page, initialized by view().
Definition Article.php:118
getAutoDeleteReason(&$hasHistory)
Definition Article.php:2559
getLinksTimestamp()
Definition Article.php:2676
getOldID()
Definition Article.php:360
protectDescription(array $limit, array $expiry)
Definition Article.php:2973
getOldestRevision()
Definition Article.php:2695
LinkRenderer $linkRenderer
Definition Article.php:130
setTimestamp( $ts)
Definition Article.php:3033
getTitle()
Get the title object of the article.
Definition Article.php:255
getActionOverrides()
Call to WikiPage function for backwards compatibility.
Definition Article.php:2549
doEditUpdates(Revision $revision, User $user, array $options=[])
Call to WikiPage function for backwards compatibility.
Definition Article.php:2496
adjustDisplayTitle(ParserOutput $pOutput)
Adjust title for pages with displaytitle, -{T|}- or language conversion.
Definition Article.php:924
updateIfNewerOn( $dbw, $revision)
Call to WikiPage function for backwards compatibility.
Definition Article.php:3090
getLatest()
Definition Article.php:2666
pageDataFromId( $dbr, $id, $options=[])
Definition Article.php:2924
insertRedirect()
Definition Article.php:2836
getContributors()
Definition Article.php:2612
showDeletedRevisionHeader()
If the revision requested for view is deleted, check permissions.
Definition Article.php:1541
isCountable( $editInfo=false)
Definition Article.php:2859
getParserOptions()
Get parser options suitable for rendering the primary article wikitext.
Definition Article.php:2321
clear()
Clear the object.
Definition Article.php:272
IContextSource null $mContext
The context this Article is executed in.
Definition Article.php:54
getContentModel()
Definition Article.php:2602
static getRedirectHeaderHtml(Language $lang, $target, $forceKnown=false)
Return the HTML for the top of a redirect page.
Definition Article.php:1761
getUserText( $audience=RevisionRecord::FOR_PUBLIC, User $user=null)
Definition Article.php:2784
protect()
action=protect handler
Definition Article.php:1836
lockAndGetLatest()
Definition Article.php:2900
isCurrent()
Returns true if the currently-referenced revision is the current edit to this page (and it exists).
Definition Article.php:574
updateRestrictions( $limit=[], $reason='', &$cascade=0, $expiry=[])
Definition Article.php:3150
showMissingArticle()
Show the error text for a missing article.
Definition Article.php:1393
getUndoContent(Revision $undo, Revision $undoafter=null)
Call to WikiPage function for backwards compatibility.
Definition Article.php:2760
__set( $fname, $fvalue)
Definition Article.php:2388
unprotect()
action=unprotect handler (alias)
Definition Article.php:1844
int $mRevIdFetched
Revision ID of revision that was loaded.
Definition Article.php:101
applyContentOverride(Content $override)
Applies a content override by constructing a fake Revision object and assigning it to mRevisionRecord...
Definition Article.php:558
isRedirect()
Definition Article.php:2869
ParserOptions null $mParserOptions
ParserOptions object for $wgUser articles.
Definition Article.php:63
newPage(Title $title)
Definition Article.php:173
makeFetchErrorContent()
Returns a Content object representing any error in $this->fetchContent, or null if there is no such e...
Definition Article.php:537
confirmDelete( $reason)
Output deletion confirmation dialog.
Definition Article.php:1983
getPage()
Get the WikiPage object of this instance.
Definition Article.php:265
addHelpLink( $to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
Definition Article.php:1808
getTouched()
Definition Article.php:2747
getRevision()
Call to WikiPage function for backwards compatibility.
Definition Article.php:2727
string bool $mRedirectUrl
URL to redirect to or false if none.
Definition Article.php:94
getTimestamp()
Definition Article.php:2737
static newFromID( $id)
Constructor from a page id.
Definition Article.php:182
getRedirectTarget()
Definition Article.php:2705
int null $mOldId
The oldid of the article that was requested to be shown, 0 for the current revision.
Definition Article.php:88
static formatRobotPolicy( $policy)
Converts a String robot policy into an associative array, to allow merging of several policies using ...
Definition Article.php:1076
fetchRevisionRecord()
Fetches the revision to work on.
Definition Article.php:467
viewRedirect( $target, $appendSubtitle=true, $forceKnown=false)
Return the HTML for the top of a redirect page.
Definition Article.php:1739
insertRedirectEntry(Title $rt, $oldLatest=null)
Definition Article.php:2848
triggerOpportunisticLinksUpdate(ParserOutput $parserOutput)
Definition Article.php:3065
makeParserOptions( $context)
Definition Article.php:2911
showPatrolFooter()
If patrol is possible, output a patrol UI box.
Definition Article.php:1215
setOldSubtitle( $oldid=0)
Generate the navigation links when browsing through an article revisions It shows the information as:...
Definition Article.php:1588
showViewFooter()
Show the footer section of an ordinary page view.
Definition Article.php:1191
Status null $fetchResult
represents the outcome of fetchRevisionRecord().
Definition Article.php:111
WikiPage $mPage
The WikiPage object of this instance.
Definition Article.php:57
loadFromRow( $data, $from)
Definition Article.php:2880
setRedirectedFrom(Title $from)
Tell the page view functions that this view was redirected from another page on the wiki.
Definition Article.php:246
isFileCacheable( $mode=HTMLFileCache::MODE_NORMAL)
Check if the page can be cached.
Definition Article.php:2262
doDeleteArticle( $reason, $suppress=false, $u1=null, $u2=null, &$error='', $immediate=false)
Definition Article.php:3175
getCreator( $audience=RevisionRecord::FOR_PUBLIC, User $user=null)
Call to WikiPage function for backwards compatibility.
Definition Article.php:2625
tryFileCache()
checkLastModified returns true if it has taken care of all output to the client that is necessary for...
Definition Article.php:2231
getRevIdFetched()
Use this to fetch the rev ID used on page views.
Definition Article.php:611
replaceSectionContent( $sectionId, Content $sectionContent, $sectionTitle='', $edittime=null)
Call to WikiPage function for backwards compatibility.
Definition Article.php:3019
getUser( $audience=RevisionRecord::FOR_PUBLIC, User $user=null)
Definition Article.php:2772
showNamespaceHeader()
Show a header specific to the namespace currently being viewed, like [[MediaWiki:Talkpagetext]].
Definition Article.php:1179
__get( $fname)
Definition Article.php:2364
getRevisionRedirectTarget(RevisionRecord $revision)
Definition Article.php:912
static newFromTitle( $title, IContextSource $context)
Create an Article object of the appropriate class for the given page.
Definition Article.php:194
hasViewableContent()
Definition Article.php:2794
getContentObject()
Returns a Content object representing the pages effective display content, not necessarily the revisi...
Definition Article.php:304
showDiffPage()
Show a diff page according to current request variables.
Definition Article.php:939
getMinorEdit()
Definition Article.php:2686
setParserOptions(ParserOptions $options)
Override the ParserOptions used to render the primary article wikitext.
Definition Article.php:2308
getEmptyPageParserOutput(ParserOptions $options)
Returns ParserOutput to use when a page does not exist.
Definition Article.php:347
render()
Handle action=render.
Definition Article.php:1825
showRedirectedFromHeader()
If this request is a redirect view, send "redirected from" subtitle to the output.
Definition Article.php:1105
PermissionManager $permManager
Definition Article.php:135
setContext( $context)
Sets the context this Article is executed in.
Definition Article.php:2335
getRedirectURL( $rt)
Definition Article.php:2716
bool $mContentLoaded
Is the target revision loaded? Set by fetchRevisionRecord().
Definition Article.php:81
getDeletionUpdates(Content $content=null)
Definition Article.php:2636
doRollback( $fromP, $summary, $token, $bot, &$resultDetails, User $user=null)
Definition Article.php:3193
Content null $mContentObject
Content of the main slot of $this->mRevisionRecord.
Definition Article.php:73
Special handling for category description pages, showing pages, subcategories and file that belong to...
const POST_EDIT_COOKIE_KEY_PREFIX
Prefix of key for cookie used to pass post-edit state.
Definition EditPage.php:80
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:33
Internationalisation code See https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation for more...
Definition Language.php:41
static revComment( $rev, $local=false, $isPublic=false, $useParentheses=true)
Wrap and format the given revision's comment block, if the current user is allowed to view it.
Definition Linker.php:1605
static revUserTools( $rev, $isPublic=false, $useParentheses=true)
Generate a user tool link cluster if the current user is allowed to view it.
Definition Linker.php:1142
static getRevDeleteLink(User $user, $rev, LinkTarget $title)
Get a revision-deletion link, or disabled link, or nothing, depending on user permissions & the setti...
Definition Linker.php:2195
static makeExternalLink( $url, $text, $escape=true, $linktype='', $attribs=[], $title=null)
Make an external link.
Definition Linker.php:846
Class to simplify the use of log pages.
Definition LogPage.php:37
MediaWiki exception.
A DatabaseBlock (unlike a SystemBlock) is stored in the database, may give rise to autoblocks and may...
Represents information returned by WikiPage::prepareContentForEdit()
Class that generates HTML links for pages.
makeKnownLink(LinkTarget $target, $text=null, array $extraAttribs=[], array $query=[])
makeLink(LinkTarget $target, $text=null, array $extraAttribs=[], array $query=[])
MediaWikiServices is the service locator for the application scope of MediaWiki.
A service class for checking permissions To obtain an instance, use MediaWikiServices::getInstance()-...
Page revision base class.
getContent( $role, $audience=self::FOR_PUBLIC, User $user=null)
Returns the Content of the given slot of this revision.
Service for looking up page revisions.
Value object representing a content slot associated with a page revision.
Wrapper allowing us to handle a system message as a Content object.
static numParam( $num)
Definition Message.php:1064
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...
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition Status.php:44
Represents a title within MediaWiki.
Definition Title.php:42
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition User.php:60
static doWatchOrUnwatch( $watch, Title $title, User $user, string $expiry=null)
Watch or unwatch a page.
Class representing a MediaWiki article and history.
Definition WikiPage.php:51
getTitle()
Get the title object of the article.
Definition WikiPage.php:318
const NS_USER
Definition Defines.php:72
const NS_FILE
Definition Defines.php:76
const NS_MEDIAWIKI
Definition Defines.php:78
const RC_LOG
Definition Defines.php:134
const NS_MEDIA
Definition Defines.php:58
const NS_USER_TALK
Definition Defines.php:73
const NS_CATEGORY
Definition Defines.php:84
Base interface for content objects.
Definition Content.php:35
Interface for objects which can provide a MediaWiki context on request.
Interface for type hinting (accepts WikiPage, Article, ImagePage, CategoryPage)
Definition Page.php:30
Basic database interface for live and lazy-loaded relation database handles.
Definition IDatabase.php:38
$cache
Definition mcc.php:33
const DB_REPLICA
Definition defines.php:25
$content
Definition router.php:76
if(!isset( $args[0])) $lang