MediaWiki master
Article.php
Go to the documentation of this file.
1<?php
7namespace MediaWiki\Page;
8
9use LogicException;
16use MediaWiki\Debug\DeprecationHelper;
21use MediaWiki\HookContainer\ProtectedHookAccessorTrait;
51use StatusValue;
52use Wikimedia\IPUtils;
53use Wikimedia\NonSerializable\NonSerializableTrait;
54use Wikimedia\Parsoid\Parsoid;
56
66class Article implements Page {
67 use ProtectedHookAccessorTrait;
68 use NonSerializableTrait;
69 use DeprecationHelper;
70
76 private $mContext;
77
79 protected $mPage;
80
85 public $mOldId;
86
88 public $mRedirectedFrom = null;
89
91 public $mRedirectUrl = false;
92
97 private $fetchResult = null;
98
104 public $mParserOutput = null;
105
111 protected $viewIsRenderAction = false;
112
114 private RevisionStore $revisionStore;
115 private UserNameUtils $userNameUtils;
116 private UserOptionsLookup $userOptionsLookup;
117 private CommentFormatter $commentFormatter;
118 private WikiPageFactory $wikiPageFactory;
119 private JobQueueGroup $jobQueueGroup;
120 private ArchivedRevisionLookup $archivedRevisionLookup;
121 private RecentChangeLookup $recentChangeLookup;
125 private ShadowPageLoader $shadowPageLoader;
126 private bool $useLegacyPostprocCache;
127
134 private $mRevisionRecord = null;
135
136 private bool $parsoidPostprocCacheAvailable;
137 private bool $legacyPostprocCacheAvailable;
138
143 public function __construct( Title $title, $oldId = null ) {
144 $this->deprecatePublicProperty( 'mContext', '1.35', __CLASS__ );
145
146 $this->mOldId = $oldId;
147
148 $services = MediaWikiServices::getInstance();
149 $this->linkRenderer = $services->getLinkRenderer();
150 $this->revisionStore = $services->getRevisionStore();
151 $this->userNameUtils = $services->getUserNameUtils();
152 $this->userOptionsLookup = $services->getUserOptionsLookup();
153 $this->commentFormatter = $services->getCommentFormatter();
154 $this->wikiPageFactory = $services->getWikiPageFactory();
155 $this->jobQueueGroup = $services->getJobQueueGroup();
156 $this->archivedRevisionLookup = $services->getArchivedRevisionLookup();
157 $this->recentChangeLookup = $services->getRecentChangeLookup();
158 $this->dbProvider = $services->getConnectionProvider();
159 $this->blockStore = $services->getDatabaseBlockStore();
160 $this->restrictionStore = $services->getRestrictionStore();
161 $this->shadowPageLoader = $services->getShadowPageLoader();
162 $this->parsoidPostprocCacheAvailable =
164 $this->legacyPostprocCacheAvailable =
166 $this->useLegacyPostprocCache = false;
167
168 // $this->newPage() makes use of wikiPageFactory service which
169 // needs to be set above before the being called, otherwise the
170 // service property will be uninitialized.
171 $this->mPage = $this->newPage( $title );
172 }
173
178 protected function newPage( Title $title ) {
179 return $this->wikiPageFactory->newFromTitle( $title );
180 }
181
186 public static function newFromID( $id ): ?static {
187 $t = Title::newFromID( $id );
188 return $t === null ? null : new static( $t );
189 }
190
197 public static function newFromTitle( $title, IContextSource $context ): static {
198 if ( $title->getNamespace() === NS_MEDIA ) {
199 // XXX: This should not be here, but where should it go?
200 $title = Title::makeTitle( NS_FILE, $title->getDBkey() );
201 }
202
203 $page = null;
204 ( new HookRunner( MediaWikiServices::getInstance()->getHookContainer() ) )
205 // @phan-suppress-next-line PhanTypeMismatchArgument Type mismatch on pass-by-ref args
206 ->onArticleFromTitle( $title, $page, $context );
207
208 $page ??= match ( $title->getNamespace() ) {
209 NS_FILE => new ImagePage( $title ),
210 NS_CATEGORY => new CategoryPage( $title ),
211 default => new Article( $title )
212 };
213 $page->setContext( $context );
214
215 return $page;
216 }
217
225 public static function newFromWikiPage( WikiPage $page, IContextSource $context ) {
226 $article = self::newFromTitle( $page->getTitle(), $context );
227 $article->mPage = $page; // override to keep process cached vars
228 return $article;
229 }
230
236 public function getRedirectedFrom() {
237 return $this->mRedirectedFrom;
238 }
239
244 public function setRedirectedFrom( Title $from ) {
245 $this->mRedirectedFrom = $from;
246 }
247
253 public function getTitle() {
254 return $this->mPage->getTitle();
255 }
256
263 public function getPage() {
264 return $this->mPage;
265 }
266
267 public function clear() {
268 $this->mRedirectedFrom = null; # Title object if set
269 $this->mRedirectUrl = false;
270 $this->mRevisionRecord = null;
271 $this->fetchResult = null;
272
273 // TODO hard-deprecate direct access to public fields
274
275 $this->mPage->clear();
276 }
277
285 public function getOldID() {
286 if ( $this->mOldId === null ) {
287 $this->mOldId = $this->getOldIDFromRequest();
288 }
289
290 return $this->mOldId;
291 }
292
298 public function getOldIDFromRequest() {
299 $this->mRedirectUrl = false;
300
301 $request = $this->getContext()->getRequest();
302 $oldid = $request->getIntOrNull( 'oldid' );
303
304 if ( $oldid === null ) {
305 return 0;
306 }
307
308 if ( $oldid !== 0 ) {
309 # Load the given revision and check whether the page is another one.
310 # In that case, update this instance to reflect the change.
311 if ( $oldid === $this->mPage->getLatest() ) {
312 $this->mRevisionRecord = $this->mPage->getRevisionRecord();
313 } else {
314 $this->mRevisionRecord = $this->revisionStore->getRevisionById( $oldid );
315 if ( $this->mRevisionRecord !== null ) {
316 $revPageId = $this->mRevisionRecord->getPageId();
317 // Revision title doesn't match the page title given?
318 if ( $this->mPage->getId() !== $revPageId ) {
319 $this->mPage = $this->wikiPageFactory->newFromID( $revPageId );
320 }
321 }
322 }
323 }
324
325 $oldRev = $this->mRevisionRecord;
326 if ( $request->getRawVal( 'direction' ) === 'next' ) {
327 $nextid = 0;
328 if ( $oldRev ) {
329 $nextRev = $this->revisionStore->getNextRevision( $oldRev );
330 if ( $nextRev ) {
331 $nextid = $nextRev->getId();
332 }
333 }
334 if ( $nextid ) {
335 $oldid = $nextid;
336 $this->mRevisionRecord = null;
337 } else {
338 $this->mRedirectUrl = $this->getTitle()->getFullURL( 'redirect=no' );
339 }
340 } elseif ( $request->getRawVal( 'direction' ) === 'prev' ) {
341 $previd = 0;
342 if ( $oldRev ) {
343 $prevRev = $this->revisionStore->getPreviousRevision( $oldRev );
344 if ( $prevRev ) {
345 $previd = $prevRev->getId();
346 }
347 }
348 if ( $previd ) {
349 $oldid = $previd;
350 $this->mRevisionRecord = null;
351 }
352 }
353
354 return $oldid;
355 }
356
366 public function fetchRevisionRecord() {
367 if ( $this->fetchResult ) {
368 return $this->mRevisionRecord;
369 }
370
371 $oldid = $this->getOldID();
372
373 // $this->mRevisionRecord might already be fetched by getOldIDFromRequest()
374 if ( !$this->mRevisionRecord ) {
375 if ( !$oldid ) {
376 $this->mRevisionRecord = $this->mPage->getRevisionRecord();
377
378 if ( !$this->mRevisionRecord ) {
379 wfDebug( __METHOD__ . " failed to find page data for title " .
380 $this->getTitle()->getPrefixedText() );
381
382 // Output for this case is done by showMissingArticle().
383 $this->fetchResult = StatusValue::newFatal( 'noarticletext' );
384 return null;
385 }
386 } else {
387 $this->mRevisionRecord = $this->revisionStore->getRevisionById( $oldid );
388
389 if ( !$this->mRevisionRecord ) {
390 wfDebug( __METHOD__ . " failed to load revision, rev_id $oldid" );
391
392 $this->fetchResult = StatusValue::newFatal( $this->getMissingRevisionMsg( $oldid ) );
393 return null;
394 }
395 }
396 }
397
398 if ( !$this->mRevisionRecord->userCan( RevisionRecord::DELETED_TEXT, $this->getContext()->getAuthority() ) ) {
399 wfDebug( __METHOD__ . " failed to retrieve content of revision " . $this->mRevisionRecord->getId() );
400
401 // Output for this case is done by showDeletedRevisionHeader().
402 // title used in wikilinks, should not contain whitespaces
403 $this->fetchResult = new StatusValue();
404 $title = $this->getTitle()->getPrefixedDBkey();
405
406 if ( $this->mRevisionRecord->isDeleted( RevisionRecord::DELETED_RESTRICTED ) ) {
407 $this->fetchResult->fatal( 'rev-suppressed-text' );
408 } else {
409 $this->fetchResult->fatal( 'rev-deleted-text-permission', $title );
410 }
411
412 return null;
413 }
414
415 $this->fetchResult = StatusValue::newGood( $this->mRevisionRecord );
416 return $this->mRevisionRecord;
417 }
418
424 public function isCurrent() {
425 # If no oldid, this is the current version.
426 if ( $this->getOldID() == 0 ) {
427 return true;
428 }
429
430 return $this->mPage->exists() &&
431 $this->mRevisionRecord &&
432 $this->mRevisionRecord->isCurrent();
433 }
434
443 public function getRevIdFetched() {
444 if ( $this->fetchResult && $this->fetchResult->isOK() ) {
446 $rev = $this->fetchResult->getValue();
447 return $rev->getId();
448 } else {
449 return $this->mPage->getLatest();
450 }
451 }
452
457 public function view() {
458 $context = $this->getContext();
459 $useFileCache = $context->getConfig()->get( MainConfigNames::UseFileCache );
460
461 # Get variables from query string
462 # As side effect this will load the revision and update the title
463 # in a revision ID is passed in the request, so this should remain
464 # the first call of this method even if $oldid is used way below.
465 $oldid = $this->getOldID();
466
467 $authority = $context->getAuthority();
468 # Another check in case getOldID() is altering the title
469 $permissionStatus = PermissionStatus::newEmpty();
470 if ( !$authority
471 ->authorizeRead( 'read', $this->getTitle(), $permissionStatus )
472 ) {
473 wfDebug( __METHOD__ . ": denied on secondary read check" );
474 throw new PermissionsError( 'read', $permissionStatus );
475 }
476
477 $outputPage = $context->getOutput();
478 # getOldID() may as well want us to redirect somewhere else
479 if ( $this->mRedirectUrl ) {
480 $outputPage->redirect( $this->mRedirectUrl );
481 wfDebug( __METHOD__ . ": redirecting due to oldid" );
482
483 return;
484 }
485
486 # If we got diff in the query, we want to see a diff page instead of the article.
487 if ( $context->getRequest()->getCheck( 'diff' ) ) {
488 wfDebug( __METHOD__ . ": showing diff page" );
489 $this->showDiffPage();
490 return;
491 }
492
493 $this->showProtectionIndicator();
494
495 # Set page title (may be overridden from ParserOutput if title conversion is enabled or DISPLAYTITLE is used)
496 $outputPage->setPageTitle( Parser::formatPageTitle(
497 str_replace( '_', ' ', $this->getTitle()->getNsText() ),
498 ':',
499 $this->getTitle()->getText(),
500 $this->getTitle()->getPageLanguage()
501 ) );
502
503 $outputPage->setArticleFlag( true );
504 # Allow frames by default
505 $outputPage->getMetadata()->setPreventClickjacking( false );
506
507 $parserOptions = $this->getParserOptions( $oldid );
508
509 $poOptions = [];
510 # Render printable version, use printable version cache
511 if ( $outputPage->isPrintable() ) {
512 $parserOptions->setIsPrintable( true );
513 $parserOptions->setSuppressSectionEditLinks();
514 $this->addMessageBoxStyles( $outputPage );
515 $outputPage->prependHTML(
516 Html::warningBox(
517 $outputPage->msg( 'printableversion-deprecated-warning' )->escaped()
518 )
519 );
520 } elseif ( $this->viewIsRenderAction || !$this->isCurrent() ||
521 !$authority->probablyCan( 'edit', $this->getTitle() )
522 ) {
523 $parserOptions->setSuppressSectionEditLinks();
524 }
525
526 # Try client and file cache
527 if ( $oldid === 0 && $this->mPage->checkTouched() ) {
528 # Try to stream the output from file cache
529 if ( $useFileCache && $this->tryFileCache() ) {
530 wfDebug( __METHOD__ . ": done file cache" );
531 # tell wgOut that output is taken care of
532 $outputPage->disable();
533 $this->mPage->doViewUpdates( $authority );
534
535 return;
536 }
537 }
538
539 $this->showRedirectedFromHeader();
540 $this->showNamespaceHeader();
541
542 if ( $this->viewIsRenderAction ) {
543 $poOptions += [ 'absoluteURLs' => true ];
544 }
545 $poOptions += [ 'includeDebugInfo' => true ];
546
547 try {
548 $continue =
549 $this->generateContentOutput( $authority, $parserOptions, $oldid, $outputPage, $poOptions );
550 } catch ( BadRevisionException ) {
551 $continue = false;
552 $this->showViewError( wfMessage( 'badrevision' )->text() );
553 }
554
555 if ( !$continue ) {
556 return;
557 }
558
559 # For the main page, overwrite the <title> element with the con-
560 # tents of 'pagetitle-view-mainpage' instead of the default (if
561 # that's not empty).
562 # This message always exists because it is in the i18n files
563 if ( $this->getTitle()->isMainPage() ) {
564 $msg = $context->msg( 'pagetitle-view-mainpage' )->inContentLanguage();
565 if ( !$msg->isDisabled() ) {
566 $outputPage->setHTMLTitle( $msg->text() );
567 }
568 }
569
570 // Enable 1-day CDN cache on this response
571 //
572 // To reduce impact of lost or delayed HTTP purges, the adaptive TTL will
573 // raise the TTL for pages not recently edited, upto $wgCdnMaxAge.
574 // This could use getTouched(), but that could be scary for major template edits.
575 $outputPage->adaptCdnTTL( $this->mPage->getTimestamp(), 86_400 );
576
577 $this->showViewFooter();
578 $this->mPage->doViewUpdates( $authority, $this->fetchRevisionRecord() );
579
580 # Load the postEdit module if the user just saved this revision
581 # See also EditPage::setPostEditCookie
582 $request = $context->getRequest();
583 $cookieKey = EditPage::POST_EDIT_COOKIE_KEY_PREFIX . $this->getRevIdFetched();
584 $postEdit = $request->getCookie( $cookieKey );
585 if ( $postEdit ) {
586 # Clear the cookie. This also prevents caching of the response.
587 $request->response()->clearCookie( $cookieKey );
588 $outputPage->addJsConfigVars( 'wgPostEdit', $postEdit );
589 $outputPage->addModules( 'mediawiki.action.view.postEdit' ); // FIXME: test this
590 if ( $this->getContext()->getConfig()->get( MainConfigNames::EnableEditRecovery )
591 && $this->userOptionsLookup->getOption( $this->getContext()->getUser(), 'editrecovery' )
592 ) {
593 $outputPage->addModules( 'mediawiki.editRecovery.postEdit' );
594 }
595 }
596 }
597
601 public function showProtectionIndicator(): void {
602 $title = $this->getTitle();
603 $context = $this->getContext();
604 $outputPage = $context->getOutput();
605
606 $protectionIndicatorsAreEnabled = $context->getConfig()
607 ->get( MainConfigNames::EnableProtectionIndicators );
608
609 if ( !$protectionIndicatorsAreEnabled || $title->isMainPage() ) {
610 return;
611 }
612
613 $protection = $this->restrictionStore->getRestrictions( $title, 'edit' );
614
615 $cascadeProtection = $this->restrictionStore->getCascadeProtectionSources( $title )[1];
616
617 $isCascadeProtected = array_key_exists( 'edit', $cascadeProtection );
618
619 if ( !$protection && !$isCascadeProtected ) {
620 return;
621 }
622
623 if ( $isCascadeProtected ) {
624 // Cascade-protected pages are protected at the sysop level. So it
625 // should not matter if we take the protection level of the first
626 // or last page that is being cascaded to the current page.
627 $protectionLevel = $cascadeProtection['edit'][0];
628 } else {
629 $protectionLevel = $protection[0];
630 }
631
632 // Protection levels are stored in the database as plain text, but
633 // they are expected to be valid protection levels. So we should be able to
634 // safely use them. However phan thinks this could be a XSS problem so we
635 // are being paranoid and escaping them once more.
636 $protectionLevel = htmlspecialchars( $protectionLevel );
637
638 $protectionExpiry = $this->restrictionStore->getRestrictionExpiry( $title, 'edit' );
639 $formattedProtectionExpiry = $context->getLanguage()
640 ->formatExpiry( $protectionExpiry ?? '' );
641
642 $protectionMsgKey = 'protection-indicator-title';
643 if ( $protectionExpiry === 'infinity' || !$protectionExpiry ) {
644 $protectionMsgKey = 'protection-indicator-title-infinity';
645 }
646
647 // Potential values: 'protection-sysop', 'protection-autoconfirmed',
648 // 'protection-sysop-cascade' etc.
649 // If the wiki has more protection levels, the additional ids that get
650 // added take the form 'protection-<protectionLevel>' and
651 // 'protection-<protectionLevel>-cascade'.
652 $protectionIndicatorId = 'protection-' . $protectionLevel . ( $isCascadeProtected ? '-cascade' : '' );
653
654 $protectionMsg = $outputPage->msg(
655 $protectionMsgKey,
656 // Messages: restriction-level-sysop, restriction-level-autoconfirmed
657 $outputPage->msg( "restriction-level-$protectionLevel" ),
658 $formattedProtectionExpiry
659 )->text();
660
661 // Use a trick similar to the one used in Action::addHelpLink() to allow wikis
662 // to customize where the help link points to.
663 $protectionHelpLink = $outputPage->msg( $protectionIndicatorId . '-helppage' );
664 if ( $protectionHelpLink->isDisabled() ) {
665 $protectionHelpLink = 'https://mediawiki.org/wiki/Special:MyLanguage/Help:Protection';
666 } else {
667 $protectionHelpLink = Skin::makeInternalOrExternalUrl( $protectionHelpLink->text() );
668 }
669
670 $outputPage->setIndicators( [
671 $protectionIndicatorId => Html::rawElement( 'a', [
672 'class' => 'mw-protection-indicator-icon--lock',
673 'title' => $protectionMsg,
674 'href' => $protectionHelpLink
675 ],
676 // Screen reader-only text describing the same thing as
677 // was mentioned in the title attribute.
678 Html::element( 'span', [], $protectionMsg ) )
679 ] );
680
681 $outputPage->addModuleStyles( 'mediawiki.protectionIndicators.styles' );
682 }
683
696 private function generateContentOutput(
697 Authority $performer,
698 ParserOptions $parserOptions,
699 int $oldid,
700 OutputPage $outputPage,
701 array $textOptions
702 ): bool {
703 # Should the parser cache be used?
704 $useParserCache = true;
705 $pOutput = null;
706 $parserOutputAccess = MediaWikiServices::getInstance()->getParserOutputAccess();
707
708 // NOTE: $outputDone and $useParserCache may be changed by the hook
709 $this->getHookRunner()->onArticleViewHeader( $this, $outputDone, $useParserCache );
710 if ( $outputDone ) {
711 if ( $outputDone instanceof ParserOutput ) {
712 $pOutput = $outputDone;
713 }
714
715 if ( $pOutput ) {
716 $this->doOutputMetaData( $pOutput, $outputPage );
717 }
718 return true;
719 }
720
721 // Early abort if the page doesn't exist
722 if ( !$this->mPage->exists() ) {
723 wfDebug( __METHOD__ . ": showing missing article" );
724 $this->showMissingArticle();
725 $this->mPage->doViewUpdates( $performer );
726 return false; // skip all further output to OutputPage
727 }
728
729 // Augment the parser options
730 $skin = $outputPage->getSkin();
731 $skin->setParserOptions( $parserOptions );
732 $skinOptions = $skin->getOptions();
733 $textOptions += [
734 // T371022, T410923
735 'allowClone' => $this->getContext()->getConfig()->get( MainConfigNames::CloneArticleParserOutput ),
736 'skin' => $skin,
737 'injectTOC' => $skinOptions['toc'],
738 ];
739 $this->modifyTextOptions( $outputPage, $textOptions );
740 foreach ( $textOptions as $key => $value ) {
741 // allowClone will disappear and should not impact cache
742 // userLang is a duplicate of userlang and should be reconciled with it
743 if ( $key === 'allowClone' || $key === 'userLang' ) {
744 continue;
745 }
746 if ( $key === 'enableSectionEditLinks' ) {
747 if ( $value === false ) {
748 wfDeprecated( __METHOD__ . " with deprecated textOption $key set to false", "1.46" );
749 $parserOptions->setSuppressSectionEditLinks();
750 }
751 continue;
752 }
753 if ( !in_array( $key, ParserOptions::$postprocOptions, true ) ) {
754 wfDeprecated( __METHOD__ . " with unknown textOption $key", "1.46" );
755 } else {
756 $parserOptions->setOption( $key, $value );
757 }
758 }
759 if ( $this->usePostProcessingCache( $parserOptions ) ) {
760 $parserOptions->enablePostproc();
761 }
762
763 // Try the latest parser cache
764 // NOTE: try latest-revision cache first to avoid loading revision.
765 if ( $useParserCache && !$oldid ) {
766 $pOutput = $parserOutputAccess->getCachedParserOutput(
767 $this->getPage(),
768 $parserOptions,
769 null,
770 [
771 // we already checked
772 ParserOutputAccess::OPT_NO_AUDIENCE_CHECK => true,
773 ],
774 );
775
776 if ( $pOutput ) {
777 if ( !$this->usePostProcessingCache( $parserOptions ) ) {
778 $pOutput = $this->postProcessOutput( $pOutput, $parserOptions, $textOptions, $skin );
779 }
780 $this->doOutputFromPostProcessedParserCache( $pOutput, $outputPage );
781 $this->doOutputMetaData( $pOutput, $outputPage );
782 return true;
783 }
784 }
785
786 $rev = $this->fetchRevisionRecord();
787 if ( !$this->fetchResult->isOK() ) {
788 $this->showViewError( Status::wrap( $this->fetchResult )->getWikiText(
789 false, false, $this->getContext()->getLanguage()
790 ) );
791 return true;
792 }
793
794 # Are we looking at an old revision
795 if ( $oldid ) {
796 $this->setOldSubtitle( $oldid );
797
798 if ( !$this->showDeletedRevisionHeader() ) {
799 wfDebug( __METHOD__ . ": cannot view deleted revision" );
800 return false; // skip all further output to OutputPage
801 }
802
803 // Try the old revision parser cache
804 // NOTE: Repeating cache check for old revision to avoid fetching $rev
805 // before it's absolutely necessary.
806 if ( $useParserCache ) {
807 $pOutput = $parserOutputAccess->getCachedParserOutput(
808 $this->getPage(),
809 $parserOptions,
810 $rev,
811 [
812 // we already checked in fetchRevisionRecord
813 ParserOutputAccess::OPT_NO_AUDIENCE_CHECK => true,
814 ],
815 );
816
817 if ( $pOutput ) {
818 if ( !$this->usePostProcessingCache( $parserOptions ) ) {
819 $pOutput = $this->postProcessOutput( $pOutput, $parserOptions, $textOptions, $skin );
820 }
821 $this->doOutputFromPostProcessedParserCache( $pOutput, $outputPage );
822 $this->doOutputMetaData( $pOutput, $outputPage );
823 return true;
824 }
825 }
826 }
827
828 # Ensure that UI elements requiring revision ID have
829 # the correct version information. (This may be overwritten after creation of ParserOutput)
830 $outputPage->setRevisionId( $this->getRevIdFetched() );
831 $outputPage->setRevisionIsCurrent( $rev->isCurrent() );
832 # Preload timestamp to avoid a DB hit
833 $outputPage->getMetadata()->setRevisionTimestamp( $rev->getTimestamp() );
834
835 # Pages containing custom CSS or JavaScript get special treatment
836 if ( $this->getTitle()->isSiteConfigPage() || $this->getTitle()->isUserConfigPage() ) {
837 $dir = $this->getContext()->getLanguage()->getDir();
838 $lang = $this->getContext()->getLanguage()->getHtmlCode();
839
840 $outputPage->wrapWikiMsg(
841 "<div id='mw-clearyourcache' lang='$lang' dir='$dir' class='mw-content-$dir'>\n$1\n</div>",
842 'clearyourcache'
843 );
844 $outputPage->addModuleStyles( 'mediawiki.action.styles' );
845 } elseif ( !$this->getHookRunner()->onArticleRevisionViewCustom(
846 $rev,
847 $this->getTitle(),
848 $oldid,
849 $outputPage )
850 ) {
851 // NOTE: sync with hooks called in DifferenceEngine::renderNewRevision()
852 // Allow extensions do their own custom view for certain pages
853 $this->doOutputMetaData( $pOutput, $outputPage );
854 return true;
855 }
856
857 # Run the parse, protected by a pool counter
858 wfDebug( __METHOD__ . ": doing uncached parse" );
859
860 $opt = [];
861
862 // we already checked the cache in case 2, don't check again; but if we're using postproc,
863 // we still want to check if we have a main parser cache entry.
864 if ( $this->usePostProcessingCache( $parserOptions ) ) {
865 $opt[ ParserOutputAccess::OPT_NO_POSTPROC_CACHE ] = true;
866 } else {
867 $opt[ ParserOutputAccess::OPT_NO_CHECK_CACHE ] = true;
868 }
869
870 // we already checked in fetchRevisionRecord()
871 $opt[ ParserOutputAccess::OPT_NO_AUDIENCE_CHECK ] = true;
872
873 // enable stampede protection
874 $opt[ ParserOutputAccess::OPT_POOL_COUNTER ]
875 = ParserOutputAccess::POOL_COUNTER_ARTICLE_VIEW;
876
877 // allow stale cached content to be served
878 $opt[ ParserOutputAccess::OPT_POOL_COUNTER_FALLBACK ] = true;
879
880 // Attempt to trigger WikiPage::triggerOpportunisticLinksUpdate
881 // Ideally this should not be the responsibility of the ParserCache to control this.
882 // See https://phabricator.wikimedia.org/T329842#8816557 for more context.
883 $opt[ ParserOutputAccess::OPT_LINKS_UPDATE ] = true;
884
885 if ( !$rev->getId() || !$useParserCache ) {
886 // fake revision or uncacheable options
887 $opt[ ParserOutputAccess::OPT_NO_CACHE ] = true;
888 }
889
890 $renderStatus = $parserOutputAccess->getParserOutput(
891 $this->getPage(),
892 $parserOptions,
893 $rev,
894 $opt
895 );
896
897 // T327164: If parsoid cache warming is enabled, we want to ensure that the page
898 // the user is currently looking at has a cached parsoid rendering, in case they
899 // open visual editor. The cache entry would typically be missing if it has expired
900 // from the cache or it was invalidated by RefreshLinksJob. When "traditional"
901 // parser output has been invalidated by RefreshLinksJob, we will render it on
902 // the fly when a user requests the page, and thereby populate the cache again,
903 // per the code above.
904 // The code below is intended to do the same for parsoid output, but asynchronously
905 // in a job, so the user does not have to wait.
906 // Note that we get here if the traditional parser output was missing from the cache.
907 // We do not check if the parsoid output is present in the cache, because that check
908 // takes time. The assumption is that if we have traditional parser output
909 // cached, we probably also have parsoid output cached.
910 // So we leave it to ParsoidCachePrewarmJob to determine whether or not parsing is
911 // needed.
912 if ( $oldid === 0 || $oldid === $this->getPage()->getLatest() ) {
913 $parsoidCacheWarmingEnabled = $this->getContext()->getConfig()
914 ->get( MainConfigNames::ParsoidCacheConfig )['WarmParsoidParserCache'];
915
916 if ( $parsoidCacheWarmingEnabled ) {
917 $parsoidJobSpec = ParsoidCachePrewarmJob::newSpec(
918 $rev->getId(),
919 $this->getPage()->toPageRecord(),
920 [ 'causeAction' => 'view' ]
921 );
922 $this->jobQueueGroup->lazyPush( $parsoidJobSpec );
923 }
924 }
925
926 $this->doOutputFromRenderStatus(
927 $renderStatus,
928 $outputPage,
929 $parserOptions,
930 $textOptions,
931 );
932
933 if ( !$renderStatus->isOK() ) {
934 return true;
935 }
936
937 $pOutput = $renderStatus->getValue();
938 $this->doOutputMetaData( $pOutput, $outputPage );
939 return true;
940 }
941
950 protected function modifyTextOptions( OutputPage $outputPage, array &$textOptions ): void {
951 }
952
953 private function doOutputMetaData( ?ParserOutput $pOutput, OutputPage $outputPage ) {
954 # Adjust title for main page & pages with displaytitle
955 if ( $pOutput ) {
956 $this->adjustDisplayTitle( $pOutput );
957
958 // It would be nice to automatically set this during the first call
959 // to OutputPage::addParserOutputMetadata, but we can't because doing
960 // so would break non-pageview actions where OutputPage::getContLangForJS
961 // has different requirements.
962 $pageLang = $pOutput->getLanguage();
963 if ( $pageLang ) {
964 $outputPage->setContentLangForJS( $pageLang );
965 }
966 }
967
968 # Check for any __NOINDEX__ tags on the page using $pOutput
969 $policy = $this->getRobotPolicy( 'view', $pOutput ?: null );
970 $outputPage->getMetadata()->setIndexPolicy( $policy['index'] );
971 $outputPage->setFollowPolicy( $policy['follow'] ); // FIXME: test this
972
973 $this->mParserOutput = $pOutput;
974 }
975
976 private function postProcessOutput(
977 ParserOutput $pOutput, ParserOptions $parserOptions, array $textOptions, Skin $skin
978 ): ParserOutput {
979 $skinOptions = $skin->getOptions();
980 $textOptions += [
981 // T371022, T410923
982 'allowClone' => $this->getContext()->getConfig()->get( MainConfigNames::CloneArticleParserOutput ),
983 'skin' => $skin,
984 'injectTOC' => $skinOptions['toc'],
985 ];
986 $pipeline = MediaWikiServices::getInstance()->getDefaultOutputPipeline();
987 $pOutput = $pipeline->run( $pOutput, $parserOptions, $textOptions );
988 return $pOutput;
989 }
990
991 private function doOutputFromPostProcessedParserCache(
992 ParserOutput $pOutput,
993 OutputPage $outputPage,
994 ) {
995 # Ensure that UI elements requiring revision ID have
996 # the correct version information.
997 $oldid = $pOutput->getCacheRevisionId() ?? $this->getRevIdFetched();
998 $outputPage->setRevisionId( $oldid );
999 $outputPage->setRevisionIsCurrent( $oldid === $this->mPage->getLatest() );
1000
1001 $outputPage->addPostProcessedParserOutput( $pOutput );
1002
1003 if ( $pOutput->getRedirectHeader() !== null ) {
1004 $outputPage->addSubtitle( "<span id=\"redirectsub\">" .
1005 $this->getContext()->msg( 'redirectpagesub' )->parse() . "</span>" );
1006 }
1007
1008 # Preload timestamp to avoid a DB hit
1009 $cachedTimestamp = $pOutput->getRevisionTimestamp();
1010 if ( $cachedTimestamp !== null ) {
1011 $outputPage->getMetadata()->setRevisionTimestamp( $cachedTimestamp );
1012 $this->mPage->setTimestamp( $cachedTimestamp );
1013 }
1014 }
1015
1016 private function doOutputFromRenderStatus(
1017 StatusValue $renderStatus,
1018 OutputPage $outputPage,
1019 ParserOptions $parserOptions,
1020 array $textOptions,
1021 ) {
1022 $context = $this->getContext();
1023 if ( !$renderStatus->isOK() ) {
1024 $this->showViewError( Status::wrap( $renderStatus )->getWikiText(
1025 false, 'view-pool-error', $context->getLanguage()
1026 ) );
1027 return;
1028 }
1029
1030 $pOutput = $renderStatus->getValue();
1031
1032 // Cache stale ParserOutput object with a short expiry
1033 if ( $renderStatus->hasMessage( 'view-pool-dirty-output' ) ) {
1034 $outputPage->lowerCdnMaxage( $context->getConfig()->get( MainConfigNames::CdnMaxageStale ) );
1035 $outputPage->setLastModified( $pOutput->getCacheTime() );
1036 $staleReason = $renderStatus->hasMessage( 'view-pool-contention' )
1037 ? $context->msg( 'view-pool-contention' )->escaped()
1038 : $context->msg( 'view-pool-timeout' )->escaped();
1039 $outputPage->addHTML( "<!-- parser cache is expired, " .
1040 "sending anyway due to $staleReason-->\n" );
1041
1042 // Ensure OutputPage knowns the id from the dirty cache, but keep the current flag (T341013)
1043 $cachedId = $pOutput->getCacheRevisionId();
1044 if ( $cachedId !== null ) {
1045 $outputPage->setRevisionId( $cachedId );
1046 $outputPage->getMetadata()->setRevisionTimestamp( $pOutput->getRevisionTimestamp() );
1047 }
1048 }
1049
1050 // TODO this will probably need to be conditional on cache access and/or hoisted one level above but for
1051 // now let's keep things in the same place and avoid editing StatusValues.
1052 if ( !$this->usePostProcessingCache( $parserOptions ) ) {
1053 $pOutput = $this->postProcessOutput( $pOutput, $parserOptions, $textOptions, $outputPage->getSkin() );
1054 }
1055
1056 $outputPage->addPostProcessedParserOutput( $pOutput );
1057
1058 if ( $pOutput->getRedirectHeader() !== null ) {
1059 $outputPage->addSubtitle( "<span id=\"redirectsub\">" .
1060 $context->msg( 'redirectpagesub' )->parse() . "</span>" );
1061 }
1062 }
1063
1067 public function adjustDisplayTitle( ParserOutput $pOutput ) {
1068 $out = $this->getContext()->getOutput();
1069
1070 # Adjust the title if it was set by displaytitle, -{T|}- or language conversion
1071 $titleText = $pOutput->getTitleText();
1072 if ( $titleText !== '' ) {
1073 # XXX T36514 / T314399 / T306440: we should have a language here
1074 # and split the namespace
1075 $out->setPageTitle( $titleText );
1076 $out->setDisplayTitle( $titleText );
1077 }
1078 }
1079
1084 protected function showDiffPage() {
1085 $context = $this->getContext();
1086 $outputPage = $context->getOutput();
1087 $outputPage->addBodyClasses( 'mw-article-diff' );
1088 $request = $context->getRequest();
1089 $diff = $request->getVal( 'diff' );
1090 $rcid = $request->getInt( 'rcid' );
1091 $purge = $request->getRawVal( 'action' ) === 'purge';
1092 $unhide = $request->getInt( 'unhide' ) === 1;
1093 $oldid = $this->getOldID();
1094
1095 $rev = $this->fetchRevisionRecord();
1096
1097 if ( !$rev ) {
1098 // T213621: $rev maybe null due to either lack of permission to view the
1099 // revision or actually not existing. So let's try loading it from the id
1100 $rev = $this->revisionStore->getRevisionById( $oldid );
1101 }
1102
1103 $services = MediaWikiServices::getInstance();
1104
1105 if ( $rev ) {
1106 $contentHandler = $services
1107 ->getContentHandlerFactory()
1108 ->getContentHandler(
1109 $rev->getMainContentModel()
1110 );
1111
1112 $de = $contentHandler->createDifferenceEngine(
1113 $context,
1114 $oldid,
1115 $diff,
1116 $rcid,
1117 $purge,
1118 $unhide
1119 );
1120 } else {
1121 // We don't have a content handler, so use the default difference engine
1122 // (this will fail eventually, but letting it get this far avoids the need
1123 // to duplicate the logic for printing the failure message)
1124 $de = new DifferenceEngine(
1125 $context,
1126 $oldid,
1127 $diff,
1128 $rcid,
1129 $purge,
1130 $unhide
1131 );
1132 }
1133
1134 $diffType = $request->getVal( 'diff-type' );
1135
1136 if ( $diffType === null ) {
1137 $diffType = $this->userOptionsLookup
1138 ->getOption( $context->getUser(), 'diff-type' );
1139 } else {
1140 $de->setExtraQueryParams( [ 'diff-type' => $diffType ] );
1141 }
1142
1143 $de->setSlotDiffOptions( [
1144 'diff-type' => $diffType,
1145 'expand-url' => $this->viewIsRenderAction,
1146 'inline-toggle' => true,
1147 ] );
1148 $de->showDiffPage( $this->isDiffOnlyView() );
1149
1150 // Run view updates for the newer revision being diffed (and shown
1151 // below the diff if not diffOnly).
1152 $this->mPage->doViewUpdates( $context->getAuthority(), $de->getNewRevision() );
1153
1154 // Add link to help page; see T321569
1155 $context->getOutput()->addHelpLink( 'Help:Diff' );
1156 }
1157
1158 protected function isDiffOnlyView(): bool {
1159 return $this->getContext()->getRequest()->getBool(
1160 'diffonly',
1161 $this->userOptionsLookup->getBoolOption( $this->getContext()->getUser(), 'diffonly' )
1162 );
1163 }
1164
1172 public function getRobotPolicy( $action, ?ParserOutput $pOutput = null ) {
1173 $context = $this->getContext();
1174 $mainConfig = $context->getConfig();
1175 $articleRobotPolicies = $mainConfig->get( MainConfigNames::ArticleRobotPolicies );
1176 $namespaceRobotPolicies = $mainConfig->get( MainConfigNames::NamespaceRobotPolicies );
1177 $defaultRobotPolicy = $mainConfig->get( MainConfigNames::DefaultRobotPolicy );
1178 $title = $this->getTitle();
1179 $ns = $title->getNamespace();
1180
1181 # Don't index user and user talk pages for blocked users (T13443)
1182 if ( $ns === NS_USER || $ns === NS_USER_TALK ) {
1183 $specificTarget = null;
1184 $vagueTarget = null;
1185 $titleText = $title->getText();
1186 if ( IPUtils::isValid( $titleText ) ) {
1187 $vagueTarget = $titleText;
1188 } else {
1189 $specificTarget = $title->getRootText();
1190 }
1191 $block = $this->blockStore->newFromTarget(
1192 $specificTarget, $vagueTarget, false, DatabaseBlockStore::AUTO_NONE );
1193 if ( $block instanceof DatabaseBlock ) {
1194 return [
1195 'index' => 'noindex',
1196 'follow' => 'nofollow'
1197 ];
1198 }
1199 }
1200
1201 if ( $this->mPage->getId() === 0 || $this->getOldID() ) {
1202 # Non-articles (special pages etc), and old revisions
1203 return [
1204 'index' => 'noindex',
1205 'follow' => 'nofollow'
1206 ];
1207 } elseif ( $context->getOutput()->isPrintable() ) {
1208 # Discourage indexing of printable versions, but encourage following
1209 return [
1210 'index' => 'noindex',
1211 'follow' => 'follow'
1212 ];
1213 } elseif ( $context->getRequest()->getInt( 'curid' ) ) {
1214 # For ?curid=x urls, disallow indexing
1215 return [
1216 'index' => 'noindex',
1217 'follow' => 'follow'
1218 ];
1219 }
1220
1221 # Otherwise, construct the policy based on the various config variables.
1222 $policy = self::formatRobotPolicy( $defaultRobotPolicy );
1223
1224 if ( isset( $namespaceRobotPolicies[$ns] ) ) {
1225 # Honour customised robot policies for this namespace
1226 $policy = array_merge(
1227 $policy,
1228 self::formatRobotPolicy( $namespaceRobotPolicies[$ns] )
1229 );
1230 }
1231 $namespaceInfo = MediaWikiServices::getInstance()->getNamespaceInfo();
1232 if (
1233 $namespaceInfo->canUseNoindex( $title->getNamespace() ) &&
1234 $pOutput && $pOutput->getIndexPolicy()
1235 ) {
1236 # __INDEX__ and __NOINDEX__ magic words, if allowed. Incorporates
1237 # a final check that we have really got the parser output.
1238 $policy['index'] = $pOutput->getIndexPolicy();
1239 }
1240
1241 if ( isset( $articleRobotPolicies[$title->getPrefixedText()] ) ) {
1242 # (T16900) site config can override user-defined __INDEX__ or __NOINDEX__
1243 $policy = array_merge(
1244 $policy,
1245 self::formatRobotPolicy( $articleRobotPolicies[$title->getPrefixedText()] )
1246 );
1247 }
1248
1249 return $policy;
1250 }
1251
1259 public static function formatRobotPolicy( $policy ) {
1260 if ( is_array( $policy ) ) {
1261 return $policy;
1262 } elseif ( !$policy ) {
1263 return [];
1264 }
1265
1266 $arr = [];
1267 foreach ( explode( ',', $policy ) as $var ) {
1268 $var = trim( $var );
1269 if ( $var === 'index' || $var === 'noindex' ) {
1270 $arr['index'] = $var;
1271 } elseif ( $var === 'follow' || $var === 'nofollow' ) {
1272 $arr['follow'] = $var;
1273 }
1274 }
1275
1276 return $arr;
1277 }
1278
1286 public function showRedirectedFromHeader() {
1287 $context = $this->getContext();
1288 $redirectSources = $context->getConfig()->get( MainConfigNames::RedirectSources );
1289 $outputPage = $context->getOutput();
1290 $request = $context->getRequest();
1291 $rdfrom = $request->getVal( 'rdfrom' );
1292
1293 // Construct a URL for the current page view, but with the target title
1294 $query = $request->getQueryValues();
1295 unset( $query['rdfrom'] );
1296 unset( $query['title'] );
1297 if ( $this->getTitle()->isRedirect() ) {
1298 // Prevent double redirects
1299 $query['redirect'] = 'no';
1300 }
1301 $redirectTargetUrl = $this->getTitle()->getLinkURL( $query );
1302
1303 if ( $this->mRedirectedFrom ) {
1304 // This is an internally redirected page view.
1305 // We'll need a backlink to the source page for navigation.
1306 if ( $this->getHookRunner()->onArticleViewRedirect( $this ) ) {
1307 $redir = $this->linkRenderer->makeKnownLink(
1308 $this->mRedirectedFrom,
1309 null,
1310 [],
1311 [ 'redirect' => 'no' ]
1312 );
1313
1314 $outputPage->addSubtitle( "<span class=\"mw-redirectedfrom\">" .
1315 $context->msg( 'redirectedfrom' )->rawParams( $redir )->parse()
1316 . "</span>" );
1317
1318 // Add the script to update the displayed URL and
1319 // set the fragment if one was specified in the redirect
1320 $outputPage->addJsConfigVars( [
1321 'wgInternalRedirectTargetUrl' => $redirectTargetUrl,
1322 ] );
1323 $outputPage->addModules( 'mediawiki.action.view.redirect' );
1324
1325 // Add a <link rel="canonical"> tag
1326 $outputPage->setCanonicalUrl( $this->getTitle()->getCanonicalURL() );
1327
1328 // Tell the output object that the user arrived at this article through a redirect
1329 $outputPage->setRedirectedFrom( $this->mRedirectedFrom );
1330
1331 return true;
1332 }
1333 } elseif ( $rdfrom ) {
1334 // This is an externally redirected view, from some other wiki.
1335 // If it was reported from a trusted site, supply a backlink.
1336 if ( $redirectSources && preg_match( $redirectSources, $rdfrom ) ) {
1337 $redir = $this->linkRenderer->makeExternalLink( $rdfrom, $rdfrom, $this->getTitle() );
1338 $outputPage->addSubtitle( "<span class=\"mw-redirectedfrom\">" .
1339 $context->msg( 'redirectedfrom' )->rawParams( $redir )->parse()
1340 . "</span>" );
1341
1342 // Add the script to update the displayed URL
1343 $outputPage->addJsConfigVars( [
1344 'wgInternalRedirectTargetUrl' => $redirectTargetUrl,
1345 ] );
1346 $outputPage->addModules( 'mediawiki.action.view.redirect' );
1347
1348 return true;
1349 }
1350 }
1351
1352 return false;
1353 }
1354
1364 public function showNamespaceHeader() {
1365 if (
1366 !$this->getTitle()->isTalkPage() &&
1367 $this->getTitle()->exists() &&
1368 !$this->getContext()->msg( 'subjectpageheader' )->isDisabled()
1369 ) {
1370 $this->getContext()->getOutput()->wrapWikiMsg(
1371 "<div class=\"mw-subjectpageheader\">\n$1\n</div>",
1372 [ 'subjectpageheader' ]
1373 );
1374 }
1375
1376 if (
1377 $this->getTitle()->isTalkPage() &&
1378 !$this->getContext()->msg( 'talkpageheader' )->isDisabled()
1379 ) {
1380 $this->getContext()->getOutput()->wrapWikiMsg(
1381 "<div class=\"mw-talkpageheader\">\n$1\n</div>",
1382 [ 'talkpageheader' ]
1383 );
1384 }
1385 }
1386
1390 public function showViewFooter() {
1391 # check if we're displaying a [[User talk:x.x.x.x]] anonymous talk page
1392 if ( $this->getTitle()->getNamespace() === NS_USER_TALK
1393 && IPUtils::isValid( $this->getTitle()->getText() )
1394 ) {
1395 $this->getContext()->getOutput()->addWikiMsg( 'anontalkpagetext' );
1396 }
1397
1398 // Show a footer allowing the user to patrol the shown revision or page if possible
1399 $patrolFooterShown = $this->showPatrolFooter();
1400
1401 $this->getHookRunner()->onArticleViewFooter( $this, $patrolFooterShown );
1402 }
1403
1414 public function showPatrolFooter() {
1415 $context = $this->getContext();
1416 $mainConfig = $context->getConfig();
1417 $useNPPatrol = $mainConfig->get( MainConfigNames::UseNPPatrol );
1418 $useRCPatrol = $mainConfig->get( MainConfigNames::UseRCPatrol );
1419 $useFilePatrol = $mainConfig->get( MainConfigNames::UseFilePatrol );
1420 $fileMigrationStage = $mainConfig->get( MainConfigNames::FileSchemaMigrationStage );
1421 // Allow hooks to decide whether to not output this at all
1422 if ( !$this->getHookRunner()->onArticleShowPatrolFooter( $this ) ) {
1423 return false;
1424 }
1425
1426 $outputPage = $context->getOutput();
1427 $user = $context->getUser();
1428 $title = $this->getTitle();
1429 $rc = false;
1430
1431 if ( !$context->getAuthority()->probablyCan( 'patrol', $title )
1432 || !( $useRCPatrol || $useNPPatrol
1433 || ( $useFilePatrol && $title->inNamespace( NS_FILE ) ) )
1434 ) {
1435 // Patrolling is disabled or the user isn't allowed to
1436 return false;
1437 }
1438
1439 if ( $this->mRevisionRecord
1440 && !RecentChange::isInRCLifespan( $this->mRevisionRecord->getTimestamp(), 21600 )
1441 ) {
1442 // The latest revision is already older than what could be in the RC table
1443 // 6h tolerance because the RC might not be cleaned out regularly
1444 return false;
1445 }
1446
1447 // Check for cached results
1448 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
1449 $key = $cache->makeKey( 'unpatrollable-page', $title->getArticleID() );
1450 if ( $cache->get( $key ) ) {
1451 return false;
1452 }
1453
1454 $dbr = $this->dbProvider->getReplicaDatabase();
1455 $oldestRevisionRow = $dbr->newSelectQueryBuilder()
1456 ->select( [ 'rev_id', 'rev_timestamp' ] )
1457 ->from( 'revision' )
1458 ->where( [ 'rev_page' => $title->getArticleID() ] )
1459 ->orderBy( [ 'rev_timestamp', 'rev_id' ] )
1460 ->caller( __METHOD__ )->fetchRow();
1461 $oldestRevisionTimestamp = $oldestRevisionRow ? $oldestRevisionRow->rev_timestamp : false;
1462
1463 // New page patrol: Get the timestamp of the oldest revision which
1464 // the revision table holds for the given page. Then we look
1465 // whether it's within the RC lifespan and if it is, we try
1466 // to get the recentchanges row belonging to that entry.
1467 $recentPageCreation = false;
1468 if ( $oldestRevisionTimestamp
1469 && RecentChange::isInRCLifespan( $oldestRevisionTimestamp, 21600 )
1470 ) {
1471 // 6h tolerance because the RC might not be cleaned out regularly
1472 $recentPageCreation = true;
1473 $rc = $this->recentChangeLookup->getRecentChangeByConds(
1474 [
1475 'rc_this_oldid' => intval( $oldestRevisionRow->rev_id ),
1476 // Avoid selecting a categorization entry
1477 'rc_source' => RecentChange::SRC_NEW,
1478 ],
1479 __METHOD__
1480 );
1481 if ( $rc ) {
1482 // Use generic patrol message for new pages
1483 $markPatrolledMsg = $context->msg( 'markaspatrolledtext' );
1484 }
1485 }
1486
1487 // File patrol: Get the timestamp of the latest upload for this page,
1488 // check whether it is within the RC lifespan and if it is, we try
1489 // to get the recentchanges row belonging to that entry
1490 // (with rc_source = SRC_LOG, rc_log_type = upload).
1491 $recentFileUpload = false;
1492 if ( ( !$rc || $rc->getAttribute( 'rc_patrolled' ) ) && $useFilePatrol
1493 && $title->getNamespace() === NS_FILE ) {
1494 // Retrieve timestamp from the current file (latest upload)
1495 if ( $fileMigrationStage & SCHEMA_COMPAT_READ_OLD ) {
1496 $newestUploadTimestamp = $dbr->newSelectQueryBuilder()
1497 ->select( 'img_timestamp' )
1498 ->from( 'image' )
1499 ->where( [ 'img_name' => $title->getDBkey() ] )
1500 ->caller( __METHOD__ )->fetchField();
1501 } else {
1502 $newestUploadTimestamp = $dbr->newSelectQueryBuilder()
1503 ->select( 'fr_timestamp' )
1504 ->from( 'file' )
1505 ->join( 'filerevision', null, 'file_latest = fr_id' )
1506 ->where( [ 'file_name' => $title->getDBkey() ] )
1507 ->caller( __METHOD__ )->fetchField();
1508 }
1509
1510 if ( $newestUploadTimestamp
1511 && RecentChange::isInRCLifespan( $newestUploadTimestamp, 21600 )
1512 ) {
1513 // 6h tolerance because the RC might not be cleaned out regularly
1514 $recentFileUpload = true;
1515 $rc = $this->recentChangeLookup->getRecentChangeByConds(
1516 [
1517 'rc_source' => RecentChange::SRC_LOG,
1518 'rc_log_type' => 'upload',
1519 'rc_timestamp' => $newestUploadTimestamp,
1520 'rc_namespace' => NS_FILE,
1521 'rc_cur_id' => $title->getArticleID()
1522 ],
1523 __METHOD__
1524 );
1525 if ( $rc ) {
1526 // Use patrol message specific to files
1527 $markPatrolledMsg = $context->msg( 'markaspatrolledtext-file' );
1528 }
1529 }
1530 }
1531
1532 if ( !$recentPageCreation && !$recentFileUpload ) {
1533 // Page creation and latest upload (for files) is too old to be in RC
1534
1535 // We definitely can't patrol so cache the information
1536 // When a new file version is uploaded, the cache is cleared
1537 $cache->set( $key, '1' );
1538
1539 return false;
1540 }
1541
1542 if ( !$rc ) {
1543 // Don't cache: This can be hit if the page gets accessed very fast after
1544 // its creation / latest upload or in case we have high replica DB lag. In case
1545 // the revision is too old, we will already return above.
1546 return false;
1547 }
1548
1549 if ( $rc->getAttribute( 'rc_patrolled' ) ) {
1550 // Patrolled RC entry around
1551
1552 // Cache the information we gathered above in case we can't patrol
1553 // Don't cache in case we can patrol as this could change
1554 $cache->set( $key, '1' );
1555
1556 return false;
1557 }
1558
1559 if ( $rc->getPerformerIdentity()->equals( $user ) ) {
1560 // Don't show a patrol link for own creations/uploads. If the user could
1561 // patrol them, they already would be patrolled
1562 return false;
1563 }
1564
1565 $outputPage->getMetadata()->setPreventClickjacking( true );
1566 $outputPage->addModules( 'mediawiki.misc-authed-curate' );
1567
1568 $patrolUrl = $title->getLinkURL( [
1569 'action' => 'markpatrolled',
1570 'rcid' => $rc->getAttribute( 'rc_id' ),
1571 ] );
1572 $link = Html::element( 'a', [
1573 'href' => $patrolUrl,
1574 'class' => 'cdx-button cdx-button--action-progressive ' .
1575 'cdx-button--fake-button cdx-button--fake-button--enabled',
1576 // @phan-suppress-next-line PhanPossiblyUndeclaredVariable $markPatrolledMsg is always set
1577 ], $markPatrolledMsg->text() );
1578
1579 $outputPage->addModuleStyles( 'mediawiki.action.styles' );
1580 $outputPage->addHTML( "<div class='patrollink' data-mw-interface>$link</div>" );
1581
1582 return true;
1583 }
1584
1591 public static function purgePatrolFooterCache( $articleID ) {
1592 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
1593 $cache->delete( $cache->makeKey( 'unpatrollable-page', $articleID ) );
1594 }
1595
1600 public function showMissingArticle() {
1601 $context = $this->getContext();
1602 $send404Code = $context->getConfig()->get( MainConfigNames::Send404Code );
1603
1604 $outputPage = $context->getOutput();
1605 // Whether the page is a root user page of an existing user (but not a subpage)
1606 $validUserPage = false;
1607
1608 $title = $this->getTitle();
1609
1610 $services = MediaWikiServices::getInstance();
1611
1612 $contextUser = $context->getUser();
1613
1614 # Indicate to client-side JS (Visual Editor in particular) whether
1615 # Parsoid would be used to render this article if created
1616 if ( $this->getParserOptions()->getUseParsoid() ) {
1617 $outputPage->addJsConfigVars(
1618 'wgParsoidHtmlVersion',
1619 Parsoid::defaultHTMLVersion()
1620 );
1621 }
1622
1623 # Show info in user (talk) namespace. Does the user exist? Are they blocked?
1624 if ( $title->getNamespace() === NS_USER
1625 || $title->getNamespace() === NS_USER_TALK
1626 ) {
1627 $rootPart = $title->getRootText();
1628 $userFactory = $services->getUserFactory();
1629 $user = $userFactory->newFromNameOrIp( $rootPart );
1630
1631 if ( $user && $user->isRegistered() && $user->isHidden() &&
1632 !$context->getAuthority()->isAllowed( 'hideuser' )
1633 ) {
1634 // T120883 if the user is hidden and the viewer cannot see hidden
1635 // users, pretend like it does not exist at all.
1636 $user = false;
1637 }
1638
1639 if ( !( $user && $user->isRegistered() ) && !$this->userNameUtils->isIP( $rootPart ) ) {
1640 $this->addMessageBoxStyles( $outputPage );
1641 // User does not exist
1642 $outputPage->addHTML( Html::warningBox(
1643 $context->msg( 'userpage-userdoesnotexist-view', wfEscapeWikiText( $rootPart ) )->parse(),
1644 'mw-userpage-userdoesnotexist'
1645 ) );
1646
1647 // Show renameuser log extract
1648 LogEventsList::showLogExtract(
1649 $outputPage,
1650 'renameuser',
1651 Title::makeTitleSafe( NS_USER, $rootPart ),
1652 '',
1653 [
1654 'lim' => 10,
1655 'showIfEmpty' => false,
1656 'msgKey' => [ 'renameuser-renamed-notice', $title->getBaseText() ]
1657 ]
1658 );
1659 } else {
1660 $validUserPage = !$title->isSubpage();
1661
1662 $blockLogBox = LogEventsList::getBlockLogWarningBox(
1663 $this->blockStore,
1664 $services->getNamespaceInfo(),
1665 $this->getContext(),
1666 $this->linkRenderer,
1667 $user,
1668 $title,
1669 [],
1670 $this->getContext()
1671 );
1672 if ( $blockLogBox !== null ) {
1673 $outputPage->addHTML( $blockLogBox );
1674 }
1675 }
1676 }
1677
1678 $this->getHookRunner()->onShowMissingArticle( $this );
1679
1680 # Show delete and move logs if there were any such events.
1681 # The logging query can DOS the site when bots/crawlers cause 404 floods,
1682 # so be careful showing this. 404 pages must be cheap as they are hard to cache.
1683 $dbCache = MediaWikiServices::getInstance()->getMainObjectStash();
1684 $key = $dbCache->makeKey( 'page-recent-delete', md5( $title->getPrefixedText() ) );
1685 $isRegistered = $contextUser->isRegistered();
1686 $sessionExists = $context->getRequest()->getSession()->isPersistent();
1687
1688 if ( $isRegistered || $dbCache->get( $key ) || $sessionExists ) {
1689 $logTypes = [ 'delete', 'move', 'protect', 'merge' ];
1690
1691 $dbr = $this->dbProvider->getReplicaDatabase();
1692
1693 $conds = [ $dbr->expr( 'log_action', '!=', 'revision' ) ];
1694 // Give extensions a chance to hide their (unrelated) log entries
1695 $this->getHookRunner()->onArticle__MissingArticleConditions( $conds, $logTypes );
1696 LogEventsList::showLogExtract(
1697 $outputPage,
1698 $logTypes,
1699 $title,
1700 '',
1701 [
1702 'lim' => 10,
1703 'conds' => $conds,
1704 'showIfEmpty' => false,
1705 'msgKey' => [ $isRegistered || $sessionExists
1706 ? 'moveddeleted-notice'
1707 : 'moveddeleted-notice-recent'
1708 ]
1709 ]
1710 );
1711 }
1712
1713 if ( !$this->mPage->hasViewableContent() && $send404Code && !$validUserPage ) {
1714 // If there's no backing content, send a 404 Not Found
1715 // for better machine handling of broken links.
1716 $context->getRequest()->response()->statusHeader( 404 );
1717 }
1718
1719 // Also apply the robot policy for nonexisting pages (even if a 404 was used)
1720 $policy = $this->getRobotPolicy( 'view' );
1721 $outputPage->getMetadata()->setIndexPolicy( $policy['index'] );
1722 $outputPage->setFollowPolicy( $policy['follow'] );
1723
1724 $hookResult = $this->getHookRunner()->onBeforeDisplayNoArticleText( $this );
1725
1726 if ( !$hookResult ) {
1727 return;
1728 }
1729
1730 // Try shadow page
1731 $oldid = $this->getOldID();
1732 if ( !$oldid ) {
1733 $view = $this->shadowPageLoader->get( $this->getTitle() )?->getView( $this->getParserOptions() );
1734 if ( $view ) {
1735 $outputPage->addParserOutputContent(
1736 $view->getParserOutput(), $view->getParserOptions() );
1737 return;
1738 }
1739 }
1740
1741 // Show error message
1742 if ( $oldid ) {
1743 $text = $this->getMissingRevisionMsg( $oldid )->plain();
1744 } elseif ( $context->getAuthority()->probablyCan( 'edit', $title ) ) {
1745 $message = $isRegistered ? 'noarticletext' : 'noarticletextanon';
1746 $text = $context->msg( $message )->plain();
1747 } else {
1748 $text = $context->msg( 'noarticletext-nopermission' )->plain();
1749 }
1750
1751 $dir = $context->getLanguage()->getDir();
1752 $lang = $context->getLanguage()->getHtmlCode();
1753 $outputPage->addWikiTextAsInterface( Html::openElement( 'div', [
1754 'class' => "noarticletext mw-content-$dir",
1755 'dir' => $dir,
1756 'lang' => $lang,
1757 ] ) . "\n$text\n</div>" );
1758 }
1759
1764 private function showViewError( string $errortext ) {
1765 $outputPage = $this->getContext()->getOutput();
1766 $outputPage->setPageTitleMsg( $this->getContext()->msg( 'errorpagetitle' ) );
1767 $outputPage->disableClientCache();
1768 $outputPage->setRobotPolicy( 'noindex,nofollow' );
1769 $outputPage->clearHTML();
1770 $this->addMessageBoxStyles( $outputPage );
1771 $outputPage->addHTML( Html::errorBox( $outputPage->parseAsContent( $errortext ) ) );
1772 }
1773
1780 public function showDeletedRevisionHeader() {
1781 if ( !$this->mRevisionRecord->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
1782 // Not deleted
1783 return true;
1784 }
1785 $outputPage = $this->getContext()->getOutput();
1786 // Used in wikilinks, should not contain whitespaces
1787 $titleText = $this->getTitle()->getPrefixedURL();
1788 $this->addMessageBoxStyles( $outputPage );
1789 // If the user is not allowed to see it...
1790 if ( !$this->mRevisionRecord->userCan(
1791 RevisionRecord::DELETED_TEXT,
1792 $this->getContext()->getAuthority()
1793 ) ) {
1794 $outputPage->addHTML(
1795 Html::warningBox(
1796 $outputPage->msg( 'rev-deleted-text-permission', $titleText )->parse(),
1797 'plainlinks'
1798 )
1799 );
1800
1801 return false;
1802 // If the user needs to confirm that they want to see it...
1803 } elseif ( $this->getContext()->getRequest()->getInt( 'unhide' ) !== 1 ) {
1804 # Give explanation and add a link to view the revision...
1805 $oldid = intval( $this->getOldID() );
1806 $link = $this->getTitle()->getFullURL( "oldid={$oldid}&unhide=1" );
1807 $msg = $this->mRevisionRecord->isDeleted( RevisionRecord::DELETED_RESTRICTED ) ?
1808 'rev-suppressed-text-unhide' : 'rev-deleted-text-unhide';
1809 $outputPage->addHTML(
1810 Html::warningBox(
1811 $outputPage->msg( $msg, $link )->parse(),
1812 'plainlinks'
1813 )
1814 );
1815
1816 return false;
1817 // We are allowed to see...
1818 } else {
1819 $msg = $this->mRevisionRecord->isDeleted( RevisionRecord::DELETED_RESTRICTED )
1820 ? [ 'rev-suppressed-text-view', $titleText ]
1821 : [ 'rev-deleted-text-view', $titleText ];
1822 $outputPage->addHTML(
1823 Html::warningBox(
1824 $outputPage->msg( $msg[0], $msg[1] )->parse(),
1825 'plainlinks'
1826 )
1827 );
1828
1829 return true;
1830 }
1831 }
1832
1833 private function addMessageBoxStyles( OutputPage $outputPage ) {
1834 $outputPage->addModuleStyles( [
1835 'mediawiki.codex.messagebox.styles',
1836 ] );
1837 }
1838
1847 public function setOldSubtitle( $oldid = 0 ) {
1848 if ( !$this->getHookRunner()->onDisplayOldSubtitle( $this, $oldid ) ) {
1849 return;
1850 }
1851
1852 $context = $this->getContext();
1853 $unhide = $context->getRequest()->getInt( 'unhide' ) === 1;
1854
1855 # Cascade unhide param in links for easy deletion browsing
1856 $extraParams = [];
1857 if ( $unhide ) {
1858 $extraParams['unhide'] = 1;
1859 }
1860
1861 if ( $this->mRevisionRecord && $this->mRevisionRecord->getId() === $oldid ) {
1862 $revisionRecord = $this->mRevisionRecord;
1863 } else {
1864 $revisionRecord = $this->revisionStore->getRevisionById( $oldid );
1865 }
1866 if ( !$revisionRecord ) {
1867 throw new LogicException( 'There should be a revision record at this point.' );
1868 }
1869
1870 $timestamp = $revisionRecord->getTimestamp();
1871
1872 $current = ( $oldid == $this->mPage->getLatest() );
1873 $language = $context->getLanguage();
1874 $user = $context->getUser();
1875
1876 $td = $language->userTimeAndDate( $timestamp, $user );
1877 $tddate = $language->userDate( $timestamp, $user );
1878 $tdtime = $language->userTime( $timestamp, $user );
1879
1880 # Show user links if allowed to see them. If hidden, then show them only if requested...
1881 $userlinks = Linker::revUserTools( $revisionRecord, !$unhide );
1882
1883 $infomsg = $current && !$context->msg( 'revision-info-current' )->isDisabled()
1884 ? 'revision-info-current'
1885 : 'revision-info';
1886
1887 $outputPage = $context->getOutput();
1888 $outputPage->addModuleStyles( [
1889 'mediawiki.action.styles',
1890 'mediawiki.interface.helpers.styles'
1891 ] );
1892
1893 $revisionUser = $revisionRecord->getUser();
1894 $revisionInfo = "<div id=\"mw-{$infomsg}\">" .
1895 $context->msg( $infomsg, $td )
1896 ->rawParams( $userlinks )
1897 ->params(
1898 $revisionRecord->getId(),
1899 $tddate,
1900 $tdtime,
1901 $revisionUser ? $revisionUser->getName() : ''
1902 )
1903 ->rawParams( $this->commentFormatter->formatRevision(
1904 $revisionRecord,
1905 $user,
1906 true,
1907 !$unhide
1908 ) )
1909 ->parse() .
1910 "</div>";
1911
1912 $lnk = $current
1913 ? $context->msg( 'currentrevisionlink' )->escaped()
1914 : $this->linkRenderer->makeKnownLink(
1915 $this->getTitle(),
1916 $context->msg( 'currentrevisionlink' )->text(),
1917 [],
1918 $extraParams
1919 );
1920 $curdiff = $current
1921 ? $context->msg( 'diff' )->escaped()
1922 : $this->linkRenderer->makeKnownLink(
1923 $this->getTitle(),
1924 $context->msg( 'diff' )->text(),
1925 [],
1926 [
1927 'diff' => 'cur',
1928 'oldid' => $oldid
1929 ] + $extraParams
1930 );
1931 $prevExist = (bool)$this->revisionStore->getPreviousRevision( $revisionRecord );
1932 $prevlink = $prevExist
1933 ? $this->linkRenderer->makeKnownLink(
1934 $this->getTitle(),
1935 $context->msg( 'previousrevision' )->text(),
1936 [],
1937 [
1938 'direction' => 'prev',
1939 'oldid' => $oldid
1940 ] + $extraParams
1941 )
1942 : $context->msg( 'previousrevision' )->escaped();
1943 $prevdiff = $prevExist
1944 ? $this->linkRenderer->makeKnownLink(
1945 $this->getTitle(),
1946 $context->msg( 'diff' )->text(),
1947 [],
1948 [
1949 'diff' => 'prev',
1950 'oldid' => $oldid
1951 ] + $extraParams
1952 )
1953 : $context->msg( 'diff' )->escaped();
1954 $nextlink = $current
1955 ? $context->msg( 'nextrevision' )->escaped()
1956 : $this->linkRenderer->makeKnownLink(
1957 $this->getTitle(),
1958 $context->msg( 'nextrevision' )->text(),
1959 [],
1960 [
1961 'direction' => 'next',
1962 'oldid' => $oldid
1963 ] + $extraParams
1964 );
1965 $nextdiff = $current
1966 ? $context->msg( 'diff' )->escaped()
1967 : $this->linkRenderer->makeKnownLink(
1968 $this->getTitle(),
1969 $context->msg( 'diff' )->text(),
1970 [],
1971 [
1972 'diff' => 'next',
1973 'oldid' => $oldid
1974 ] + $extraParams
1975 );
1976
1977 $cdel = Linker::getRevDeleteLink(
1978 $context->getAuthority(),
1979 $revisionRecord,
1980 $this->getTitle()
1981 );
1982 if ( $cdel !== '' ) {
1983 $cdel .= ' ';
1984 }
1985
1986 // the outer div is need for styling the revision info and nav in MobileFrontend
1987 $this->addMessageBoxStyles( $outputPage );
1988 $outputPage->addSubtitle(
1989 Html::warningBox(
1990 $revisionInfo .
1991 "<div id=\"mw-revision-nav\">" . $cdel .
1992 $context->msg( 'revision-nav' )->rawParams(
1993 $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff
1994 )->escaped() . "</div>",
1995 'mw-revision'
1996 )
1997 );
1998 }
1999
2008 public function addHelpLink( $to, $overrideBaseUrl = false ) {
2009 $out = $this->getContext()->getOutput();
2010 $msg = $out->msg( 'namespace-' . $this->getTitle()->getNamespace() . '-helppage' );
2011
2012 if ( !$msg->isDisabled() ) {
2013 $title = Title::newFromText( $msg->plain() );
2014 if ( $title instanceof Title ) {
2015 $out->addHelpLink( $title->getLocalURL(), true );
2016 }
2017 } else {
2018 $out->addHelpLink( $to, $overrideBaseUrl );
2019 }
2020 }
2021
2025 public function render() {
2026 $this->getContext()->getRequest()->response()->header( 'X-Robots-Tag: noindex' );
2027 $this->getContext()->getOutput()->setArticleBodyOnly( true );
2028 // We later set suppress section edit links based on this; also used by ImagePage
2029 $this->viewIsRenderAction = true;
2030 $this->view();
2031 }
2032
2036 public function protect() {
2037 $form = new ProtectionForm( $this );
2038 $form->execute();
2039 }
2040
2044 public function unprotect() {
2045 $this->protect();
2046 }
2047
2048 /* Caching functions */
2049
2057 protected function tryFileCache() {
2058 static $called = false;
2059
2060 if ( $called ) {
2061 wfDebug( "Article::tryFileCache(): called twice!?" );
2062 return false;
2063 }
2064
2065 $called = true;
2066 if ( $this->isFileCacheable() ) {
2067 $cache = new HTMLFileCache( $this->getTitle(), 'view' );
2068 if ( $cache->isCacheGood( $this->mPage->getTouched() ) ) {
2069 wfDebug( "Article::tryFileCache(): about to load file" );
2070 $cache->loadFromFileCache( $this->getContext() );
2071 return true;
2072 } else {
2073 wfDebug( "Article::tryFileCache(): starting buffer" );
2074 ob_start( $cache->saveToFileCache( ... ) );
2075 }
2076 } else {
2077 wfDebug( "Article::tryFileCache(): not cacheable" );
2078 }
2079
2080 return false;
2081 }
2082
2088 public function isFileCacheable( $mode = HTMLFileCache::MODE_NORMAL ) {
2089 $cacheable = false;
2090
2091 if ( HTMLFileCache::useFileCache( $this->getContext(), $mode ) ) {
2092 $cacheable = $this->mPage->getId()
2093 && !$this->mRedirectedFrom && !$this->getTitle()->isRedirect();
2094 // Extension may have reason to disable file caching on some pages.
2095 if ( $cacheable ) {
2096 $cacheable = $this->getHookRunner()->onIsFileCacheable( $this ) ?? false;
2097 }
2098 }
2099
2100 return $cacheable;
2101 }
2102
2116 public function getParserOutput( $oldid = null, ?UserIdentity $user = null, array $options = [] ) {
2117 $parserOptions = $this->getParserOptions( $oldid, $user );
2118 return $this->mPage->getParserOutput( $parserOptions, $oldid, options: $options );
2119 }
2120
2125 public function getParserOptions( ?int $oldid = null, ?UserIdentity $user = null ) {
2126 $parserOptions =
2127 $this->mPage->makeParserOptions( $user ?? $this->getContext() );
2128 $parserOptions->setRenderReason( $oldid ? 'page_view_oldid' : 'page_view' );
2129 # Allow extensions to vary parser options used for article rendering
2130 $services = MediaWikiServices::getInstance();
2131 ( new HookRunner( $services->getHookContainer() ) )
2132 ->onArticleParserOptions( $this, $parserOptions );
2133
2134 return $parserOptions;
2135 }
2136
2143 public function setContext( $context ) {
2144 $this->mContext = $context;
2145 }
2146
2153 public function getContext(): IContextSource {
2154 if ( $this->mContext instanceof IContextSource ) {
2155 return $this->mContext;
2156 } else {
2157 wfDebug( __METHOD__ . " called and \$mContext is null. " .
2158 "Return RequestContext::getMain()" );
2159 return RequestContext::getMain();
2160 }
2161 }
2162
2168 public function getActionOverrides() {
2169 return $this->mPage->getActionOverrides();
2170 }
2171
2172 private function getMissingRevisionMsg( int $oldid ): Message {
2173 // T251066: Try loading the revision from the archive table.
2174 // Show link to view it if it exists and the user has permission to view it.
2175 // (Ignore the given title, if any; look it up from the revision instead.)
2176 $context = $this->getContext();
2177 $revRecord = $this->archivedRevisionLookup->getArchivedRevisionRecord( null, $oldid );
2178 if (
2179 $revRecord &&
2180 $revRecord->userCan(
2181 RevisionRecord::DELETED_TEXT,
2182 $context->getAuthority()
2183 ) &&
2184 $context->getAuthority()->isAllowedAny( 'deletedtext', 'undelete' )
2185 ) {
2186 // You can see deleted content. So we know exactly what page is it on
2187 // and can render the message in that context regardless of what
2188 // title (if any) is given in URL params
2189 return $context->msg(
2190 'missing-revision-permission',
2191 $oldid,
2192 $revRecord->getTimestamp(),
2193 Title::newFromPageIdentity( $revRecord->getPage() )->getPrefixedURL()
2194 )->page( $revRecord->getPage() );
2195 }
2196
2197 if ( $context->getRequest()->getCheck( 'title' ) ) {
2198 // If you specify a title then link to the deletion log for that title
2199 return $context->msg( 'missing-revision', $oldid );
2200 } else {
2201 // Don't show the deletion log for the main page if you don't specify a title
2202 return $context->msg( 'missing-revision-nolog', $oldid );
2203 }
2204 }
2205
2206 private function usePostProcessingCache( ParserOptions $poptions ): bool {
2207 if ( $poptions->getUseParsoid() ) {
2208 return $this->parsoidPostprocCacheAvailable;
2209 }
2210 return $this->legacyPostprocCacheAvailable && $this->useLegacyPostprocCache;
2211 }
2212
2218 public function setUseLegacyPostprocCache( bool $val = true ): void {
2219 $this->useLegacyPostprocCache = $val;
2220 }
2221}
2222
2224class_alias( Article::class, 'Article' );
const NS_USER
Definition Defines.php:53
const NS_FILE
Definition Defines.php:57
const SCHEMA_COMPAT_READ_OLD
Definition Defines.php:294
const NS_MEDIA
Definition Defines.php:39
const NS_USER_TALK
Definition Defines.php:54
const NS_CATEGORY
Definition Defines.php:65
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfEscapeWikiText( $input)
Escapes the given text so that it may be output using addWikiText() without any linking,...
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
if(!defined('MW_SETUP_CALLBACK'))
Definition WebStart.php:71
A DatabaseBlock (unlike a SystemBlock) is stored in the database, may give rise to autoblocks and may...
Page view caching in the file system.
This is the main service interface for converting single-line comments from various DB comment fields...
Group all the pieces relevant to the context of a request into one instance.
makeTitle( $linkId)
Convert a link ID to a Title.to override Title
DifferenceEngine is responsible for rendering the difference between two revisions as HTML.
The HTML user interface for page editing.
Definition EditPage.php:131
Show an error when a user tries to do something they do not have the necessary permissions for.
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
This class is a collection of static functions that serve two purposes:
Definition Html.php:44
Handle enqueueing of background jobs.
Class that generates HTML for internal links.
Some internal bits split of from Skin.php.
Definition Linker.php:48
A class containing constants representing the names of configuration variables.
const UsePostprocCacheParsoid
Name constant for the UsePostprocCacheParsoid setting, for use with Config::get()
const UsePostprocCacheLegacy
Name constant for the UsePostprocCacheLegacy setting, for use with Config::get()
Service locator for MediaWiki core services.
static getInstance()
Returns the global default instance of the top level service locator.
The Message class deals with fetching and processing of interface message into a variety of formats.
Definition Message.php:144
This is one of the Core classes and should be read at least once by any new developers.
setContentLangForJS(Bcp47Code $lang)
setFollowPolicy( $policy)
Set the follow policy for the page, but leave the index policy un- touched.
addSubtitle( $str)
Add $str to the subtitle.
addModuleStyles( $modules)
Load the styles of one or more style-only ResourceLoader modules on this page.
getMetadata()
Return a ParserOutput that can be used to set metadata properties for the current page.
Legacy class representing an editable page and handling UI for some page actions.
Definition Article.php:66
render()
Handle action=render.
Definition Article.php:2025
static formatRobotPolicy( $policy)
Converts a String robot policy into an associative array, to allow merging of several policies using ...
Definition Article.php:1259
static purgePatrolFooterCache( $articleID)
Purge the cache used to check if it is worth showing the patrol footer For example,...
Definition Article.php:1591
static newFromWikiPage(WikiPage $page, IContextSource $context)
Create an Article object of the appropriate class for the given page.
Definition Article.php:225
showNamespaceHeader()
Show a header specific to the namespace currently being viewed, such as [[MediaWiki:Subjectpageheader...
Definition Article.php:1364
showViewFooter()
Show the footer section of an ordinary page view.
Definition Article.php:1390
IConnectionProvider $dbProvider
Definition Article.php:122
int null $mOldId
The oldid of the article that was requested to be shown, 0 for the latest revision.
Definition Article.php:85
showPatrolFooter()
If patrol is possible, output a patrol UI box.
Definition Article.php:1414
setOldSubtitle( $oldid=0)
Generate the navigation links when browsing through an article revisions It shows the information as:...
Definition Article.php:1847
Title null $mRedirectedFrom
Title from which we were redirected here, if any.
Definition Article.php:88
setContext( $context)
Sets the context this Article is executed in.
Definition Article.php:2143
bool $viewIsRenderAction
Whether render() was called.
Definition Article.php:111
getRedirectedFrom()
Get the page this view was redirected from.
Definition Article.php:236
showProtectionIndicator()
Show a lock icon above the article body if the page is protected.
Definition Article.php:601
view()
This is the default action of the index.php entry point: just view the page of the given title.
Definition Article.php:457
getRevIdFetched()
Use this to fetch the rev ID used on page views.
Definition Article.php:443
getParserOutput( $oldid=null, ?UserIdentity $user=null, array $options=[])
Lightweight method to get the parser output for a page, checking the parser cache and so on.
Definition Article.php:2116
string false $mRedirectUrl
URL to redirect to or false if none.
Definition Article.php:91
isCurrent()
Returns true if the currently-referenced revision is the current edit to this page (and it exists).
Definition Article.php:424
getParserOptions(?int $oldid=null, ?UserIdentity $user=null)
Get parser options suitable for rendering the primary article wikitext.
Definition Article.php:2125
static newFromID( $id)
Constructor from a page id.
Definition Article.php:186
tryFileCache()
checkLastModified returns true if it has taken care of all output to the client that is necessary for...
Definition Article.php:2057
showRedirectedFromHeader()
If this request is a redirect view, send "redirected from" subtitle to the output.
Definition Article.php:1286
getPage()
Get the WikiPage object of this instance.
Definition Article.php:263
protect()
action=protect handler
Definition Article.php:2036
ParserOutput null false $mParserOutput
The ParserOutput generated for viewing the page, initialized by view().
Definition Article.php:104
fetchRevisionRecord()
Fetches the revision to work on.
Definition Article.php:366
DatabaseBlockStore $blockStore
Definition Article.php:123
newPage(Title $title)
Definition Article.php:178
RestrictionStore $restrictionStore
Definition Article.php:124
LinkRenderer $linkRenderer
Definition Article.php:113
setUseLegacyPostprocCache(bool $val=true)
By default, we do not use the postprocessing cache for legacy parses; however, we want to be able to ...
Definition Article.php:2218
showDeletedRevisionHeader()
If the revision requested for view is deleted, check permissions.
Definition Article.php:1780
getTitle()
Get the title object of the article.
Definition Article.php:253
showDiffPage()
Show a diff page according to current request variables.
Definition Article.php:1084
getActionOverrides()
Call to WikiPage function for backwards compatibility.
Definition Article.php:2168
WikiPage $mPage
The WikiPage object of this instance.
Definition Article.php:79
isFileCacheable( $mode=HTMLFileCache::MODE_NORMAL)
Check if the page can be cached.
Definition Article.php:2088
setRedirectedFrom(Title $from)
Tell the page view functions that this view was redirected from another page on the wiki.
Definition Article.php:244
adjustDisplayTitle(ParserOutput $pOutput)
Adjust title for pages with displaytitle, -{T|}- or language conversion.
Definition Article.php:1067
showMissingArticle()
Show the error text for a missing article.
Definition Article.php:1600
getRobotPolicy( $action, ?ParserOutput $pOutput=null)
Get the robot policy to be used for the current view.
Definition Article.php:1172
unprotect()
action=unprotect handler (alias)
Definition Article.php:2044
getContext()
Gets the context this Article is executed in.
Definition Article.php:2153
modifyTextOptions(OutputPage $outputPage, array &$textOptions)
Allow subclasses to adjust the post-processing options used when rendering the page content (the pars...
Definition Article.php:950
addHelpLink( $to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
Definition Article.php:2008
__construct(Title $title, $oldId=null)
Definition Article.php:143
getOldIDFromRequest()
Sets $this->mRedirectUrl to a correct URL if the query parameters are incorrect.
Definition Article.php:298
static newFromTitle( $title, IContextSource $context)
Create an Article object of the appropriate class for the given page.
Definition Article.php:197
Special handling for category description pages.
Rendering of file description pages.
Definition ImagePage.php:37
Handles the page protection UI and backend.
Service for creating WikiPage objects.
Base representation for an editable wiki page.
Definition WikiPage.php:83
getTitle()
Get the title object of the article.
Definition WikiPage.php:251
Set options of the Parser.
ParserOutput is a rendering of a Content object or a message.
PHP Parser - Processes wiki markup (which uses a more user-friendly syntax, such as "[[link]]" for ma...
Definition Parser.php:138
A StatusValue for permission errors.
Utility class for creating and reading rows in the recentchanges table.
Exception raised when the text of a revision is permanently missing or corrupt.
Page revision base class.
Service for looking up page revisions.
A service which loads shadow content, which is content that is displayed on a nonexistent page with a...
The base class for all skins.
Definition Skin.php:54
setParserOptions(ParserOptions $parserOptions)
Definition Skin.php:2589
getOptions()
Get current skin's options.
Definition Skin.php:2458
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:69
Provides access to user options.
UserNameUtils service.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
hasMessage(string $message)
Returns true if the specified message is present as a warning or error.
isOK()
Returns whether the operation completed.
return[ 'config-schema-inverse'=>['default'=>['ConfigRegistry'=>['main'=> 'MediaWiki\\Config\\GlobalVarConfig::newInstance',], 'Sitename'=> 'MediaWiki', 'Server'=> false, 'CanonicalServer'=> false, 'ServerName'=> false, 'AssumeProxiesUseDefaultProtocolPorts'=> true, 'HttpsPort'=> 443, 'ForceHTTPS'=> false, 'ScriptPath'=> '/wiki', 'UsePathInfo'=> null, 'Script'=> false, 'LoadScript'=> false, 'RestPath'=> false, 'StylePath'=> false, 'LocalStylePath'=> false, 'ExtensionAssetsPath'=> false, 'ExtensionDirectory'=> null, 'StyleDirectory'=> null, 'ArticlePath'=> false, 'UploadPath'=> false, 'ImgAuthPath'=> false, 'ThumbPath'=> false, 'UploadDirectory'=> false, 'FileCacheDirectory'=> false, 'Logo'=> false, 'Logos'=> false, 'Favicon'=> '/favicon.ico', 'AppleTouchIcon'=> false, 'ReferrerPolicy'=> false, 'TmpDirectory'=> false, 'UploadBaseUrl'=> '', 'UploadStashScalerBaseUrl'=> false, 'ActionPaths'=>[], 'MainPageIsDomainRoot'=> false, 'EnableUploads'=> false, 'UploadStashMaxAge'=> 21600, 'EnableAsyncUploads'=> false, 'EnableAsyncUploadsByURL'=> false, 'UploadMaintenance'=> false, 'IllegalFileChars'=> ':\\/\\\\', 'DeletedDirectory'=> false, 'ImgAuthDetails'=> false, 'ImgAuthUrlPathMap'=>[], 'LocalFileRepo'=>['class'=> 'MediaWiki\\FileRepo\\LocalRepo', 'name'=> 'local', 'directory'=> null, 'scriptDirUrl'=> null, 'favicon'=> null, 'url'=> null, 'hashLevels'=> null, 'thumbScriptUrl'=> null, 'transformVia404'=> null, 'deletedDir'=> null, 'deletedHashLevels'=> null, 'updateCompatibleMetadata'=> null, 'reserializeMetadata'=> null,], 'ForeignFileRepos'=>[], 'UseInstantCommons'=> false, 'UseSharedUploads'=> false, 'SharedUploadDirectory'=> null, 'SharedUploadPath'=> null, 'HashedSharedUploadDirectory'=> true, 'RepositoryBaseUrl'=> 'https:'FetchCommonsDescriptions'=> false, 'SharedUploadDBname'=> false, 'SharedUploadDBprefix'=> '', 'CacheSharedUploads'=> true, 'ForeignUploadTargets'=>['local',], 'UploadDialog'=>['fields'=>['description'=> true, 'date'=> false, 'categories'=> false,], 'licensemessages'=>['local'=> 'generic-local', 'foreign'=> 'generic-foreign',], 'comment'=>['local'=> '', 'foreign'=> '',], 'format'=>['filepage'=> ' $DESCRIPTION', 'description'=> ' $TEXT', 'ownwork'=> '', 'license'=> '', 'uncategorized'=> '',],], 'FileBackends'=>[], 'LockManagers'=>[], 'DefaultLockManager'=> null, 'ShowEXIF'=> null, 'UpdateCompatibleMetadata'=> false, 'AllowCopyUploads'=> false, 'CopyUploadsDomains'=>[], 'CopyUploadsFromSpecialUpload'=> false, 'CopyUploadProxy'=> false, 'CopyUploadTimeout'=> false, 'CopyUploadAllowOnWikiDomainConfig'=> false, 'MaxUploadSize'=> 104857600, 'MinUploadChunkSize'=> 1024, 'UploadNavigationUrl'=> false, 'UploadMissingFileUrl'=> false, 'ThumbnailScriptPath'=> false, 'SharedThumbnailScriptPath'=> false, 'HashedUploadDirectory'=> true, 'CSPUploadEntryPoint'=> true, 'FileExtensions'=>['png', 'gif', 'jpg', 'jpeg', 'webp',], 'ProhibitedFileExtensions'=>['html', 'htm', 'js', 'jsb', 'mhtml', 'mht', 'xhtml', 'xht', 'php', 'phtml', 'php3', 'php4', 'php5', 'phps', 'phar', 'shtml', 'jhtml', 'pl', 'py', 'cgi', 'exe', 'scr', 'dll', 'msi', 'vbs', 'bat', 'com', 'pif', 'cmd', 'vxd', 'cpl', 'xml',], 'MimeTypeExclusions'=>['text/html', 'application/javascript', 'text/javascript', 'text/x-javascript', 'application/x-shellscript', 'application/x-php', 'text/x-php', 'text/x-python', 'text/x-perl', 'text/x-bash', 'text/x-sh', 'text/x-csh', 'text/scriptlet', 'application/x-msdownload', 'application/x-msmetafile', 'application/java', 'application/xml', 'text/xml',], 'CheckFileExtensions'=> true, 'StrictFileExtensions'=> true, 'DisableUploadScriptChecks'=> false, 'UploadSizeWarning'=> false, 'TrustedMediaFormats'=>['BITMAP', 'AUDIO', 'VIDEO', 'image/svg+xml', 'application/pdf',], 'MediaHandlers'=>[], 'NativeImageLazyLoading'=> false, 'ParserTestMediaHandlers'=>['image/jpeg'=> 'MockBitmapHandler', 'image/png'=> 'MockBitmapHandler', 'image/gif'=> 'MockBitmapHandler', 'image/tiff'=> 'MockBitmapHandler', 'image/webp'=> 'MockBitmapHandler', 'image/x-ms-bmp'=> 'MockBitmapHandler', 'image/x-bmp'=> 'MockBitmapHandler', 'image/x-xcf'=> 'MockBitmapHandler', 'image/svg+xml'=> 'MockSvgHandler', 'image/vnd.djvu'=> 'MockDjVuHandler',], 'UseImageResize'=> true, 'UseImageMagick'=> false, 'ImageMagickConvertCommand'=> '/usr/bin/convert', 'MaxInterlacingAreas'=>[], 'SharpenParameter'=> '0x0.4', 'SharpenReductionThreshold'=> 0.85, 'ImageMagickTempDir'=> false, 'CustomConvertCommand'=> false, 'JpegTran'=> '/usr/bin/jpegtran', 'JpegPixelFormat'=> 'yuv420', 'JpegQuality'=> 80, 'Exiv2Command'=> '/usr/bin/exiv2', 'Exiftool'=> '/usr/bin/exiftool', 'SVGConverters'=>['ImageMagick'=> ' $path/convert -background "#ffffff00" -thumbnail $widthx$height\\! $input PNG:$output', 'inkscape'=> ' $path/inkscape -w $width -o $output $input', 'batik'=> 'java -Djava.awt.headless=true -jar $path/batik-rasterizer.jar -w $width -d $output $input', 'rsvg'=> ' $path/rsvg-convert -w $width -h $height -o $output $input', 'ImagickExt'=>['SvgHandler::rasterizeImagickExt',],], 'SVGConverter'=> 'ImageMagick', 'SVGConverterPath'=> '', 'SVGMaxSize'=> 5120, 'SVGMetadataCutoff'=> 5242880, 'SVGNativeRendering'=> true, 'SVGNativeRenderingSizeLimit'=> 51200, 'MediaInTargetLanguage'=> true, 'MaxImageArea'=> 12500000, 'MaxAnimatedGifArea'=> 12500000, 'TiffThumbnailType'=>[], 'ThumbnailEpoch'=> '20030516000000', 'AttemptFailureEpoch'=> 1, 'IgnoreImageErrors'=> false, 'GenerateThumbnailOnParse'=> true, 'ShowArchiveThumbnails'=> true, 'EnableAutoRotation'=> null, 'Antivirus'=> null, 'AntivirusSetup'=>['clamav'=>['command'=> 'clamscan --no-summary ', 'codemap'=>[0=> 0, 1=> 1, 52=> -1, ' *'=> false,], 'messagepattern'=> '/.*?:(.*)/sim',],], 'AntivirusRequired'=> true, 'VerifyMimeType'=> true, 'MimeTypeFile'=> 'internal', 'MimeInfoFile'=> 'internal', 'MimeDetectorCommand'=> null, 'TrivialMimeDetection'=> false, 'XMLMimeTypes'=>['http:'svg'=> 'image/svg+xml', 'http:'http:'html'=> 'text/html',], 'ImageLimits'=>[[320, 240,], [640, 480,], [800, 600,], [1024, 768,], [1280, 1024,], [2560, 2048,],], 'ThumbLimits'=>[120, 150, 180, 200, 220, 250, 300, 400,], 'ThumbnailNamespaces'=>[6,], 'ThumbnailSteps'=> null, 'ThumbnailBuckets'=> null, 'ThumbnailMinimumBucketDistance'=> 50, 'UploadThumbnailRenderMap'=>[], 'UploadThumbnailRenderMethod'=> 'jobqueue', 'UploadThumbnailRenderHttpCustomHost'=> false, 'UploadThumbnailRenderHttpCustomDomain'=> false, 'UseTinyRGBForJPGThumbnails'=> false, 'GalleryOptions'=>[], 'ThumbUpright'=> 0.75, 'DirectoryMode'=> 511, 'ResponsiveImages'=> true, 'ImagePreconnect'=> false, 'TrackMediaRequestProvenance'=> false, 'DjvuUseBoxedCommand'=> false, 'DjvuDump'=> null, 'DjvuRenderer'=> null, 'DjvuTxt'=> null, 'DjvuPostProcessor'=> 'pnmtojpeg', 'DjvuOutputExtension'=> 'jpg', 'EmergencyContact'=> false, 'RestTermsOfServiceUrl'=> null, 'PasswordSender'=> false, 'NoReplyAddress'=> false, 'EnableEmail'=> true, 'EnableUserEmail'=> true, 'UserEmailUseReplyTo'=> true, 'PasswordReminderResendTime'=> 24, 'NewPasswordExpiry'=> 604800, 'UserEmailConfirmationTokenExpiry'=> 604800, 'PasswordExpirationDays'=> false, 'PasswordExpireGrace'=> 604800, 'SMTP'=> false, 'AdditionalMailParams'=> null, 'AllowHTMLEmail'=> false, 'EnotifFromEditor'=> false, 'EmailAuthentication'=> true, 'EmailConfirmationBanner'=> false, 'EnotifWatchlist'=> false, 'EnotifUserTalk'=> false, 'EnotifRevealEditorAddress'=> false, 'EnotifMinorEdits'=> true, 'EnotifUseRealName'=> false, 'UsersNotifiedOnAllChanges'=>[], 'DBname'=> 'my_wiki', 'DBmwschema'=> null, 'DBprefix'=> '', 'DBserver'=> 'localhost', 'DBport'=> 5432, 'DBuser'=> 'wikiuser', 'DBpassword'=> '', 'DBtype'=> 'mysql', 'DBssl'=> false, 'DBcompress'=> false, 'DBStrictWarnings'=> false, 'DBadminuser'=> null, 'DBadminpassword'=> null, 'SearchType'=> null, 'SearchTypeAlternatives'=> null, 'DBTableOptions'=> 'ENGINE=InnoDB, DEFAULT CHARSET=binary', 'SQLMode'=> '', 'SQLiteDataDir'=> '', 'SharedDB'=> null, 'SharedPrefix'=> false, 'SharedTables'=>['user', 'user_properties', 'user_autocreate_serial',], 'SharedSchema'=> false, 'DBservers'=> false, 'LBFactoryConf'=>['class'=> 'Wikimedia\\Rdbms\\LBFactorySimple',], 'DataCenterUpdateStickTTL'=> 10, 'DBerrorLog'=> false, 'DBerrorLogTZ'=> false, 'LocalDatabases'=>[], 'DatabaseReplicaLagWarning'=> 10, 'DatabaseReplicaLagCritical'=> 30, 'MaxExecutionTimeForExpensiveQueries'=> 0, 'VirtualDomainsMapping'=>[], 'FileSchemaMigrationStage'=> 3, 'ExternalLinksDomainGaps'=>[], 'ContentHandlers'=>['wikitext'=>['class'=> 'MediaWiki\\Content\\WikitextContentHandler', 'services'=>['TitleFactory', 'ParserFactory', 'GlobalIdGenerator', 'LanguageNameUtils', 'LinkRenderer', 'MagicWordFactory', 'ParsoidParserFactory',],], 'javascript'=>['class'=> 'MediaWiki\\Content\\JavaScriptContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'UserOptionsLookup', 'CodeHighlighter',],], 'json'=>['class'=> 'MediaWiki\\Content\\JsonContentHandler', 'services'=>['ParsoidParserFactory', 'TitleFactory',],], 'css'=>['class'=> 'MediaWiki\\Content\\CssContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'UserOptionsLookup', 'CodeHighlighter',],], 'vue'=>['class'=> 'MediaWiki\\Content\\VueContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'CodeHighlighter',],], 'text'=> 'MediaWiki\\Content\\TextContentHandler', 'unknown'=> 'MediaWiki\\Content\\FallbackContentHandler',], 'NamespaceContentModels'=>[], 'TextModelsToParse'=>['wikitext', 'javascript', 'css',], 'CompressRevisions'=> false, 'ExternalStores'=>[], 'ExternalServers'=>[], 'DefaultExternalStore'=> false, 'RevisionCacheExpiry'=> 604800, 'PageLanguageUseDB'=> false, 'DiffEngine'=> null, 'ExternalDiffEngine'=> false, 'Wikidiff2Options'=>[], 'RequestTimeLimit'=> null, 'TransactionalTimeLimit'=> 120, 'CriticalSectionTimeLimit'=> 180.0, 'MiserMode'=> false, 'DisableQueryPages'=> false, 'QueryCacheLimit'=> 1000, 'WantedPagesThreshold'=> 1, 'AllowSlowParserFunctions'=> false, 'AllowSchemaUpdates'=> true, 'MaxArticleSize'=> 2048, 'MemoryLimit'=> '50M', 'PoolCounterConf'=> null, 'PoolCountClientConf'=>['servers'=>['127.0.0.1',], 'timeout'=> 0.1,], 'MaxUserDBWriteDuration'=> false, 'MaxJobDBWriteDuration'=> false, 'LinkHolderBatchSize'=> 1000, 'MaximumMovedPages'=> 100, 'ForceDeferredUpdatesPreSend'=> false, 'MultiShardSiteStats'=> false, 'CacheDirectory'=> false, 'MainCacheType'=> 0, 'MessageCacheType'=> -1, 'ParserCacheType'=> -1, 'SessionCacheType'=> -1, 'AnonSessionCacheType'=> false, 'LanguageConverterCacheType'=> -1, 'ObjectCaches'=>[0=>['class'=> 'Wikimedia\\ObjectCache\\EmptyBagOStuff', 'reportDupes'=> false,], 1=>['class'=> 'MediaWiki\\ObjectCache\\SqlBagOStuff', 'loggroup'=> 'SQLBagOStuff',], 'memcached-php'=>['class'=> 'Wikimedia\\ObjectCache\\MemcachedPhpBagOStuff', 'loggroup'=> 'memcached',], 'memcached-pecl'=>['class'=> 'Wikimedia\\ObjectCache\\MemcachedPeclBagOStuff', 'loggroup'=> 'memcached',], 'hash'=>['class'=> 'Wikimedia\\ObjectCache\\HashBagOStuff', 'reportDupes'=> false,], 'apc'=>['class'=> 'Wikimedia\\ObjectCache\\APCUBagOStuff', 'reportDupes'=> false,], 'apcu'=>['class'=> 'Wikimedia\\ObjectCache\\APCUBagOStuff', 'reportDupes'=> false,],], 'WANObjectCache'=>[], 'MicroStashType'=> -1, 'MainStash'=> 1, 'ParsoidCacheConfig'=>['StashType'=> null, 'StashDuration'=> 86400, 'WarmParsoidParserCache'=> false,], 'ParsoidSelectiveUpdateSampleRate'=> 0, 'ParserCacheFilterConfig'=>['pcache'=>['default'=>['minCpuTime'=> 0,],], 'postproc-pcache'=>['default'=>['minCpuTime'=> 9223372036854775807,],], 'parsoid-pcache'=>['default'=>['minCpuTime'=> 0,],], 'postproc-parsoid-pcache'=>['default'=>['minCpuTime'=> 0,],],], 'ChronologyProtectorSecret'=> '', 'ParserCacheExpireTime'=> 86400, 'ParserCacheAsyncExpireTime'=> 60, 'ParserCacheAsyncRefreshJobs'=> true, 'OldRevisionParserCacheExpireTime'=> 3600, 'ObjectCacheSessionExpiry'=> 3600, 'SuspiciousIpExpiry'=> false, 'SessionPbkdf2Iterations'=> 10001, 'UseSessionCookieJwt'=> false, 'JwtSessionCookieIssuer'=> null, 'MemCachedServers'=>['127.0.0.1:11211',], 'MemCachedPersistent'=> false, 'MemCachedTimeout'=> 500000, 'UseLocalMessageCache'=> false, 'AdaptiveMessageCache'=> false, 'LocalisationCacheConf'=>['class'=> 'MediaWiki\\Language\\LocalisationCache', 'store'=> 'detect', 'storeClass'=> false, 'storeDirectory'=> false, 'storeServer'=>[], 'forceRecache'=> false, 'manualRecache'=> false,], 'CachePages'=> true, 'CacheEpoch'=> '20030516000000', 'GitInfoCacheDirectory'=> false, 'UseFileCache'=> false, 'FileCacheDepth'=> 2, 'RenderHashAppend'=> '', 'EnableSidebarCache'=> false, 'SidebarCacheExpiry'=> 86400, 'UseGzip'=> false, 'InvalidateCacheOnLocalSettingsChange'=> true, 'ExtensionInfoMTime'=> false, 'EnableRemoteBagOStuffTests'=> false, 'UseCdn'=> false, 'VaryOnXFP'=> false, 'InternalServer'=> false, 'CdnMaxAge'=> 18000, 'CdnMaxageLagged'=> 30, 'CdnMaxageStale'=> 10, 'CdnReboundPurgeDelay'=> 0, 'CdnMaxageSubstitute'=> 60, 'ForcedRawSMaxage'=> 300, 'CdnServers'=>[], 'CdnServersNoPurge'=>[], 'HTCPRouting'=>[], 'HTCPMulticastTTL'=> 1, 'UsePrivateIPs'=> false, 'CdnMatchParameterOrder'=> true, 'LanguageCode'=> 'en', 'GrammarForms'=>[], 'InterwikiMagic'=> true, 'HideInterlanguageLinks'=> false, 'ExtraInterlanguageLinkPrefixes'=>[], 'InterlanguageLinkCodeMap'=>[], 'ExtraLanguageNames'=>[], 'ExtraLanguageCodes'=>['bh'=> 'bho', 'no'=> 'nb', 'simple'=> 'en',], 'DummyLanguageCodes'=>[], 'AllUnicodeFixes'=> false, 'LegacyEncoding'=> false, 'AmericanDates'=> false, 'TranslateNumerals'=> true, 'UseDatabaseMessages'=> true, 'MaxMsgCacheEntrySize'=> 10000, 'DisableLangConversion'=> false, 'DisableTitleConversion'=> false, 'DefaultLanguageVariant'=> false, 'UsePigLatinVariant'=> false, 'DisabledVariants'=>[], 'VariantArticlePath'=> false, 'UseXssLanguage'=> false, 'LoginLanguageSelector'=> false, 'ForceUIMsgAsContentMsg'=>[], 'RawHtmlMessages'=>[], 'Localtimezone'=> null, 'LocalTZoffset'=> null, 'OverrideUcfirstCharacters'=>[], 'MimeType'=> 'text/html', 'Html5Version'=> null, 'EditSubmitButtonLabelPublish'=> false, 'XhtmlNamespaces'=>[], 'SiteNotice'=> '', 'BrowserFormatDetection'=> 'telephone=no', 'SkinMetaTags'=>[], 'DefaultSkin'=> 'vector-2022', 'FallbackSkin'=> 'fallback', 'SkipSkins'=>[], 'DisableOutputCompression'=> false, 'FragmentMode'=>['html5', 'legacy',], 'ExternalInterwikiFragmentMode'=> 'legacy', 'FooterIcons'=>['copyright'=>['copyright'=>[],], 'poweredby'=>['mediawiki'=>['src'=> null, 'url'=> 'https:'alt'=> 'Powered by MediaWiki', 'lang'=> 'en',],],], 'EnableSectionShare'=> false, 'UseCombinedLoginLink'=> false, 'Edititis'=> false, 'Send404Code'=> true, 'ShowRollbackEditCount'=> 10, 'EnableCanonicalServerLink'=> false, 'InterwikiLogoOverride'=>[], 'ResourceModules'=>[], 'ResourceModuleSkinStyles'=>[], 'ResourceLoaderSources'=>[], 'ResourceBasePath'=> null, 'ResourceLoaderMaxage'=>[], 'ResourceLoaderDebug'=> false, 'ResourceLoaderMaxQueryLength'=> false, 'ResourceLoaderValidateJS'=> true, 'ResourceLoaderEnableJSProfiler'=> false, 'ResourceLoaderStorageEnabled'=> true, 'ResourceLoaderStorageVersion'=> 1, 'ResourceLoaderEnableSourceMapLinks'=> true, 'AllowSiteCSSOnRestrictedPages'=> false, 'VueDevelopmentMode'=> false, 'CodexDevelopmentDir'=> null, 'MetaNamespace'=> false, 'MetaNamespaceTalk'=> false, 'CanonicalNamespaceNames'=>[-2=> 'Media', -1=> 'Special', 0=> '', 1=> 'Talk', 2=> 'User', 3=> 'User_talk', 4=> 'Project', 5=> 'Project_talk', 6=> 'File', 7=> 'File_talk', 8=> 'MediaWiki', 9=> 'MediaWiki_talk', 10=> 'Template', 11=> 'Template_talk', 12=> 'Help', 13=> 'Help_talk', 14=> 'Category', 15=> 'Category_talk',], 'ExtraNamespaces'=>[], 'ExtraGenderNamespaces'=>[], 'NamespaceAliases'=>[], 'LegalTitleChars'=> ' %!"$&\'()*,\\-.\\/0-9:;=?@A-Z\\\\^_`a-z~\\x80-\\xFF+', 'CapitalLinks' => true, 'CapitalLinkOverrides' => [ ], 'NamespacesWithSubpages' => [ 1 => true, 2 => true, 3 => true, 4 => true, 5 => true, 7 => true, 8 => true, 9 => true, 10 => true, 11 => true, 12 => true, 13 => true, 15 => true, ], 'NamespacesWithoutAutoSummaries' => [ ], 'ContentNamespaces' => [ 0, ], 'ShortPagesNamespaceExclusions' => [ ], 'ExtraSignatureNamespaces' => [ ], 'InvalidRedirectTargets' => [ 'Filepath', 'Mypage', 'Mytalk', 'Redirect', 'Mylog', ], 'DisableHardRedirects' => false, 'FixDoubleRedirects' => false, 'LocalInterwikis' => [ ], 'InterwikiExpiry' => 10800, 'InterwikiCache' => false, 'InterwikiScopes' => 3, 'InterwikiFallbackSite' => 'wiki', 'RedirectSources' => false, 'SiteTypes' => [ 'mediawiki' => 'MediaWiki\\Site\\MediaWikiSite', ], 'MaxTocLevel' => 999, 'MaxPPNodeCount' => 1000000, 'MaxTemplateDepth' => 100, 'MaxPPExpandDepth' => 100, 'UrlProtocols' => [ 'bitcoin:', 'ftp: 'ftps: 'geo:', 'git: 'gopher: 'http: 'https: 'irc: 'ircs: 'magnet:', 'mailto:', 'matrix:', 'mms: 'news:', 'nntp: 'redis: 'sftp: 'sip:', 'sips:', 'sms:', 'ssh: 'svn: 'tel:', 'telnet: 'urn:', 'wikipedia: 'worldwind: 'xmpp:', ' ], 'CleanSignatures' => true, 'AllowExternalImages' => false, 'AllowExternalImagesFrom' => '', 'EnableImageWhitelist' => false, 'TidyConfig' => [ ], 'ParsoidSettings' => [ 'useSelser' => true, ], 'ParsoidExperimentalParserFunctionOutput' => false, 'RawHtml' => false, 'ExternalLinkTarget' => false, 'NoFollowLinks' => true, 'NoFollowNsExceptions' => [ ], 'NoFollowDomainExceptions' => [ 'mediawiki.org', ], 'RegisterInternalExternals' => false, 'ExternalLinksIgnoreDomains' => [ ], 'AllowDisplayTitle' => true, 'RestrictDisplayTitle' => true, 'ExpensiveParserFunctionLimit' => 100, 'PreprocessorCacheThreshold' => 1000, 'EnableScaryTranscluding' => false, 'TranscludeCacheExpiry' => 3600, 'EnableMagicLinks' => [ 'ISBN' => false, 'PMID' => false, 'RFC' => false, ], 'ParserEnableUserLanguage' => false, 'ArticleCountMethod' => 'link', 'ActiveUserDays' => 30, 'LearnerEdits' => 10, 'LearnerMemberSince' => 4, 'ExperiencedUserEdits' => 500, 'ExperiencedUserMemberSince' => 30, 'ManualRevertSearchRadius' => 15, 'RevertedTagMaxDepth' => 15, 'CentralIdLookupProviders' => [ 'local' => [ 'class' => 'MediaWiki\\User\\CentralId\\LocalIdLookup', 'services' => [ 'MainConfig', 'DBLoadBalancerFactory', 'HideUserUtils', ], ], ], 'CentralIdLookupProvider' => 'local', 'UserRegistrationProviders' => [ 'local' => [ 'class' => 'MediaWiki\\User\\Registration\\LocalUserRegistrationProvider', 'services' => [ 'ConnectionProvider', ], ], ], 'PasswordPolicy' => [ 'policies' => [ 'bureaucrat' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'sysop' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'interface-admin' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'bot' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'default' => [ 'MinimalPasswordLength' => [ 'value' => 8, 'suggestChangeOnLogin' => true, ], 'PasswordCannotBeSubstringInUsername' => [ 'value' => true, 'suggestChangeOnLogin' => true, ], 'PasswordCannotMatchDefaults' => [ 'value' => true, 'suggestChangeOnLogin' => true, ], 'MaximalPasswordLength' => [ 'value' => 4096, 'suggestChangeOnLogin' => true, ], 'PasswordNotInCommonList' => [ 'value' => true, 'suggestChangeOnLogin' => true, ], ], ], 'checks' => [ 'MinimalPasswordLength' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkMinimalPasswordLength', ], 'MinimumPasswordLengthToLogin' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkMinimumPasswordLengthToLogin', ], 'PasswordCannotBeSubstringInUsername' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkPasswordCannotBeSubstringInUsername', ], 'PasswordCannotMatchDefaults' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkPasswordCannotMatchDefaults', ], 'MaximalPasswordLength' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkMaximalPasswordLength', ], 'PasswordNotInCommonList' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkPasswordNotInCommonList', ], ], ], 'AuthManagerConfig' => null, 'AuthManagerAutoConfig' => [ 'preauth' => [ 'MediaWiki\\Auth\\ThrottlePreAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\ThrottlePreAuthenticationProvider', 'sort' => 0, ], 'MediaWiki\\Auth\\PreviouslyRenamedAccountPreAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\PreviouslyRenamedAccountPreAuthenticationProvider', 'services' => [ 'ConnectionProvider', 'UserFactory', ], 'sort' => 0, ], ], 'primaryauth' => [ 'MediaWiki\\Auth\\TemporaryPasswordPrimaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\TemporaryPasswordPrimaryAuthenticationProvider', 'services' => [ 'DBLoadBalancerFactory', 'UserOptionsLookup', ], 'args' => [ [ 'authoritative' => false, ], ], 'sort' => 0, ], 'MediaWiki\\Auth\\LocalPasswordPrimaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\LocalPasswordPrimaryAuthenticationProvider', 'services' => [ 'DBLoadBalancerFactory', ], 'args' => [ [ 'authoritative' => true, ], ], 'sort' => 100, ], ], 'secondaryauth' => [ 'MediaWiki\\Auth\\CheckBlocksSecondaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\CheckBlocksSecondaryAuthenticationProvider', 'sort' => 0, ], 'MediaWiki\\Auth\\ResetPasswordSecondaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\ResetPasswordSecondaryAuthenticationProvider', 'sort' => 100, ], 'MediaWiki\\Auth\\EmailNotificationSecondaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\EmailNotificationSecondaryAuthenticationProvider', 'services' => [ 'DBLoadBalancerFactory', ], 'sort' => 200, ], ], ], 'RememberMe' => 'choose', 'ReauthenticateTime' => [ 'default' => 3600, ], 'ChangeCredentialsBlacklist' => [ 'MediaWiki\\Auth\\TemporaryPasswordAuthenticationRequest', ], 'RemoveCredentialsBlacklist' => [ 'MediaWiki\\Auth\\PasswordAuthenticationRequest', ], 'InvalidPasswordReset' => true, 'PasswordDefault' => 'pbkdf2', 'PasswordConfig' => [ 'A' => [ 'class' => 'MediaWiki\\Password\\MWOldPassword', ], 'B' => [ 'class' => 'MediaWiki\\Password\\MWSaltedPassword', ], 'pbkdf2-legacyA' => [ 'class' => 'MediaWiki\\Password\\LayeredParameterizedPassword', 'types' => [ 'A', 'pbkdf2', ], ], 'pbkdf2-legacyB' => [ 'class' => 'MediaWiki\\Password\\LayeredParameterizedPassword', 'types' => [ 'B', 'pbkdf2', ], ], 'bcrypt' => [ 'class' => 'MediaWiki\\Password\\BcryptPassword', 'cost' => 9, ], 'pbkdf2' => [ 'class' => 'MediaWiki\\Password\\Pbkdf2PasswordUsingOpenSSL', 'algo' => 'sha512', 'cost' => '30000', 'length' => '64', ], 'argon2' => [ 'class' => 'MediaWiki\\Password\\Argon2Password', 'algo' => 'auto', ], ], 'PasswordResetRoutes' => [ 'username' => true, 'email' => true, ], 'MaxSigChars' => 255, 'SignatureValidation' => 'warning', 'SignatureAllowedLintErrors' => [ 'obsolete-tag', ], 'MaxNameChars' => 255, 'ReservedUsernames' => [ 'MediaWiki default', 'Conversion script', 'Maintenance script', 'Template namespace initialisation script', 'ScriptImporter', 'Delete page script', 'Move page script', 'Command line script', 'Unknown user', 'msg:double-redirect-fixer', 'msg:usermessage-editor', 'msg:proxyblocker', 'msg:sorbs', 'msg:spambot_username', 'msg:autochange-username', ], 'DefaultUserOptions' => [ 'ccmeonemails' => 0, 'date' => 'default', 'diffonly' => 0, 'diff-type' => 'table', 'disablemail' => 0, 'editfont' => 'monospace', 'editondblclick' => 0, 'editrecovery' => 0, 'editsectiononrightclick' => 0, 'email-allow-new-users' => 1, 'enotifminoredits' => 0, 'enotifrevealaddr' => 0, 'enotifusertalkpages' => 1, 'enotifwatchlistpages' => 1, 'extendwatchlist' => 1, 'fancysig' => 0, 'forceeditsummary' => 0, 'forcesafemode' => 0, 'gender' => 'unknown', 'hidecategorization' => 1, 'hideminor' => 0, 'hidepatrolled' => 0, 'imagesize' => 2, 'minordefault' => 0, 'newpageshidepatrolled' => 0, 'nickname' => '', 'norollbackdiff' => 0, 'prefershttps' => 1, 'previewonfirst' => 0, 'previewontop' => 1, 'pst-cssjs' => 1, 'rcdays' => 7, 'rcenhancedfilters-disable' => 0, 'rclimit' => 50, 'requireemail' => 0, 'search-match-redirect' => true, 'search-special-page' => 'Search', 'search-thumbnail-extra-namespaces' => true, 'searchlimit' => 20, 'showhiddencats' => 0, 'shownumberswatching' => 1, 'showrollbackconfirmation' => 0, 'skin' => false, 'skin-responsive' => 1, 'thumbsize' => 5, 'underline' => 2, 'useeditwarning' => 1, 'uselivepreview' => 0, 'usenewrc' => 1, 'watchcreations' => 1, 'watchcreations-expiry' => 'infinite', 'watchdefault' => 1, 'watchdefault-expiry' => 'infinite', 'watchdeletion' => 0, 'watchlistdays' => 7, 'watchlisthideanons' => 0, 'watchlisthidebots' => 0, 'watchlisthidecategorization' => 1, 'watchlisthideliu' => 0, 'watchlisthideminor' => 0, 'watchlisthideown' => 0, 'watchlisthidepatrolled' => 0, 'watchlistreloadautomatically' => 0, 'watchlistunwatchlinks' => 0, 'watchmoves' => 0, 'watchrollback' => 0, 'watchuploads' => 1, 'watchrollback-expiry' => 'infinite', 'watchstar-expiry' => 'infinite', 'wlenhancedfilters-disable' => 0, 'wllimit' => 250, ], 'ConditionalUserOptions' => [ ], 'HiddenPrefs' => [ ], 'UserJsPrefLimit' => 100, 'InvalidUsernameCharacters' => '@:>=', 'UserrightsInterwikiDelimiter' => '@', 'SecureLogin' => false, 'AuthenticationTokenVersion' => null, 'SessionProviders' => [ 'MediaWiki\\Session\\CookieSessionProvider' => [ 'class' => 'MediaWiki\\Session\\CookieSessionProvider', 'args' => [ [ 'priority' => 30, ], ], 'services' => [ 'JwtCodec', 'UrlUtils', ], ], 'MediaWiki\\Session\\BotPasswordSessionProvider' => [ 'class' => 'MediaWiki\\Session\\BotPasswordSessionProvider', 'args' => [ [ 'priority' => 75, ], ], 'services' => [ 'GrantsInfo', ], ], ], 'AutoCreateTempUser' => [ 'known' => false, 'enabled' => false, 'actions' => [ 'edit', ], 'genPattern' => '~$1', 'matchPattern' => null, 'reservedPattern' => '~$1', 'serialProvider' => [ 'type' => 'local', 'useYear' => true, ], 'serialMapping' => [ 'type' => 'readable-numeric', ], 'expireAfterDays' => 90, 'notifyBeforeExpirationDays' => 10, ], 'AutoblockExemptions' => [ ], 'AutoblockExpiry' => 86400, 'BlockAllowsUTEdit' => true, 'BlockCIDRLimit' => [ 'IPv4' => 16, 'IPv6' => 19, ], 'BlockDisablesLogin' => false, 'EnableMultiBlocks' => false, 'WhitelistRead' => false, 'WhitelistReadRegexp' => false, 'EmailConfirmToEdit' => false, 'HideIdentifiableRedirects' => true, 'GroupPermissions' => [ '*' => [ 'createaccount' => true, 'autocreateaccount' => true, 'read' => true, 'edit' => true, 'createpage' => true, 'createtalk' => true, 'viewmyprivateinfo' => true, 'editmyprivateinfo' => true, 'editmyoptions' => true, ], 'user' => [ 'move' => true, 'move-subpages' => true, 'move-rootuserpages' => true, 'move-categorypages' => true, 'movefile' => true, 'read' => true, 'edit' => true, 'createpage' => true, 'createtalk' => true, 'upload' => true, 'reupload' => true, 'reupload-shared' => true, 'minoredit' => true, 'editmyusercss' => true, 'editmyuserjson' => true, 'editmyuserjs' => true, 'editmyuserjsredirect' => true, 'sendemail' => true, 'applychangetags' => true, 'changetags' => true, 'viewmywatchlist' => true, 'editmywatchlist' => true, 'createwithcontentmodel' => true, 'logout' => true, ], 'autoconfirmed' => [ 'autoconfirmed' => true, 'editsemiprotected' => true, ], 'bot' => [ 'bot' => true, 'autoconfirmed' => true, 'editsemiprotected' => true, 'nominornewtalk' => true, 'autopatrol' => true, 'suppressredirect' => true, 'apihighlimits' => true, ], 'sysop' => [ 'block' => true, 'createaccount' => true, 'createpreviouslyrenamedaccount' => true, 'delete' => true, 'bigdelete' => true, 'deletedhistory' => true, 'deletedtext' => true, 'undelete' => true, 'editcontentmodel' => true, 'editinterface' => true, 'editsitejson' => true, 'edituserjson' => true, 'import' => true, 'importupload' => true, 'move' => true, 'move-subpages' => true, 'move-rootuserpages' => true, 'move-categorypages' => true, 'patrol' => true, 'autopatrol' => true, 'protect' => true, 'editprotected' => true, 'rollback' => true, 'upload' => true, 'reupload' => true, 'reupload-shared' => true, 'unwatchedpages' => true, 'autoconfirmed' => true, 'editsemiprotected' => true, 'ipblock-exempt' => true, 'blockemail' => true, 'markbotedits' => true, 'apihighlimits' => true, 'browsearchive' => true, 'noratelimit' => true, 'movefile' => true, 'unblockself' => true, 'suppressredirect' => true, 'mergehistory' => true, 'managechangetags' => true, 'deletechangetags' => true, ], 'interface-admin' => [ 'editinterface' => true, 'editsitecss' => true, 'editsitejson' => true, 'editsitejs' => true, 'editusercss' => true, 'edituserjson' => true, 'edituserjs' => true, ], 'bureaucrat' => [ 'userrights' => true, 'noratelimit' => true, 'renameuser' => true, ], 'suppress' => [ 'hideuser' => true, 'suppressrevision' => true, 'viewsuppressed' => true, 'suppressionlog' => true, 'deleterevision' => true, 'deletelogentry' => true, ], ], 'PrivilegedGroups' => [ 'bureaucrat', 'interface-admin', 'suppress', 'sysop', ], 'RevokePermissions' => [ ], 'GroupInheritsPermissions' => [ ], 'ImplicitGroups' => [ '*', 'user', 'autoconfirmed', ], 'GroupsAddToSelf' => [ ], 'GroupsRemoveFromSelf' => [ ], 'RestrictedGroups' => [ ], 'UserRequirementsPrivateConditions' => [ ], 'RestrictionTypes' => [ 'create', 'edit', 'move', 'upload', ], 'RestrictionLevels' => [ '', 'autoconfirmed', 'sysop', ], 'CascadingRestrictionLevels' => [ 'sysop', ], 'SemiprotectedRestrictionLevels' => [ 'autoconfirmed', ], 'NamespaceProtection' => [ ], 'RestrictUserPageEditing' => false, 'NonincludableNamespaces' => [ ], 'AutoConfirmAge' => 0, 'AutoConfirmCount' => 0, 'Autopromote' => [ 'autoconfirmed' => [ '&', [ 1, null, ], [ 2, null, ], ], ], 'AutopromoteOnce' => [ 'onEdit' => [ ], ], 'AutopromoteOnceLogInRC' => true, 'AutopromoteOnceRCExcludedGroups' => [ ], 'AddGroups' => [ ], 'RemoveGroups' => [ ], 'AvailableRights' => [ ], 'ImplicitRights' => [ ], 'DeleteRevisionsLimit' => 0, 'DeleteRevisionsBatchSize' => 1000, 'HideUserContribLimit' => 1000, 'AccountCreationThrottle' => [ [ 'count' => 0, 'seconds' => 86400, ], ], 'TempAccountCreationThrottle' => [ [ 'count' => 1, 'seconds' => 600, ], [ 'count' => 6, 'seconds' => 86400, ], ], 'TempAccountNameAcquisitionThrottle' => [ [ 'count' => 60, 'seconds' => 86400, ], ], 'SpamRegex' => [ ], 'SummarySpamRegex' => [ ], 'EnableDnsBlacklist' => false, 'DnsBlacklistUrls' => [ ], 'ProxyList' => [ ], 'ProxyWhitelist' => [ ], 'SoftBlockRanges' => [ ], 'ApplyIpBlocksToXff' => false, 'RateLimits' => [ 'edit' => [ 'ip' => [ 8, 60, ], 'newbie' => [ 8, 60, ], 'user' => [ 90, 60, ], ], 'move' => [ 'newbie' => [ 2, 120, ], 'user' => [ 8, 60, ], ], 'upload' => [ 'ip' => [ 8, 60, ], 'newbie' => [ 8, 60, ], ], 'rollback' => [ 'user' => [ 10, 60, ], 'newbie' => [ 5, 120, ], ], 'mailpassword' => [ 'ip' => [ 5, 3600, ], ], 'sendemail' => [ 'ip' => [ 5, 86400, ], 'newbie' => [ 5, 86400, ], 'user' => [ 20, 86400, ], ], 'changeemail' => [ 'ip-all' => [ 10, 3600, ], 'user' => [ 4, 86400, ], ], 'confirmemail' => [ 'ip-all' => [ 10, 3600, ], 'user' => [ 4, 86400, ], ], 'purge' => [ 'ip' => [ 30, 60, ], 'user' => [ 30, 60, ], ], 'linkpurge' => [ 'ip' => [ 30, 60, ], 'user' => [ 30, 60, ], ], 'renderfile' => [ 'ip' => [ 700, 30, ], 'user' => [ 700, 30, ], ], 'renderfile-nonstandard' => [ 'ip' => [ 70, 30, ], 'user' => [ 70, 30, ], ], 'stashedit' => [ 'ip' => [ 30, 60, ], 'newbie' => [ 30, 60, ], ], 'stashbasehtml' => [ 'ip' => [ 5, 60, ], 'newbie' => [ 5, 60, ], ], 'changetags' => [ 'ip' => [ 8, 60, ], 'newbie' => [ 8, 60, ], ], 'editcontentmodel' => [ 'newbie' => [ 2, 120, ], 'user' => [ 8, 60, ], ], ], 'RateLimitsExcludedIPs' => [ ], 'PutIPinRC' => true, 'QueryPageDefaultLimit' => 50, 'ExternalQuerySources' => [ ], 'PasswordAttemptThrottle' => [ [ 'count' => 5, 'seconds' => 300, ], [ 'count' => 150, 'seconds' => 172800, ], ], 'GrantPermissions' => [ 'basic' => [ 'autocreateaccount' => true, 'autoconfirmed' => true, 'autopatrol' => true, 'editsemiprotected' => true, 'ipblock-exempt' => true, 'nominornewtalk' => true, 'patrolmarks' => true, 'read' => true, 'unwatchedpages' => true, ], 'highvolume' => [ 'bot' => true, 'apihighlimits' => true, 'noratelimit' => true, 'markbotedits' => true, ], 'import' => [ 'import' => true, 'importupload' => true, ], 'editpage' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'pagelang' => true, ], 'editprotected' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editprotected' => true, ], 'editmycssjs' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editmyusercss' => true, 'editmyuserjson' => true, 'editmyuserjs' => true, ], 'editmyoptions' => [ 'editmyoptions' => true, 'editmyuserjson' => true, ], 'editinterface' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editinterface' => true, 'edituserjson' => true, 'editsitejson' => true, ], 'editsiteconfig' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editinterface' => true, 'edituserjson' => true, 'editsitejson' => true, 'editusercss' => true, 'edituserjs' => true, 'editsitecss' => true, 'editsitejs' => true, ], 'createeditmovepage' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'createpage' => true, 'createtalk' => true, 'delete-redirect' => true, 'move' => true, 'move-rootuserpages' => true, 'move-subpages' => true, 'move-categorypages' => true, 'suppressredirect' => true, ], 'uploadfile' => [ 'upload' => true, 'reupload-own' => true, ], 'uploadeditmovefile' => [ 'upload' => true, 'reupload-own' => true, 'reupload' => true, 'reupload-shared' => true, 'upload_by_url' => true, 'movefile' => true, 'suppressredirect' => true, ], 'patrol' => [ 'patrol' => true, ], 'rollback' => [ 'rollback' => true, ], 'blockusers' => [ 'block' => true, 'blockemail' => true, ], 'viewdeleted' => [ 'browsearchive' => true, 'deletedhistory' => true, 'deletedtext' => true, ], 'viewrestrictedlogs' => [ 'suppressionlog' => true, ], 'delete' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'browsearchive' => true, 'deletedhistory' => true, 'deletedtext' => true, 'delete' => true, 'bigdelete' => true, 'deletelogentry' => true, 'deleterevision' => true, 'undelete' => true, ], 'oversight' => [ 'suppressrevision' => true, 'viewsuppressed' => true, ], 'protect' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editprotected' => true, 'protect' => true, ], 'viewmywatchlist' => [ 'viewmywatchlist' => true, ], 'editmywatchlist' => [ 'editmywatchlist' => true, ], 'sendemail' => [ 'sendemail' => true, ], 'createaccount' => [ 'createaccount' => true, ], 'privateinfo' => [ 'viewmyprivateinfo' => true, ], 'mergehistory' => [ 'mergehistory' => true, ], 'managesessions' => [ 'logout' => true, ], ], 'GrantPermissionGroups' => [ 'basic' => 'hidden', 'editpage' => 'page-interaction', 'createeditmovepage' => 'page-interaction', 'editprotected' => 'page-interaction', 'patrol' => 'page-interaction', 'uploadfile' => 'file-interaction', 'uploadeditmovefile' => 'file-interaction', 'sendemail' => 'email', 'viewmywatchlist' => 'watchlist-interaction', 'editviewmywatchlist' => 'watchlist-interaction', 'editmycssjs' => 'customization', 'editmyoptions' => 'customization', 'editinterface' => 'administration', 'editsiteconfig' => 'administration', 'rollback' => 'administration', 'blockusers' => 'administration', 'delete' => 'administration', 'viewdeleted' => 'administration', 'viewrestrictedlogs' => 'administration', 'protect' => 'administration', 'oversight' => 'administration', 'createaccount' => 'administration', 'mergehistory' => 'administration', 'import' => 'administration', 'highvolume' => 'high-volume', 'privateinfo' => 'private-information', 'managesessions' => 'private-information', ], 'GrantRiskGroups' => [ 'basic' => 'low', 'editpage' => 'low', 'createeditmovepage' => 'low', 'editprotected' => 'vandalism', 'patrol' => 'low', 'uploadfile' => 'low', 'uploadeditmovefile' => 'low', 'sendemail' => 'security', 'viewmywatchlist' => 'low', 'editviewmywatchlist' => 'low', 'editmycssjs' => 'security', 'editmyoptions' => 'security', 'editinterface' => 'vandalism', 'editsiteconfig' => 'security', 'rollback' => 'low', 'blockusers' => 'vandalism', 'delete' => 'vandalism', 'viewdeleted' => 'vandalism', 'viewrestrictedlogs' => 'security', 'protect' => 'vandalism', 'oversight' => 'security', 'createaccount' => 'low', 'mergehistory' => 'vandalism', 'import' => 'security', 'highvolume' => 'low', 'privateinfo' => 'low', ], 'EnableBotPasswords' => true, 'BotPasswordsCluster' => false, 'BotPasswordsDatabase' => false, 'BotPasswordsLimit' => 100, 'SecretKey' => false, 'JwtPrivateKey' => false, 'JwtPublicKey' => false, 'AllowUserJs' => false, 'ReauthenticateForActions' => [ 'edituserjs' => 'edituserjscss', 'editusercss' => 'edituserjscss', 'editsitejs' => 'editsitejscss', 'editsitecss' => 'editsitejscss', ], 'AllowUserCss' => false, 'AllowUserCssPrefs' => true, 'UseSiteJs' => true, 'UseSiteCss' => true, 'BreakFrames' => false, 'EditPageFrameOptions' => 'DENY', 'ApiFrameOptions' => 'DENY', 'CSPHeader' => false, 'CSPReportOnlyHeader' => false, 'CSPUseReportURIDirective' => false, 'CSPFalsePositiveUrls' => [ 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'chrome-extension' => true, ], 'AllowCrossOrigin' => false, 'RestAllowCrossOriginCookieAuth' => false, 'SessionSecret' => false, 'CookieExpiration' => 2592000, 'ExtendedLoginCookieExpiration' => 15552000, 'SessionCookieJwtExpiration' => 14400, 'CookieDomain' => '', 'CookiePath' => '/', 'CookieSecure' => 'detect', 'CookiePrefix' => false, 'CookieHttpOnly' => true, 'CookieSameSite' => null, 'CacheVaryCookies' => [ ], 'SessionName' => false, 'CookieSetOnAutoblock' => true, 'CookieSetOnIpBlock' => true, 'DebugLogFile' => '', 'DebugLogPrefix' => '', 'DebugRedirects' => false, 'DebugRawPage' => false, 'DebugComments' => false, 'DebugDumpSql' => false, 'TrxProfilerLimits' => [ 'GET' => [ 'masterConns' => 0, 'writes' => 0, 'readQueryTime' => 5, 'readQueryRows' => 10000, ], 'POST' => [ 'readQueryTime' => 5, 'writeQueryTime' => 1, 'readQueryRows' => 100000, 'maxAffected' => 1000, ], 'POST-nonwrite' => [ 'writes' => 0, 'readQueryTime' => 5, 'readQueryRows' => 10000, ], 'PostSend-GET' => [ 'readQueryTime' => 5, 'writeQueryTime' => 1, 'readQueryRows' => 10000, 'maxAffected' => 1000, 'masterConns' => 0, 'writes' => 0, ], 'PostSend-POST' => [ 'readQueryTime' => 5, 'writeQueryTime' => 1, 'readQueryRows' => 100000, 'maxAffected' => 1000, ], 'JobRunner' => [ 'readQueryTime' => 30, 'writeQueryTime' => 5, 'readQueryRows' => 100000, 'maxAffected' => 500, ], 'Maintenance' => [ 'writeQueryTime' => 5, 'maxAffected' => 1000, ], ], 'DebugLogGroups' => [ ], 'MWLoggerDefaultSpi' => [ 'class' => 'MediaWiki\\Logger\\LegacySpi', ], 'ShowDebug' => false, 'SpecialVersionShowHooks' => false, 'ShowExceptionDetails' => false, 'LogExceptionBacktrace' => true, 'PropagateErrors' => true, 'ShowHostnames' => false, 'OverrideHostname' => false, 'DevelopmentWarnings' => false, 'DeprecationReleaseLimit' => false, 'Profiler' => [ ], 'StatsdServer' => false, 'StatsdMetricPrefix' => 'MediaWiki', 'StatsTarget' => null, 'StatsFormat' => null, 'StatsPrefix' => 'mediawiki', 'OpenTelemetryConfig' => null, 'PageInfoTransclusionLimit' => 50, 'EnableJavaScriptTest' => false, 'CachePrefix' => false, 'DebugToolbar' => false, 'ApiClientErrorSampleRate' => 1.0, 'DisableTextSearch' => false, 'AdvancedSearchHighlighting' => false, 'SearchHighlightBoundaries' => '[\\p{Z}\\p{P}\\p{C}]', 'OpenSearchTemplates' => [ 'application/x-suggestions+json' => false, 'application/x-suggestions+xml' => false, ], 'OpenSearchDefaultLimit' => 10, 'OpenSearchDescriptionLength' => 100, 'SearchSuggestCacheExpiry' => 1200, 'DisableSearchUpdate' => false, 'NamespacesToBeSearchedDefault' => [ true, ], 'DisableInternalSearch' => false, 'SearchForwardUrl' => null, 'SitemapNamespaces' => false, 'SitemapNamespacesPriorities' => false, 'SitemapApiConfig' => [ ], 'SpecialSearchFormOptions' => [ ], 'SearchMatchRedirectPreference' => false, 'SearchRunSuggestedQuery' => true, 'Diff3' => '/usr/bin/diff3', 'Diff' => '/usr/bin/diff', 'PreviewOnOpenNamespaces' => [ 14 => true, ], 'UniversalEditButton' => true, 'UseAutomaticEditSummaries' => true, 'CommandLineDarkBg' => false, 'ReadOnly' => null, 'ReadOnlyWatchedItemStore' => false, 'ReadOnlyFile' => false, 'UpgradeKey' => false, 'GitBin' => '/usr/bin/git', 'GitRepositoryViewers' => [ 'https: 'ssh: 'https: 'git@github\\.com:(.*?)(\\.git)?' => 'https: ], 'InstallerInitialPages' => [ [ 'titlemsg' => 'mainpage', 'text' => '{{subst:int:mainpagetext}}{{subst:int:mainpagedocfooter}}', ], ], 'RCMaxAge' => 7776000, 'WatchersMaxAge' => 15552000, 'UnwatchedPageSecret' => 1, 'RCFilterByAge' => false, 'RCLinkLimits' => [ 50, 100, 250, 500, ], 'RCLinkDays' => [ 1, 3, 7, 14, 30, ], 'RCFeeds' => [ ], 'RCWatchCategoryMembership' => false, 'UseRCPatrol' => true, 'StructuredChangeFiltersLiveUpdatePollingRate' => 3, 'UseNPPatrol' => true, 'UseFilePatrol' => true, 'Feed' => true, 'FeedLimit' => 50, 'FeedCacheTimeout' => 60, 'FeedDiffCutoff' => 32768, 'OverrideSiteFeed' => [ ], 'FeedClasses' => [ 'rss' => 'MediaWiki\\Feed\\RSSFeed', 'atom' => 'MediaWiki\\Feed\\AtomFeed', ], 'AdvertisedFeedTypes' => [ 'atom', ], 'RCShowWatchingUsers' => false, 'RCShowChangedSize' => true, 'RCChangedSizeThreshold' => 500, 'ShowUpdatedMarker' => true, 'DisableAnonTalk' => false, 'UseTagFilter' => true, 'SoftwareTags' => [ 'mw-contentmodelchange' => true, 'mw-new-redirect' => true, 'mw-removed-redirect' => true, 'mw-changed-redirect-target' => true, 'mw-blank' => true, 'mw-replace' => true, 'mw-recreated' => true, 'mw-rollback' => true, 'mw-undo' => true, 'mw-manual-revert' => true, 'mw-reverted' => true, 'mw-server-side-upload' => true, 'mw-ipblock-appeal' => true, 'mw-edited-other-users-js' => true, 'mw-edited-other-users-css' => true, ], 'RestrictedTagViewRights' => [ ], 'UnwatchedPageThreshold' => false, 'RecentChangesFlags' => [ 'newpage' => [ 'letter' => 'newpageletter', 'title' => 'recentchanges-label-newpage', 'legend' => 'recentchanges-legend-newpage', 'grouping' => 'any', ], 'minor' => [ 'letter' => 'minoreditletter', 'title' => 'recentchanges-label-minor', 'legend' => 'recentchanges-legend-minor', 'class' => 'minoredit', 'grouping' => 'all', ], 'bot' => [ 'letter' => 'boteditletter', 'title' => 'recentchanges-label-bot', 'legend' => 'recentchanges-legend-bot', 'class' => 'botedit', 'grouping' => 'all', ], 'unpatrolled' => [ 'letter' => 'unpatrolledletter', 'title' => 'recentchanges-label-unpatrolled', 'legend' => 'recentchanges-legend-unpatrolled', 'grouping' => 'any', ], ], 'WatchlistExpiry' => false, 'EnableWatchstarPopover' => false, 'EnableWatchlistLabels' => false, 'WatchlistLabelsMaxPerUser' => 100, 'WatchlistPurgeRate' => 0.1, 'WatchlistExpiryMaxDuration' => '1 year', 'EnableChangesListQueryPartitioning' => false, 'RightsPage' => null, 'RightsUrl' => null, 'RightsText' => null, 'RightsIcon' => null, 'UseCopyrightUpload' => false, 'MaxCredits' => 0, 'ShowCreditsIfMax' => true, 'ImportSources' => [ ], 'ImportTargetNamespace' => null, 'ExportAllowHistory' => true, 'ExportMaxHistory' => 0, 'ExportAllowListContributors' => false, 'ExportMaxLinkDepth' => 0, 'ExportFromNamespaces' => false, 'ExportAllowAll' => false, 'ExportPagelistLimit' => 5000, 'XmlDumpSchemaVersion' => '0.11', 'WikiFarmSettingsDirectory' => null, 'WikiFarmSettingsExtension' => 'yaml', 'ExtensionFunctions' => [ ], 'ExtensionMessagesFiles' => [ ], 'MessagesDirs' => [ ], 'TranslationAliasesDirs' => [ ], 'ExtensionEntryPointListFiles' => [ ], 'EnableParserLimitReporting' => true, 'ValidSkinNames' => [ ], 'SpecialPages' => [ ], 'ExtensionCredits' => [ ], 'Hooks' => [ ], 'ServiceWiringFiles' => [ ], 'JobClasses' => [ 'deletePage' => 'MediaWiki\\Page\\DeletePageJob', 'refreshLinks' => 'MediaWiki\\JobQueue\\Jobs\\RefreshLinksJob', 'deleteLinks' => 'MediaWiki\\Page\\DeleteLinksJob', 'htmlCacheUpdate' => 'MediaWiki\\JobQueue\\Jobs\\HTMLCacheUpdateJob', 'sendMail' => [ 'class' => 'MediaWiki\\Mail\\EmaillingJob', 'services' => [ 'Emailer', ], ], 'enotifNotify' => [ 'class' => 'MediaWiki\\RecentChanges\\RecentChangeNotifyJob', 'services' => [ 'RecentChangeLookup', ], ], 'fixDoubleRedirect' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\DoubleRedirectJob', 'services' => [ 'RevisionLookup', 'MagicWordFactory', 'WikiPageFactory', ], 'needsPage' => true, ], 'AssembleUploadChunks' => 'MediaWiki\\JobQueue\\Jobs\\AssembleUploadChunksJob', 'PublishStashedFile' => 'MediaWiki\\JobQueue\\Jobs\\PublishStashedFileJob', 'ThumbnailRender' => 'MediaWiki\\JobQueue\\Jobs\\ThumbnailRenderJob', 'UploadFromUrl' => 'MediaWiki\\JobQueue\\Jobs\\UploadFromUrlJob', 'recentChangesUpdate' => 'MediaWiki\\RecentChanges\\RecentChangesUpdateJob', 'refreshLinksPrioritized' => 'MediaWiki\\JobQueue\\Jobs\\RefreshLinksJob', 'refreshLinksDynamic' => 'MediaWiki\\JobQueue\\Jobs\\RefreshLinksJob', 'activityUpdateJob' => 'MediaWiki\\Watchlist\\ActivityUpdateJob', 'categoryMembershipChange' => [ 'class' => 'MediaWiki\\RecentChanges\\CategoryMembershipChangeJob', 'services' => [ 'RecentChangeFactory', ], ], 'CategoryCountUpdateJob' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\CategoryCountUpdateJob', 'services' => [ 'ConnectionProvider', 'NamespaceInfo', ], ], 'clearUserWatchlist' => 'MediaWiki\\Watchlist\\ClearUserWatchlistJob', 'watchlistExpiry' => 'MediaWiki\\Watchlist\\WatchlistExpiryJob', 'cdnPurge' => 'MediaWiki\\JobQueue\\Jobs\\CdnPurgeJob', 'userGroupExpiry' => 'MediaWiki\\User\\UserGroupExpiryJob', 'clearWatchlistNotifications' => 'MediaWiki\\Watchlist\\ClearWatchlistNotificationsJob', 'userOptionsUpdate' => 'MediaWiki\\User\\Options\\UserOptionsUpdateJob', 'revertedTagUpdate' => 'MediaWiki\\JobQueue\\Jobs\\RevertedTagUpdateJob', 'null' => 'MediaWiki\\JobQueue\\Jobs\\NullJob', 'userEditCountInit' => 'MediaWiki\\User\\UserEditCountInitJob', 'parsoidCachePrewarm' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\ParsoidCachePrewarmJob', 'services' => [ 'ParserOutputAccess', 'PageStore', 'RevisionLookup', 'ParsoidSiteConfig', ], 'needsPage' => false, ], 'renameUserTable' => [ 'class' => 'MediaWiki\\RenameUser\\Job\\RenameUserTableJob', 'services' => [ 'MainConfig', 'DBLoadBalancerFactory', ], ], 'renameUserDerived' => [ 'class' => 'MediaWiki\\RenameUser\\Job\\RenameUserDerivedJob', 'services' => [ 'RenameUserFactory', 'UserFactory', ], ], 'renameUser' => [ 'class' => 'MediaWiki\\RenameUser\\Job\\RenameUserTableJob', 'services' => [ 'MainConfig', 'DBLoadBalancerFactory', ], ], ], 'JobTypesExcludedFromDefaultQueue' => [ 'AssembleUploadChunks', 'PublishStashedFile', 'UploadFromUrl', ], 'JobBackoffThrottling' => [ ], 'JobTypeConf' => [ 'default' => [ 'class' => 'MediaWiki\\JobQueue\\JobQueueDB', 'order' => 'random', 'claimTTL' => 3600, ], ], 'JobQueueIncludeInMaxLagFactor' => false, 'SpecialPageCacheUpdates' => [ 'Statistics' => [ 'MediaWiki\\Deferred\\SiteStatsUpdate', 'cacheUpdate', ], ], 'PagePropLinkInvalidations' => [ 'hiddencat' => 'categorylinks', ], 'CategoryMagicGallery' => true, 'CategoryPagingLimit' => 200, 'CategoryCollation' => 'uppercase', 'TempCategoryCollations' => [ ], 'SortedCategories' => false, 'TrackingCategories' => [ ], 'LogTypes' => [ '', 'block', 'protect', 'rights', 'delete', 'upload', 'move', 'import', 'interwiki', 'patrol', 'merge', 'suppress', 'tag', 'managetags', 'contentmodel', 'renameuser', ], 'LogRestrictions' => [ 'suppress' => 'suppressionlog', ], 'FilterLogTypes' => [ 'patrol' => true, 'tag' => true, 'newusers' => false, ], 'LogNames' => [ '' => 'all-logs-page', 'block' => 'blocklogpage', 'protect' => 'protectlogpage', 'rights' => 'rightslog', 'delete' => 'dellogpage', 'upload' => 'uploadlogpage', 'move' => 'movelogpage', 'import' => 'importlogpage', 'patrol' => 'patrol-log-page', 'merge' => 'mergelog', 'suppress' => 'suppressionlog', ], 'LogHeaders' => [ '' => 'alllogstext', 'block' => 'blocklogtext', 'delete' => 'dellogpagetext', 'import' => 'importlogpagetext', 'merge' => 'mergelogpagetext', 'move' => 'movelogpagetext', 'patrol' => 'patrol-log-header', 'protect' => 'protectlogtext', 'rights' => 'rightslogtext', 'suppress' => 'suppressionlogtext', 'upload' => 'uploadlogpagetext', ], 'LogActions' => [ ], 'LogActionsHandlers' => [ 'block/block' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'block/reblock' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'block/unblock' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'contentmodel/change' => 'MediaWiki\\Logging\\ContentModelLogFormatter', 'contentmodel/new' => 'MediaWiki\\Logging\\ContentModelLogFormatter', 'delete/delete' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/delete_redir' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/delete_redir2' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/event' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/restore' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/revision' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'import/interwiki' => 'MediaWiki\\Logging\\ImportLogFormatter', 'import/upload' => 'MediaWiki\\Logging\\ImportLogFormatter', 'interwiki/iw_add' => 'MediaWiki\\Logging\\InterwikiLogFormatter', 'interwiki/iw_delete' => 'MediaWiki\\Logging\\InterwikiLogFormatter', 'interwiki/iw_edit' => 'MediaWiki\\Logging\\InterwikiLogFormatter', 'managetags/activate' => 'MediaWiki\\Logging\\LogFormatter', 'managetags/create' => 'MediaWiki\\Logging\\LogFormatter', 'managetags/deactivate' => 'MediaWiki\\Logging\\LogFormatter', 'managetags/delete' => 'MediaWiki\\Logging\\LogFormatter', 'merge/merge' => [ 'class' => 'MediaWiki\\Logging\\MergeLogFormatter', 'services' => [ 'TitleParser', ], ], 'merge/merge-into' => [ 'class' => 'MediaWiki\\Logging\\MergeLogFormatter', 'services' => [ 'TitleParser', ], ], 'move/move' => [ 'class' => 'MediaWiki\\Logging\\MoveLogFormatter', 'services' => [ 'TitleParser', ], ], 'move/move_redir' => [ 'class' => 'MediaWiki\\Logging\\MoveLogFormatter', 'services' => [ 'TitleParser', ], ], 'patrol/patrol' => 'MediaWiki\\Logging\\PatrolLogFormatter', 'patrol/autopatrol' => 'MediaWiki\\Logging\\PatrolLogFormatter', 'protect/modify' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'protect/move_prot' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'protect/protect' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'protect/unprotect' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'renameuser/renameuser' => [ 'class' => 'MediaWiki\\Logging\\RenameuserLogFormatter', 'services' => [ 'TitleParser', ], ], 'rights/autopromote' => 'MediaWiki\\Logging\\RightsLogFormatter', 'rights/rights' => 'MediaWiki\\Logging\\RightsLogFormatter', 'suppress/block' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'suppress/delete' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'suppress/event' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'suppress/reblock' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'suppress/revision' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'tag/update' => 'MediaWiki\\Logging\\TagLogFormatter', 'upload/overwrite' => 'MediaWiki\\Logging\\UploadLogFormatter', 'upload/revert' => 'MediaWiki\\Logging\\UploadLogFormatter', 'upload/upload' => 'MediaWiki\\Logging\\UploadLogFormatter', ], 'ActionFilteredLogs' => [ 'block' => [ 'block' => [ 'block', ], 'reblock' => [ 'reblock', ], 'unblock' => [ 'unblock', ], ], 'contentmodel' => [ 'change' => [ 'change', ], 'new' => [ 'new', ], ], 'delete' => [ 'delete' => [ 'delete', ], 'delete_redir' => [ 'delete_redir', 'delete_redir2', ], 'restore' => [ 'restore', ], 'event' => [ 'event', ], 'revision' => [ 'revision', ], ], 'import' => [ 'interwiki' => [ 'interwiki', ], 'upload' => [ 'upload', ], ], 'managetags' => [ 'create' => [ 'create', ], 'delete' => [ 'delete', ], 'activate' => [ 'activate', ], 'deactivate' => [ 'deactivate', ], ], 'move' => [ 'move' => [ 'move', ], 'move_redir' => [ 'move_redir', ], ], 'newusers' => [ 'create' => [ 'create', 'newusers', ], 'create2' => [ 'create2', ], 'autocreate' => [ 'autocreate', ], 'byemail' => [ 'byemail', ], ], 'protect' => [ 'protect' => [ 'protect', ], 'modify' => [ 'modify', ], 'unprotect' => [ 'unprotect', ], 'move_prot' => [ 'move_prot', ], ], 'rights' => [ 'rights' => [ 'rights', ], 'autopromote' => [ 'autopromote', ], ], 'suppress' => [ 'event' => [ 'event', ], 'revision' => [ 'revision', ], 'delete' => [ 'delete', ], 'block' => [ 'block', ], 'reblock' => [ 'reblock', ], ], 'upload' => [ 'upload' => [ 'upload', ], 'overwrite' => [ 'overwrite', ], 'revert' => [ 'revert', ], ], ], 'NewUserLog' => true, 'PageCreationLog' => true, 'AllowSpecialInclusion' => true, 'DisableQueryPageUpdate' => false, 'CountCategorizedImagesAsUsed' => false, 'MaxRedirectLinksRetrieved' => 500, 'RangeContributionsCIDRLimit' => [ 'IPv4' => 16, 'IPv6' => 32, ], 'Actions' => [ ], 'DefaultRobotPolicy' => 'index,follow', 'NamespaceRobotPolicies' => [ ], 'ArticleRobotPolicies' => [ ], 'ExemptFromUserRobotsControl' => null, 'DebugAPI' => false, 'APIModules' => [ ], 'APIFormatModules' => [ ], 'APIMetaModules' => [ ], 'APIPropModules' => [ ], 'APIListModules' => [ ], 'APIMaxDBRows' => 5000, 'APIMaxResultSize' => 8388608, 'APIMaxUncachedDiffs' => 1, 'APIMaxLagThreshold' => 7, 'APICacheHelpTimeout' => 3600, 'APIUselessQueryPages' => [ 'MIMEsearch', 'LinkSearch', ], 'AjaxLicensePreview' => true, 'CrossSiteAJAXdomains' => [ ], 'CrossSiteAJAXdomainExceptions' => [ ], 'AllowedCorsHeaders' => [ 'Accept', 'Accept-Language', 'Content-Language', 'Content-Type', 'Accept-Encoding', 'DNT', 'Origin', 'User-Agent', 'Api-User-Agent', 'Promise-Non-Write-API-Action', 'Access-Control-Max-Age', 'Authorization', ], 'RestAPIAdditionalRouteFiles' => [ ], 'RestSandboxSpecs' => [ ], 'RestLocalModuleTestBaseUrl' => null, 'RestModuleOverrides' => [ ], 'RestExternalModules' => [ ], 'MaxShellMemory' => 307200, 'MaxShellFileSize' => 102400, 'MaxShellTime' => 180, 'MaxShellWallClockTime' => 180, 'ShellCgroup' => false, 'PhpCli' => '/usr/bin/php', 'ShellRestrictionMethod' => 'autodetect', 'ShellboxUrls' => [ 'default' => null, ], 'ShellboxSecretKey' => null, 'ShellboxShell' => '/bin/sh', 'HTTPTimeout' => 25, 'HTTPConnectTimeout' => 5.0, 'HTTPMaxTimeout' => 0, 'HTTPMaxConnectTimeout' => 0, 'HTTPImportTimeout' => 25, 'AsyncHTTPTimeout' => 25, 'HTTPProxy' => '', 'LocalVirtualHosts' => [ ], 'LocalHTTPProxy' => false, 'AllowExternalReqID' => false, 'GenerateReqIDFormat' => 'rand24', 'JobRunRate' => 1, 'RunJobsAsync' => false, 'UpdateRowsPerJob' => 300, 'UpdateRowsPerQuery' => 100, 'RedirectOnLogin' => null, 'VirtualRestConfig' => [ 'paths' => [ ], 'modules' => [ ], 'global' => [ 'timeout' => 360, 'forwardCookies' => false, 'HTTPProxy' => null, ], ], 'EventRelayerConfig' => [ 'default' => [ 'class' => 'Wikimedia\\EventRelayer\\EventRelayerNull', ], ], 'Pingback' => false, 'OriginTrials' => [ ], 'ReportToExpiry' => 86400, 'ReportToEndpoints' => [ ], 'FeaturePolicyReportOnly' => [ ], 'SkinsPreferred' => [ 'vector-2022', 'vector', ], 'SpecialContributeSkinsEnabled' => [ ], 'SpecialContributeNewPageTarget' => null, 'EnableEditRecovery' => false, 'EditRecoveryExpiry' => 2592000, 'UseCodexSpecialBlock' => false, 'ShowLogoutConfirmation' => false, 'EnableProtectionIndicators' => true, 'OutputPipelineStages' => [ ], 'FeatureShutdown' => [ ], 'CloneArticleParserOutput' => true, 'UseLeximorph' => false, 'UsePostprocCacheLegacy' => false, 'UsePostprocCacheParsoid' => true, 'ParserOptionsLogUnsafeSampleRate' => 0, 'ReturnExperimentalPFragmentTypes' => [ ], ], 'type' => [ 'ConfigRegistry' => 'object', 'AssumeProxiesUseDefaultProtocolPorts' => 'boolean', 'ForceHTTPS' => 'boolean', 'ExtensionDirectory' => [ 'string', 'null', ], 'StyleDirectory' => [ 'string', 'null', ], 'UploadDirectory' => [ 'string', 'boolean', 'null', ], 'Logos' => [ 'object', 'boolean', ], 'ReferrerPolicy' => [ 'array', 'string', 'boolean', ], 'ActionPaths' => 'object', 'MainPageIsDomainRoot' => 'boolean', 'ImgAuthUrlPathMap' => 'object', 'LocalFileRepo' => 'object', 'ForeignFileRepos' => 'array', 'UseSharedUploads' => 'boolean', 'SharedUploadDirectory' => [ 'string', 'null', ], 'SharedUploadPath' => [ 'string', 'null', ], 'HashedSharedUploadDirectory' => 'boolean', 'FetchCommonsDescriptions' => 'boolean', 'SharedUploadDBname' => [ 'boolean', 'string', ], 'SharedUploadDBprefix' => 'string', 'CacheSharedUploads' => 'boolean', 'ForeignUploadTargets' => 'array', 'UploadDialog' => 'object', 'FileBackends' => 'object', 'LockManagers' => 'array', 'DefaultLockManager' => [ 'string', 'null', ], 'CopyUploadsDomains' => 'array', 'CopyUploadTimeout' => [ 'boolean', 'integer', ], 'SharedThumbnailScriptPath' => [ 'string', 'boolean', ], 'HashedUploadDirectory' => 'boolean', 'CSPUploadEntryPoint' => 'boolean', 'FileExtensions' => 'array', 'ProhibitedFileExtensions' => 'array', 'MimeTypeExclusions' => 'array', 'TrustedMediaFormats' => 'array', 'MediaHandlers' => 'object', 'NativeImageLazyLoading' => 'boolean', 'ParserTestMediaHandlers' => 'object', 'MaxInterlacingAreas' => 'object', 'SVGConverters' => 'object', 'SVGNativeRendering' => [ 'string', 'boolean', ], 'MaxImageArea' => [ 'string', 'integer', 'boolean', ], 'TiffThumbnailType' => 'array', 'GenerateThumbnailOnParse' => 'boolean', 'EnableAutoRotation' => [ 'boolean', 'null', ], 'Antivirus' => [ 'string', 'null', ], 'AntivirusSetup' => 'object', 'MimeDetectorCommand' => [ 'string', 'null', ], 'XMLMimeTypes' => 'object', 'ImageLimits' => 'array', 'ThumbLimits' => 'array', 'ThumbnailNamespaces' => 'array', 'ThumbnailSteps' => [ 'array', 'null', ], 'ThumbnailBuckets' => [ 'array', 'null', ], 'UploadThumbnailRenderMap' => 'object', 'GalleryOptions' => 'object', 'DjvuDump' => [ 'string', 'null', ], 'DjvuRenderer' => [ 'string', 'null', ], 'DjvuTxt' => [ 'string', 'null', ], 'DjvuPostProcessor' => [ 'string', 'null', ], 'RestTermsOfServiceUrl' => [ 'string', 'null', ], 'SMTP' => [ 'boolean', 'object', ], 'EnotifFromEditor' => 'boolean', 'EmailConfirmationBanner' => 'boolean', 'EnotifRevealEditorAddress' => 'boolean', 'UsersNotifiedOnAllChanges' => 'object', 'DBmwschema' => [ 'string', 'null', ], 'SharedTables' => 'array', 'DBservers' => [ 'boolean', 'array', ], 'LBFactoryConf' => 'object', 'LocalDatabases' => 'array', 'VirtualDomainsMapping' => 'object', 'FileSchemaMigrationStage' => 'integer', 'ExternalLinksDomainGaps' => 'object', 'ContentHandlers' => 'object', 'NamespaceContentModels' => 'object', 'TextModelsToParse' => 'array', 'ExternalStores' => 'array', 'ExternalServers' => 'object', 'DefaultExternalStore' => [ 'array', 'boolean', ], 'RevisionCacheExpiry' => 'integer', 'PageLanguageUseDB' => 'boolean', 'DiffEngine' => [ 'string', 'null', ], 'ExternalDiffEngine' => [ 'string', 'boolean', ], 'Wikidiff2Options' => 'object', 'RequestTimeLimit' => [ 'integer', 'null', ], 'CriticalSectionTimeLimit' => 'number', 'PoolCounterConf' => [ 'object', 'null', ], 'PoolCountClientConf' => 'object', 'MaxUserDBWriteDuration' => [ 'integer', 'boolean', ], 'MaxJobDBWriteDuration' => [ 'integer', 'boolean', ], 'MultiShardSiteStats' => 'boolean', 'ObjectCaches' => 'object', 'WANObjectCache' => 'object', 'MicroStashType' => [ 'string', 'integer', ], 'ParsoidCacheConfig' => 'object', 'ParsoidSelectiveUpdateSampleRate' => 'integer', 'ParserCacheFilterConfig' => 'object', 'ChronologyProtectorSecret' => 'string', 'SuspiciousIpExpiry' => [ 'integer', 'boolean', ], 'MemCachedServers' => 'array', 'LocalisationCacheConf' => 'object', 'ExtensionInfoMTime' => [ 'integer', 'boolean', ], 'CdnServers' => 'object', 'CdnServersNoPurge' => 'object', 'HTCPRouting' => 'object', 'GrammarForms' => 'object', 'ExtraInterlanguageLinkPrefixes' => 'array', 'InterlanguageLinkCodeMap' => 'object', 'ExtraLanguageNames' => 'object', 'ExtraLanguageCodes' => 'object', 'DummyLanguageCodes' => 'object', 'DisabledVariants' => 'object', 'ForceUIMsgAsContentMsg' => 'object', 'RawHtmlMessages' => 'array', 'OverrideUcfirstCharacters' => 'object', 'XhtmlNamespaces' => 'object', 'BrowserFormatDetection' => 'string', 'SkinMetaTags' => 'object', 'SkipSkins' => 'object', 'FragmentMode' => 'array', 'FooterIcons' => 'object', 'InterwikiLogoOverride' => 'array', 'ResourceModules' => 'object', 'ResourceModuleSkinStyles' => 'object', 'ResourceLoaderSources' => 'object', 'ResourceLoaderMaxage' => 'object', 'ResourceLoaderMaxQueryLength' => [ 'integer', 'boolean', ], 'CanonicalNamespaceNames' => 'object', 'ExtraNamespaces' => 'object', 'ExtraGenderNamespaces' => 'object', 'NamespaceAliases' => 'object', 'CapitalLinkOverrides' => 'object', 'NamespacesWithSubpages' => 'object', 'NamespacesWithoutAutoSummaries' => 'array', 'ContentNamespaces' => 'array', 'ShortPagesNamespaceExclusions' => 'array', 'ExtraSignatureNamespaces' => 'array', 'InvalidRedirectTargets' => 'array', 'LocalInterwikis' => 'array', 'InterwikiCache' => [ 'boolean', 'object', ], 'SiteTypes' => 'object', 'UrlProtocols' => 'array', 'TidyConfig' => 'object', 'ParsoidSettings' => 'object', 'ParsoidExperimentalParserFunctionOutput' => 'boolean', 'NoFollowNsExceptions' => 'array', 'NoFollowDomainExceptions' => 'array', 'ExternalLinksIgnoreDomains' => 'array', 'EnableMagicLinks' => 'object', 'ManualRevertSearchRadius' => 'integer', 'RevertedTagMaxDepth' => 'integer', 'CentralIdLookupProviders' => 'object', 'CentralIdLookupProvider' => 'string', 'UserRegistrationProviders' => 'object', 'PasswordPolicy' => 'object', 'AuthManagerConfig' => [ 'object', 'null', ], 'AuthManagerAutoConfig' => 'object', 'RememberMe' => 'string', 'ReauthenticateTime' => 'object', 'ChangeCredentialsBlacklist' => 'array', 'RemoveCredentialsBlacklist' => 'array', 'PasswordConfig' => 'object', 'PasswordResetRoutes' => 'object', 'SignatureAllowedLintErrors' => 'array', 'ReservedUsernames' => 'array', 'DefaultUserOptions' => 'object', 'ConditionalUserOptions' => 'object', 'HiddenPrefs' => 'array', 'UserJsPrefLimit' => 'integer', 'AuthenticationTokenVersion' => [ 'string', 'null', ], 'SessionProviders' => 'object', 'AutoCreateTempUser' => 'object', 'AutoblockExemptions' => 'array', 'BlockCIDRLimit' => 'object', 'EnableMultiBlocks' => 'boolean', 'GroupPermissions' => 'object', 'PrivilegedGroups' => 'array', 'RevokePermissions' => 'object', 'GroupInheritsPermissions' => 'object', 'ImplicitGroups' => 'array', 'GroupsAddToSelf' => 'object', 'GroupsRemoveFromSelf' => 'object', 'RestrictedGroups' => 'object', 'UserRequirementsPrivateConditions' => 'array', 'RestrictionTypes' => 'array', 'RestrictionLevels' => 'array', 'CascadingRestrictionLevels' => 'array', 'SemiprotectedRestrictionLevels' => 'array', 'NamespaceProtection' => 'object', 'RestrictUserPageEditing' => 'boolean', 'NonincludableNamespaces' => 'object', 'Autopromote' => 'object', 'AutopromoteOnce' => 'object', 'AutopromoteOnceRCExcludedGroups' => 'array', 'AddGroups' => 'object', 'RemoveGroups' => 'object', 'AvailableRights' => 'array', 'ImplicitRights' => 'array', 'AccountCreationThrottle' => [ 'integer', 'array', ], 'TempAccountCreationThrottle' => 'array', 'TempAccountNameAcquisitionThrottle' => 'array', 'SpamRegex' => 'array', 'SummarySpamRegex' => 'array', 'DnsBlacklistUrls' => 'array', 'ProxyList' => [ 'string', 'array', ], 'ProxyWhitelist' => 'array', 'SoftBlockRanges' => 'array', 'RateLimits' => 'object', 'RateLimitsExcludedIPs' => 'array', 'ExternalQuerySources' => 'object', 'PasswordAttemptThrottle' => 'array', 'GrantPermissions' => 'object', 'GrantPermissionGroups' => 'object', 'GrantRiskGroups' => 'object', 'EnableBotPasswords' => 'boolean', 'BotPasswordsCluster' => [ 'string', 'boolean', ], 'BotPasswordsDatabase' => [ 'string', 'boolean', ], 'BotPasswordsLimit' => 'integer', 'ReauthenticateForActions' => 'object', 'CSPHeader' => [ 'boolean', 'object', ], 'CSPReportOnlyHeader' => [ 'boolean', 'object', ], 'CSPUseReportURIDirective' => [ 'boolean', 'object', ], 'CSPFalsePositiveUrls' => 'object', 'AllowCrossOrigin' => 'boolean', 'RestAllowCrossOriginCookieAuth' => 'boolean', 'CookieSameSite' => [ 'string', 'null', ], 'CacheVaryCookies' => 'array', 'TrxProfilerLimits' => 'object', 'DebugLogGroups' => 'object', 'MWLoggerDefaultSpi' => 'object', 'Profiler' => 'object', 'StatsTarget' => [ 'string', 'null', ], 'StatsFormat' => [ 'string', 'null', ], 'StatsPrefix' => 'string', 'OpenTelemetryConfig' => [ 'object', 'null', ], 'OpenSearchTemplates' => 'object', 'NamespacesToBeSearchedDefault' => 'object', 'SitemapNamespaces' => [ 'boolean', 'array', ], 'SitemapNamespacesPriorities' => [ 'boolean', 'object', ], 'SitemapApiConfig' => 'object', 'SpecialSearchFormOptions' => 'object', 'SearchMatchRedirectPreference' => 'boolean', 'SearchRunSuggestedQuery' => 'boolean', 'PreviewOnOpenNamespaces' => 'object', 'ReadOnlyWatchedItemStore' => 'boolean', 'GitRepositoryViewers' => 'object', 'InstallerInitialPages' => 'array', 'RCLinkLimits' => 'array', 'RCLinkDays' => 'array', 'RCFeeds' => 'object', 'OverrideSiteFeed' => 'object', 'FeedClasses' => 'object', 'AdvertisedFeedTypes' => 'array', 'SoftwareTags' => 'object', 'RestrictedTagViewRights' => 'object', 'RecentChangesFlags' => 'object', 'WatchlistExpiry' => 'boolean', 'EnableWatchstarPopover' => 'boolean', 'EnableWatchlistLabels' => 'boolean', 'WatchlistLabelsMaxPerUser' => 'integer', 'WatchlistPurgeRate' => 'number', 'WatchlistExpiryMaxDuration' => [ 'string', 'null', ], 'EnableChangesListQueryPartitioning' => 'boolean', 'ImportSources' => 'object', 'ExtensionFunctions' => 'array', 'ExtensionMessagesFiles' => 'object', 'MessagesDirs' => 'object', 'TranslationAliasesDirs' => 'object', 'ExtensionEntryPointListFiles' => 'object', 'ValidSkinNames' => 'object', 'SpecialPages' => 'object', 'ExtensionCredits' => 'object', 'Hooks' => 'object', 'ServiceWiringFiles' => 'array', 'JobClasses' => 'object', 'JobTypesExcludedFromDefaultQueue' => 'array', 'JobBackoffThrottling' => 'object', 'JobTypeConf' => 'object', 'SpecialPageCacheUpdates' => 'object', 'PagePropLinkInvalidations' => 'object', 'TempCategoryCollations' => 'array', 'SortedCategories' => 'boolean', 'TrackingCategories' => 'array', 'LogTypes' => 'array', 'LogRestrictions' => 'object', 'FilterLogTypes' => 'object', 'LogNames' => 'object', 'LogHeaders' => 'object', 'LogActions' => 'object', 'LogActionsHandlers' => 'object', 'ActionFilteredLogs' => 'object', 'RangeContributionsCIDRLimit' => 'object', 'Actions' => 'object', 'NamespaceRobotPolicies' => 'object', 'ArticleRobotPolicies' => 'object', 'ExemptFromUserRobotsControl' => [ 'array', 'null', ], 'APIModules' => 'object', 'APIFormatModules' => 'object', 'APIMetaModules' => 'object', 'APIPropModules' => 'object', 'APIListModules' => 'object', 'APIUselessQueryPages' => 'array', 'CrossSiteAJAXdomains' => 'object', 'CrossSiteAJAXdomainExceptions' => 'object', 'AllowedCorsHeaders' => 'array', 'RestAPIAdditionalRouteFiles' => 'array', 'RestSandboxSpecs' => 'object', 'RestLocalModuleTestBaseUrl' => [ 'string', 'null', ], 'RestModuleOverrides' => 'object', 'RestExternalModules' => 'object', 'ShellRestrictionMethod' => [ 'string', 'boolean', ], 'ShellboxUrls' => 'object', 'ShellboxSecretKey' => [ 'string', 'null', ], 'ShellboxShell' => [ 'string', 'null', ], 'HTTPTimeout' => 'number', 'HTTPConnectTimeout' => 'number', 'HTTPMaxTimeout' => 'number', 'HTTPMaxConnectTimeout' => 'number', 'LocalVirtualHosts' => 'object', 'LocalHTTPProxy' => [ 'string', 'boolean', ], 'GenerateReqIDFormat' => 'string', 'VirtualRestConfig' => 'object', 'EventRelayerConfig' => 'object', 'Pingback' => 'boolean', 'OriginTrials' => 'array', 'ReportToExpiry' => 'integer', 'ReportToEndpoints' => 'array', 'FeaturePolicyReportOnly' => 'array', 'SkinsPreferred' => 'array', 'SpecialContributeSkinsEnabled' => 'array', 'SpecialContributeNewPageTarget' => [ 'string', 'null', ], 'EnableEditRecovery' => 'boolean', 'EditRecoveryExpiry' => 'integer', 'UseCodexSpecialBlock' => 'boolean', 'ShowLogoutConfirmation' => 'boolean', 'EnableProtectionIndicators' => 'boolean', 'OutputPipelineStages' => 'object', 'FeatureShutdown' => 'array', 'CloneArticleParserOutput' => 'boolean', 'UseLeximorph' => 'boolean', 'UsePostprocCacheLegacy' => 'boolean', 'UsePostprocCacheParsoid' => 'boolean', 'ParserOptionsLogUnsafeSampleRate' => 'integer', 'ReturnExperimentalPFragmentTypes' => 'array', ], 'mergeStrategy' => [ 'TiffThumbnailType' => 'replace', 'LBFactoryConf' => 'replace', 'InterwikiCache' => 'replace', 'PasswordPolicy' => 'array_replace_recursive', 'AuthManagerAutoConfig' => 'array_plus_2d', 'GroupPermissions' => 'array_plus_2d', 'RevokePermissions' => 'array_plus_2d', 'AddGroups' => 'array_merge_recursive', 'RemoveGroups' => 'array_merge_recursive', 'RateLimits' => 'array_plus_2d', 'GrantPermissions' => 'array_plus_2d', 'MWLoggerDefaultSpi' => 'replace', 'Profiler' => 'replace', 'Hooks' => 'array_merge_recursive', 'RestModuleOverrides' => 'array_replace_recursive', 'RestExternalModules' => 'array_replace_recursive', 'VirtualRestConfig' => 'array_plus_2d', ], 'dynamicDefault' => [ 'UsePathInfo' => [ 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultUsePathInfo', ], ], 'Script' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultScript', ], ], 'LoadScript' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLoadScript', ], ], 'RestPath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultRestPath', ], ], 'StylePath' => [ 'use' => [ 'ResourceBasePath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultStylePath', ], ], 'LocalStylePath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLocalStylePath', ], ], 'ExtensionAssetsPath' => [ 'use' => [ 'ResourceBasePath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultExtensionAssetsPath', ], ], 'ArticlePath' => [ 'use' => [ 'Script', 'UsePathInfo', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultArticlePath', ], ], 'UploadPath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultUploadPath', ], ], 'FileCacheDirectory' => [ 'use' => [ 'UploadDirectory', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultFileCacheDirectory', ], ], 'Logo' => [ 'use' => [ 'ResourceBasePath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLogo', ], ], 'DeletedDirectory' => [ 'use' => [ 'UploadDirectory', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultDeletedDirectory', ], ], 'ShowEXIF' => [ 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultShowEXIF', ], ], 'SharedPrefix' => [ 'use' => [ 'DBprefix', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultSharedPrefix', ], ], 'SharedSchema' => [ 'use' => [ 'DBmwschema', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultSharedSchema', ], ], 'DBerrorLogTZ' => [ 'use' => [ 'Localtimezone', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultDBerrorLogTZ', ], ], 'Localtimezone' => [ 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLocaltimezone', ], ], 'LocalTZoffset' => [ 'use' => [ 'Localtimezone', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLocalTZoffset', ], ], 'ResourceBasePath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultResourceBasePath', ], ], 'MetaNamespace' => [ 'use' => [ 'Sitename', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultMetaNamespace', ], ], 'CookieSecure' => [ 'use' => [ 'ForceHTTPS', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultCookieSecure', ], ], 'CookiePrefix' => [ 'use' => [ 'SharedDB', 'SharedPrefix', 'SharedTables', 'DBname', 'DBprefix', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultCookiePrefix', ], ], 'ReadOnlyFile' => [ 'use' => [ 'UploadDirectory', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultReadOnlyFile', ], ], ], ], 'config-schema' => [ 'UploadStashScalerBaseUrl' => [ 'deprecated' => 'since 1.36 Use thumbProxyUrl in $wgLocalFileRepo', ], 'IllegalFileChars' => [ 'deprecated' => 'since 1.41; no longer customizable', ], 'ThumbnailNamespaces' => [ 'items' => [ 'type' => 'integer', ], ], 'LocalDatabases' => [ 'items' => [ 'type' => 'string', ], ], 'ParserCacheFilterConfig' => [ 'additionalProperties' => [ 'type' => 'object', 'description' => 'A map of namespace IDs to filter definitions.', 'additionalProperties' => [ 'type' => 'object', 'description' => 'A map of filter names to values.', 'properties' => [ 'minCpuTime' => [ 'type' => 'number', ], ], ], ], ], 'RawHtmlMessages' => [ 'items' => [ 'type' => 'string', ], ], 'InterwikiLogoOverride' => [ 'items' => [ 'type' => 'string', ], ], 'LegalTitleChars' => [ 'deprecated' => 'since 1.41; use Extension:TitleBlacklist to customize', ], 'ReauthenticateTime' => [ 'additionalProperties' => [ 'type' => 'integer', ], ], 'ChangeCredentialsBlacklist' => [ 'items' => [ 'type' => 'string', ], ], 'RemoveCredentialsBlacklist' => [ 'items' => [ 'type' => 'string', ], ], 'GroupPermissions' => [ 'additionalProperties' => [ 'type' => 'object', 'additionalProperties' => [ 'type' => 'boolean', ], ], ], 'GroupInheritsPermissions' => [ 'additionalProperties' => [ 'type' => 'string', ], ], 'AvailableRights' => [ 'items' => [ 'type' => 'string', ], ], 'ImplicitRights' => [ 'items' => [ 'type' => 'string', ], ], 'SoftBlockRanges' => [ 'items' => [ 'type' => 'string', ], ], 'ExternalQuerySources' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'enabled' => [ 'type' => 'boolean', 'default' => false, ], 'url' => [ 'type' => 'string', 'format' => 'uri', ], 'timeout' => [ 'type' => 'integer', 'default' => 10, ], ], 'required' => [ 'enabled', 'url', ], 'additionalProperties' => false, ], ], 'GrantPermissions' => [ 'additionalProperties' => [ 'type' => 'object', 'additionalProperties' => [ 'type' => 'boolean', ], ], ], 'GrantPermissionGroups' => [ 'additionalProperties' => [ 'type' => 'string', ], ], 'SitemapNamespacesPriorities' => [ 'deprecated' => 'since 1.45 and ignored', ], 'SitemapApiConfig' => [ 'additionalProperties' => [ 'enabled' => [ 'type' => 'bool', ], 'sitemapsPerIndex' => [ 'type' => 'int', ], 'pagesPerSitemap' => [ 'type' => 'int', ], 'expiry' => [ 'type' => 'int', ], ], ], 'SoftwareTags' => [ 'additionalProperties' => [ 'type' => 'boolean', ], ], 'UseCopyrightUpload' => [ 'deprecated' => 'since 1.47 This feature is being removed.', ], 'JobBackoffThrottling' => [ 'additionalProperties' => [ 'type' => 'number', ], ], 'JobTypeConf' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'class' => [ 'type' => 'string', ], 'order' => [ 'type' => 'string', ], 'claimTTL' => [ 'type' => 'integer', ], ], ], ], 'TrackingCategories' => [ 'deprecated' => 'since 1.25 Extensions should now register tracking categories using the new extension registration system.', ], 'RangeContributionsCIDRLimit' => [ 'additionalProperties' => [ 'type' => 'integer', ], ], 'RestSandboxSpecs' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'url' => [ 'type' => 'string', 'format' => 'url', ], 'name' => [ 'type' => 'string', ], 'file' => [ 'type' => 'string', ], 'msg' => [ 'type' => 'string', 'description' => 'a message key', ], ], ], ], 'RestModuleOverrides' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'mode' => [ 'type' => 'string', ], ], 'required' => [ 'mode', ], ], ], 'RestExternalModules' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'info' => [ 'type' => 'object', 'properties' => [ 'version' => [ 'type' => 'string', ], 'title' => [ 'type' => 'string', ], 'x-i18n-title' => [ 'type' => 'string', ], 'description' => [ 'type' => 'string', ], 'x-i18n-description' => [ 'type' => 'string', ], ], 'required' => [ 'version', ], ], 'base' => [ 'type' => 'string', 'format' => 'uri', ], 'spec' => [ 'type' => 'string', 'format' => 'uri', ], ], 'required' => [ 'info', 'base', 'spec', ], ], ], 'ShellboxUrls' => [ 'additionalProperties' => [ 'type' => [ 'string', 'boolean', 'null', ], ], ], ], 'obsolete-config' => [ 'MangleFlashPolicy' => 'Since 1.39; no longer has any effect.', 'EnableOpenSearchSuggest' => 'Since 1.35, no longer used', 'AutoloadAttemptLowercase' => 'Since 1.40; no longer has any effect.', ],]
Interface for objects which can provide a MediaWiki context on request.
Interface for type hinting (accepts WikiPage, Article, ImagePage, CategoryPage)
Definition Page.php:18
This interface represents the authority associated with the current execution context,...
Definition Authority.php:23
Interface for objects representing user identity.
Provide primary and replica IDatabase connections.