MediaWiki master
PermissionManager.php
Go to the documentation of this file.
1<?php
8
9use InvalidArgumentException;
10use LogicException;
41use StatusValue;
45use Wikimedia\ScopedCallback;
46
54
56 public const RIGOR_QUICK = 'quick';
57
59 public const RIGOR_FULL = 'full';
60
62 public const RIGOR_SECURE = 'secure';
63
67 public const CONSTRUCTOR_OPTIONS = [
80 ];
81
82 private ServiceOptions $options;
83 private SpecialPageFactory $specialPageFactory;
84 private NamespaceInfo $nsInfo;
85 private GroupPermissionsLookup $groupPermissionsLookup;
86 private UserGroupManager $userGroupManager;
87 private BlockManager $blockManager;
88 private BlockErrorFormatter $blockErrorFormatter;
89 private HookRunner $hookRunner;
90 private UserIdentityLookup $userIdentityLookup;
91 private RedirectLookup $redirectLookup;
92 private RestrictionStore $restrictionStore;
93 private TitleFormatter $titleFormatter;
94 private TempUserConfig $tempUserConfig;
95 private UserFactory $userFactory;
96 private ActionFactory $actionFactory;
97
99 private $allRights;
100
102 private $implicitRights;
103
105 private $usersRights = [];
106
111 private $temporaryUserRights = [];
112
114 private $cachedRights = [];
115
122 private const CORE_RIGHTS = [
123 'apihighlimits',
124 'applychangetags',
125 'autoconfirmed',
126 'autocreateaccount',
127 'autopatrol',
128 'bigdelete',
129 'block',
130 'blockemail',
131 'bot',
132 'browsearchive',
133 'changetags',
134 'createaccount',
135 'createwithcontentmodel',
136 'createpage',
137 'createtalk',
138 'delete',
139 'delete-redirect',
140 'deletechangetags',
141 'deletedhistory',
142 'deletedtext',
143 'deletelogentry',
144 'deleterevision',
145 'edit',
146 'editcontentmodel',
147 'editinterface',
148 'editprotected',
149 'editmyoptions',
150 'editmyprivateinfo',
151 'editmyusercss',
152 'editmyuserjson',
153 'editmyuserjs',
154 'editmyuserjsredirect',
155 'editmywatchlist',
156 'editsemiprotected',
157 'editsitecss',
158 'editsitejson',
159 'editsitejs',
160 'editusercss',
161 'edituserjson',
162 'edituserjs',
163 'hideuser',
164 'ignore-restricted-groups',
165 'import',
166 'importupload',
167 'interwiki',
168 'ipblock-exempt',
169 'managechangetags',
170 'markbotedits',
171 'mergehistory',
172 'minoredit',
173 'move',
174 'movefile',
175 'move-categorypages',
176 'move-rootuserpages',
177 'move-subpages',
178 'nominornewtalk',
179 'noratelimit',
180 'override-export-depth',
181 'pagelang',
182 'patrol',
183 'patrolmarks',
184 'protect',
185 'read',
186 'renameuser',
187 'renameuser-global',
188 'reupload',
189 'reupload-own',
190 'reupload-shared',
191 'rollback',
192 'sendemail',
193 'siteadmin',
194 'suppressionlog',
195 'suppressredirect',
196 'suppressrevision',
197 'unblockself',
198 'undelete',
199 'unwatchedpages',
200 'upload',
201 'upload_by_url',
202 'userrights',
203 'userrights-interwiki',
204 'viewmyprivateinfo',
205 'viewmywatchlist',
206 'viewsuppressed',
207 ];
208
215 private const CORE_IMPLICIT_RIGHTS = [
216 'renderfile',
217 'renderfile-nonstandard',
218 'stashedit',
219 'stashbasehtml',
220 'mailpassword',
221 'changeemail',
222 'confirmemail',
223 'linkpurge',
224 'purge',
225 ];
226
227 public function __construct(
228 ServiceOptions $options,
229 SpecialPageFactory $specialPageFactory,
230 NamespaceInfo $nsInfo,
231 GroupPermissionsLookup $groupPermissionsLookup,
232 UserGroupManager $userGroupManager,
233 BlockManager $blockManager,
234 BlockErrorFormatter $blockErrorFormatter,
235 HookContainer $hookContainer,
236 UserIdentityLookup $userIdentityLookup,
237 RedirectLookup $redirectLookup,
238 RestrictionStore $restrictionStore,
239 TitleFormatter $titleFormatter,
240 TempUserConfig $tempUserConfig,
241 UserFactory $userFactory,
242 ActionFactory $actionFactory
243 ) {
244 $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
245 $this->options = $options;
246 $this->specialPageFactory = $specialPageFactory;
247 $this->nsInfo = $nsInfo;
248 $this->groupPermissionsLookup = $groupPermissionsLookup;
249 $this->userGroupManager = $userGroupManager;
250 $this->blockManager = $blockManager;
251 $this->blockErrorFormatter = $blockErrorFormatter;
252 $this->hookRunner = new HookRunner( $hookContainer );
253 $this->userIdentityLookup = $userIdentityLookup;
254 $this->redirectLookup = $redirectLookup;
255 $this->restrictionStore = $restrictionStore;
256 $this->titleFormatter = $titleFormatter;
257 $this->tempUserConfig = $tempUserConfig;
258 $this->userFactory = $userFactory;
259 $this->actionFactory = $actionFactory;
260 }
261
279 public function userCan( $action, User $user, LinkTarget $page, $rigor = self::RIGOR_FULL ): bool {
280 return $this->getPermissionStatus( $action, $user, $page, $rigor, true )->isGood();
281 }
282
298 public function quickUserCan( $action, User $user, LinkTarget $page ): bool {
299 return $this->userCan( $action, $user, $page, self::RIGOR_QUICK );
300 }
301
303 private const BLOCK_CODES = [
304 'blockedtext' => true,
305 'blockedtext-partial' => true,
306 'autoblockedtext' => true,
307 'systemblockedtext' => true,
308 'blockedtext-composite' => true,
309 'blockedtext-tempuser' => true,
310 'autoblockedtext-tempuser' => true,
311 ];
312
338 public function getPermissionErrors(
339 $action,
340 User $user,
341 LinkTarget $page,
342 $rigor = self::RIGOR_SECURE,
343 $ignoreErrors = []
344 ): array {
345 $status = $this->getPermissionStatus( $action, $user, $page, $rigor );
346 $result = [];
347
348 // Produce a result in the weird format used by this function
349 foreach ( $status->getErrors() as [ 'message' => $keyOrMsg, 'params' => $params ] ) {
350 $key = $keyOrMsg instanceof MessageSpecifier ? $keyOrMsg->getKey() : $keyOrMsg;
351 // Remove the errors being ignored.
352 if ( !in_array( $key, $ignoreErrors ) ) {
353 // Remove modern block info that is not expected by users of this legacy API
354 if ( isset( self::BLOCK_CODES[ $key ] ) && $keyOrMsg instanceof MessageSpecifier ) {
355 $params = $keyOrMsg->getParams();
356 $keyOrMsg = $key;
357 }
358 $result[] = [ $keyOrMsg, ...$params ];
359 }
360 }
361 return $result;
362 }
363
379 public function throwPermissionErrors(
380 $action,
381 User $user,
382 LinkTarget $page,
383 $rigor = self::RIGOR_SECURE,
384 $ignoreErrors = []
385 ): void {
386 $status = $this->getPermissionStatus(
387 $action, $user, $page, $rigor );
388 if ( $status->hasMessagesExcept( ...$ignoreErrors ) ) {
389 throw new PermissionsError( $action, $status );
390 }
391 }
392
402 public function isBlockedFrom( User $user, $page, $fromReplica = false ): bool {
403 return (bool)$this->getApplicableBlock(
404 'edit',
405 $user,
406 $fromReplica ? self::RIGOR_FULL : self::RIGOR_SECURE,
407 $page,
408 $user->getRequest()
409 );
410 }
411
430 public function getPermissionStatus(
431 $action,
432 User $user,
433 LinkTarget $page,
434 $rigor = self::RIGOR_SECURE,
435 $short = false
437 if ( !in_array( $rigor, [ self::RIGOR_QUICK, self::RIGOR_FULL, self::RIGOR_SECURE ] ) ) {
438 throw new InvalidArgumentException( "Invalid rigor parameter '$rigor'." );
439 }
440
441 // With RIGOR_QUICK we can assume automatic account creation will
442 // occur. At a higher rigor level, the caller is required to opt
443 // in by either passing in a temp placeholder user or by actually
444 // creating the account.
445 if ( $rigor === self::RIGOR_QUICK
446 && !$user->isRegistered()
447 && $this->tempUserConfig->isAutoCreateAction( $action )
448 ) {
449 $user = $this->userFactory->newTempPlaceholder();
450 }
451
452 # Read has special handling
453 if ( $action === 'read' ) {
454 $checks = [
455 $this->checkPermissionHooks( ... ),
456 $this->checkReadPermissions( ... ),
457 $this->checkUserBlock( ... ), // for wgBlockDisablesLogin
458 ];
459 } elseif ( $action === 'create' ) {
460 # Don't call checkSpecialsAndNSPermissions, checkSiteConfigPermissions
461 # or checkUserConfigPermissions here as it will lead to duplicate
462 # error messages. This is okay to do since anywhere that checks for
463 # create will also check for edit, and those checks are called for edit.
464 $checks = [
465 $this->checkQuickPermissions( ... ),
466 $this->checkPermissionHooks( ... ),
467 $this->checkPageRestrictions( ... ),
468 $this->checkCascadingSourcesRestrictions( ... ),
469 $this->checkActionPermissions( ... ),
470 $this->checkUserBlock( ... ),
471 ];
472 } else {
473 // Exclude checkUserConfigPermissions on actions that cannot change the
474 // content of the configuration pages.
475 $skipUserConfigActions = [
476 // Allow patrolling per T21818
477 'patrol',
478
479 // Allow (un)watch (T373758)
480 'editmywatchlist',
481
482 // Allow admins and oversighters to delete. For user pages we want to avoid the
483 // situation where an unprivileged user can post abusive content on
484 // their subpages and only very highly privileged users could remove it.
485 // See T200176.
486 'delete',
487 'deleterevision',
488 'suppressrevision',
489
490 // Allow admins and oversighters to view deleted content, even if they
491 // cannot restore it. See T202989
492 'deletedhistory',
493 'deletedtext',
494 'viewsuppressed',
495 ];
496
497 $checks = [
498 $this->checkQuickPermissions( ... ),
499 $this->checkPermissionHooks( ... ),
500 $this->checkSpecialsAndNSPermissions( ... ),
501 $this->checkSiteConfigPermissions( ... ),
502 ];
503 if ( !in_array( $action, $skipUserConfigActions, true ) ) {
504 $checks[] = $this->checkUserConfigPermissions( ... );
505 }
506 $checks = [
507 ...$checks,
508 $this->checkPageRestrictions( ... ),
509 $this->checkCascadingSourcesRestrictions( ... ),
510 $this->checkActionPermissions( ... ),
511 $this->checkUserBlock( ... )
512 ];
513 }
514
515 $status = PermissionStatus::newEmpty();
516 foreach ( $checks as $callback ) {
517 $callback( $action, $user, $status, $rigor, $short, $page );
518
519 if ( $short && !$status->isGood() ) {
520 break;
521 }
522 }
523
524 // Clone the status to prevent users of this hook from modifying the original
525 $this->hookRunner->onPermissionStatusAudit( $page, $user, $action, $rigor, clone $status );
526
527 return $status;
528 }
529
543 private function checkPermissionHooks(
544 $action,
545 User $user,
546 PermissionStatus $status,
547 $rigor,
548 $short,
549 LinkTarget $page
550 ): void {
551 // TODO: remove when LinkTarget usage will expand further
552 $title = Title::newFromLinkTarget( $page );
553 // Use getUserPermissionsErrors instead
554 $result = '';
555 if ( !$this->hookRunner->onUserCan( $title, $user, $action, $result ) ) {
556 if ( !$result ) {
557 $status->fatal( 'badaccess-group0' );
558 }
559 return;
560 }
561 // Check getUserPermissionsErrors hook
562 if ( !$this->hookRunner->onGetUserPermissionsErrors( $title, $user, $action, $result ) ) {
563 $this->resultToStatus( $status, $result );
564 }
565 // Check getUserPermissionsErrorsExpensive hook
566 if (
567 $rigor !== self::RIGOR_QUICK
568 && !( $short && !$status->isGood() )
569 && !$this->hookRunner->onGetUserPermissionsErrorsExpensive(
570 $title, $user, $action, $result )
571 ) {
572 $this->resultToStatus( $status, $result );
573 }
574 }
575
582 private function resultToStatus( PermissionStatus $status, $result ): void {
583 if ( is_array( $result ) && count( $result ) && !is_array( $result[0] ) ) {
584 // A single array representing an error
585 $status->fatal( ...$result );
586 } elseif ( is_array( $result ) && count( $result ) && is_array( $result[0] ) ) {
587 // A nested array representing multiple errors
588 foreach ( $result as $result1 ) {
589 $this->resultToStatus( $status, $result1 );
590 }
591 } elseif ( is_string( $result ) && $result !== '' ) {
592 // A string representing a message-id
593 $status->fatal( $result );
594 } elseif ( $result instanceof MessageSpecifier ) {
595 // A message specifier representing an error
596 $status->fatal( $result );
597 } elseif ( $result === false ) {
598 // a generic "We don't want them to do that"
599 $status->fatal( 'badaccess-group0' );
600 }
601 // If we got here, $results is the empty array or empty string, which mean no errors.
602 }
603
617 private function checkReadPermissions(
618 $action,
619 User $user,
620 PermissionStatus $status,
621 $rigor,
622 $short,
623 LinkTarget $page
624 ): void {
625 // TODO: remove when LinkTarget usage will expand further
626 $title = Title::newFromLinkTarget( $page );
627
628 $whiteListRead = $this->options->get( MainConfigNames::WhitelistRead );
629 $allowed = false;
630 if ( $this->isEveryoneAllowed( 'read' ) ) {
631 // Shortcut for public wikis, allows skipping quite a bit of code
632 $allowed = true;
633 } elseif ( $this->userHasRight( $user, 'read' ) ) {
634 // If the user is allowed to read pages, they are allowed to read all pages
635 $allowed = true;
636 } elseif ( $this->isSameSpecialPage( 'Userlogin', $page )
637 || $this->isSameSpecialPage( 'PasswordReset', $page )
638 || $this->isSameSpecialPage( 'Userlogout', $page )
639 ) {
640 // Always grant access to the login page.
641 // Even anons need to be able to log in.
642 $allowed = true;
643 } elseif ( $this->isSameSpecialPage( 'RunJobs', $page ) ) {
644 // relies on HMAC key signature alone
645 $allowed = true;
646 } elseif ( is_array( $whiteListRead ) && count( $whiteListRead ) ) {
647 // Time to check the whitelist
648 // Only do these checks if there's something to check against
649 $name = $title->getPrefixedText();
650 $dbName = $title->getPrefixedDBkey();
651
652 // Check for explicit whitelisting with and without underscores
653 if ( in_array( $name, $whiteListRead, true )
654 || in_array( $dbName, $whiteListRead, true )
655 ) {
656 $allowed = true;
657 } elseif ( $page->getNamespace() === NS_MAIN ) {
658 // Old settings might have the title prefixed with
659 // a colon for main-namespace pages
660 if ( in_array( ':' . $name, $whiteListRead ) ) {
661 $allowed = true;
662 }
663 } elseif ( $title->isSpecialPage() ) {
664 // If it's a special page, ditch the subpage bit and check again
665 $name = $title->getDBkey();
666 [ $name, /* $subpage */ ] =
667 $this->specialPageFactory->resolveAlias( $name );
668 if ( $name ) {
669 $pure = SpecialPage::getTitleFor( $name )->getPrefixedText();
670 if ( in_array( $pure, $whiteListRead, true ) ) {
671 $allowed = true;
672 }
673 }
674 }
675 }
676
677 $whitelistReadRegexp = $this->options->get( MainConfigNames::WhitelistReadRegexp );
678 if ( !$allowed && is_array( $whitelistReadRegexp )
679 && $whitelistReadRegexp
680 ) {
681 $name = $title->getPrefixedText();
682 // Check for regex whitelisting
683 foreach ( $whitelistReadRegexp as $listItem ) {
684 if ( preg_match( $listItem, $name ) ) {
685 $allowed = true;
686 break;
687 }
688 }
689 }
690
691 if ( !$allowed ) {
692 // If the title is not allowed, give extensions a chance to do so
693 $this->hookRunner->onTitleReadWhitelist( $title, $user, $allowed );
694 if ( !$allowed ) {
695 $this->missingPermissionError( $action, $short, $status );
696 }
697 }
698 }
699
707 private function missingPermissionError( string $action, bool $short, PermissionStatus $status ): void {
708 // We avoid expensive display logic for quickUserCan's and such
709 if ( $short ) {
710 $status->fatal( 'badaccess-group0' );
711 return;
712 }
713
714 // TODO: it would be a good idea to replace the method below with something else like
715 // maybe callback injection
716 $context = RequestContext::getMain();
717 $fatalStatus = $this->newFatalPermissionDeniedStatus( $action, $context );
718 $status->merge( $fatalStatus );
719 $statusPermission = $fatalStatus->getPermission();
720 if ( $statusPermission ) {
721 $status->setPermission( $statusPermission );
722 }
723 }
724
735 public function newFatalPermissionDeniedStatus( $permission, IContextSource $context ): StatusValue {
736 $groups = $this->groupPermissionsLookup->getGroupsWithPermission( $permission );
737 if ( !$groups ) {
738 $status = PermissionStatus::newFatal( 'badaccess-group0' );
739 $status->setPermission( $permission );
740 return $status;
741 }
742
743 $groupLinks = array_map(
744 static fn ( $group ) => UserGroupMembership::getLinkWiki( $group, $context ),
745 $groups
746 );
747
748 $userDisabledGroups = $this->userGroupManager->getUserDisabledGroups( $context->getUser() );
749 $groupIntersection = array_intersect( $groups, $userDisabledGroups );
750 if ( $groupIntersection !== [] ) {
751 $groupIntersectionNames = array_map(
752 static fn ( $group ) => $context->getLanguage()->getGroupName( $group ),
753 $groupIntersection
754 );
755 $status = PermissionStatus::newFatal(
756 'badaccess-groups-disabled',
757 Message::listParam( $groupLinks, ListType::COMMA ),
758 count( $groupLinks ),
759 Message::listParam( $groupIntersectionNames, ListType::COMMA ),
760 count( $groupIntersectionNames )
761 );
762 $status->setPermission( $permission );
763 return $status;
764 }
765
766 $status = PermissionStatus::newFatal(
767 'badaccess-groups',
768 Message::listParam( $groupLinks, ListType::COMMA ),
769 count( $groupLinks )
770 );
771 $status->setPermission( $permission );
772 return $status;
773 }
774
782 private function isSameSpecialPage( $name, LinkTarget $page ): bool {
783 if ( $page->getNamespace() === NS_SPECIAL ) {
784 [ $pageName ] = $this->specialPageFactory->resolveAlias( $page->getDBkey() );
785 if ( $name === $pageName ) {
786 return true;
787 }
788 }
789 return false;
790 }
791
805 private function checkUserBlock(
806 $action,
807 User $user,
808 PermissionStatus $status,
809 $rigor,
810 $short,
811 LinkTarget $page
812 ): void {
813 $block = $this->getApplicableBlock(
814 $action,
815 $user,
816 $rigor,
817 $page,
818 $user->getRequest()
819 );
820
821 if ( $block ) {
822 $status->setBlock( $block );
823
824 // @todo FIXME: Pass the relevant context into this function.
825 $context = RequestContext::getMain();
826 $messages = $this->blockErrorFormatter->getMessages(
827 $block,
828 $user,
829 $context->getRequest()->getIP()
830 );
831
832 foreach ( $messages as $message ) {
833 $status->fatal( $message );
834 }
835 }
836 }
837
854 public function getApplicableBlock(
855 string $action,
856 User $user,
857 string $rigor,
858 $page,
859 ?WebRequest $request
860 ): ?Block {
861 // Unblocking handled in SpecialUnblock
862 if ( $rigor === self::RIGOR_QUICK || in_array( $action, [ 'unblock' ] ) ) {
863 return null;
864 }
865
866 // Optimize for a very common case
867 if ( $action === 'read' && !$this->options->get( MainConfigNames::BlockDisablesLogin ) ) {
868 return null;
869 }
870
871 // Implicit rights aren't blockable (T350117, T350202).
872 if ( in_array( $action, $this->getImplicitRights(), true ) ) {
873 return null;
874 }
875
876 $useReplica = $rigor !== self::RIGOR_SECURE;
877 $isExempt = $this->userHasRight( $user, 'ipblock-exempt' );
878 $requestIfNotExempt = $isExempt ? null : $request;
879
880 // Create account blocks are implemented separately due to weird IP exemption rules
881 if ( in_array( $action, [ 'createaccount', 'autocreateaccount' ], true ) ) {
882 return $this->blockManager->getCreateAccountBlock(
883 $user,
884 $requestIfNotExempt,
885 $useReplica
886 );
887 }
888
889 $block = $this->blockManager->getBlock( $user, $requestIfNotExempt, $useReplica );
890 if ( !$block ) {
891 return null;
892 }
893 $userIsHidden = $block->getHideName();
894
895 // Remove elements from the block that explicitly allow the action
896 // (like "read" or "upload").
897 $block = $this->blockManager->filter(
898 $block,
899 static function ( AbstractBlock $originalBlock ) use ( $action ) {
900 // Remove the block if it explicitly allows the action
901 return $originalBlock->appliesToRight( $action ) !== false;
902 }
903 );
904 if ( !$block ) {
905 return null;
906 }
907
908 // Convert the input page to a Title
909 $targetTitle = null;
910 if ( $page ) {
911 $targetTitle = $page instanceof PageReference ?
912 Title::castFromPageReference( $page ) :
913 Title::castFromLinkTarget( $page );
914
915 if ( !$targetTitle->canExist() ) {
916 $targetTitle = null;
917 }
918 }
919
920 // What gets passed into this method is a user right, not an action name.
921 // There is no way to instantiate an action by restriction. However, this
922 // will get the action where the restriction is the same. This may result
923 // in actions being blocked that shouldn't be.
924 $actionInfo = $this->actionFactory->getActionInfo( $action, $targetTitle );
925
926 // Ensure that the retrieved action matches the restriction.
927 if ( $actionInfo && $actionInfo->getRestriction() !== $action ) {
928 $actionInfo = null;
929 }
930
931 // Return null if the action does not require an unblocked user.
932 // If no ActionInfo is returned, assume that the action requires unblock
933 // which is the default.
934 // NOTE: We may get null here even for known actions, if a wiki's main page
935 // is set to a special page, e.g. Special:MyLanguage/Main_Page (T348451, T346036).
936 if ( $actionInfo && !$actionInfo->requiresUnblock() ) {
937 return null;
938 }
939
940 // Remove elements from the block that do not apply to the specific page
941 if ( $targetTitle ) {
942 $targetIsUserTalk = !$userIsHidden && $targetTitle->equals( $user->getTalkPage() );
943 $block = $this->blockManager->filter(
944 $block,
945 static function ( AbstractBlock $originalBlock )
946 use ( $action, $targetTitle, $targetIsUserTalk ) {
947 if ( $originalBlock->appliesToRight( $action ) ) {
948 // An action block takes precedence over appliesToTitle().
949 // Block::appliesToRight('edit') always returns null,
950 // allowing title-based exemptions to take effect.
951 return true;
952 } elseif ( $targetIsUserTalk ) {
953 // Special handling for a user's own talk page. The block is not aware
954 // of the user, so this must be done here.
955 return $originalBlock->appliesToUsertalk( $targetTitle );
956 } else {
957 return $originalBlock->appliesToTitle( $targetTitle );
958 }
959 }
960 );
961 }
962
963 if ( $targetTitle && $block
964 && $block instanceof AbstractBlock // for phan
965 ) {
966 // Allow extensions to let a blocked user access a particular page
967 $allowUsertalk = $block->isUsertalkEditAllowed();
968 $blocked = true;
969 $this->hookRunner->onUserIsBlockedFrom( $user, $targetTitle, $blocked, $allowUsertalk );
970 if ( !$blocked ) {
971 $block = null;
972 }
973 }
974 return $block;
975 }
976
990 private function checkQuickPermissions(
991 $action,
992 User $user,
993 PermissionStatus $status,
994 $rigor,
995 $short,
996 LinkTarget $page
997 ): void {
998 // TODO: remove when LinkTarget usage will expand further
999 $title = Title::newFromLinkTarget( $page );
1000
1001 // This method is always called first, so $status is guaranteed to be empty, so we can
1002 // just pass an empty $errors array, instead of converting it to the legacy format and back.
1003 $errors = [];
1004 if ( !$this->hookRunner->onTitleQuickPermissions( $title, $user, $action,
1005 $errors, $rigor !== self::RIGOR_QUICK, $short )
1006 ) {
1007 // $errors is an array of results, not a result, but resultToStatus() handles
1008 // arrays of arrays with recursion so this will work
1009 $this->resultToStatus( $status, $errors );
1010 return;
1011 }
1012
1013 $isSubPage =
1014 $this->nsInfo->hasSubpages( $title->getNamespace() ) &&
1015 str_contains( $title->getText(), '/' );
1016
1017 if ( $action === 'create' ) {
1018 if (
1019 ( $this->nsInfo->isTalk( $title->getNamespace() ) &&
1020 !$this->userHasRight( $user, 'createtalk' ) ) ||
1021 ( !$this->nsInfo->isTalk( $title->getNamespace() ) &&
1022 !$this->userHasRight( $user, 'createpage' ) )
1023 ) {
1024 $status->fatal( $user->isNamed() ? 'nocreate-loggedin' : 'nocreatetext' );
1025 }
1026 } elseif ( $action === 'move' ) {
1027 if ( !$this->userHasRight( $user, 'move-rootuserpages' )
1028 && $title->getNamespace() === NS_USER && !$isSubPage
1029 ) {
1030 // Show user page-specific message only if the user can move other pages
1031 $status->fatal( 'cant-move-user-page' );
1032 }
1033
1034 // Check if user is allowed to move files if it's a file
1035 if ( $title->getNamespace() === NS_FILE &&
1036 !$this->userHasRight( $user, 'movefile' )
1037 ) {
1038 $status->fatal( 'movenotallowedfile' );
1039 }
1040
1041 // Check if user is allowed to move category pages if it's a category page
1042 if ( $title->getNamespace() === NS_CATEGORY &&
1043 !$this->userHasRight( $user, 'move-categorypages' )
1044 ) {
1045 $status->fatal( 'cant-move-category-page' );
1046 }
1047
1048 if ( !$this->userHasRight( $user, 'move' ) ) {
1049 // User can't move anything
1050 $userCanMove = $this->groupPermissionsLookup
1051 ->groupHasPermission( 'user', 'move' );
1052 $autoconfirmedCanMove = $this->groupPermissionsLookup
1053 ->groupHasPermission( 'autoconfirmed', 'move' );
1054 if ( $user->isAnon()
1055 && ( $userCanMove || $autoconfirmedCanMove )
1056 ) {
1057 // custom message if logged-in users without any special rights can move
1058 $status->fatal( 'movenologintext' );
1059 } elseif ( $user->isTemp() && $autoconfirmedCanMove ) {
1060 // Temp user may be able to move if they log in as a proper account
1061 $status->fatal( 'movenologintext' );
1062 } else {
1063 $status->fatal( 'movenotallowed' );
1064 }
1065 }
1066 } elseif ( $action === 'move-target' ) {
1067 if ( !$this->userHasRight( $user, 'move' ) ) {
1068 // User can't move anything
1069 $status->fatal( 'movenotallowed' );
1070 } elseif ( !$this->userHasRight( $user, 'move-rootuserpages' )
1071 && $title->getNamespace() === NS_USER
1072 && !$isSubPage
1073 ) {
1074 // Show user page-specific message only if the user can move other pages
1075 $status->fatal( 'cant-move-to-user-page' );
1076 } elseif ( !$this->userHasRight( $user, 'move-categorypages' )
1077 && $title->getNamespace() === NS_CATEGORY
1078 ) {
1079 // Show category page-specific message only if the user can move other pages
1080 $status->fatal( 'cant-move-to-category-page' );
1081 }
1082 } elseif ( $action === 'autocreateaccount' ) {
1083 // createaccount implies autocreateaccount
1084 if ( !$this->userHasAnyRight( $user, 'autocreateaccount', 'createaccount' ) ) {
1085 $this->missingPermissionError( $action, $short, $status );
1086 }
1087 } elseif ( !$this->userHasRight( $user, $action ) ) {
1088 $this->missingPermissionError( $action, $short, $status );
1089 }
1090 }
1091
1108 private function checkPageRestrictions(
1109 $action,
1110 UserIdentity $user,
1111 PermissionStatus $status,
1112 $rigor,
1113 $short,
1114 LinkTarget $page
1115 ): void {
1116 // TODO: remove & rework upon further use of LinkTarget
1117 $title = Title::newFromLinkTarget( $page );
1118 foreach ( $this->restrictionStore->getRestrictions( $title, $action ) as $level ) {
1119 // Messages: restriction-level-sysop, restriction-level-autoconfirmed
1120 $levelMsg = MessageValue::new( "restriction-level-$level" );
1121
1122 $right = $level;
1123 // Backwards compatibility, rewrite sysop -> editprotected
1124 if ( $right === 'sysop' ) {
1125 $right = 'editprotected';
1126 }
1127 // Backwards compatibility, rewrite autoconfirmed -> editsemiprotected
1128 if ( $right === 'autoconfirmed' ) {
1129 $right = 'editsemiprotected';
1130 }
1131 if ( $right == '' ) {
1132 continue;
1133 }
1134 if ( !$this->userHasRight( $user, $right ) ) {
1135 // The parameters are not used by the default message text,
1136 // but they're available to be used in on-wiki overrides
1137 $status->fatal( 'protectedpagetext', $right, $action, $levelMsg );
1138 } elseif ( $this->restrictionStore->areRestrictionsCascading( $title ) &&
1139 !$this->userHasRight( $user, 'protect' )
1140 ) {
1141 // The parameters are not used by the default message text,
1142 // but they're available to be used in on-wiki overrides
1143 $status->fatal( 'protectedpagetext', 'protect', $action, $levelMsg );
1144 }
1145 }
1146 }
1147
1161 private function checkCascadingSourcesRestrictions(
1162 $action,
1163 UserIdentity $user,
1164 PermissionStatus $status,
1165 $rigor,
1166 $short,
1167 LinkTarget $page
1168 ): void {
1169 // TODO: remove & rework upon further use of LinkTarget
1170 $title = Title::newFromLinkTarget( $page );
1171
1172 if ( $rigor !== self::RIGOR_QUICK && !$title->isUserConfigPage() ) {
1173 [ $sources, $restrictions, $tlSources, $ilSources ] = $this->restrictionStore
1174 ->getCascadeProtectionSources( $title );
1175
1176 // If the file Wikitext isn't transcluded then we
1177 // don't care about edit cascade restrictions for edit action
1178 if ( $action === 'edit' && $page->getNamespace() === NS_FILE && !$tlSources ) {
1179 return;
1180 }
1181
1182 // For the purposes of cascading protection, edit restrictions should apply to uploads or moves
1183 // Thus remap upload and move to edit
1184 // Unless the file content itself is not transcluded
1185 if ( $ilSources && ( $action === 'upload' || $action === 'move' ) ) {
1186 $restrictedAction = 'edit';
1187 } else {
1188 $restrictedAction = $action;
1189 }
1190
1191 // Cascading protection depends on more than this page...
1192 // Several cascading protected pages may include this page...
1193 // Check each cascading level
1194 // This is only for protection restrictions, not for all actions
1195 if ( isset( $restrictions[$restrictedAction] ) ) {
1196 foreach ( $restrictions[$restrictedAction] as $right ) {
1197 // Backwards compatibility, rewrite sysop -> editprotected
1198 if ( $right === 'sysop' ) {
1199 $right = 'editprotected';
1200 }
1201 // Backwards compatibility, rewrite autoconfirmed -> editsemiprotected
1202 if ( $right === 'autoconfirmed' ) {
1203 $right = 'editsemiprotected';
1204 }
1205 if ( $right != '' && !$this->userHasAllRights( $user, 'protect', $right ) ) {
1206 $wikiPages = '';
1207 foreach ( $sources as $pageIdentity ) {
1208 $wikiPages .= '* [[:' . $this->titleFormatter->getPrefixedText( $pageIdentity ) . "]]\n";
1209 }
1210 $status->fatal( 'cascadeprotected', count( $sources ), $wikiPages, $action );
1211 }
1212 }
1213 }
1214 }
1215 }
1216
1230 private function checkActionPermissions(
1231 $action,
1232 User $user,
1233 PermissionStatus $status,
1234 $rigor,
1235 $short,
1236 LinkTarget $page
1237 ): void {
1238 // TODO: remove & rework upon further use of LinkTarget
1239 $title = Title::newFromLinkTarget( $page );
1240
1241 if ( $rigor !== self::RIGOR_QUICK && !defined( 'MW_NO_SESSION' ) ) {
1242 $sessionRestrictions = $user->getRequest()->getSession()->getRestrictions();
1243 if ( $sessionRestrictions ) {
1244 $userCan = $sessionRestrictions->userCan( $title );
1245 if ( !$userCan->isOK() ) {
1246 $status->merge( $userCan );
1247 }
1248 }
1249 }
1250
1251 if ( $action === 'protect' ) {
1252 if ( !$this->getPermissionStatus( 'edit', $user, $title, $rigor, true )->isGood() ) {
1253 // If they can't edit, they shouldn't protect.
1254 $status->fatal( 'protect-cantedit' );
1255 }
1256 } elseif ( $action === 'create' ) {
1257 $createProtection = $this->restrictionStore->getCreateProtection( $title );
1258 if ( $createProtection ) {
1259 if ( $createProtection['permission'] == ''
1260 || !$this->userHasRight( $user, $createProtection['permission'] )
1261 ) {
1262 $protectUserIdentity = $this->userIdentityLookup
1263 ->getUserIdentityByUserId( $createProtection['user'] );
1264 $status->fatal(
1265 'titleprotected',
1266 $protectUserIdentity ? $protectUserIdentity->getName() : '',
1267 $createProtection['reason']
1268 );
1269 }
1270 }
1271 } elseif ( $action === 'move' ) {
1272 // Check for immobile pages
1273 if ( !$this->nsInfo->isMovable( $title->getNamespace() ) ) {
1274 // Specific message for this case
1275 $nsText = $title->getNsText();
1276 if ( $nsText === '' ) {
1277 $nsText = wfMessage( 'blanknamespace' )->text();
1278 }
1279 $status->fatal( 'immobile-source-namespace', $nsText );
1280 } elseif ( !$title->isMovable() ) {
1281 // Less specific message for rarer cases
1282 $status->fatal( 'immobile-source-page' );
1283 }
1284 } elseif ( $action === 'move-target' ) {
1285 if ( !$this->nsInfo->isMovable( $title->getNamespace() ) ) {
1286 $nsText = $title->getNsText();
1287 if ( $nsText === '' ) {
1288 $nsText = wfMessage( 'blanknamespace' )->text();
1289 }
1290 $status->fatal( 'immobile-target-namespace', $nsText );
1291 } elseif ( !$title->isMovable() ) {
1292 $status->fatal( 'immobile-target-page' );
1293 }
1294 } elseif ( $action === 'delete' || $action === 'delete-redirect' ) {
1295 $tempStatus = PermissionStatus::newEmpty();
1296 $this->checkPageRestrictions( 'edit', $user, $tempStatus, $rigor, true, $title );
1297 if ( $tempStatus->isGood() ) {
1298 $this->checkCascadingSourcesRestrictions( 'edit',
1299 $user, $tempStatus, $rigor, true, $title );
1300 }
1301 if ( !$tempStatus->isGood() ) {
1302 // If protection keeps them from editing, they shouldn't be able to delete.
1303 $status->fatal( 'deleteprotected' );
1304 }
1305 if ( $rigor !== self::RIGOR_QUICK
1306 && $action === 'delete'
1307 && $this->options->get( MainConfigNames::DeleteRevisionsLimit )
1308 && !$this->userCan( 'bigdelete', $user, $title )
1309 && $title->isBigDeletion()
1310 ) {
1311 // NOTE: This check is deprecated since 1.37, see T288759
1312 $status->fatal(
1313 'delete-toobig',
1314 Message::numParam( $this->options->get( MainConfigNames::DeleteRevisionsLimit ) )
1315 );
1316 }
1317 } elseif ( $action === 'undelete' ) {
1318 if ( !$this->getPermissionStatus( 'edit', $user, $title, $rigor, true )->isGood() ) {
1319 // Undeleting implies editing
1320 $status->fatal( 'undelete-cantedit' );
1321 }
1322 if ( !$title->exists()
1323 && !$this->getPermissionStatus( 'create', $user, $title, $rigor, true )->isGood()
1324 ) {
1325 // Undeleting where nothing currently exists implies creating
1326 $status->fatal( 'undelete-cantcreate' );
1327 }
1328 } elseif ( $action === 'edit' ) {
1329 if ( $this->options->get( MainConfigNames::EmailConfirmToEdit )
1330 && !$user->isEmailConfirmed()
1331 ) {
1332 $status->fatal( 'confirmedittext' );
1333 }
1334
1335 if ( !$title->exists() ) {
1336 $status->merge(
1337 $this->getPermissionStatus(
1338 'create',
1339 $user,
1340 $title,
1341 $rigor,
1342 true
1343 )
1344 );
1345 }
1346 }
1347 }
1348
1362 private function checkSpecialsAndNSPermissions(
1363 $action,
1364 UserIdentity $user,
1365 PermissionStatus $status,
1366 $rigor,
1367 $short,
1368 LinkTarget $page
1369 ): void {
1370 // TODO: remove & rework upon further use of LinkTarget
1371 $title = Title::newFromLinkTarget( $page );
1372
1373 // Only 'createaccount' can be performed on special pages,
1374 // which don't actually exist in the DB.
1375 if ( $title->getNamespace() === NS_SPECIAL
1376 && !in_array( $action, [ 'createaccount', 'autocreateaccount' ], true )
1377 ) {
1378 $status->fatal( 'ns-specialprotected' );
1379 }
1380
1381 // Check $wgNamespaceProtection for restricted namespaces
1382 if ( $this->isNamespaceProtected( $title->getNamespace(), $user )
1383 // Allow admins and oversighters to view deleted content, even if they
1384 // cannot restore it. See T362536. Allow (un)watch too (T373758)
1385 && !in_array( $action, [ 'deletedhistory', 'deletedtext', 'viewsuppressed', 'editmywatchlist' ], true )
1386 ) {
1387 $ns = $title->getNamespace() === NS_MAIN ?
1388 wfMessage( 'nstab-main' )->text() : $title->getNsText();
1389 if ( $title->getNamespace() === NS_MEDIAWIKI ) {
1390 $status->fatal( 'protectedinterface', $action );
1391 } else {
1392 $status->fatal( 'namespaceprotected', $ns, $action );
1393 }
1394 }
1395 }
1396
1410 private function checkSiteConfigPermissions(
1411 $action,
1412 UserIdentity $user,
1413 PermissionStatus $status,
1414 $rigor,
1415 $short,
1416 LinkTarget $page
1417 ): void {
1418 // TODO: remove & rework upon further use of LinkTarget
1419 $title = Title::newFromLinkTarget( $page );
1420
1421 if ( $action === 'patrol' || $action === 'editmywatchlist' ) {
1422 return;
1423 }
1424
1425 if ( in_array( $action, [ 'deletedhistory', 'deletedtext', 'viewsuppressed' ], true ) ) {
1426 // Allow admins and oversighters to view deleted content, even if they
1427 // cannot restore it. See T202989
1428 // Not using the same handling in `getPermissionStatus` as the checks
1429 // for skipping `checkUserConfigPermissions` since normal admins can delete
1430 // user scripts, but not sitewide scripts
1431 return;
1432 }
1433
1434 // Sitewide CSS/JSON/JS/RawHTML changes, like all NS_MEDIAWIKI changes, also require the
1435 // editinterface right. That's implemented as a restriction so no check needed here.
1436 if ( $title->isSiteCssConfigPage() && !$this->userHasRight( $user, 'editsitecss' ) ) {
1437 $status->fatal( 'sitecssprotected', $action );
1438 } elseif ( $title->isSiteJsonConfigPage() && !$this->userHasRight( $user, 'editsitejson' ) ) {
1439 $status->fatal( 'sitejsonprotected', $action );
1440 } elseif ( $title->isSiteJsConfigPage() && !$this->userHasRight( $user, 'editsitejs' ) ) {
1441 $status->fatal( 'sitejsprotected', $action );
1442 }
1443 if ( $title->isRawHtmlMessage() && !$this->userCanEditRawHtmlPage( $user ) ) {
1444 $status->fatal( 'siterawhtmlprotected', $action );
1445 }
1446 }
1447
1461 private function checkUserConfigPermissions(
1462 $action,
1463 UserIdentity $user,
1464 PermissionStatus $status,
1465 $rigor,
1466 $short,
1467 LinkTarget $page
1468 ): void {
1469 // TODO: remove & rework upon further use of LinkTarget
1470 $title = Title::newFromLinkTarget( $page );
1471
1472 // Protect css/json/js subpages of user pages
1473 // XXX: this might be better using restrictions
1474 if ( preg_match( '/^' . preg_quote( $user->getName(), '/' ) . '\//', $title->getText() ) ) {
1475 // Users need editmyuser* to edit their own CSS/JSON/JS subpages.
1476 if (
1477 $title->isUserCssConfigPage()
1478 && !$this->userHasAnyRight( $user, 'editmyusercss', 'editusercss' )
1479 ) {
1480 $status->fatal( 'mycustomcssprotected', $action );
1481 } elseif (
1482 $title->isUserJsonConfigPage()
1483 && !$this->userHasAnyRight( $user, 'editmyuserjson', 'edituserjson' )
1484 ) {
1485 $status->fatal( 'mycustomjsonprotected', $action );
1486 } elseif (
1487 $title->isUserJsConfigPage()
1488 && !$this->userHasAnyRight( $user, 'editmyuserjs', 'edituserjs' )
1489 ) {
1490 $status->fatal( 'mycustomjsprotected', $action );
1491 } elseif (
1492 $title->isUserJsConfigPage()
1493 && !$this->userHasAnyRight( $user, 'edituserjs', 'editmyuserjsredirect' )
1494 ) {
1495 // T207750 - do not allow users to edit a redirect if they couldn't edit the target
1496 $target = $this->redirectLookup->getRedirectTarget( $title );
1497 if ( $target && (
1498 !$target->inNamespace( NS_USER )
1499 || !preg_match( '/^' . preg_quote( $user->getName(), '/' ) . '\//', $target->getText() )
1500 ) ) {
1501 $status->fatal( 'mycustomjsredirectprotected', $action );
1502 }
1503 }
1504 } else {
1505 // Users need edituser* to edit others' CSS/JSON/JS subpages.
1506 // The checks to exclude deletion/suppression, which cannot be used for
1507 // attacks and should be excluded to avoid the situation where an
1508 // unprivileged user can post abusive content on their subpages
1509 // and only very highly privileged users could remove it,
1510 // are now a part of `getPermissionStatus` and this method isn't called.
1511 if (
1512 $title->isUserCssConfigPage()
1513 && !$this->userHasRight( $user, 'editusercss' )
1514 ) {
1515 $status->fatal( 'customcssprotected', $action );
1516 } elseif (
1517 $title->isUserJsonConfigPage()
1518 && !$this->userHasRight( $user, 'edituserjson' )
1519 ) {
1520 $status->fatal( 'customjsonprotected', $action );
1521 } elseif (
1522 $title->isUserJsConfigPage()
1523 && !$this->userHasRight( $user, 'edituserjs' )
1524 ) {
1525 $status->fatal( 'customjsprotected', $action );
1526 }
1527 }
1528 }
1529
1538 public function userHasRight( UserIdentity $user, $action = '' ): bool {
1539 if ( $action === '' ) {
1540 // In the spirit of DWIM
1541 return true;
1542 }
1543 // Use strict parameter to avoid matching numeric 0 accidentally inserted
1544 // by misconfiguration: 0 == 'foo'
1545 return in_array( $action, $this->getImplicitRights(), true )
1546 || in_array( $action, $this->getUserPermissions( $user ), true );
1547 }
1548
1557 public function userHasAnyRight( UserIdentity $user, ...$actions ): bool {
1558 foreach ( $actions as $action ) {
1559 if ( $this->userHasRight( $user, $action ) ) {
1560 return true;
1561 }
1562 }
1563 return false;
1564 }
1565
1574 public function userHasAllRights( UserIdentity $user, ...$actions ): bool {
1575 foreach ( $actions as $action ) {
1576 if ( !$this->userHasRight( $user, $action ) ) {
1577 return false;
1578 }
1579 }
1580 return true;
1581 }
1582
1590 public function getUserPermissions( UserIdentity $user ): array {
1591 $rightsCacheKey = $this->getRightsCacheKey( $user );
1592 if ( !isset( $this->usersRights[ $rightsCacheKey ] ) ) {
1593 $userObj = $this->userFactory->newFromUserIdentity( $user );
1594 $rights = $this->groupPermissionsLookup->getGroupPermissions(
1595 $this->userGroupManager->getUserEffectiveGroups( $user )
1596 );
1597 // Hook requires a full User object
1598 $this->hookRunner->onUserGetRights( $userObj, $rights );
1599
1600 // Deny any rights denied by the user's session, unless this
1601 // endpoint has no sessions.
1602 if ( !defined( 'MW_NO_SESSION' ) ) {
1603 // FIXME: $userObj->getRequest().. need to be replaced with something else
1604 $allowedRights = $userObj->getRequest()->getSession()->getAllowedUserRights();
1605 if ( $allowedRights !== null ) {
1606 $rights = array_intersect( $rights, $allowedRights );
1607 }
1608 }
1609
1610 // Hook requires a full User object
1611 $this->hookRunner->onUserGetRightsRemove( $userObj, $rights );
1612 // Force reindexation of rights when a hook has unset one of them
1613 $rights = array_values( array_unique( $rights ) );
1614
1615 // If BlockDisablesLogin is true, remove rights that anonymous
1616 // users don't have. This has to be done after the hooks so that
1617 // we know whether the user is exempt. (T129738)
1618 if (
1619 $userObj->isRegistered()
1620 && $this->options->get( MainConfigNames::BlockDisablesLogin )
1621 ) {
1622 // Stash the permissions as they are before triggering any block checks for BlockDisablesLogin
1623 // to avoid a potential infinite loop, since GetUserBlock handlers may themselves check
1624 // permissions on this user. (T384197)
1625 $this->usersRights[ $rightsCacheKey ] = $rights;
1626
1627 $isExempt = in_array( 'ipblock-exempt', $rights, true );
1628 if ( $this->blockManager->getBlock(
1629 $userObj,
1630 $isExempt ? null : $userObj->getRequest()
1631 ) ) {
1632 $anon = $this->userFactory->newAnonymous();
1633 $rights = array_intersect( $rights, $this->getUserPermissions( $anon ) );
1634 }
1635 }
1636
1637 $this->usersRights[ $rightsCacheKey ] = $rights;
1638 } else {
1639 $rights = $this->usersRights[ $rightsCacheKey ];
1640 }
1641 foreach ( $this->temporaryUserRights[ $user->getId() ] ?? [] as $overrides ) {
1642 $rights = array_values( array_unique( array_merge( $rights, $overrides ) ) );
1643 }
1644 return $rights;
1645 }
1646
1654 public function invalidateUsersRightsCache( $user = null ): void {
1655 if ( $user !== null ) {
1656 $rightsCacheKey = $this->getRightsCacheKey( $user );
1657 unset( $this->usersRights[ $rightsCacheKey ] );
1658 } else {
1659 $this->usersRights = [];
1660 }
1661 }
1662
1669 private function getRightsCacheKey( UserIdentity $user ): string {
1670 return $user->isRegistered() ? "u:{$user->getId()}" : "anon:{$user->getName()}";
1671 }
1672
1687 public function isEveryoneAllowed( $right ): bool {
1688 // Use the cached results, except in unit tests which rely on
1689 // being able change the permission mid-request
1690 if ( isset( $this->cachedRights[$right] ) ) {
1691 return $this->cachedRights[$right];
1692 }
1693
1694 if ( !isset( $this->options->get( MainConfigNames::GroupPermissions )['*'][$right] )
1695 || !$this->options->get( MainConfigNames::GroupPermissions )['*'][$right]
1696 ) {
1697 $this->cachedRights[$right] = false;
1698 return false;
1699 }
1700
1701 // If it's revoked anywhere, then everyone doesn't have it
1702 foreach ( $this->options->get( MainConfigNames::RevokePermissions ) as $rights ) {
1703 if ( isset( $rights[$right] ) && $rights[$right] ) {
1704 $this->cachedRights[$right] = false;
1705 return false;
1706 }
1707 }
1708
1709 // Remove any rights that aren't allowed to the global-session user,
1710 // unless there are no sessions for this endpoint.
1711 if ( !defined( 'MW_NO_SESSION' ) ) {
1712 // XXX: think what could be done with the below
1713 $allowedRights = RequestContext::getMain()->getRequest()->getSession()->getAllowedUserRights();
1714 if ( $allowedRights !== null && !in_array( $right, $allowedRights, true ) ) {
1715 $this->cachedRights[$right] = false;
1716 return false;
1717 }
1718 }
1719
1720 // Allow extensions to say false
1721 if ( !$this->hookRunner->onUserIsEveryoneAllowed( $right ) ) {
1722 $this->cachedRights[$right] = false;
1723 return false;
1724 }
1725
1726 $this->cachedRights[$right] = true;
1727 return true;
1728 }
1729
1739 public function getAllPermissions(): array {
1740 if ( $this->allRights === null ) {
1741 if ( count( $this->options->get( MainConfigNames::AvailableRights ) ) ) {
1742 $this->allRights = array_unique( array_merge(
1743 self::CORE_RIGHTS,
1744 $this->options->get( MainConfigNames::AvailableRights )
1745 ) );
1746 } else {
1747 $this->allRights = self::CORE_RIGHTS;
1748 }
1749 $this->hookRunner->onUserGetAllRights( $this->allRights );
1750 }
1751 return $this->allRights;
1752 }
1753
1765 public function getImplicitRights(): array {
1766 if ( $this->implicitRights === null ) {
1767 $rights = array_unique( array_merge(
1768 self::CORE_IMPLICIT_RIGHTS,
1769 $this->options->get( MainConfigNames::ImplicitRights )
1770 ) );
1771
1772 $this->implicitRights = array_diff( $rights, $this->getAllPermissions() );
1773 }
1774 return $this->implicitRights;
1775 }
1776
1784 private function isNamespaceProtected( $index, UserIdentity $user ): bool {
1785 $namespaceProtection = $this->options->get( MainConfigNames::NamespaceProtection );
1786 if ( isset( $namespaceProtection[$index] ) ) {
1787 return !$this->userHasAllRights( $user, ...(array)$namespaceProtection[$index] );
1788 }
1789 return false;
1790 }
1791
1800 public function getNamespaceRestrictionLevels( $index, ?UserIdentity $user = null ): array {
1801 if ( !isset( $this->options->get( MainConfigNames::NamespaceProtection )[$index] ) ) {
1802 // All levels are valid if there's no namespace restriction.
1803 // But still filter by user, if necessary
1804 $levels = $this->options->get( MainConfigNames::RestrictionLevels );
1805 if ( $user ) {
1806 $levels = array_values( array_filter( $levels, function ( $level ) use ( $user ) {
1807 $right = $level;
1808 if ( $right === 'sysop' ) {
1809 $right = 'editprotected'; // BC
1810 }
1811 if ( $right === 'autoconfirmed' ) {
1812 $right = 'editsemiprotected'; // BC
1813 }
1814 return $this->userHasRight( $user, $right );
1815 } ) );
1816 }
1817 return $levels;
1818 }
1819
1820 // $wgNamespaceProtection can require one or more rights to edit the namespace, which
1821 // may be satisfied by membership in multiple groups each giving a subset of those rights.
1822 // A restriction level is redundant if, for any one of the namespace rights, all groups
1823 // giving that right also give the restriction level's right. Or, conversely, a
1824 // restriction level is not redundant if, for every namespace right, there's at least one
1825 // group giving that right without the restriction level's right.
1826 //
1827 // First, for each right, get a list of groups with that right.
1828 $namespaceRightGroups = [];
1829 foreach ( (array)$this->options->get( MainConfigNames::NamespaceProtection )[$index] as $right ) {
1830 if ( $right === 'sysop' ) {
1831 $right = 'editprotected'; // BC
1832 }
1833 if ( $right === 'autoconfirmed' ) {
1834 $right = 'editsemiprotected'; // BC
1835 }
1836 if ( $right != '' ) {
1837 $namespaceRightGroups[$right] = $this->groupPermissionsLookup->getGroupsWithPermission( $right );
1838 }
1839 }
1840
1841 // Now, go through the protection levels one by one.
1842 $usableLevels = [ '' ];
1843 foreach ( $this->options->get( MainConfigNames::RestrictionLevels ) as $level ) {
1844 $right = $level;
1845 if ( $right === 'sysop' ) {
1846 $right = 'editprotected'; // BC
1847 }
1848 if ( $right === 'autoconfirmed' ) {
1849 $right = 'editsemiprotected'; // BC
1850 }
1851
1852 if ( $right != '' &&
1853 !isset( $namespaceRightGroups[$right] ) &&
1854 ( !$user || $this->userHasRight( $user, $right ) )
1855 ) {
1856 // Do any of the namespace rights imply the restriction right? (see explanation above)
1857 foreach ( $namespaceRightGroups as $groups ) {
1858 if ( !array_diff( $groups, $this->groupPermissionsLookup->getGroupsWithPermission( $right ) ) ) {
1859 // Yes, this one does.
1860 continue 2;
1861 }
1862 }
1863 // No, keep the restriction level
1864 $usableLevels[] = $level;
1865 }
1866 }
1867
1868 return $usableLevels;
1869 }
1870
1880 private function userCanEditRawHtmlPage( UserIdentity $user ): bool {
1881 return $this->userHasAllRights( $user, 'editsitecss', 'editsitejs' );
1882 }
1883
1900 #[\NoDiscard]
1901 public function addTemporaryUserRights( UserIdentity $user, $rights ): ScopedCallback {
1902 $userId = $user->getId();
1903 $nextKey = count( $this->temporaryUserRights[$userId] ?? [] );
1904 $this->temporaryUserRights[$userId][$nextKey] = (array)$rights;
1905 return new ScopedCallback( function () use ( $userId, $nextKey ) {
1906 unset( $this->temporaryUserRights[$userId][$nextKey] );
1907 } );
1908 }
1909
1918 public function overrideUserRightsForTesting( $user, $rights = [] ) {
1919 if ( !defined( 'MW_PHPUNIT_TEST' ) ) {
1920 throw new LogicException( __METHOD__ . ' can not be called outside of tests' );
1921 }
1922 $this->usersRights[ $this->getRightsCacheKey( $user ) ] =
1923 is_array( $rights ) ? $rights : [ $rights ];
1924 }
1925
1926}
const NS_USER
Definition Defines.php:53
const NS_FILE
Definition Defines.php:57
const NS_MAIN
Definition Defines.php:51
const NS_MEDIAWIKI
Definition Defines.php:59
const NS_SPECIAL
Definition Defines.php:40
const NS_CATEGORY
Definition Defines.php:65
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
if(!defined('MW_SETUP_CALLBACK'))
Definition WebStart.php:69
A service class for getting formatted information about a block.
A service class for checking blocks.
A class for passing options to services.
assertRequiredOptions(array $expectedKeys)
Assert that the list of options provided in this instance exactly match $expectedKeys,...
Group all the pieces relevant to the context of a request into one instance.
Show an error when a user tries to do something they do not have the necessary permissions for.
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
A class containing constants representing the names of configuration variables.
const AvailableRights
Name constant for the AvailableRights setting, for use with Config::get()
const NamespaceProtection
Name constant for the NamespaceProtection setting, for use with Config::get()
const RevokePermissions
Name constant for the RevokePermissions setting, for use with Config::get()
const WhitelistRead
Name constant for the WhitelistRead setting, for use with Config::get()
const BlockDisablesLogin
Name constant for the BlockDisablesLogin setting, for use with Config::get()
const DeleteRevisionsLimit
Name constant for the DeleteRevisionsLimit setting, for use with Config::get()
const EmailConfirmToEdit
Name constant for the EmailConfirmToEdit setting, for use with Config::get()
const RateLimits
Name constant for the RateLimits setting, for use with Config::get()
const GroupPermissions
Name constant for the GroupPermissions setting, for use with Config::get()
const RestrictionLevels
Name constant for the RestrictionLevels setting, for use with Config::get()
const WhitelistReadRegexp
Name constant for the WhitelistReadRegexp setting, for use with Config::get()
const ImplicitRights
Name constant for the ImplicitRights setting, for use with Config::get()
The Message class deals with fetching and processing of interface message into a variety of formats.
Definition Message.php:144
A service class for checking permissions To obtain an instance, use MediaWikiServices::getInstance()-...
getPermissionStatus( $action, User $user, LinkTarget $page, $rigor=self::RIGOR_SECURE, $short=false)
Can $user perform $action on a page?
userCan( $action, User $user, LinkTarget $page, $rigor=self::RIGOR_FULL)
Can $user perform $action on a page?
getNamespaceRestrictionLevels( $index, ?UserIdentity $user=null)
Determine which restriction levels it makes sense to use in a namespace, optionally filtered by a use...
quickUserCan( $action, User $user, LinkTarget $page)
A convenience method for calling PermissionManager::userCan with PermissionManager::RIGOR_QUICK.
isEveryoneAllowed( $right)
Check if all users may be assumed to have the given permission.
getPermissionErrors( $action, User $user, LinkTarget $page, $rigor=self::RIGOR_SECURE, $ignoreErrors=[])
Can $user perform $action on a page?
invalidateUsersRightsCache( $user=null)
Clear the in-process permission cache for one or all users.
overrideUserRightsForTesting( $user, $rights=[])
Override the user permissions cache.
addTemporaryUserRights(UserIdentity $user, $rights)
Add temporary user rights, only valid for the current function scope.
throwPermissionErrors( $action, User $user, LinkTarget $page, $rigor=self::RIGOR_SECURE, $ignoreErrors=[])
Like getPermissionErrors, but immediately throw if there are any errors.
getUserPermissions(UserIdentity $user)
Get the permissions this user has.
getApplicableBlock(string $action, User $user, string $rigor, $page, ?WebRequest $request)
Return the Block object applicable for the given permission check, if any.
getImplicitRights()
Get a list of implicit rights.
userHasAnyRight(UserIdentity $user,... $actions)
Whether the user is generally allowed to perform at least one of the actions.
__construct(ServiceOptions $options, SpecialPageFactory $specialPageFactory, NamespaceInfo $nsInfo, GroupPermissionsLookup $groupPermissionsLookup, UserGroupManager $userGroupManager, BlockManager $blockManager, BlockErrorFormatter $blockErrorFormatter, HookContainer $hookContainer, UserIdentityLookup $userIdentityLookup, RedirectLookup $redirectLookup, RestrictionStore $restrictionStore, TitleFormatter $titleFormatter, TempUserConfig $tempUserConfig, UserFactory $userFactory, ActionFactory $actionFactory)
getAllPermissions()
Get a list of all permissions that can be managed through group permissions.
userHasRight(UserIdentity $user, $action='')
Whether the user is generally allowed to perform the given action.
newFatalPermissionDeniedStatus( $permission, IContextSource $context)
Factory function for fatal permission-denied errors.
isBlockedFrom(User $user, $page, $fromReplica=false)
Check if user is blocked from editing a particular article.
userHasAllRights(UserIdentity $user,... $actions)
Whether the user is allowed to perform all of the given actions.
A StatusValue for permission errors.
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form,...
Factory for handling the special page list and generating SpecialPage objects.
Parent class for all special pages.
This is a utility class for dealing with namespaces that encodes all the "magic" behaviors of them ba...
A title formatter service for MediaWiki.
Represents a title within MediaWiki.
Definition Title.php:69
Create User objects.
Manage user group memberships.
Represents the membership of one user in one user group.
User class for the MediaWiki software.
Definition User.php:130
isRegistered()
Get whether the user is registered.
Definition User.php:2091
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Value object representing a message for i18n.
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'=>[], '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, 'ThumbnailStepsRatio'=> 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, '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',],], 'json'=>['class'=> 'MediaWiki\\Content\\JsonContentHandler', 'services'=>['ParsoidParserFactory', 'TitleFactory',],], 'css'=>['class'=> 'MediaWiki\\Content\\CssContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'UserOptionsLookup',],], 'vue'=>['class'=> 'MediaWiki\\Content\\VueContentHandler', 'services'=>['MainConfig', 'ParserFactory',],], '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',],],], '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, ], '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, 'UseLegacyMediaStyles' => 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, ], ], '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, ], 'AllowSecuritySensitiveOperationIfCannotReauthenticate' => [ 'default' => true, ], '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, '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, ], '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, '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' => [ ], '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, ], ], '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', ], '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, 'AllowUserCss' => false, 'AllowUserCssPrefs' => true, 'UseSiteJs' => true, 'UseSiteCss' => true, 'BreakFrames' => false, 'EditPageFrameOptions' => 'DENY', 'ApiFrameOptions' => 'DENY', 'CSPHeader' => false, 'CSPReportOnlyHeader' => 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: ], '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, ], '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, '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\\JobQueue\\Jobs\\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', 'Access-Control-Max-Age', 'Authorization', ], 'RestAPIAdditionalRouteFiles' => [ ], 'RestSandboxSpecs' => [ ], '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, ], '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', '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', ], 'ThumbnailStepsRatio' => [ 'number', 'null', ], 'ThumbnailBuckets' => [ 'array', 'null', ], 'UploadThumbnailRenderMap' => 'object', 'GalleryOptions' => 'object', 'DjvuDump' => [ 'string', 'null', ], 'DjvuRenderer' => [ 'string', 'null', ], 'DjvuTxt' => [ 'string', 'null', ], 'DjvuPostProcessor' => [ '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', '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', 'AllowSecuritySensitiveOperationIfCannotReauthenticate' => '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', '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', 'CSPHeader' => [ 'boolean', 'object', ], 'CSPReportOnlyHeader' => [ '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', 'RecentChangesFlags' => 'object', 'WatchlistExpiry' => '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', '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', ], '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', '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', ], ], 'AllowSecuritySensitiveOperationIfCannotReauthenticate' => [ 'additionalProperties' => [ 'type' => 'boolean', ], ], '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', ], ], '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', ], ], ], ], '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 which can provide a MediaWiki context on request.
Represents the target of a wiki link.
Interface for objects (potentially) representing an editable wiki page.
Interface for objects (potentially) representing a page that can be viewable and linked to on a wiki.
Service for resolving a wiki page redirect.
Interface for temporary user creation config and name matching.
Service for looking up UserIdentity.
Interface for objects representing user identity.
getId( $wikiId=self::LOCAL)
getKey()
Returns the message key.
ListType
The constants used to specify list types.
Definition ListType.php:9