MediaWiki master
Skin.php
Go to the documentation of this file.
1<?php
7namespace MediaWiki\Skin;
8
11use MediaWiki\HookContainer\ProtectedHookAccessorTrait;
38use Wikimedia\IPUtils;
41
54abstract class Skin extends ContextSource {
55 use ProtectedHookAccessorTrait;
56
60 private $defaultLinkOptions;
61
65 protected $skinname = null;
66
70 protected $options = [];
72 protected $mRelevantTitle = null;
73
77 private $mRelevantUser = false;
78
80 protected const VERSION_MAJOR = 1;
81
83 private $languageLinks;
84
86 private $sidebar;
87
91 private $componentRegistry = null;
92
93 private ?ParserOptions $parserOptions = null;
94
102 public static function getVersion() {
103 return self::VERSION_MAJOR;
104 }
105
111 final protected function getComponent( string $name ): SkinComponent {
112 return $this->componentRegistry->getComponent( $name );
113 }
114
136 public function getTemplateData() {
137 $title = $this->getTitle();
138 $out = $this->getOutput();
139 $user = $this->getUser();
140 $isMainPage = $title->isMainPage();
141 $blankedHeading = false;
142 // Heading can only be blanked on "views". It should
143 // still show on action=edit, diff pages and action=history
144 $isHeadingOverridable = $this->getContext()->getActionName() === 'view' &&
145 !$this->getRequest()->getRawVal( 'diff' );
146
147 if ( $isMainPage && $isHeadingOverridable ) {
148 // Special casing for the main page to allow more freedom to editors, to
149 // design their home page differently. This came up in T290480.
150 // The parameter for logged in users is optional and may
151 // or may not be used.
152 $titleMsg = $user->isAnon() ?
153 $this->msg( 'mainpage-title' ) :
154 $this->msg( 'mainpage-title-loggedin', $user->getName() );
155
156 // T298715: Use content language rather than user language so that
157 // the custom page heading is shown to all users, not just those that have
158 // their interface set to the site content language.
159 //
160 // T331095: Avoid Message::inContentLanguage and, just like Parser,
161 // pick the language variant based on the current URL and/or user
162 // preference if their variant relates to the content language.
163 $forceUIMsgAsContentMsg = $this->getConfig()
165 if ( !in_array( $titleMsg->getKey(), (array)$forceUIMsgAsContentMsg ) ) {
166 $services = MediaWikiServices::getInstance();
167 $contLangVariant = $services->getLanguageConverterFactory()
168 ->getLanguageConverter( $services->getContentLanguage() )
169 ->getPreferredVariant();
170 $titleMsg->inLanguage( $contLangVariant );
171 }
172 $titleMsg->setInterfaceMessageFlag( true );
173 $blankedHeading = $titleMsg->isBlank();
174 if ( !$titleMsg->isDisabled() ) {
175 $htmlTitle = $titleMsg->parse();
176 } else {
177 $htmlTitle = $out->getPageTitle();
178 }
179 } else {
180 $htmlTitle = $out->getPageTitle();
181 }
182
183 $data = [
184 // raw HTML
185 'html-title-heading' => Html::rawElement(
186 'h1',
187 [
188 'id' => 'firstHeading',
189 'class' => 'firstHeading mw-first-heading',
190 'style' => $blankedHeading ? 'display: none' : null
191 ] + $this->getUserLanguageAttributes(),
192 $htmlTitle
193 ),
194 'html-title' => $htmlTitle ?: null,
195 // Boolean values
196 'is-title-blank' => $blankedHeading, // @since 1.38
197 'is-anon' => $user->isAnon(),
198 'is-article' => $out->isArticle(),
199 'is-mainpage' => $isMainPage,
200 'is-specialpage' => $title->isSpecialPage(),
201 'canonical-url' => $this->getCanonicalUrl(),
202 ];
203
204 $components = $this->componentRegistry->getComponents();
205 foreach ( $components as $componentName => $component ) {
206 $data['data-' . $componentName] = $component->getTemplateData();
207 }
208 return $data;
209 }
210
220 public static function normalizeKey( string $key ) {
221 $config = MediaWikiServices::getInstance()->getMainConfig();
222 $defaultSkin = $config->get( MainConfigNames::DefaultSkin );
223 $fallbackSkin = $config->get( MainConfigNames::FallbackSkin );
224 $skinFactory = MediaWikiServices::getInstance()->getSkinFactory();
225 $skinNames = $skinFactory->getInstalledSkins();
226
227 // Make keys lowercase for case-insensitive matching.
228 $skinNames = array_change_key_case( $skinNames, CASE_LOWER );
229 $key = strtolower( $key );
230 $defaultSkin = strtolower( $defaultSkin );
231 $fallbackSkin = strtolower( $fallbackSkin );
232
233 if ( $key == '' || $key == 'default' ) {
234 // Don't return the default immediately;
235 // in a misconfiguration we need to fall back.
236 $key = $defaultSkin;
237 }
238
239 if ( isset( $skinNames[$key] ) ) {
240 return $key;
241 }
242
243 // Older versions of the software used a numeric setting
244 // in the user preferences.
245 $fallback = [
246 0 => $defaultSkin,
247 2 => 'cologneblue'
248 ];
249
250 if ( isset( $fallback[$key] ) ) {
251 // @phan-suppress-next-line PhanTypeMismatchDimFetch False positive
252 $key = $fallback[$key];
253 }
254
255 if ( isset( $skinNames[$key] ) ) {
256 return $key;
257 } elseif ( isset( $skinNames[$defaultSkin] ) ) {
258 return $defaultSkin;
259 } else {
260 return $fallbackSkin;
261 }
262 }
263
331 public function __construct( $options = null ) {
332 if ( is_string( $options ) ) {
333 $this->skinname = $options;
334 } elseif ( $options ) {
335 $name = $options['name'] ?? null;
336
337 if ( !$name ) {
338 throw new SkinException( 'Skin name must be specified' );
339 }
340
341 // Defaults are set in Skin::getOptions()
342 $this->options = $options;
343 $this->skinname = $name;
344 }
345 $this->defaultLinkOptions = $this->getOptions()['link'];
346 $this->componentRegistry = new SkinComponentRegistry(
348 );
349 }
350
354 public function getSkinName() {
355 return $this->skinname;
356 }
357
367 public function isResponsive() {
368 $isSkinResponsiveCapable = $this->getOptions()['responsive'];
369 $userOptionsLookup = MediaWikiServices::getInstance()->getUserOptionsLookup();
370
371 return $isSkinResponsiveCapable &&
372 $userOptionsLookup->getBoolOption( $this->getUser(), 'skin-responsive' );
373 }
374
379 public function initPage( OutputPage $out ) {
380 $skinMetaTags = $this->getConfig()->get( MainConfigNames::SkinMetaTags );
381 $siteName = $this->getConfig()->get( MainConfigNames::Sitename );
382 $this->preloadExistence();
383
384 if ( $this->isResponsive() ) {
385 $out->addMeta(
386 'viewport',
387 'width=device-width, initial-scale=1.0, ' .
388 'user-scalable=yes, minimum-scale=0.25, maximum-scale=5.0'
389 );
390 } else {
391 // Force the desktop experience on an iPad by resizing the mobile viewport to
392 // the value of @min-width-breakpoint-desktop (1120px).
393 // This is as @min-width-breakpoint-desktop-wide usually tends to optimize
394 // for larger screens with max-widths and margins.
395 // The initial-scale SHOULD NOT be set here as defining it will impact zoom
396 // on mobile devices.
397 $out->addMeta(
398 'viewport',
399 'width=1120'
400 );
401 }
402
403 $tags = [
404 'og:site_name' => $siteName,
405 'og:title' => $out->getHTMLTitle(),
406 'twitter:card' => 'summary_large_image',
407 'og:type' => 'website',
408 ];
409
410 // Support sharing on platforms such as Facebook and Twitter
411 foreach ( $tags as $key => $value ) {
412 if ( in_array( $key, $skinMetaTags ) ) {
413 $out->addMeta( $key, $value );
414 }
415 }
416 }
417
429 public function getDefaultModules() {
430 $out = $this->getOutput();
431
432 $options = $this->getOptions();
433 // Modules declared in the $modules literal are loaded
434 // for ALL users, on ALL pages, in ALL skins.
435 // Keep this list as small as possible!
436 $modules = [
437 // The 'styles' key sets render-blocking style modules
438 // Unlike other keys in $modules, this is an associative array
439 // where each key is its own group pointing to a list of modules
440 'styles' => [
441 'skin' => $options['styles'],
442 'core' => [],
443 'content' => [],
444 'syndicate' => [],
445 'user' => []
446 ],
447 'core' => [
448 'site',
449 'mediawiki.page.ready',
450 ],
451 // modules that enhance the content in some way
452 'content' => [],
453 // modules relating to search functionality
454 'search' => [],
455 // Skins can register their own scripts
456 'skin' => $options['scripts'],
457 // modules relating to functionality relating to watching an article
458 'watch' => [],
459 // modules which relate to the current users preferences
460 'user' => [],
461 // modules relating to RSS/Atom Feeds
462 'syndicate' => [],
463 ];
464
465 $bodyHtml = $out->getHTML();
466 // Preload jquery.tablesorter for mediawiki.page.ready
467 if ( str_contains( $bodyHtml, 'sortable' ) ) {
468 $modules['content'][] = 'jquery.tablesorter';
469 $modules['styles']['content'][] = 'jquery.tablesorter.styles';
470 }
471
472 // Preload jquery.makeCollapsible for mediawiki.page.ready
473 if ( str_contains( $bodyHtml, 'mw-collapsible' ) ) {
474 $modules['content'][] = 'jquery.makeCollapsible';
475 $modules['styles']['content'][] = 'jquery.makeCollapsible.styles';
476 }
477
478 // Load relevant styles on wiki pages that use mw-ui-button.
479 // Since 1.26, this no longer loads unconditionally. Special pages
480 // and extensions should load this via addModuleStyles() instead.
481 if ( str_contains( $bodyHtml, 'mw-ui-button' ) ) {
482 $modules['styles']['content'][] = 'mediawiki.ui.button';
483 }
484 // Since 1.41, styling for mw-message-box is only required for
485 // messages that appear in article content.
486 // This should only be removed when a suitable alternative exists
487 // e.g. https://phabricator.wikimedia.org/T363607 is resolved.
488 if ( str_contains( $bodyHtml, 'mw-message-box' ) ) {
489 $modules['styles']['content'][] = 'mediawiki.legacy.messageBox';
490 }
491
492 // Many templates mirror the old HTML syntax of thumbnails and expect similar
493 // styling. Once those templates have a migration path we can deprecate this
494 // module and remove it from the page similar to the above.
495 // More information at https://phabricator.wikimedia.org/T318433',
496 if ( str_contains( $bodyHtml, 'floatright' ) ||
497 str_contains( $bodyHtml, 'floatleft' ) || str_contains( $bodyHtml, 'thumbinner' ) ) {
498 $modules['styles']['content'][] = 'mediawiki.skins.legacy';
499 }
500 // Since 1.46, links to temporary accounts in page content are expected to be styled.
501 if ( str_contains( $bodyHtml, 'mw-tempuserlink' ) ) {
502 $modules['styles']['content'][] = 'mediawiki.interface.helpers.styles';
503 $modules['styles']['content'][] = 'mediawiki.interface.helpers.linker.styles';
504 }
505
506 $title = $this->getTitle();
507 $namespace = $title ? $title->getNamespace() : 0;
508 // If the page is using Codex message box markup load Codex styles.
509 // Since 1.41. Skins can unset this if they prefer to handle this via other
510 // means.
511 // For content, this should not be considered stable, and will likely
512 // be removed when https://phabricator.wikimedia.org/T363607 is resolved.
513 $containsUserGeneratedContent = str_contains( $bodyHtml, 'mw-parser-output' );
514 $containsCodexMessageBox = str_contains( $bodyHtml, 'cdx-message' );
515 if ( $containsCodexMessageBox && $containsUserGeneratedContent && $namespace !== NS_SPECIAL ) {
516 $modules['styles']['content'][] = 'mediawiki.codex.messagebox.styles';
517 }
518
519 if ( $out->isTOCEnabled() ) {
520 $modules['content'][] = 'mediawiki.toc';
521 }
522
523 $authority = $this->getAuthority();
524 $relevantTitle = $this->getRelevantTitle();
525 if ( $authority->isRegistered()
526 && $authority->isAllowedAll( 'viewmywatchlist', 'editmywatchlist' )
527 && $relevantTitle && $relevantTitle->canExist()
528 ) {
529 $modules['watch'][] = 'mediawiki.page.watch.ajax';
530 }
531
532 $userOptionsLookup = MediaWikiServices::getInstance()->getUserOptionsLookup();
533 $userIdentity = $authority->getUser();
534 if ( $userOptionsLookup->getBoolOption( $userIdentity, 'editsectiononrightclick' )
535 || ( $out->isArticle() && $userOptionsLookup->getOption( $userIdentity, 'editondblclick' ) )
536 ) {
537 $modules['user'][] = 'mediawiki.misc-authed-pref';
538 }
539
540 if ( $out->isSyndicated() ) {
541 $modules['styles']['syndicate'][] = 'mediawiki.feedlink';
542 }
543
544 if ( $authority->isTemp() ) {
545 $modules['user'][] = 'mediawiki.tempUserBanner';
546 $modules['styles']['user'][] = 'mediawiki.tempUserBanner.styles';
547 }
548
549 if ( $namespace === NS_FILE ) {
550 $modules['styles']['core'][] = 'filepage'; // local Filepage.css, T31277, T356505
551 }
552
553 return $modules;
554 }
555
559 private function preloadExistence() {
560 $titles = [];
561
562 // User/talk link
563 $user = $this->getUser();
564 if ( $user->isRegistered() ) {
565 $titles[] = $user->getUserPage();
566 $titles[] = $user->getTalkPage();
567 }
568
569 // Check, if the page can hold some kind of content, otherwise do nothing
570 $title = $this->getRelevantTitle();
571 if ( $title && $title->canExist() && $title->canHaveTalkPage() ) {
572 $namespaceInfo = MediaWikiServices::getInstance()->getNamespaceInfo();
573 if ( $title->isTalkPage() ) {
574 $titles[] = $namespaceInfo->getSubjectPage( $title );
575 } else {
576 $titles[] = $namespaceInfo->getTalkPage( $title );
577 }
578 }
579
580 // Preload for self::getCategoryLinks
581 $allCats = $this->getOutput()->getCategoryLinks();
582 if ( isset( $allCats['normal'] ) && $allCats['normal'] !== [] ) {
583 $catLink = Title::newFromText( $this->msg( 'pagecategorieslink' )->inContentLanguage()->text() );
584 if ( $catLink ) {
585 // If this is a special page, the LinkBatch would skip it
586 $titles[] = $catLink;
587 }
588 }
589
590 $this->getHookRunner()->onSkinPreloadExistence( $titles, $this );
591
592 if ( $titles ) {
593 $linkBatchFactory = MediaWikiServices::getInstance()->getLinkBatchFactory();
594 $linkBatchFactory->newLinkBatch( $titles )
595 ->setCaller( __METHOD__ )
596 ->execute();
597 }
598 }
599
604 public function setRelevantTitle( $t ) {
605 $this->mRelevantTitle = $t;
606 }
607
619 public function getRelevantTitle() {
620 return $this->mRelevantTitle ?? $this->getTitle();
621 }
622
627 public function setRelevantUser( ?UserIdentity $u ) {
628 $this->mRelevantUser = $u;
629 }
630
640 public function getRelevantUser(): ?UserIdentity {
641 if ( $this->mRelevantUser === false ) {
642 $this->mRelevantUser = null; // false indicates we never attempted to load it.
643 $title = $this->getRelevantTitle();
644 if ( $title->hasSubjectNamespace( NS_USER ) ) {
645 $services = MediaWikiServices::getInstance();
646 $rootUser = $title->getRootText();
647 $userNameUtils = $services->getUserNameUtils();
648 if ( $userNameUtils->isIP( $rootUser ) ) {
649 $this->mRelevantUser = UserIdentityValue::newAnonymous( $rootUser );
650 } else {
651 $user = $services->getUserIdentityLookup()->getUserIdentityByName( $rootUser );
652 $this->mRelevantUser = $user && $user->isRegistered() ? $user : null;
653 }
654 }
655 }
656
657 // The relevant user should only be set if it exists. However, if it exists but is hidden,
658 // and the viewer cannot see hidden users, this exposes the fact that the user exists;
659 // pretend like the user does not exist in such cases, by setting it to null. T120883
660 if ( $this->mRelevantUser && $this->mRelevantUser->isRegistered() ) {
661 $userBlock = MediaWikiServices::getInstance()
662 ->getBlockManager()
663 ->getBlock( $this->mRelevantUser, null );
664 if ( $userBlock && $userBlock->getHideName() &&
665 !$this->getAuthority()->isAllowed( 'hideuser' )
666 ) {
667 $this->mRelevantUser = null;
668 }
669 }
670
671 return $this->mRelevantUser;
672 }
673
678 final public function outputPageFinal( OutputPage $out ) {
679 // generate body
680 ob_start();
681 $this->outputPage();
682 $html = ob_get_contents();
683 ob_end_clean();
684
685 // T259955: OutputPage::headElement must be called last
686 // as it calls OutputPage::getRlClient, which freezes the ResourceLoader
687 // modules queue for the current page load.
688 // Since Skins can add ResourceLoader modules via OutputPage::addModule
689 // and OutputPage::addModuleStyles changing this order can lead to
690 // bugs.
691 $head = $out->headElement( $this );
692 $tail = $out->tailElement( $this );
693
694 echo $head . $html . $tail;
695 }
696
700 abstract public function outputPage();
701
707 public function getPageClasses( $title ) {
708 $services = MediaWikiServices::getInstance();
709 $ns = $title->getNamespace();
710 $numeric = 'ns-' . $ns;
711
712 if ( $title->isSpecialPage() ) {
713 $type = 'ns-special';
714 // T25315: provide a class based on the canonical special page name without subpages
715 [ $canonicalName ] = $services->getSpecialPageFactory()->resolveAlias( $title->getDBkey() );
716 if ( $canonicalName ) {
717 $type .= ' ' . Sanitizer::escapeClass( "mw-special-$canonicalName" );
718 } else {
719 $type .= ' mw-invalidspecialpage';
720 }
721 } else {
722 if ( $title->isTalkPage() ) {
723 $type = 'ns-talk';
724 } else {
725 $type = 'ns-subject';
726 }
727 // T208315: add HTML class when the user can edit the page
728 if ( $this->getAuthority()->probablyCan( 'edit', $title ) ) {
729 $type .= ' mw-editable';
730 }
731 }
732
733 $titleFormatter = $services->getTitleFormatter();
734 $name = Sanitizer::escapeClass( 'page-' . $titleFormatter->getPrefixedText( $title ) );
735 $root = Sanitizer::escapeClass( 'rootpage-' . $titleFormatter->formatTitle( $ns, $title->getRootText() ) );
736 // Add a static class that is not subject to translation to allow extensions/skins/global code to target main
737 // pages reliably (T363281)
738 if ( $title->isMainPage() ) {
739 $name .= ' page-Main_Page';
740 }
741
742 return "$numeric $type $name $root";
743 }
744
749 public function getHtmlElementAttributes() {
750 $lang = $this->getLanguage();
751 return [
752 'lang' => $lang->getHtmlCode(),
753 'dir' => $lang->getDir(),
754 'class' => 'client-nojs',
755 ];
756 }
757
761 public function getCategoryLinks() {
762 $out = $this->getOutput();
763 $allCats = $out->getCategoryLinks();
764 $title = $this->getTitle();
765 $services = MediaWikiServices::getInstance();
766 $linkRenderer = $services->getLinkRenderer();
767
768 if ( $allCats === [] ) {
769 return '';
770 }
771
772 $embed = "<li>";
773 $pop = "</li>";
774
775 $s = '';
776 $colon = $this->msg( 'colon-separator' )->escaped();
777
778 if ( !empty( $allCats['normal'] ) ) {
779 $t = $embed . implode( $pop . $embed, $allCats['normal'] ) . $pop;
780
781 $msg = $this->msg( 'pagecategories' )->numParams( count( $allCats['normal'] ) );
782 $linkPage = $this->msg( 'pagecategorieslink' )->inContentLanguage()->text();
783 $pageCategoriesLinkTitle = Title::newFromText( $linkPage );
784 if ( $pageCategoriesLinkTitle ) {
785 $link = $linkRenderer->makeLink( $pageCategoriesLinkTitle, $msg->text() );
786 } else {
787 $link = $msg->escaped();
788 }
789 $s .= Html::rawElement(
790 'div',
791 [ 'id' => 'mw-normal-catlinks', 'class' => 'mw-normal-catlinks' ],
792 $link . $colon . Html::rawElement( 'ul', [], $t )
793 );
794 }
795
796 # Hidden categories
797 if ( isset( $allCats['hidden'] ) ) {
798 $userOptionsLookup = $services->getUserOptionsLookup();
799
800 if ( $userOptionsLookup->getBoolOption( $this->getUser(), 'showhiddencats' ) ) {
801 $class = ' mw-hidden-cats-user-shown';
802 } elseif ( $title->inNamespace( NS_CATEGORY ) ) {
803 $class = ' mw-hidden-cats-ns-shown';
804 } else {
805 $class = ' mw-hidden-cats-hidden';
806 }
807
808 $s .= Html::rawElement(
809 'div',
810 [ 'id' => 'mw-hidden-catlinks', 'class' => "mw-hidden-catlinks$class" ],
811 $this->msg( 'hidden-categories' )->numParams( count( $allCats['hidden'] ) )->escaped() .
812 $colon .
813 Html::rawElement(
814 'ul',
815 [],
816 $embed . implode( $pop . $embed, $allCats['hidden'] ) . $pop
817 )
818 );
819 }
820
821 return $s;
822 }
823
827 public function getCategories() {
828 $userOptionsLookup = MediaWikiServices::getInstance()->getUserOptionsLookup();
829 $showHiddenCats = $userOptionsLookup->getBoolOption( $this->getUser(), 'showhiddencats' );
830
831 $catlinks = $this->getCategoryLinks();
832 // Check what we're showing
833 $allCats = $this->getOutput()->getCategoryLinks();
834 $showHidden = $showHiddenCats || $this->getTitle()->inNamespace( NS_CATEGORY );
835
836 $classes = [ 'catlinks' ];
837 if ( empty( $allCats['normal'] ) && !( !empty( $allCats['hidden'] ) && $showHidden ) ) {
838 $classes[] = 'catlinks-allhidden';
839 }
840
841 return Html::rawElement(
842 'div',
843 [ 'id' => 'catlinks', 'class' => $classes,
844 'data-mw-interface' => '' ],
845 $catlinks
846 );
847 }
848
863 protected function afterContentHook() {
864 $data = '';
865
866 if ( $this->getHookRunner()->onSkinAfterContent( $data, $this ) ) {
867 // adding just some spaces shouldn't toggle the output
868 // of the whole <div/>, so we use trim() here
869 if ( trim( $data ) != '' ) {
870 // Doing this here instead of in the skins to
871 // ensure that the div has the same ID in all
872 // skins
873 $data = "<div id='mw-data-after-content'>\n" .
874 "\t$data\n" .
875 "</div>\n";
876 }
877 } else {
878 wfDebug( "Hook SkinAfterContent changed output processing." );
879 }
880
881 return $data;
882 }
883
889 private function getCanonicalUrl() {
890 $title = $this->getTitle();
891 $oldid = $this->getOutput()->getRevisionId();
892 if ( $oldid ) {
893 return $title->getCanonicalURL( 'oldid=' . $oldid );
894 } else {
895 // oldid not available for non existing pages
896 return $title->getCanonicalURL();
897 }
898 }
899
907 public function printSource() {
908 $urlUtils = MediaWikiServices::getInstance()->getUrlUtils();
909 $url = htmlspecialchars( $urlUtils->expandIRI( $this->getCanonicalUrl() ) ?? '' );
910
911 return $this->msg( 'retrievedfrom' )
912 ->rawParams( '<a dir="ltr" href="' . $url . '">' . $url . '</a>' )
913 ->parse();
914 }
915
919 public function getUndeleteLink() {
920 $action = $this->getRequest()->getRawVal( 'action' ) ?? 'view';
921 $title = $this->getTitle();
922 $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
923
924 if ( ( !$title->exists() || $action == 'history' ) &&
925 $this->getAuthority()->probablyCan( 'deletedhistory', $title )
926 ) {
927 $n = $title->getDeletedEditsCount();
928
929 if ( $n ) {
930 if ( $this->getAuthority()->probablyCan( 'undelete', $title ) ) {
931 $msg = 'thisisdeleted';
932 } else {
933 $msg = 'viewdeleted';
934 }
935
936 $subtitle = $this->msg( $msg )->rawParams(
937 $linkRenderer->makeKnownLink(
938 SpecialPage::getTitleFor( 'Undelete', $title->getPrefixedDBkey() ),
939 $this->msg( 'restorelink' )->numParams( $n )->text() )
940 )->escaped();
941
942 $links = [];
943 // Add link to page logs, unless we're on the history page (which
944 // already has one)
945 if ( $action !== 'history' ) {
946 $links[] = $linkRenderer->makeKnownLink(
947 SpecialPage::getTitleFor( 'Log' ),
948 $this->msg( 'viewpagelogs-lowercase' )->text(),
949 [],
950 [ 'page' => $title->getPrefixedText() ]
951 );
952 }
953
954 // Allow extensions to add more links
955 $this->getHookRunner()->onUndeletePageToolLinks(
956 $this->getContext(), $linkRenderer, $links );
957
958 if ( $links ) {
959 $subtitle .= ''
960 . $this->msg( 'word-separator' )->escaped()
961 . $this->msg( 'parentheses' )
962 ->rawParams( $this->getLanguage()->pipeList( $links ) )
963 ->escaped();
964 }
965
966 return Html::rawElement( 'div', [ 'class' => 'mw-undelete-subtitle' ], $subtitle );
967 }
968 }
969
970 return '';
971 }
972
976 private function subPageSubtitleInternal() {
977 $services = MediaWikiServices::getInstance();
978 $linkRenderer = $services->getLinkRenderer();
979 $out = $this->getOutput();
980 $title = $out->getTitle();
981 $subpages = '';
982
983 if ( !$this->getHookRunner()->onSkinSubPageSubtitle( $subpages, $this, $out ) ) {
984 return $subpages;
985 }
986
987 $hasSubpages = $services->getNamespaceInfo()->hasSubpages( $title->getNamespace() );
988 if ( !$out->isArticle() || !$hasSubpages ) {
989 return $subpages;
990 }
991
992 $ptext = $title->getPrefixedText();
993 if ( str_contains( $ptext, '/' ) ) {
994 $links = explode( '/', $ptext );
995 array_pop( $links );
996 $count = 0;
997 $growingLink = '';
998 $display = '';
999 $lang = $this->getLanguage();
1000
1001 foreach ( $links as $link ) {
1002 $growingLink .= $link;
1003 $display .= $link;
1004 $linkObj = Title::newFromText( $growingLink );
1005
1006 if ( $linkObj && $linkObj->isKnown() ) {
1007 $getlink = $linkRenderer->makeKnownLink( $linkObj, $display );
1008
1009 $count++;
1010
1011 if ( $count > 1 ) {
1012 $subpages .= $this->msg( 'pipe-separator' )->escaped();
1013 } else {
1014 $subpages .= '&lt; ';
1015 }
1016
1017 $subpages .= Html::rawElement( 'bdi', [ 'dir' => $lang->getDir() ], $getlink );
1018 $display = '';
1019 } else {
1020 $display .= '/';
1021 }
1022 $growingLink .= '/';
1023 }
1024 }
1025
1026 return $subpages;
1027 }
1028
1036 private function getFooterTemplateDataItem( string $dataKey, string $name ) {
1037 $footerData = $this->getComponent( 'footer' )->getTemplateData();
1038 $items = $footerData[ $dataKey ]['array-items'] ?? [];
1039 foreach ( $items as $item ) {
1040 if ( $item['name'] === $name ) {
1041 return $item['html'];
1042 }
1043 }
1044 return '';
1045 }
1046
1047 final public function getCopyright(): string {
1048 return $this->getFooterTemplateDataItem( 'data-info', 'copyright' );
1049 }
1050
1055 public function logoText( $align = '' ) {
1056 if ( $align != '' ) {
1057 $a = " style='float: {$align};'";
1058 } else {
1059 $a = '';
1060 }
1061
1062 $mp = $this->msg( 'mainpage' )->escaped();
1063 $url = htmlspecialchars( Title::newMainPage()->getLocalURL() );
1064
1065 $logourl = RL\SkinModule::getAvailableLogos(
1066 $this->getConfig(),
1067 $this->getLanguage()->getCode()
1068 )[ '1x' ];
1069 return "<a href='{$url}'><img{$a} src='{$logourl}' alt='[{$mp}]' /></a>";
1070 }
1071
1081 final public function getFooterIcons() {
1082 return SkinComponentFooter::getFooterIconsData(
1083 $this->getConfig()
1084 );
1085 }
1086
1098 final public function makeFooterIcon( $icon, $withImage = 'withImage' ) {
1099 return SkinComponentFooter::makeFooterIconHTML(
1100 $this->getConfig(), $icon, $withImage
1101 );
1102 }
1103
1111 public function editUrlOptions() {
1112 $options = [ 'action' => 'edit' ];
1113 $out = $this->getOutput();
1114
1115 if ( !$out->isRevisionCurrent() ) {
1116 $options['oldid'] = intval( $out->getRevisionId() );
1117 }
1118
1119 # preserve variant/uselang options from the current URL
1120 $request = $this->getRequest();
1121 $variant = $request->getText( 'variant' );
1122 if ( $variant ) {
1123 $options['variant'] = $variant;
1124 }
1125 $uselang = $request->getVal( 'uselang' );
1126 if ( $uselang ) {
1127 $options['uselang'] = $uselang;
1128 }
1129
1130 return $options;
1131 }
1132
1137 public function showEmailUser( $id ) {
1138 if ( $id instanceof UserIdentity ) {
1139 $targetUser = User::newFromIdentity( $id );
1140 } else {
1141 $targetUser = User::newFromId( $id );
1142 }
1143
1144 # The sending user must have a confirmed email address and the receiving
1145 # user must accept emails from the sender.
1146 $emailUser = MediaWikiServices::getInstance()->getEmailUserFactory()
1147 ->newEmailUser( $this->getUser() );
1148
1149 return $emailUser->canSend()->isOK()
1150 && $emailUser->validateTarget( $targetUser )->isOK();
1151 }
1152
1153 /* these are used extensively in SkinTemplate, but also some other places */
1154
1159 public static function makeMainPageUrl( $urlaction = '' ) {
1160 $title = Title::newMainPage();
1161
1162 return $title->getLinkURL( $urlaction );
1163 }
1164
1171 public static function makeInternalOrExternalUrl( $name ) {
1172 $protocols = MediaWikiServices::getInstance()->getUrlUtils()->validProtocols();
1173
1174 if ( preg_match( '/^(?i:' . $protocols . ')/', $name ) ) {
1175 return $name;
1176 } else {
1177 $title = $name instanceof Title ? $name : Title::newFromText( $name );
1178 return $title ? $title->getLinkURL() : '';
1179 }
1180 }
1181
1188 protected static function makeUrlDetails( $name, $urlaction = '' ) {
1189 $title = $name instanceof Title ? $name : Title::newFromText( $name );
1190 return [
1191 'href' => $title ? $title->getLocalURL( $urlaction ) : '',
1192 'exists' => $title && $title->isKnown(),
1193 ];
1194 }
1195
1202 protected static function makeKnownUrlDetails( $name, $urlaction = '' ) {
1203 $title = $name instanceof Title ? $name : Title::newFromText( $name );
1204 return [
1205 'href' => $title ? $title->getLocalURL( $urlaction ) : '',
1206 'exists' => (bool)$title,
1207 ];
1208 }
1209
1220 public function mapInterwikiToLanguage( $code ) {
1221 wfDeprecated( __METHOD__, '1.47' );
1222 $map = $this->getConfig()->get( MainConfigNames::InterlanguageLinkCodeMap );
1223 return $map[ $code ] ?? $code;
1224 }
1225
1234 public function getLanguages() {
1235 if ( $this->getConfig()->get( MainConfigNames::HideInterlanguageLinks ) ) {
1236 return [];
1237 }
1238 if ( $this->languageLinks === null ) {
1239 $helper = new SkinLanguageHelper(
1240 $this->getTitle(),
1241 $this->getLanguage(),
1242 $this->getContext(),
1243 $this->getOutput(),
1244 $this->getOutput()->getLanguageLinks(),
1245 $this->getConfig()
1246 );
1247 $this->languageLinks = $helper->getData();
1248 }
1249
1250 return $this->languageLinks;
1251 }
1252
1259 protected function buildNavUrls() {
1260 $services = MediaWikiServices::getInstance();
1261 $out = $this->getOutput();
1262 $title = $this->getTitle();
1263 $thispage = $title->getPrefixedDBkey();
1264 $uploadNavigationUrl = $this->getConfig()->get( MainConfigNames::UploadNavigationUrl );
1265
1266 $nav_urls = [];
1267 $nav_urls['mainpage'] = [ 'href' => self::makeMainPageUrl() ];
1268 if ( $uploadNavigationUrl ) {
1269 $nav_urls['upload'] = [ 'href' => $uploadNavigationUrl ];
1270 } elseif ( UploadBase::isEnabled() && UploadBase::isAllowed( $this->getAuthority() ) === true ) {
1271 $nav_urls['upload'] = [ 'href' => SkinComponentUtils::makeSpecialUrl( 'Upload' ) ];
1272 } else {
1273 $nav_urls['upload'] = false;
1274 }
1275
1276 $nav_urls['print'] = false;
1277 $nav_urls['permalink'] = false;
1278 $nav_urls['info'] = false;
1279 $nav_urls['whatlinkshere'] = false;
1280 $nav_urls['recentchangeslinked'] = false;
1281 $nav_urls['contributions'] = false;
1282 $nav_urls['log'] = false;
1283 $nav_urls['blockip'] = false;
1284 $nav_urls['changeblockip'] = false;
1285 $nav_urls['unblockip'] = false;
1286 $nav_urls['mute'] = false;
1287 $nav_urls['emailuser'] = false;
1288 $nav_urls['userrights'] = false;
1289
1290 // A print stylesheet is attached to all pages, but nobody ever
1291 // figures that out. :) Add a link...
1292 if ( !$out->isPrintable() && ( $out->isArticle() || $title->isSpecialPage() ) ) {
1293 $nav_urls['print'] = [
1294 'text' => $this->msg( 'printableversion' )->text(),
1295 'href' => 'javascript:print();'
1296 ];
1297 }
1298
1299 if ( $out->isArticle() ) {
1300 // Also add a "permalink" while we're at it
1301 $revid = $out->getRevisionId();
1302 if ( $revid ) {
1303 $nav_urls['permalink'] = [
1304 'icon' => 'link',
1305 'text' => $this->msg( 'permalink' )->text(),
1306 'href' => $title->getLocalURL( "oldid=$revid" )
1307 ];
1308 }
1309 }
1310
1311 if ( $out->isArticleRelated() ) {
1312 $nav_urls['whatlinkshere'] = [
1313 'href' => SpecialPage::getTitleFor( 'Whatlinkshere', $thispage )->getLocalURL()
1314 ];
1315
1316 $nav_urls['info'] = [
1317 'icon' => 'infoFilled',
1318 'text' => $this->msg( 'pageinfo-toolboxlink' )->text(),
1319 'href' => $title->getLocalURL( "action=info" )
1320 ];
1321
1322 if ( $title->exists() || $title->inNamespace( NS_CATEGORY ) ) {
1323 $nav_urls['recentchangeslinked'] = [
1324 'href' => SpecialPage::getTitleFor( 'Recentchangeslinked', $thispage )->getLocalURL()
1325 ];
1326 }
1327 }
1328
1329 $user = $this->getRelevantUser();
1330 $target = false;
1331 $targetIsIpRange = false;
1332 if ( $user ) {
1333 // This will either be an account or an IP
1334 $target = $user->getName();
1335 } else {
1336 // Support finding the IP range if its the target
1337 $pageTarget = $this->getPageTarget();
1338 if ( $pageTarget ) {
1339 $CIDRLimit = $this->getConfig()
1340 ->get( MainConfigNames::BlockCIDRLimit );
1341 [ $ip, $range ] = explode( '/', $pageTarget, 2 );
1342 if (
1343 ( IPUtils::isIPv4( $ip ) && $range >= $CIDRLimit['IPv4'] ) ||
1344 ( IPUtils::isIPv6( $ip ) && $range >= $CIDRLimit['IPv6'] )
1345 ) {
1346 $target = IPUtils::sanitizeRange( $pageTarget );
1347 $targetIsIpRange = true;
1348 }
1349 }
1350 }
1351 if ( !$target ) {
1352 return $nav_urls;
1353 }
1354
1355 $nav_urls['contributions'] = [
1356 'text' => $this->msg( 'tool-link-contributions', $target )->text(),
1357 'href' => SkinComponentUtils::makeSpecialUrlSubpage( 'Contributions', $target ),
1358 'tooltip-params' => [ $target ],
1359 ];
1360 $nav_urls['log'] = [
1361 'icon' => 'listBullet',
1362 'href' => SkinComponentUtils::makeSpecialUrlSubpage( 'Log', $target )
1363 ];
1364
1365 // Check if the user/ip/ip range is blocked
1366 if ( $this->getAuthority()->isAllowed( 'block' ) ) {
1367 $userBlock = null;
1368 if ( $targetIsIpRange ) {
1369 // getBlock doesn't support ip range lookups so check independently if the target is a range
1370 $userBlock = $services
1371 ->getBlockManager()
1372 ->getIpRangeBlock( $target );
1373 } elseif ( $user ) {
1374 // Check if the user or IP is already blocked
1375 $userBlock = $services
1376 ->getBlockManager()
1377 ->getBlock( $user, null );
1378 }
1379
1380 // If the block exists, only continue if it's not an autoblock. See T384147.
1381 if (
1382 $userBlock &&
1383 $userBlock->getType() !== Block::TYPE_AUTO
1384 ) {
1385 $useCodex = $this->getConfig()->get( MainConfigNames::UseCodexSpecialBlock );
1386 $nav_urls[ $useCodex ? 'block-manage-blocks' : 'changeblockip' ] = [
1387 'icon' => 'block',
1388 'href' => SkinComponentUtils::makeSpecialUrlSubpage( 'Block', $target )
1389 ];
1390 if ( !$useCodex ) {
1391 $nav_urls['unblockip'] = [
1392 'icon' => 'unBlock',
1393 'href' => SkinComponentUtils::makeSpecialUrlSubpage( 'Unblock', $target ),
1394 'text' => $this->msg(
1395 $targetIsIpRange ? 'unblockiprange' : 'unblockip'
1396 )->text(),
1397 ];
1398 }
1399 } else {
1400 $nav_urls['blockip'] = [
1401 'icon' => 'block',
1402 'text' => $this->msg(
1403 $targetIsIpRange ? 'blockiprange' : 'blockip'
1404 )->text(),
1405 'href' => SkinComponentUtils::makeSpecialUrlSubpage( 'Block', $target ),
1406 ];
1407 }
1408 }
1409
1410 if ( $user ) {
1411 if ( $this->showEmailUser( $user ) ) {
1412 $nav_urls['emailuser'] = [
1413 'text' => $this->msg( 'tool-link-emailuser', $target )->text(),
1414 'href' => SkinComponentUtils::makeSpecialUrlSubpage( 'Emailuser', $target ),
1415 'tooltip-params' => [ $target ],
1416 ];
1417 }
1418
1419 if ( $user->isRegistered() ) {
1420 if ( $this->getUser()->isNamed() ) {
1421 $nav_urls['mute'] = [
1422 'text' => $this->msg( 'mute-preferences' )->text(),
1423 'href' => SkinComponentUtils::makeSpecialUrlSubpage( 'Mute', $target )
1424 ];
1425 }
1426
1427 // Don't show links to Special:UserRights for temporary accounts (as they cannot have groups)
1428 $userNameUtils = $services->getUserNameUtils();
1429 $userGroupsAssignmentService = $services->getUserGroupAssignmentService();
1430 if ( !$userNameUtils->isTemp( $user->getName() ) ) {
1431 $canChange = $userGroupsAssignmentService->userCanChangeRights(
1432 $this->getAuthority(),
1433 $user
1434 );
1435 $delimiter = $this->getConfig()->get(
1436 MainConfigNames::UserrightsInterwikiDelimiter );
1437 if ( str_contains( $target, $delimiter ) ) {
1438 // Username contains interwiki delimiter, link it via the
1439 // #{userid} syntax. (T260222)
1440 $linkArgs = [ false, [ 'user' => '#' . $user->getId() ] ];
1441 } else {
1442 $linkArgs = [ $target ];
1443 }
1444 $nav_urls['userrights'] = [
1445 'icon' => 'userGroup',
1446 'text' => $this->msg(
1447 $canChange ? 'tool-link-userrights' : 'tool-link-userrights-readonly',
1448 $target
1449 )->text(),
1450 'href' => SkinComponentUtils::makeSpecialUrlSubpage( 'Userrights', ...$linkArgs )
1451 ];
1452 }
1453 }
1454 }
1455
1456 return $nav_urls;
1457 }
1458
1464 final protected function buildFeedUrls() {
1465 $feeds = [];
1466 $out = $this->getOutput();
1467 if ( $out->isSyndicated() ) {
1468 foreach ( $out->getSyndicationLinks() as $format => $link ) {
1469 $feeds[$format] = [
1470 // Messages: feed-atom, feed-rss
1471 'text' => $this->msg( "feed-$format" )->text(),
1472 'href' => $link
1473 ];
1474 }
1475 }
1476 return $feeds;
1477 }
1478
1504 public function buildSidebar() {
1505 if ( $this->sidebar === null ) {
1506 $services = MediaWikiServices::getInstance();
1507 $callback = function ( $old = null, &$ttl = null ) {
1508 $bar = [];
1509 $this->addToSidebar( $bar, 'sidebar' );
1510
1511 // This hook may vary its behaviour by skin.
1512 $this->getHookRunner()->onSkinBuildSidebar( $this, $bar );
1513 $msgCache = MediaWikiServices::getInstance()->getMessageCache();
1514 if ( $msgCache->isDisabled() ) {
1515 // Don't cache the fallback if DB query failed. T133069
1516 $ttl = WANObjectCache::TTL_UNCACHEABLE;
1517 }
1518
1519 return $bar;
1520 };
1521
1522 $msgCache = $services->getMessageCache();
1523 $wanCache = $services->getMainWANObjectCache();
1524 $config = $this->getConfig();
1525 $languageCode = $this->getLanguage()->getCode();
1526
1527 $sidebar = $config->get( MainConfigNames::EnableSidebarCache )
1528 ? $wanCache->getWithSetCallback(
1529 $wanCache->makeKey( 'sidebar', $languageCode, $this->getSkinName() ?? '' ),
1530 $config->get( MainConfigNames::SidebarCacheExpiry ),
1531 $callback,
1532 [
1533 'checkKeys' => [
1534 // Unless there is both no exact $code override nor an i18n definition
1535 // in the software, the only MediaWiki page to check is for $code.
1536 $msgCache->getCheckKey( $languageCode )
1537 ],
1538 'lockTSE' => 30
1539 ]
1540 )
1541 : $callback();
1542
1543 $sidebar['TOOLBOX'] = array_merge(
1544 $this->makeToolbox(
1545 $this->buildNavUrls(),
1546 $this->buildFeedUrls()
1547 ), $sidebar['TOOLBOX'] ?? []
1548 );
1549
1550 $sidebar['LANGUAGES'] = $this->getLanguages();
1551 // Apply post-processing to the cached value
1552 $this->getHookRunner()->onSidebarBeforeOutput( $this, $sidebar );
1553
1554 $this->sidebar = $sidebar;
1555 }
1556
1557 return $this->sidebar;
1558 }
1559
1569 public function addToSidebar( &$bar, $message ) {
1570 $this->addToSidebarPlain( $bar, $this->msg( $message )->inContentLanguage()->plain() );
1571 }
1572
1579 private function createSidebarItem( $target, $text ) {
1580 $config = $this->getConfig();
1581 $messageTitle = $config->get( MainConfigNames::EnableSidebarCache )
1582 ? Title::newMainPage() : $this->getTitle();
1583 $services = MediaWikiServices::getInstance();
1584 $urlUtils = $services->getUrlUtils();
1585
1586 $extraAttribs = [];
1587
1588 $msgLink = $this->msg( $target )->page( $messageTitle )->inContentLanguage();
1589 if ( $msgLink->exists() ) {
1590 $link = $msgLink->text();
1591 // Extra check in case a message does fancy stuff with {{#if:… and such
1592 if ( $link === '-' ) {
1593 return null;
1594 }
1595 } else {
1596 $link = $target;
1597 }
1598 $msgText = $this->msg( $text )->page( $messageTitle );
1599 if ( $msgText->exists() ) {
1600 $parsedText = $msgText->text();
1601 } else {
1602 $parsedText = $text;
1603 }
1604
1605 if ( preg_match( '/^(?i:' . $urlUtils->validProtocols() . ')/', $link ) ) {
1606 $href = $link;
1607
1608 // Parser::getExternalLinkAttribs won't work here because of the Namespace things
1609 if ( $config->get( MainConfigNames::NoFollowLinks ) &&
1610 !$urlUtils->matchesDomainList(
1611 (string)$href,
1612 (array)$config->get( MainConfigNames::NoFollowDomainExceptions )
1613 )
1614 ) {
1615 $extraAttribs['rel'] = 'nofollow';
1616 }
1617
1618 if ( $config->get( MainConfigNames::ExternalLinkTarget ) ) {
1619 $extraAttribs['target'] =
1620 $config->get( MainConfigNames::ExternalLinkTarget );
1621 }
1622 } else {
1623 $title = Title::newFromText( $link );
1624 $href = $title ? $title->fixSpecialName()->getLinkURL() : '';
1625 }
1626
1627 $id = strtr( $text, ' ', '-' );
1628 return $extraAttribs + [
1629 'text' => $parsedText,
1630 'href' => $href,
1631 'icon' => $this->getSidebarIcon( $id ),
1632 'id' => Sanitizer::escapeIdForAttribute( 'n-' . $id ),
1633 'active' => false,
1634 ];
1635 }
1636
1644 public function addToSidebarPlain( &$bar, $text ) {
1645 $lines = explode( "\n", $text );
1646
1647 $heading = '';
1648 $config = $this->getConfig();
1649 $messageTitle = $config->get( MainConfigNames::EnableSidebarCache )
1650 ? Title::newMainPage() : $this->getTitle();
1651 $services = MediaWikiServices::getInstance();
1652 $messageParser = $services->getMessageParser();
1653 $urlUtils = $services->getUrlUtils();
1654
1655 foreach ( $lines as $line ) {
1656 if ( !str_starts_with( $line, '*' ) ) {
1657 continue;
1658 }
1659 $line = rtrim( $line, "\r" ); // for Windows compat
1660
1661 if ( !str_starts_with( $line, '**' ) ) {
1662 $heading = trim( $line, '* ' );
1663 if ( !array_key_exists( $heading, $bar ) ) {
1664 $bar[$heading] = [];
1665 }
1666 } else {
1667 $line = trim( $line, '* ' );
1668
1669 if ( str_contains( $line, '|' ) ) {
1670 $line = $messageParser->transform( $line, false, null, $messageTitle );
1671 $line = array_map( 'trim', explode( '|', $line, 2 ) );
1672 if ( count( $line ) !== 2 ) {
1673 // Second check, could be hit by people doing
1674 // funky stuff with parserfuncs... (T35321)
1675 continue;
1676 }
1677
1678 $item = $this->createSidebarItem( $line[0], $line[1] );
1679 if ( $item !== null ) {
1680 $bar[$heading][] = $item;
1681 }
1682 }
1683 }
1684 }
1685
1686 return $bar;
1687 }
1688
1693 private function getSidebarIcon( string $id ) {
1694 switch ( $id ) {
1695 case 'mainpage-description':
1696 return 'home';
1697 case 'randompage':
1698 return 'die';
1699 case 'recentchanges':
1700 return 'recentChanges';
1701 // These menu items are commonly added in MediaWiki:Sidebar. We should
1702 // reconsider the location of this logic in future.
1703 case 'help':
1704 case 'help-mediawiki':
1705 return 'help';
1706 case 'specialpages':
1707 return 'specialPages';
1708 default:
1709 return null;
1710 }
1711 }
1712
1729 private function hideNewTalkMessagesForCurrentSession() {
1730 // Only show new talk page notification if there is a session,
1731 // (the client edited a page from this browser, or is logged-in).
1732 return !$this->getRequest()->getSession()->isPersistent();
1733 }
1734
1740 public function getNewtalks() {
1741 if ( $this->hideNewTalkMessagesForCurrentSession() ) {
1742 return '';
1743 }
1744
1745 $newMessagesAlert = '';
1746 $user = $this->getUser();
1747 $services = MediaWikiServices::getInstance();
1748 $linkRenderer = $services->getLinkRenderer();
1749 $userHasNewMessages = $services->getTalkPageNotificationManager()
1750 ->userHasNewMessages( $user );
1751 $timestamp = $services->getTalkPageNotificationManager()
1752 ->getLatestSeenMessageTimestamp( $user );
1753 $newtalks = !$userHasNewMessages ? [] : [
1754 [
1755 // TODO: Deprecate adding wiki and link to array and redesign GetNewMessagesAlert hook
1756 'wiki' => WikiMap::getCurrentWikiId(),
1757 'link' => $user->getTalkPage()->getLocalURL(),
1758 'rev' => $timestamp ? $services->getRevisionLookup()
1759 ->getRevisionByTimestamp( $user->getTalkPage(), $timestamp ) : null
1760 ]
1761 ];
1762 $out = $this->getOutput();
1763
1764 // Allow extensions to disable or modify the new messages alert
1765 if ( !$this->getHookRunner()->onGetNewMessagesAlert(
1766 $newMessagesAlert, $newtalks, $user, $out )
1767 ) {
1768 return '';
1769 }
1770 if ( $newMessagesAlert ) {
1771 return $newMessagesAlert;
1772 }
1773
1774 if ( $newtalks !== [] ) {
1775 $uTalkTitle = $user->getTalkPage();
1776 $lastSeenRev = $newtalks[0]['rev'];
1777 $numAuthors = 0;
1778 if ( $lastSeenRev !== null ) {
1779 $plural = true; // Default if we have a last seen revision: if unknown, use plural
1780 $revStore = $services->getRevisionStore();
1781 $latestRev = $revStore->getRevisionByTitle(
1782 $uTalkTitle,
1783 0,
1784 IDBAccessObject::READ_NORMAL
1785 );
1786 if ( $latestRev !== null ) {
1787 // Singular if only 1 unseen revision, plural if several unseen revisions.
1788 $plural = $latestRev->getParentId() !== $lastSeenRev->getId();
1789 $numAuthors = $revStore->countAuthorsBetween(
1790 $uTalkTitle->getArticleID(),
1791 $lastSeenRev,
1792 $latestRev,
1793 null,
1794 10,
1795 RevisionStore::INCLUDE_NEW
1796 );
1797 }
1798 } else {
1799 // Singular if no revision -> diff link will show latest change only in any case
1800 $plural = false;
1801 }
1802 $plural = $plural ? 999 : 1;
1803 // 999 signifies "more than one revision". We don't know how many, and even if we did,
1804 // the number of revisions or authors is not necessarily the same as the number of
1805 // "messages".
1806 $newMessagesLink = $linkRenderer->makeKnownLink(
1807 $uTalkTitle,
1808 $this->msg( 'new-messages-link-plural', $plural )->text(),
1809 [],
1810 $uTalkTitle->isRedirect() ? [ 'redirect' => 'no' ] : []
1811 );
1812
1813 $newMessagesDiffLink = $linkRenderer->makeKnownLink(
1814 $uTalkTitle,
1815 $this->msg( 'new-messages-diff-link-plural', $plural )->text(),
1816 [],
1817 $lastSeenRev !== null
1818 ? [ 'oldid' => $lastSeenRev->getId(), 'diff' => 'cur' ]
1819 : [ 'diff' => 'cur' ]
1820 );
1821
1822 if ( $numAuthors >= 1 && $numAuthors <= 10 ) {
1823 $newMessagesAlert = $this->msg(
1824 'new-messages-from-users'
1825 )->rawParams(
1826 $newMessagesLink,
1827 $newMessagesDiffLink
1828 )->numParams(
1829 $numAuthors,
1830 $plural
1831 );
1832 } else {
1833 // $numAuthors === 11 signifies "11 or more" ("more than 10")
1834 $newMessagesAlert = $this->msg(
1835 $numAuthors > 10 ? 'new-messages-from-many-users' : 'new-messages'
1836 )->rawParams(
1837 $newMessagesLink,
1838 $newMessagesDiffLink
1839 )->numParams( $plural );
1840 }
1841 $newMessagesAlert = $newMessagesAlert->parse();
1842 }
1843
1844 return $newMessagesAlert;
1845 }
1846
1853 protected function getEmailConfirmationNotice(): string {
1854 $services = MediaWikiServices::getInstance();
1855 $component = new SkinComponentEmailConfirmationBanner(
1856 $services->getEmailConfirmationBannerHandler(),
1857 $this->getContext()
1858 );
1859 return $component->getTemplateData()['html'];
1860 }
1861
1869 private function getCachedNotice( $name ) {
1870 $config = $this->getConfig();
1871
1872 if ( $name === 'default' ) {
1873 // special case
1874 $notice = $config->get( MainConfigNames::SiteNotice );
1875 if ( !$notice ) {
1876 return false;
1877 }
1878 } else {
1879 $msg = $this->msg( $name )->inContentLanguage();
1880 if ( $msg->isBlank() ) {
1881 return '';
1882 } elseif ( $msg->isDisabled() ) {
1883 return false;
1884 }
1885 $notice = $msg->plain();
1886 }
1887
1888 $services = MediaWikiServices::getInstance();
1889 $cache = $services->getMainWANObjectCache();
1890 $parsed = $cache->getWithSetCallback(
1891 // Use the extra hash appender to let eg SSL variants separately cache
1892 // Key is verified with md5 hash of unparsed wikitext
1893 $cache->makeKey(
1894 $name, $config->get( MainConfigNames::RenderHashAppend ), md5( $notice ) ),
1895 // TTL in seconds
1896 600,
1897 function () use ( $notice ) {
1898 return $this->getOutput()->parseAsInterface( $notice );
1899 }
1900 );
1901
1902 $contLang = $services->getContentLanguage();
1903 return Html::rawElement(
1904 'div',
1905 [
1906 'class' => $name,
1907 'lang' => $contLang->getHtmlCode(),
1908 'dir' => $contLang->getDir()
1909 ],
1910 $parsed
1911 );
1912 }
1913
1917 public function getSiteNotice() {
1918 $siteNotice = '';
1919
1920 // Extensions may disable or replace the built-in sitenotice
1921 $applyDefault = $this->getHookRunner()->onSiteNoticeBefore( $siteNotice, $this );
1922 if ( $applyDefault ) {
1923 if ( $this->getUser()->isNamed() ) {
1924 $siteNotice = $this->getCachedNotice( 'sitenotice' );
1925 } else {
1926 $anonNotice = $this->getCachedNotice( 'anonnotice' );
1927 if ( $anonNotice === false ) {
1928 $siteNotice = $this->getCachedNotice( 'sitenotice' );
1929 } else {
1930 $siteNotice = $anonNotice;
1931 }
1932 }
1933 if ( $siteNotice === false ) {
1934 $siteNotice = $this->getCachedNotice( 'default' ) ?: '';
1935 }
1936 if ( $this->canUseWikiPage() ) {
1937 $ns = $this->getWikiPage()->getNamespace();
1938 $nsNotice = $this->getCachedNotice( "namespacenotice-$ns" );
1939 if ( $nsNotice ) {
1940 $siteNotice .= $nsNotice;
1941 }
1942 }
1943 if ( $siteNotice !== '' ) {
1944 $siteNotice = Html::rawElement( 'div', [ 'id' => 'localNotice', 'data-nosnippet' => '' ], $siteNotice );
1945 }
1946 }
1947 $isDisabled = ( !$applyDefault && $siteNotice === '' );
1948 if ( $isDisabled ) {
1949 return '';
1950 }
1951
1952 $this->getHookRunner()->onSiteNoticeAfter( $siteNotice, $this );
1953
1954 // T418336: Inject here instead of under $applyDefault, because the DismissableSiteNotice extension
1955 // wraps the sitenotice. The email confirmation notice should render in the same area as sitenotice,
1956 // but not be dismissable or visually appear as part of it.
1957 $siteNotice = $this->getEmailConfirmationNotice() . $siteNotice;
1958
1959 if ( $this->getOptions()[ 'wrapSiteNotice' ] ) {
1960 $siteNotice = Html::rawElement( 'div', [ 'id' => 'siteNotice' ], $siteNotice );
1961 }
1962 return $siteNotice;
1963 }
1964
1977 public function doEditSectionLink( Title $nt, $section, $sectionTitle, Language $lang ) {
1978 // HTML generated here should probably have userlangattributes
1979 // added to it for LTR text on RTL pages
1980
1981 $attribs = [];
1982 $attribs['title'] = $this->msg( 'editsectionhint' )->plaintextParams( $sectionTitle )
1983 ->inLanguage( $lang )->text();
1984
1985 $links = [
1986 'editsection' => [
1987 'icon' => 'edit',
1988 'text' => $this->msg( 'editsection' )->inLanguage( $lang )->text(),
1989 'targetTitle' => $nt,
1990 'attribs' => $attribs,
1991 'query' => [ 'action' => 'edit', 'section' => $section ]
1992 ]
1993 ];
1994
1995 $this->getHookRunner()->onSkinEditSectionLinks( $this, $nt, $section, $sectionTitle, $links, $lang );
1996
1997 $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
1998 $newLinks = [];
1999 $options = $this->defaultLinkOptions + [
2000 'class-as-property' => true,
2001 ];
2002 $ctx = $this->getContext();
2003 foreach ( $links as $key => $linkDetails ) {
2004 $targetTitle = $linkDetails['targetTitle'];
2005 $attrs = $linkDetails['attribs'];
2006 $query = $linkDetails['query'];
2007 unset( $linkDetails['targetTitle'] );
2008 unset( $linkDetails['query'] );
2009 unset( $linkDetails['attribs'] );
2010 unset( $linkDetails['options' ] );
2011 $component = new SkinComponentLink(
2012 $key, $linkDetails + [
2013 'href' => Title::newFromLinkTarget( $targetTitle )->getLinkURL( $query, false ),
2014 ] + $attrs, $ctx, $options
2015 );
2016 $newLinks[] = $component->getTemplateData();
2017 }
2018 $sectionShare = $this->getConfig()
2019 ->get( MainConfigNames::EnableSectionShare );
2020 if ( $sectionShare ) {
2021 $nt->setFragment( $sectionTitle );
2022 $component = new SkinComponentLink(
2023 'sectionshare', [
2024 'icon' => 'share',
2025 'class' => 'mw-section-share mw-selflink-fragment',
2026 'text' => $this->msg( 'sharesection' )->inLanguage( $lang )->text(),
2027 'targetTitle' => $nt,
2028 'title' => $this->msg( 'sharesectionhint' )->plaintextParams( $sectionTitle )
2029 ->inLanguage( $lang )->text(),
2030 'href' => Title::newFromLinkTarget( $nt )->getLinkURL( [
2031 'wprov' => 'shaw1'
2032 ] ),
2033 ], $ctx, $options
2034 );
2035 $newLinks[] = $component->getTemplateData();
2036 }
2037
2038 return $this->doEditSectionLinksHTML( $newLinks, $lang );
2039 }
2040
2048 protected function doEditSectionLinksHTML( array $links, Language $lang ) {
2049 $result = Html::openElement( 'span', [ 'class' => 'mw-editsection' ] );
2050 // TODO: Remove these elements from the HTML. (T268900)
2051 $result .= Html::rawElement( 'span', [ 'class' => 'mw-editsection-bracket' ], '[' );
2052
2053 $linksHtml = array_column( $links, 'html' );
2054
2055 if ( count( $linksHtml ) === 1 ) {
2056 $result .= $linksHtml[0];
2057 } else {
2058 $result .= implode(
2059 // TODO: Remove these elements from the HTML. (T268900)
2060 Html::element( 'span',
2061 [ 'class' => 'mw-editsection-divider' ],
2062 $this->msg( 'pipe-separator' )->inLanguage( $lang )->text()
2063 ),
2064 $linksHtml
2065 );
2066 }
2067
2068 // TODO: Remove these elements from the HTML. (T268900)
2069 $result .= Html::rawElement( 'span', [ 'class' => 'mw-editsection-bracket' ], ']' );
2070 $result .= Html::closeElement( 'span' );
2071 return $result;
2072 }
2073
2083 public function makeToolbox( $navUrls, $feedUrls ) {
2084 $toolbox = [];
2085 if ( $navUrls['whatlinkshere'] ?? null ) {
2086 $toolbox['whatlinkshere'] = $navUrls['whatlinkshere'];
2087 $toolbox['whatlinkshere']['id'] = 't-whatlinkshere';
2088 $toolbox['whatlinkshere']['icon'] = 'articleRedirect';
2089 }
2090 if ( $navUrls['recentchangeslinked'] ?? null ) {
2091 $toolbox['recentchangeslinked'] = $navUrls['recentchangeslinked'];
2092 $toolbox['recentchangeslinked']['msg'] = 'recentchangeslinked-toolbox';
2093 $toolbox['recentchangeslinked']['id'] = 't-recentchangeslinked';
2094 $toolbox['recentchangeslinked']['rel'] = 'nofollow';
2095 }
2096 if ( $feedUrls ) {
2097 $toolbox['feeds']['id'] = 'feedlinks';
2098 $toolbox['feeds']['links'] = [];
2099 foreach ( $feedUrls as $key => $feed ) {
2100 $toolbox['feeds']['links'][$key] = $feed;
2101 $toolbox['feeds']['links'][$key]['id'] = "feed-$key";
2102 $toolbox['feeds']['links'][$key]['rel'] = 'alternate';
2103 $toolbox['feeds']['links'][$key]['type'] = "application/{$key}+xml";
2104 $toolbox['feeds']['links'][$key]['class'] = 'feedlink';
2105 }
2106 }
2107 foreach ( [ 'contributions', 'log', 'blockip', 'changeblockip', 'unblockip',
2108 'block-manage-blocks', 'emailuser', 'mute', 'userrights', 'upload' ] as $special
2109 ) {
2110 if ( $navUrls[$special] ?? null ) {
2111 $toolbox[$special] = $navUrls[$special];
2112 $toolbox[$special]['id'] = "t-$special";
2113 }
2114 }
2115 if ( $navUrls['print'] ?? null ) {
2116 $toolbox['print'] = $navUrls['print'];
2117 $toolbox['print']['id'] = 't-print';
2118 $toolbox['print']['rel'] = 'alternate';
2119 $toolbox['print']['msg'] = 'printableversion';
2120 }
2121 if ( $navUrls['permalink'] ?? null ) {
2122 $toolbox['permalink'] = $navUrls['permalink'];
2123 $toolbox['permalink']['id'] = 't-permalink';
2124 }
2125 if ( $navUrls['info'] ?? null ) {
2126 $toolbox['info'] = $navUrls['info'];
2127 $toolbox['info']['id'] = 't-info';
2128 }
2129
2130 return $toolbox;
2131 }
2132
2139 protected function getIndicatorsData( array $indicators ): array {
2140 $indicatorData = [];
2141 foreach ( $indicators as $id => $content ) {
2142 $indicatorData[] = [
2143 'id' => Sanitizer::escapeIdForAttribute( "mw-indicator-$id" ),
2144 'class' => 'mw-indicator',
2145 'html' => $content,
2146 ];
2147 }
2148 return $indicatorData;
2149 }
2150
2165 final public function getPersonalToolsForMakeListItem( $urls, $applyClassesToListItems = false ) {
2166 $personal_tools = [];
2167 foreach ( $urls as $key => $plink ) {
2168 # The class on a personal_urls item is meant to go on the <a> instead
2169 # of the <li> so we have to use a single item "links" array instead
2170 # of using most of the personal_url's keys directly.
2171 $ptool = [
2172 'links' => [
2173 [ 'single-id' => "pt-$key" ],
2174 ],
2175 'id' => "pt-$key",
2176 'icon' => $plink[ 'icon' ] ?? null,
2177 ];
2178 if ( $applyClassesToListItems && isset( $plink['class'] ) ) {
2179 $ptool['class'] = $plink['class'];
2180 }
2181 if ( isset( $plink['active'] ) ) {
2182 $ptool['active'] = $plink['active'];
2183 }
2184 // Set class for the link to link-class, when defined.
2185 // This allows newer notifications content navigation to retain their classes
2186 // when merged back into the personal tools.
2187 // Doing this here allows the loop below to overwrite the class if defined directly.
2188 if ( isset( $plink['link-class'] ) ) {
2189 $ptool['links'][0]['class'] = $plink['link-class'];
2190 }
2191 $props = [
2192 'href',
2193 'text',
2194 'dir',
2195 'data',
2196 'exists',
2197 // @todo: Remove data-mw once migration from data-mw to data-mw-interface is complete.
2198 // this should probably go through the deprecation process for 3rd party support.
2199 'data-mw',
2200 'data-mw-interface',
2201 'link-html',
2202 ];
2203 if ( !$applyClassesToListItems ) {
2204 $props[] = 'class';
2205 }
2206 foreach ( $props as $k ) {
2207 if ( isset( $plink[$k] ) ) {
2208 $ptool['links'][0][$k] = $plink[$k];
2209 }
2210 }
2211 $personal_tools[$key] = $ptool;
2212 }
2213 return $personal_tools;
2214 }
2215
2277 final public function makeLink( $key, $item, $linkOptions = [] ) {
2278 $options = $linkOptions + $this->defaultLinkOptions;
2279 $component = new SkinComponentLink(
2280 $key, $item, $this->getContext(), $options
2281 );
2282 return $component->getTemplateData()[ 'html' ];
2283 }
2284
2320 final public function makeListItem( $key, $item, $options = [] ) {
2321 $component = new SkinComponentListItem(
2322 $key, $item, $this->getContext(), $options, $this->defaultLinkOptions
2323 );
2324 return $component->getTemplateData()[ 'html-item' ];
2325 }
2326
2337 public function getAfterPortlet( string $name ): string {
2338 $html = '';
2339
2340 if ( $name === 'user-page' && $this->getUser()->isTemp() ) {
2341 $html .= Html::rawElement( 'div', [ 'class' => 'mw-temp-user-banner-tooltip' ],
2342 Html::rawElement( 'button', [
2343 'id' => 'mw-temp-user-banner-tooltip-button',
2344 'class' => 'mw-temp-user-banner-tooltip-summary cdx-button '
2345 . 'cdx-button--icon-only cdx-button--weight-quiet',
2346 'aria-label' => $this->msg( 'temp-user-banner-tooltip-label' )->text(),
2347 'data-event-name' => 'temp-user-banner.info',
2348 ],
2349 Html::element( 'span', [ 'class' => 'mw-temp-user-banner-tooltip-icon' ] )
2350 )
2351 );
2352 }
2353
2354 $this->getHookRunner()->onSkinAfterPortlet( $this, $name, $html );
2355
2356 return $html;
2357 }
2358
2365 final public function prepareSubtitle( bool $withContainer = true ) {
2366 $out = $this->getOutput();
2367 $subpagestr = $this->subPageSubtitleInternal();
2368 if ( $subpagestr !== '' ) {
2369 $subpagestr = Html::rawElement( 'div', [ 'class' => 'subpages' ], $subpagestr );
2370 }
2371 $html = $subpagestr . $out->getSubtitle();
2372 return $withContainer ? Html::rawElement( 'div', [
2373 'id' => 'mw-content-subtitle',
2374 ] + $this->getUserLanguageAttributes(), $html ) : $html;
2375 }
2376
2384 protected function getJsConfigVars(): array {
2385 return [];
2386 }
2387
2393 final protected function getUserLanguageAttributes() {
2394 $userLang = $this->getLanguage();
2395 $userLangCode = $userLang->getHtmlCode();
2396 $userLangDir = $userLang->getDir();
2397 $contLang = MediaWikiServices::getInstance()->getContentLanguage();
2398 if (
2399 $userLangCode !== $contLang->getHtmlCode() ||
2400 $userLangDir !== $contLang->getDir()
2401 ) {
2402 return [
2403 'lang' => $userLangCode,
2404 'dir' => $userLangDir,
2405 ];
2406 }
2407 return [];
2408 }
2409
2415 final protected function prepareUserLanguageAttributes() {
2416 return Html::expandAttributes(
2417 $this->getUserLanguageAttributes()
2418 );
2419 }
2420
2426 final protected function prepareUndeleteLink() {
2427 $undelete = $this->getUndeleteLink();
2428 return $undelete === '' ? null : '<div class="subpages">' . $undelete . '</div>';
2429 }
2430
2439 protected function wrapHTML( $title, $html ) {
2440 // This wraps the "real" body content (i.e. parser output or special page).
2441 // On page views, elements like categories and contentSub are outside of this.
2442 return Html::rawElement( 'div', [
2443 'id' => 'mw-content-text',
2444 'class' => [
2445 'mw-body-content',
2446 ],
2447 ], $html );
2448 }
2449
2458 final public function getOptions(): array {
2459 return $this->options + [
2460 'styles' => [],
2461 'scripts' => [],
2462 'toc' => true,
2463 'format' => 'html',
2464 'bodyClasses' => [],
2465 'clientPrefEnabled' => false,
2466 'responsive' => false,
2467 'link' => [],
2468 'tempUserBanner' => false,
2469 'wrapSiteNotice' => false,
2470 'menus' => [
2471 // Legacy keys that are enabled by default for backwards compatibility
2472 'associated-pages',
2473 'views',
2474 'actions',
2475 'variants',
2476 'personal',
2477 // Opt-in menus
2478 // * 'associated-pages'
2479 // * 'notifications'
2480 // * 'user-interface-preferences',
2481 // * 'user-page',
2482 // * 'user-menu',
2483 ]
2484 ];
2485 }
2486
2495 public function supportsMenu( string $menu ): bool {
2496 $options = $this->getOptions();
2497 return in_array( $menu, $options['menus'] );
2498 }
2499
2514 public static function getPortletLinkOptions( RL\Context $context ): array {
2515 $skinName = $context->getSkin();
2516 $skinFactory = MediaWikiServices::getInstance()->getSkinFactory();
2517 $options = $skinFactory->getSkinOptions( $skinName );
2518 $portletLinkOptions = $options['link'] ?? [];
2519 // Normalize link options to always have this key
2520 $portletLinkOptions += [ 'text-wrapper' => [] ];
2521 // Normalize text-wrapper to always be an array of arrays
2522 if ( isset( $portletLinkOptions['text-wrapper']['tag'] ) ) {
2523 $portletLinkOptions['text-wrapper'] = [ $portletLinkOptions['text-wrapper'] ];
2524 }
2525 return $portletLinkOptions;
2526 }
2527
2535 final protected function getPortletData( string $name, array $items ): array {
2536 $portletComponent = new SkinComponentMenu(
2537 $name,
2538 $items,
2539 $this->getContext(),
2540 '',
2541 $this->defaultLinkOptions,
2542 $this->getAfterPortlet( $name )
2543 );
2544 return $portletComponent->getTemplateData();
2545 }
2546
2558 public function getPageTarget() {
2559 $target = '';
2560 // Check if the target user is a valid user or IP
2561 $relUser = $this->getRelevantUser();
2562 if ( $relUser ) {
2563 $target = $relUser->getName();
2564 }
2565
2566 // Otherwise, check if the target is an IP range which should also be supported
2567 if ( !$target ) {
2568 // Check for the target parameter first
2569 $pageTarget = trim( $this->getRequest()->getText( 'target' ) );
2570
2571 // If it doesn't exist, check for the subpage next
2572 if ( !$pageTarget ) {
2573 $pageTarget = $this->getTitle()->getFullSubpageText();
2574 }
2575
2576 // If it exists, only set the target if it's an IP range
2577 // as that's the only case not covered by Skin->getRelevantUser()
2578 if (
2579 $pageTarget &&
2580 IPUtils::isValidRange( $pageTarget )
2581 ) {
2582 $target = $pageTarget;
2583 }
2584 }
2585
2586 return $target;
2587 }
2588
2589 public function setParserOptions( ParserOptions $parserOptions ): void {
2590 $this->parserOptions = $parserOptions;
2591 }
2592
2593 public function getParserOptions(): ?ParserOptions {
2594 return $this->parserOptions;
2595 }
2596}
2597
2599class_alias( Skin::class, 'Skin' );
const NS_USER
Definition Defines.php:53
const NS_FILE
Definition Defines.php:57
const NS_SPECIAL
Definition Defines.php:40
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.
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
$fallback
if(!defined('MW_SETUP_CALLBACK'))
Definition WebStart.php:71
The simplest way of implementing IContextSource is to hold a RequestContext as a member variable and ...
This class is a collection of static functions that serve two purposes:
Definition Html.php:44
Base class for language-specific code.
Definition Language.php:65
A class containing constants representing the names of configuration variables.
const DefaultSkin
Name constant for the DefaultSkin setting, for use with Config::get()
const Sitename
Name constant for the Sitename setting, for use with Config::get()
const FallbackSkin
Name constant for the FallbackSkin setting, for use with Config::get()
const SkinMetaTags
Name constant for the SkinMetaTags setting, for use with Config::get()
const ForceUIMsgAsContentMsg
Name constant for the ForceUIMsgAsContentMsg 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.
This is one of the Core classes and should be read at least once by any new developers.
tailElement( $skin)
The final bits that go to the bottom of a page HTML document including the closing tags.
getHTMLTitle()
Return the "HTML title", i.e.
headElement(Skin $sk, $includeStyle=true)
addMeta( $name, $val)
Add a new "<meta>" tag To add an http-equiv meta tag, precede the name with "http:".
Set options of the Parser.
HTML sanitizer for MediaWiki.
Definition Sanitizer.php:34
Service for looking up page revisions.
getTemplateData()
This returns all the data that is needed to the component.Returned array must be serialized....
Helper class for generating interlanguage link data.
Exceptions for skin-related failures.
The base class for all skins.
Definition Skin.php:54
buildNavUrls()
Build array of common navigation links.
Definition Skin.php:1259
getRelevantTitle()
Return the "relevant" title.
Definition Skin.php:619
array $options
Skin options passed into constructor.
Definition Skin.php:70
buildFeedUrls()
Build data structure representing syndication links.
Definition Skin.php:1464
string null $skinname
Definition Skin.php:65
getDefaultModules()
Defines the ResourceLoader modules that should be added to the skin It is recommended that skins wish...
Definition Skin.php:429
static getVersion()
Get the current major version of Skin.
Definition Skin.php:102
logoText( $align='')
Definition Skin.php:1055
getPageClasses( $title)
TODO: document.
Definition Skin.php:707
supportsMenu(string $menu)
Does the skin support the named menu? e.g.
Definition Skin.php:2495
getJsConfigVars()
Returns array of config variables that should be added only to this skin for use in JavaScript.
Definition Skin.php:2384
getComponent(string $name)
Definition Skin.php:111
doEditSectionLink(Title $nt, $section, $sectionTitle, Language $lang)
Create a section edit link.
Definition Skin.php:1977
static makeKnownUrlDetails( $name, $urlaction='')
Make URL details where the article exists (or at least it's convenient to think so)
Definition Skin.php:1202
setParserOptions(ParserOptions $parserOptions)
Definition Skin.php:2589
mapInterwikiToLanguage( $code)
Allows correcting the language of interlanguage links which, mostly due to legacy reasons,...
Definition Skin.php:1220
getAfterPortlet(string $name)
Allows extensions to hook into known portlets and add stuff to them.
Definition Skin.php:2337
static makeInternalOrExternalUrl( $name)
If url string starts with http, consider as external URL, else internal.
Definition Skin.php:1171
prepareUndeleteLink()
Prepare undelete link for output in page.
Definition Skin.php:2426
outputPageFinal(OutputPage $out)
Outputs the HTML for the page.
Definition Skin.php:678
afterContentHook()
This runs a hook to allow extensions placing their stuff after content and article metadata (e....
Definition Skin.php:863
outputPage()
Outputs the HTML generated by other functions.
makeToolbox( $navUrls, $feedUrls)
Create an array of common toolbox items from the data in the quicktemplate stored by SkinTemplate.
Definition Skin.php:2083
static makeMainPageUrl( $urlaction='')
Definition Skin.php:1159
setRelevantTitle( $t)
Definition Skin.php:604
prepareUserLanguageAttributes()
Prepare user language attribute links.
Definition Skin.php:2415
addToSidebar(&$bar, $message)
Add content from a sidebar system message Currently only used for MediaWiki:Sidebar (but may be used ...
Definition Skin.php:1569
static getPortletLinkOptions(RL\Context $context)
Returns skin options for portlet links, used by addPortletLink.
Definition Skin.php:2514
editUrlOptions()
Return URL options for the 'edit page' link.
Definition Skin.php:1111
buildSidebar()
Build an array that represents the sidebar(s), the navigation bar among them.
Definition Skin.php:1504
makeFooterIcon( $icon, $withImage='withImage')
Renders a $wgFooterIcons icon according to the method's arguments.
Definition Skin.php:1098
getOptions()
Get current skin's options.
Definition Skin.php:2458
initPage(OutputPage $out)
Definition Skin.php:379
getPersonalToolsForMakeListItem( $urls, $applyClassesToListItems=false)
Create an array of personal tools items from the data in the quicktemplate stored by SkinTemplate.
Definition Skin.php:2165
static normalizeKey(string $key)
Normalize a skin preference value to a form that can be loaded.
Definition Skin.php:220
wrapHTML( $title, $html)
Wrap the body text with language information and identifiable element.
Definition Skin.php:2439
getUserLanguageAttributes()
Get user language attribute links array.
Definition Skin.php:2393
Title null $mRelevantTitle
Definition Skin.php:72
static makeUrlDetails( $name, $urlaction='')
these return an array with the 'href' and boolean 'exists'
Definition Skin.php:1188
getPortletData(string $name, array $items)
Definition Skin.php:2535
printSource()
Text with the permalink to the source page, usually shown on the footer of a printed page.
Definition Skin.php:907
const VERSION_MAJOR
The current major version of the skin specification.
Definition Skin.php:80
prepareSubtitle(bool $withContainer=true)
Prepare the subtitle of the page for output in the skin if one has been set.
Definition Skin.php:2365
doEditSectionLinksHTML(array $links, Language $lang)
Definition Skin.php:2048
getEmailConfirmationNotice()
Return HTML for an email confirmation notice banner if the current user has a registered but unconfir...
Definition Skin.php:1853
getHtmlElementAttributes()
Return values for <html> element.
Definition Skin.php:749
makeListItem( $key, $item, $options=[])
Generates a list item for a navigation, portlet, portal, sidebar... list.
Definition Skin.php:2320
getIndicatorsData(array $indicators)
Return an array of indicator data.
Definition Skin.php:2139
isResponsive()
Indicates if this skin is responsive.
Definition Skin.php:367
setRelevantUser(?UserIdentity $u)
Definition Skin.php:627
__construct( $options=null)
Definition Skin.php:331
getFooterIcons()
Get template representation of the footer.
Definition Skin.php:1081
getLanguages()
Generates array of language links for the current page.
Definition Skin.php:1234
getNewtalks()
Gets new talk page messages for the current user and returns an appropriate alert message (or an empt...
Definition Skin.php:1740
makeLink( $key, $item, $linkOptions=[])
Makes a link, usually used by makeListItem to generate a link for an item in a list used in navigatio...
Definition Skin.php:2277
getRelevantUser()
Return the "relevant" user.
Definition Skin.php:640
addToSidebarPlain(&$bar, $text)
Add content from plain text.
Definition Skin.php:1644
Parent class for all special pages.
Represents a title within MediaWiki.
Definition Title.php:69
setFragment( $fragment)
Set the fragment for this title.
Definition Title.php:1775
UploadBase and subclasses are the backend of MediaWiki's file uploads.
getBoolOption(UserIdentity $user, string $oname, int $queryFlags=IDBAccessObject::READ_NORMAL)
Get the user's current setting for a given option, as a boolean value.
getOption(UserIdentity $user, string $oname, $defaultOverride=null, bool $ignoreHidden=false, int $queryFlags=IDBAccessObject::READ_NORMAL)
Get the user's current setting for a given option.
Value object representing a user's identity.
User class for the MediaWiki software.
Definition User.php:129
Tools for dealing with other locally-hosted wikis.
Definition WikiMap.php:19
Multi-datacenter aware caching interface.
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, 'PHPSessionHandling'=> 'warn', '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', 'PHPSessionHandling' => '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', ], ], ], ], ], 'PHPSessionHandling' => [ 'deprecated' => 'since 1.45 Integration with PHP session handling will be removed in the future', ], '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.', ],]
Represents a block that may prevent users from performing specific operations.
Definition Block.php:31
Interface for objects representing user identity.
Interface for database access objects.
msg( $key,... $params)