MediaWiki master
PermissionManager.php
Go to the documentation of this file.
1<?php
7
8use InvalidArgumentException;
9use LogicException;
40use StatusValue;
43use Wikimedia\ScopedCallback;
44
52
54 public const RIGOR_QUICK = 'quick';
55
57 public const RIGOR_FULL = 'full';
58
60 public const RIGOR_SECURE = 'secure';
61
65 public const CONSTRUCTOR_OPTIONS = [
78 ];
79
80 private ServiceOptions $options;
81 private SpecialPageFactory $specialPageFactory;
82 private NamespaceInfo $nsInfo;
83 private GroupPermissionsLookup $groupPermissionsLookup;
84 private UserGroupManager $userGroupManager;
85 private BlockManager $blockManager;
86 private BlockErrorFormatter $blockErrorFormatter;
87 private HookRunner $hookRunner;
88 private UserIdentityLookup $userIdentityLookup;
89 private RedirectLookup $redirectLookup;
90 private RestrictionStore $restrictionStore;
91 private TitleFormatter $titleFormatter;
92 private TempUserConfig $tempUserConfig;
93 private UserFactory $userFactory;
94 private ActionFactory $actionFactory;
95
97 private $allRights;
98
100 private $implicitRights;
101
103 private $usersRights = [];
104
109 private $temporaryUserRights = [];
110
112 private $cachedRights = [];
113
120 private const CORE_RIGHTS = [
121 'apihighlimits',
122 'applychangetags',
123 'autoconfirmed',
124 'autocreateaccount',
125 'autopatrol',
126 'bigdelete',
127 'block',
128 'blockemail',
129 'bot',
130 'browsearchive',
131 'changetags',
132 'createaccount',
133 'createpage',
134 'createtalk',
135 'delete',
136 'delete-redirect',
137 'deletechangetags',
138 'deletedhistory',
139 'deletedtext',
140 'deletelogentry',
141 'deleterevision',
142 'edit',
143 'editcontentmodel',
144 'editinterface',
145 'editprotected',
146 'editmyoptions',
147 'editmyprivateinfo',
148 'editmyusercss',
149 'editmyuserjson',
150 'editmyuserjs',
151 'editmyuserjsredirect',
152 'editmywatchlist',
153 'editsemiprotected',
154 'editsitecss',
155 'editsitejson',
156 'editsitejs',
157 'editusercss',
158 'edituserjson',
159 'edituserjs',
160 'hideuser',
161 'ignore-restricted-groups',
162 'import',
163 'importupload',
164 'interwiki',
165 'ipblock-exempt',
166 'managechangetags',
167 'markbotedits',
168 'mergehistory',
169 'minoredit',
170 'move',
171 'movefile',
172 'move-categorypages',
173 'move-rootuserpages',
174 'move-subpages',
175 'nominornewtalk',
176 'noratelimit',
177 'override-export-depth',
178 'pagelang',
179 'patrol',
180 'patrolmarks',
181 'protect',
182 'read',
183 'renameuser',
184 'renameuser-global',
185 'reupload',
186 'reupload-own',
187 'reupload-shared',
188 'rollback',
189 'sendemail',
190 'siteadmin',
191 'suppressionlog',
192 'suppressredirect',
193 'suppressrevision',
194 'unblockself',
195 'undelete',
196 'unwatchedpages',
197 'upload',
198 'upload_by_url',
199 'userrights',
200 'userrights-interwiki',
201 'viewmyprivateinfo',
202 'viewmywatchlist',
203 'viewsuppressed',
204 ];
205
212 private const CORE_IMPLICIT_RIGHTS = [
213 'renderfile',
214 'renderfile-nonstandard',
215 'stashedit',
216 'stashbasehtml',
217 'mailpassword',
218 'changeemail',
219 'confirmemail',
220 'linkpurge',
221 'purge',
222 ];
223
224 public function __construct(
225 ServiceOptions $options,
226 SpecialPageFactory $specialPageFactory,
227 NamespaceInfo $nsInfo,
228 GroupPermissionsLookup $groupPermissionsLookup,
229 UserGroupManager $userGroupManager,
230 BlockManager $blockManager,
231 BlockErrorFormatter $blockErrorFormatter,
232 HookContainer $hookContainer,
233 UserIdentityLookup $userIdentityLookup,
234 RedirectLookup $redirectLookup,
235 RestrictionStore $restrictionStore,
236 TitleFormatter $titleFormatter,
237 TempUserConfig $tempUserConfig,
238 UserFactory $userFactory,
239 ActionFactory $actionFactory
240 ) {
241 $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
242 $this->options = $options;
243 $this->specialPageFactory = $specialPageFactory;
244 $this->nsInfo = $nsInfo;
245 $this->groupPermissionsLookup = $groupPermissionsLookup;
246 $this->userGroupManager = $userGroupManager;
247 $this->blockManager = $blockManager;
248 $this->blockErrorFormatter = $blockErrorFormatter;
249 $this->hookRunner = new HookRunner( $hookContainer );
250 $this->userIdentityLookup = $userIdentityLookup;
251 $this->redirectLookup = $redirectLookup;
252 $this->restrictionStore = $restrictionStore;
253 $this->titleFormatter = $titleFormatter;
254 $this->tempUserConfig = $tempUserConfig;
255 $this->userFactory = $userFactory;
256 $this->actionFactory = $actionFactory;
257 }
258
276 public function userCan( $action, User $user, LinkTarget $page, $rigor = self::RIGOR_FULL ): bool {
277 return $this->getPermissionStatus( $action, $user, $page, $rigor, true )->isGood();
278 }
279
295 public function quickUserCan( $action, User $user, LinkTarget $page ): bool {
296 return $this->userCan( $action, $user, $page, self::RIGOR_QUICK );
297 }
298
300 private const BLOCK_CODES = [
301 'blockedtext' => true,
302 'blockedtext-partial' => true,
303 'autoblockedtext' => true,
304 'systemblockedtext' => true,
305 'blockedtext-composite' => true,
306 'blockedtext-tempuser' => true,
307 'autoblockedtext-tempuser' => true,
308 ];
309
335 public function getPermissionErrors(
336 $action,
337 User $user,
338 LinkTarget $page,
339 $rigor = self::RIGOR_SECURE,
340 $ignoreErrors = []
341 ): array {
342 $status = $this->getPermissionStatus( $action, $user, $page, $rigor );
343 $result = [];
344
345 // Produce a result in the weird format used by this function
346 foreach ( $status->getErrors() as [ 'message' => $keyOrMsg, 'params' => $params ] ) {
347 $key = $keyOrMsg instanceof MessageSpecifier ? $keyOrMsg->getKey() : $keyOrMsg;
348 // Remove the errors being ignored.
349 if ( !in_array( $key, $ignoreErrors ) ) {
350 // Remove modern block info that is not expected by users of this legacy API
351 if ( isset( self::BLOCK_CODES[ $key ] ) && $keyOrMsg instanceof MessageSpecifier ) {
352 $params = $keyOrMsg->getParams();
353 $keyOrMsg = $key;
354 }
355 $result[] = [ $keyOrMsg, ...$params ];
356 }
357 }
358 return $result;
359 }
360
376 public function throwPermissionErrors(
377 $action,
378 User $user,
379 LinkTarget $page,
380 $rigor = self::RIGOR_SECURE,
381 $ignoreErrors = []
382 ): void {
383 $status = $this->getPermissionStatus(
384 $action, $user, $page, $rigor );
385 if ( $status->hasMessagesExcept( ...$ignoreErrors ) ) {
386 throw new PermissionsError( $action, $status );
387 }
388 }
389
399 public function isBlockedFrom( User $user, $page, $fromReplica = false ): bool {
400 return (bool)$this->getApplicableBlock(
401 'edit',
402 $user,
403 $fromReplica ? self::RIGOR_FULL : self::RIGOR_SECURE,
404 $page,
405 $user->getRequest()
406 );
407 }
408
427 public function getPermissionStatus(
428 $action,
429 User $user,
430 LinkTarget $page,
431 $rigor = self::RIGOR_SECURE,
432 $short = false
434 if ( !in_array( $rigor, [ self::RIGOR_QUICK, self::RIGOR_FULL, self::RIGOR_SECURE ] ) ) {
435 throw new InvalidArgumentException( "Invalid rigor parameter '$rigor'." );
436 }
437
438 // With RIGOR_QUICK we can assume automatic account creation will
439 // occur. At a higher rigor level, the caller is required to opt
440 // in by either passing in a temp placeholder user or by actually
441 // creating the account.
442 if ( $rigor === self::RIGOR_QUICK
443 && !$user->isRegistered()
444 && $this->tempUserConfig->isAutoCreateAction( $action )
445 ) {
446 $user = $this->userFactory->newTempPlaceholder();
447 }
448
449 # Read has special handling
450 if ( $action === 'read' ) {
451 $checks = [
452 $this->checkPermissionHooks( ... ),
453 $this->checkReadPermissions( ... ),
454 $this->checkUserBlock( ... ), // for wgBlockDisablesLogin
455 ];
456 } elseif ( $action === 'create' ) {
457 # Don't call checkSpecialsAndNSPermissions, checkSiteConfigPermissions
458 # or checkUserConfigPermissions here as it will lead to duplicate
459 # error messages. This is okay to do since anywhere that checks for
460 # create will also check for edit, and those checks are called for edit.
461 $checks = [
462 $this->checkQuickPermissions( ... ),
463 $this->checkPermissionHooks( ... ),
464 $this->checkPageRestrictions( ... ),
465 $this->checkCascadingSourcesRestrictions( ... ),
466 $this->checkActionPermissions( ... ),
467 $this->checkUserBlock( ... ),
468 ];
469 } else {
470 // Exclude checkUserConfigPermissions on actions that cannot change the
471 // content of the configuration pages.
472 $skipUserConfigActions = [
473 // Allow patrolling per T21818
474 'patrol',
475
476 // Allow (un)watch (T373758)
477 'editmywatchlist',
478
479 // Allow admins and oversighters to delete. For user pages we want to avoid the
480 // situation where an unprivileged user can post abusive content on
481 // their subpages and only very highly privileged users could remove it.
482 // See T200176.
483 'delete',
484 'deleterevision',
485 'suppressrevision',
486
487 // Allow admins and oversighters to view deleted content, even if they
488 // cannot restore it. See T202989
489 'deletedhistory',
490 'deletedtext',
491 'viewsuppressed',
492 ];
493
494 $checks = [
495 $this->checkQuickPermissions( ... ),
496 $this->checkPermissionHooks( ... ),
497 $this->checkSpecialsAndNSPermissions( ... ),
498 $this->checkSiteConfigPermissions( ... ),
499 ];
500 if ( !in_array( $action, $skipUserConfigActions, true ) ) {
501 $checks[] = $this->checkUserConfigPermissions( ... );
502 }
503 $checks = [
504 ...$checks,
505 $this->checkPageRestrictions( ... ),
506 $this->checkCascadingSourcesRestrictions( ... ),
507 $this->checkActionPermissions( ... ),
508 $this->checkUserBlock( ... )
509 ];
510 }
511
512 $status = PermissionStatus::newEmpty();
513 foreach ( $checks as $callback ) {
514 $callback( $action, $user, $status, $rigor, $short, $page );
515
516 if ( $short && !$status->isGood() ) {
517 break;
518 }
519 }
520
521 // Clone the status to prevent users of this hook from modifying the original
522 $this->hookRunner->onPermissionStatusAudit( $page, $user, $action, $rigor, clone $status );
523
524 return $status;
525 }
526
540 private function checkPermissionHooks(
541 $action,
542 User $user,
543 PermissionStatus $status,
544 $rigor,
545 $short,
546 LinkTarget $page
547 ): void {
548 // TODO: remove when LinkTarget usage will expand further
549 $title = Title::newFromLinkTarget( $page );
550 // Use getUserPermissionsErrors instead
551 $result = '';
552 if ( !$this->hookRunner->onUserCan( $title, $user, $action, $result ) ) {
553 if ( !$result ) {
554 $status->fatal( 'badaccess-group0' );
555 }
556 return;
557 }
558 // Check getUserPermissionsErrors hook
559 if ( !$this->hookRunner->onGetUserPermissionsErrors( $title, $user, $action, $result ) ) {
560 $this->resultToStatus( $status, $result );
561 }
562 // Check getUserPermissionsErrorsExpensive hook
563 if (
564 $rigor !== self::RIGOR_QUICK
565 && !( $short && !$status->isGood() )
566 && !$this->hookRunner->onGetUserPermissionsErrorsExpensive(
567 $title, $user, $action, $result )
568 ) {
569 $this->resultToStatus( $status, $result );
570 }
571 }
572
579 private function resultToStatus( PermissionStatus $status, $result ): void {
580 if ( is_array( $result ) && count( $result ) && !is_array( $result[0] ) ) {
581 // A single array representing an error
582 $status->fatal( ...$result );
583 } elseif ( is_array( $result ) && count( $result ) && is_array( $result[0] ) ) {
584 // A nested array representing multiple errors
585 foreach ( $result as $result1 ) {
586 $this->resultToStatus( $status, $result1 );
587 }
588 } elseif ( is_string( $result ) && $result !== '' ) {
589 // A string representing a message-id
590 $status->fatal( $result );
591 } elseif ( $result instanceof MessageSpecifier ) {
592 // A message specifier representing an error
593 $status->fatal( $result );
594 } elseif ( $result === false ) {
595 // a generic "We don't want them to do that"
596 $status->fatal( 'badaccess-group0' );
597 }
598 // If we got here, $results is the empty array or empty string, which mean no errors.
599 }
600
614 private function checkReadPermissions(
615 $action,
616 User $user,
617 PermissionStatus $status,
618 $rigor,
619 $short,
620 LinkTarget $page
621 ): void {
622 // TODO: remove when LinkTarget usage will expand further
623 $title = Title::newFromLinkTarget( $page );
624
625 $whiteListRead = $this->options->get( MainConfigNames::WhitelistRead );
626 $allowed = false;
627 if ( $this->isEveryoneAllowed( 'read' ) ) {
628 // Shortcut for public wikis, allows skipping quite a bit of code
629 $allowed = true;
630 } elseif ( $this->userHasRight( $user, 'read' ) ) {
631 // If the user is allowed to read pages, they are allowed to read all pages
632 $allowed = true;
633 } elseif ( $this->isSameSpecialPage( 'Userlogin', $page )
634 || $this->isSameSpecialPage( 'PasswordReset', $page )
635 || $this->isSameSpecialPage( 'Userlogout', $page )
636 ) {
637 // Always grant access to the login page.
638 // Even anons need to be able to log in.
639 $allowed = true;
640 } elseif ( $this->isSameSpecialPage( 'RunJobs', $page ) ) {
641 // relies on HMAC key signature alone
642 $allowed = true;
643 } elseif ( is_array( $whiteListRead ) && count( $whiteListRead ) ) {
644 // Time to check the whitelist
645 // Only do these checks if there's something to check against
646 $name = $title->getPrefixedText();
647 $dbName = $title->getPrefixedDBkey();
648
649 // Check for explicit whitelisting with and without underscores
650 if ( in_array( $name, $whiteListRead, true )
651 || in_array( $dbName, $whiteListRead, true )
652 ) {
653 $allowed = true;
654 } elseif ( $page->getNamespace() === NS_MAIN ) {
655 // Old settings might have the title prefixed with
656 // a colon for main-namespace pages
657 if ( in_array( ':' . $name, $whiteListRead ) ) {
658 $allowed = true;
659 }
660 } elseif ( $title->isSpecialPage() ) {
661 // If it's a special page, ditch the subpage bit and check again
662 $name = $title->getDBkey();
663 [ $name, /* $subpage */ ] =
664 $this->specialPageFactory->resolveAlias( $name );
665 if ( $name ) {
666 $pure = SpecialPage::getTitleFor( $name )->getPrefixedText();
667 if ( in_array( $pure, $whiteListRead, true ) ) {
668 $allowed = true;
669 }
670 }
671 }
672 }
673
674 $whitelistReadRegexp = $this->options->get( MainConfigNames::WhitelistReadRegexp );
675 if ( !$allowed && is_array( $whitelistReadRegexp )
676 && $whitelistReadRegexp
677 ) {
678 $name = $title->getPrefixedText();
679 // Check for regex whitelisting
680 foreach ( $whitelistReadRegexp as $listItem ) {
681 if ( preg_match( $listItem, $name ) ) {
682 $allowed = true;
683 break;
684 }
685 }
686 }
687
688 if ( !$allowed ) {
689 // If the title is not allowed, give extensions a chance to do so
690 $this->hookRunner->onTitleReadWhitelist( $title, $user, $allowed );
691 if ( !$allowed ) {
692 $this->missingPermissionError( $action, $short, $status );
693 }
694 }
695 }
696
704 private function missingPermissionError( string $action, bool $short, PermissionStatus $status ): void {
705 // We avoid expensive display logic for quickUserCan's and such
706 if ( $short ) {
707 $status->fatal( 'badaccess-group0' );
708 return;
709 }
710
711 // TODO: it would be a good idea to replace the method below with something else like
712 // maybe callback injection
713 $context = RequestContext::getMain();
714 $fatalStatus = $this->newFatalPermissionDeniedStatus( $action, $context );
715 $status->merge( $fatalStatus );
716 $statusPermission = $fatalStatus->getPermission();
717 if ( $statusPermission ) {
718 $status->setPermission( $statusPermission );
719 }
720 }
721
732 public function newFatalPermissionDeniedStatus( $permission, IContextSource $context ): StatusValue {
733 $groups = [];
734 foreach ( $this->groupPermissionsLookup->getGroupsWithPermission( $permission ) as $group ) {
735 $groups[] = UserGroupMembership::getLinkWiki( $group, $context );
736 }
737
738 if ( $groups ) {
739 return PermissionStatus::newFatal(
740 'badaccess-groups',
741 Message::listParam( $groups, ListType::COMMA ),
742 count( $groups )
743 );
744 }
745
746 $status = PermissionStatus::newFatal( 'badaccess-group0' );
747 $status->setPermission( $permission );
748 return $status;
749 }
750
758 private function isSameSpecialPage( $name, LinkTarget $page ): bool {
759 if ( $page->getNamespace() === NS_SPECIAL ) {
760 [ $pageName ] = $this->specialPageFactory->resolveAlias( $page->getDBkey() );
761 if ( $name === $pageName ) {
762 return true;
763 }
764 }
765 return false;
766 }
767
781 private function checkUserBlock(
782 $action,
783 User $user,
784 PermissionStatus $status,
785 $rigor,
786 $short,
787 LinkTarget $page
788 ): void {
789 $block = $this->getApplicableBlock(
790 $action,
791 $user,
792 $rigor,
793 $page,
794 $user->getRequest()
795 );
796
797 if ( $block ) {
798 $status->setBlock( $block );
799
800 // @todo FIXME: Pass the relevant context into this function.
801 $context = RequestContext::getMain();
802 $messages = $this->blockErrorFormatter->getMessages(
803 $block,
804 $user,
805 $context->getRequest()->getIP()
806 );
807
808 foreach ( $messages as $message ) {
809 $status->fatal( $message );
810 }
811 }
812 }
813
830 public function getApplicableBlock(
831 string $action,
832 User $user,
833 string $rigor,
834 $page,
835 ?WebRequest $request
836 ): ?Block {
837 // Unblocking handled in SpecialUnblock
838 if ( $rigor === self::RIGOR_QUICK || in_array( $action, [ 'unblock' ] ) ) {
839 return null;
840 }
841
842 // Optimize for a very common case
843 if ( $action === 'read' && !$this->options->get( MainConfigNames::BlockDisablesLogin ) ) {
844 return null;
845 }
846
847 // Implicit rights aren't blockable (T350117, T350202).
848 if ( in_array( $action, $this->getImplicitRights(), true ) ) {
849 return null;
850 }
851
852 $useReplica = $rigor !== self::RIGOR_SECURE;
853 $isExempt = $this->userHasRight( $user, 'ipblock-exempt' );
854 $requestIfNotExempt = $isExempt ? null : $request;
855
856 // Create account blocks are implemented separately due to weird IP exemption rules
857 if ( in_array( $action, [ 'createaccount', 'autocreateaccount' ], true ) ) {
858 return $this->blockManager->getCreateAccountBlock(
859 $user,
860 $requestIfNotExempt,
861 $useReplica
862 );
863 }
864
865 $block = $this->blockManager->getBlock( $user, $requestIfNotExempt, $useReplica );
866 if ( !$block ) {
867 return null;
868 }
869 $userIsHidden = $block->getHideName();
870
871 // Remove elements from the block that explicitly allow the action
872 // (like "read" or "upload").
873 $block = $this->blockManager->filter(
874 $block,
875 static function ( AbstractBlock $originalBlock ) use ( $action ) {
876 // Remove the block if it explicitly allows the action
877 return $originalBlock->appliesToRight( $action ) !== false;
878 }
879 );
880 if ( !$block ) {
881 return null;
882 }
883
884 // Convert the input page to a Title
885 $targetTitle = null;
886 if ( $page ) {
887 $targetTitle = $page instanceof PageReference ?
888 Title::castFromPageReference( $page ) :
889 Title::castFromLinkTarget( $page );
890
891 if ( !$targetTitle->canExist() ) {
892 $targetTitle = null;
893 }
894 }
895
896 // What gets passed into this method is a user right, not an action name.
897 // There is no way to instantiate an action by restriction. However, this
898 // will get the action where the restriction is the same. This may result
899 // in actions being blocked that shouldn't be.
900 $actionInfo = $this->actionFactory->getActionInfo( $action, $targetTitle );
901
902 // Ensure that the retrieved action matches the restriction.
903 if ( $actionInfo && $actionInfo->getRestriction() !== $action ) {
904 $actionInfo = null;
905 }
906
907 // Return null if the action does not require an unblocked user.
908 // If no ActionInfo is returned, assume that the action requires unblock
909 // which is the default.
910 // NOTE: We may get null here even for known actions, if a wiki's main page
911 // is set to a special page, e.g. Special:MyLanguage/Main_Page (T348451, T346036).
912 if ( $actionInfo && !$actionInfo->requiresUnblock() ) {
913 return null;
914 }
915
916 // Remove elements from the block that do not apply to the specific page
917 if ( $targetTitle ) {
918 $targetIsUserTalk = !$userIsHidden && $targetTitle->equals( $user->getTalkPage() );
919 $block = $this->blockManager->filter(
920 $block,
921 static function ( AbstractBlock $originalBlock )
922 use ( $action, $targetTitle, $targetIsUserTalk ) {
923 if ( $originalBlock->appliesToRight( $action ) ) {
924 // An action block takes precedence over appliesToTitle().
925 // Block::appliesToRight('edit') always returns null,
926 // allowing title-based exemptions to take effect.
927 return true;
928 } elseif ( $targetIsUserTalk ) {
929 // Special handling for a user's own talk page. The block is not aware
930 // of the user, so this must be done here.
931 return $originalBlock->appliesToUsertalk( $targetTitle );
932 } else {
933 return $originalBlock->appliesToTitle( $targetTitle );
934 }
935 }
936 );
937 }
938
939 if ( $targetTitle && $block
940 && $block instanceof AbstractBlock // for phan
941 ) {
942 // Allow extensions to let a blocked user access a particular page
943 $allowUsertalk = $block->isUsertalkEditAllowed();
944 $blocked = true;
945 $this->hookRunner->onUserIsBlockedFrom( $user, $targetTitle, $blocked, $allowUsertalk );
946 if ( !$blocked ) {
947 $block = null;
948 }
949 }
950 return $block;
951 }
952
966 private function checkQuickPermissions(
967 $action,
968 User $user,
969 PermissionStatus $status,
970 $rigor,
971 $short,
972 LinkTarget $page
973 ): void {
974 // TODO: remove when LinkTarget usage will expand further
975 $title = Title::newFromLinkTarget( $page );
976
977 // This method is always called first, so $status is guaranteed to be empty, so we can
978 // just pass an empty $errors array, instead of converting it to the legacy format and back.
979 $errors = [];
980 if ( !$this->hookRunner->onTitleQuickPermissions( $title, $user, $action,
981 $errors, $rigor !== self::RIGOR_QUICK, $short )
982 ) {
983 // $errors is an array of results, not a result, but resultToStatus() handles
984 // arrays of arrays with recursion so this will work
985 $this->resultToStatus( $status, $errors );
986 return;
987 }
988
989 $isSubPage =
990 $this->nsInfo->hasSubpages( $title->getNamespace() ) &&
991 str_contains( $title->getText(), '/' );
992
993 if ( $action === 'create' ) {
994 if (
995 ( $this->nsInfo->isTalk( $title->getNamespace() ) &&
996 !$this->userHasRight( $user, 'createtalk' ) ) ||
997 ( !$this->nsInfo->isTalk( $title->getNamespace() ) &&
998 !$this->userHasRight( $user, 'createpage' ) )
999 ) {
1000 $status->fatal( $user->isNamed() ? 'nocreate-loggedin' : 'nocreatetext' );
1001 }
1002 } elseif ( $action === 'move' ) {
1003 if ( !$this->userHasRight( $user, 'move-rootuserpages' )
1004 && $title->getNamespace() === NS_USER && !$isSubPage
1005 ) {
1006 // Show user page-specific message only if the user can move other pages
1007 $status->fatal( 'cant-move-user-page' );
1008 }
1009
1010 // Check if user is allowed to move files if it's a file
1011 if ( $title->getNamespace() === NS_FILE &&
1012 !$this->userHasRight( $user, 'movefile' )
1013 ) {
1014 $status->fatal( 'movenotallowedfile' );
1015 }
1016
1017 // Check if user is allowed to move category pages if it's a category page
1018 if ( $title->getNamespace() === NS_CATEGORY &&
1019 !$this->userHasRight( $user, 'move-categorypages' )
1020 ) {
1021 $status->fatal( 'cant-move-category-page' );
1022 }
1023
1024 if ( !$this->userHasRight( $user, 'move' ) ) {
1025 // User can't move anything
1026 $userCanMove = $this->groupPermissionsLookup
1027 ->groupHasPermission( 'user', 'move' );
1028 $autoconfirmedCanMove = $this->groupPermissionsLookup
1029 ->groupHasPermission( 'autoconfirmed', 'move' );
1030 if ( $user->isAnon()
1031 && ( $userCanMove || $autoconfirmedCanMove )
1032 ) {
1033 // custom message if logged-in users without any special rights can move
1034 $status->fatal( 'movenologintext' );
1035 } elseif ( $user->isTemp() && $autoconfirmedCanMove ) {
1036 // Temp user may be able to move if they log in as a proper account
1037 $status->fatal( 'movenologintext' );
1038 } else {
1039 $status->fatal( 'movenotallowed' );
1040 }
1041 }
1042 } elseif ( $action === 'move-target' ) {
1043 if ( !$this->userHasRight( $user, 'move' ) ) {
1044 // User can't move anything
1045 $status->fatal( 'movenotallowed' );
1046 } elseif ( !$this->userHasRight( $user, 'move-rootuserpages' )
1047 && $title->getNamespace() === NS_USER
1048 && !$isSubPage
1049 ) {
1050 // Show user page-specific message only if the user can move other pages
1051 $status->fatal( 'cant-move-to-user-page' );
1052 } elseif ( !$this->userHasRight( $user, 'move-categorypages' )
1053 && $title->getNamespace() === NS_CATEGORY
1054 ) {
1055 // Show category page-specific message only if the user can move other pages
1056 $status->fatal( 'cant-move-to-category-page' );
1057 }
1058 } elseif ( $action === 'autocreateaccount' ) {
1059 // createaccount implies autocreateaccount
1060 if ( !$this->userHasAnyRight( $user, 'autocreateaccount', 'createaccount' ) ) {
1061 $this->missingPermissionError( $action, $short, $status );
1062 }
1063 } elseif ( !$this->userHasRight( $user, $action ) ) {
1064 $this->missingPermissionError( $action, $short, $status );
1065 }
1066 }
1067
1084 private function checkPageRestrictions(
1085 $action,
1086 UserIdentity $user,
1087 PermissionStatus $status,
1088 $rigor,
1089 $short,
1090 LinkTarget $page
1091 ): void {
1092 // TODO: remove & rework upon further use of LinkTarget
1093 $title = Title::newFromLinkTarget( $page );
1094 foreach ( $this->restrictionStore->getRestrictions( $title, $action ) as $right ) {
1095 // Backwards compatibility, rewrite sysop -> editprotected
1096 if ( $right === 'sysop' ) {
1097 $right = 'editprotected';
1098 }
1099 // Backwards compatibility, rewrite autoconfirmed -> editsemiprotected
1100 if ( $right === 'autoconfirmed' ) {
1101 $right = 'editsemiprotected';
1102 }
1103 if ( $right == '' ) {
1104 continue;
1105 }
1106 if ( !$this->userHasRight( $user, $right ) ) {
1107 $status->fatal( 'protectedpagetext', $right, $action );
1108 } elseif ( $this->restrictionStore->areRestrictionsCascading( $title ) &&
1109 !$this->userHasRight( $user, 'protect' )
1110 ) {
1111 $status->fatal( 'protectedpagetext', 'protect', $action );
1112 }
1113 }
1114 }
1115
1129 private function checkCascadingSourcesRestrictions(
1130 $action,
1131 UserIdentity $user,
1132 PermissionStatus $status,
1133 $rigor,
1134 $short,
1135 LinkTarget $page
1136 ): void {
1137 // TODO: remove & rework upon further use of LinkTarget
1138 $title = Title::newFromLinkTarget( $page );
1139
1140 if ( $rigor !== self::RIGOR_QUICK && !$title->isUserConfigPage() ) {
1141 [ $sources, $restrictions, $tlSources, $ilSources ] = $this->restrictionStore
1142 ->getCascadeProtectionSources( $title );
1143
1144 // If the file Wikitext isn't transcluded then we
1145 // don't care about edit cascade restrictions for edit action
1146 if ( $action === 'edit' && $page->getNamespace() === NS_FILE && !$tlSources ) {
1147 return;
1148 }
1149
1150 // For the purposes of cascading protection, edit restrictions should apply to uploads or moves
1151 // Thus remap upload and move to edit
1152 // Unless the file content itself is not transcluded
1153 if ( $ilSources && ( $action === 'upload' || $action === 'move' ) ) {
1154 $restrictedAction = 'edit';
1155 } else {
1156 $restrictedAction = $action;
1157 }
1158
1159 // Cascading protection depends on more than this page...
1160 // Several cascading protected pages may include this page...
1161 // Check each cascading level
1162 // This is only for protection restrictions, not for all actions
1163 if ( isset( $restrictions[$restrictedAction] ) ) {
1164 foreach ( $restrictions[$restrictedAction] as $right ) {
1165 // Backwards compatibility, rewrite sysop -> editprotected
1166 if ( $right === 'sysop' ) {
1167 $right = 'editprotected';
1168 }
1169 // Backwards compatibility, rewrite autoconfirmed -> editsemiprotected
1170 if ( $right === 'autoconfirmed' ) {
1171 $right = 'editsemiprotected';
1172 }
1173 if ( $right != '' && !$this->userHasAllRights( $user, 'protect', $right ) ) {
1174 $wikiPages = '';
1175 foreach ( $sources as $pageIdentity ) {
1176 $wikiPages .= '* [[:' . $this->titleFormatter->getPrefixedText( $pageIdentity ) . "]]\n";
1177 }
1178 $status->fatal( 'cascadeprotected', count( $sources ), $wikiPages, $action );
1179 }
1180 }
1181 }
1182 }
1183 }
1184
1198 private function checkActionPermissions(
1199 $action,
1200 User $user,
1201 PermissionStatus $status,
1202 $rigor,
1203 $short,
1204 LinkTarget $page
1205 ): void {
1206 // TODO: remove & rework upon further use of LinkTarget
1207 $title = Title::newFromLinkTarget( $page );
1208
1209 if ( $rigor !== self::RIGOR_QUICK && !defined( 'MW_NO_SESSION' ) ) {
1210 $sessionRestrictions = $user->getRequest()->getSession()->getRestrictions();
1211 if ( $sessionRestrictions ) {
1212 $userCan = $sessionRestrictions->userCan( $title );
1213 if ( !$userCan->isOK() ) {
1214 $status->merge( $userCan );
1215 }
1216 }
1217 }
1218
1219 if ( $action === 'protect' ) {
1220 if ( !$this->getPermissionStatus( 'edit', $user, $title, $rigor, true )->isGood() ) {
1221 // If they can't edit, they shouldn't protect.
1222 $status->fatal( 'protect-cantedit' );
1223 }
1224 } elseif ( $action === 'create' ) {
1225 $createProtection = $this->restrictionStore->getCreateProtection( $title );
1226 if ( $createProtection ) {
1227 if ( $createProtection['permission'] == ''
1228 || !$this->userHasRight( $user, $createProtection['permission'] )
1229 ) {
1230 $protectUserIdentity = $this->userIdentityLookup
1231 ->getUserIdentityByUserId( $createProtection['user'] );
1232 $status->fatal(
1233 'titleprotected',
1234 $protectUserIdentity ? $protectUserIdentity->getName() : '',
1235 $createProtection['reason']
1236 );
1237 }
1238 }
1239 } elseif ( $action === 'move' ) {
1240 // Check for immobile pages
1241 if ( !$this->nsInfo->isMovable( $title->getNamespace() ) ) {
1242 // Specific message for this case
1243 $nsText = $title->getNsText();
1244 if ( $nsText === '' ) {
1245 $nsText = wfMessage( 'blanknamespace' )->text();
1246 }
1247 $status->fatal( 'immobile-source-namespace', $nsText );
1248 } elseif ( !$title->isMovable() ) {
1249 // Less specific message for rarer cases
1250 $status->fatal( 'immobile-source-page' );
1251 }
1252 } elseif ( $action === 'move-target' ) {
1253 if ( !$this->nsInfo->isMovable( $title->getNamespace() ) ) {
1254 $nsText = $title->getNsText();
1255 if ( $nsText === '' ) {
1256 $nsText = wfMessage( 'blanknamespace' )->text();
1257 }
1258 $status->fatal( 'immobile-target-namespace', $nsText );
1259 } elseif ( !$title->isMovable() ) {
1260 $status->fatal( 'immobile-target-page' );
1261 }
1262 } elseif ( $action === 'delete' || $action === 'delete-redirect' ) {
1263 $tempStatus = PermissionStatus::newEmpty();
1264 $this->checkPageRestrictions( 'edit', $user, $tempStatus, $rigor, true, $title );
1265 if ( $tempStatus->isGood() ) {
1266 $this->checkCascadingSourcesRestrictions( 'edit',
1267 $user, $tempStatus, $rigor, true, $title );
1268 }
1269 if ( !$tempStatus->isGood() ) {
1270 // If protection keeps them from editing, they shouldn't be able to delete.
1271 $status->fatal( 'deleteprotected' );
1272 }
1273 if ( $rigor !== self::RIGOR_QUICK
1274 && $action === 'delete'
1275 && $this->options->get( MainConfigNames::DeleteRevisionsLimit )
1276 && !$this->userCan( 'bigdelete', $user, $title )
1277 && $title->isBigDeletion()
1278 ) {
1279 // NOTE: This check is deprecated since 1.37, see T288759
1280 $status->fatal(
1281 'delete-toobig',
1282 Message::numParam( $this->options->get( MainConfigNames::DeleteRevisionsLimit ) )
1283 );
1284 }
1285 } elseif ( $action === 'undelete' ) {
1286 if ( !$this->getPermissionStatus( 'edit', $user, $title, $rigor, true )->isGood() ) {
1287 // Undeleting implies editing
1288 $status->fatal( 'undelete-cantedit' );
1289 }
1290 if ( !$title->exists()
1291 && !$this->getPermissionStatus( 'create', $user, $title, $rigor, true )->isGood()
1292 ) {
1293 // Undeleting where nothing currently exists implies creating
1294 $status->fatal( 'undelete-cantcreate' );
1295 }
1296 } elseif ( $action === 'edit' ) {
1297 if ( $this->options->get( MainConfigNames::EmailConfirmToEdit )
1298 && !$user->isEmailConfirmed()
1299 ) {
1300 $status->fatal( 'confirmedittext' );
1301 }
1302
1303 if ( !$title->exists() ) {
1304 $status->merge(
1305 $this->getPermissionStatus(
1306 'create',
1307 $user,
1308 $title,
1309 $rigor,
1310 true
1311 )
1312 );
1313 }
1314 }
1315 }
1316
1330 private function checkSpecialsAndNSPermissions(
1331 $action,
1332 UserIdentity $user,
1333 PermissionStatus $status,
1334 $rigor,
1335 $short,
1336 LinkTarget $page
1337 ): void {
1338 // TODO: remove & rework upon further use of LinkTarget
1339 $title = Title::newFromLinkTarget( $page );
1340
1341 // Only 'createaccount' can be performed on special pages,
1342 // which don't actually exist in the DB.
1343 if ( $title->getNamespace() === NS_SPECIAL
1344 && !in_array( $action, [ 'createaccount', 'autocreateaccount' ], true )
1345 ) {
1346 $status->fatal( 'ns-specialprotected' );
1347 }
1348
1349 // Check $wgNamespaceProtection for restricted namespaces
1350 if ( $this->isNamespaceProtected( $title->getNamespace(), $user )
1351 // Allow admins and oversighters to view deleted content, even if they
1352 // cannot restore it. See T362536. Allow (un)watch too (T373758)
1353 && !in_array( $action, [ 'deletedhistory', 'deletedtext', 'viewsuppressed', 'editmywatchlist' ], true )
1354 ) {
1355 $ns = $title->getNamespace() === NS_MAIN ?
1356 wfMessage( 'nstab-main' )->text() : $title->getNsText();
1357 if ( $title->getNamespace() === NS_MEDIAWIKI ) {
1358 $status->fatal( 'protectedinterface', $action );
1359 } else {
1360 $status->fatal( 'namespaceprotected', $ns, $action );
1361 }
1362 }
1363 }
1364
1378 private function checkSiteConfigPermissions(
1379 $action,
1380 UserIdentity $user,
1381 PermissionStatus $status,
1382 $rigor,
1383 $short,
1384 LinkTarget $page
1385 ): void {
1386 // TODO: remove & rework upon further use of LinkTarget
1387 $title = Title::newFromLinkTarget( $page );
1388
1389 if ( $action === 'patrol' || $action === 'editmywatchlist' ) {
1390 return;
1391 }
1392
1393 if ( in_array( $action, [ 'deletedhistory', 'deletedtext', 'viewsuppressed' ], true ) ) {
1394 // Allow admins and oversighters to view deleted content, even if they
1395 // cannot restore it. See T202989
1396 // Not using the same handling in `getPermissionStatus` as the checks
1397 // for skipping `checkUserConfigPermissions` since normal admins can delete
1398 // user scripts, but not sitewide scripts
1399 return;
1400 }
1401
1402 // Sitewide CSS/JSON/JS/RawHTML changes, like all NS_MEDIAWIKI changes, also require the
1403 // editinterface right. That's implemented as a restriction so no check needed here.
1404 if ( $title->isSiteCssConfigPage() && !$this->userHasRight( $user, 'editsitecss' ) ) {
1405 $status->fatal( 'sitecssprotected', $action );
1406 } elseif ( $title->isSiteJsonConfigPage() && !$this->userHasRight( $user, 'editsitejson' ) ) {
1407 $status->fatal( 'sitejsonprotected', $action );
1408 } elseif ( $title->isSiteJsConfigPage() && !$this->userHasRight( $user, 'editsitejs' ) ) {
1409 $status->fatal( 'sitejsprotected', $action );
1410 }
1411 if ( $title->isRawHtmlMessage() && !$this->userCanEditRawHtmlPage( $user ) ) {
1412 $status->fatal( 'siterawhtmlprotected', $action );
1413 }
1414 }
1415
1429 private function checkUserConfigPermissions(
1430 $action,
1431 UserIdentity $user,
1432 PermissionStatus $status,
1433 $rigor,
1434 $short,
1435 LinkTarget $page
1436 ): void {
1437 // TODO: remove & rework upon further use of LinkTarget
1438 $title = Title::newFromLinkTarget( $page );
1439
1440 // Protect css/json/js subpages of user pages
1441 // XXX: this might be better using restrictions
1442 if ( preg_match( '/^' . preg_quote( $user->getName(), '/' ) . '\//', $title->getText() ) ) {
1443 // Users need editmyuser* to edit their own CSS/JSON/JS subpages.
1444 if (
1445 $title->isUserCssConfigPage()
1446 && !$this->userHasAnyRight( $user, 'editmyusercss', 'editusercss' )
1447 ) {
1448 $status->fatal( 'mycustomcssprotected', $action );
1449 } elseif (
1450 $title->isUserJsonConfigPage()
1451 && !$this->userHasAnyRight( $user, 'editmyuserjson', 'edituserjson' )
1452 ) {
1453 $status->fatal( 'mycustomjsonprotected', $action );
1454 } elseif (
1455 $title->isUserJsConfigPage()
1456 && !$this->userHasAnyRight( $user, 'editmyuserjs', 'edituserjs' )
1457 ) {
1458 $status->fatal( 'mycustomjsprotected', $action );
1459 } elseif (
1460 $title->isUserJsConfigPage()
1461 && !$this->userHasAnyRight( $user, 'edituserjs', 'editmyuserjsredirect' )
1462 ) {
1463 // T207750 - do not allow users to edit a redirect if they couldn't edit the target
1464 $target = $this->redirectLookup->getRedirectTarget( $title );
1465 if ( $target && (
1466 !$target->inNamespace( NS_USER )
1467 || !preg_match( '/^' . preg_quote( $user->getName(), '/' ) . '\//', $target->getText() )
1468 ) ) {
1469 $status->fatal( 'mycustomjsredirectprotected', $action );
1470 }
1471 }
1472 } else {
1473 // Users need edituser* to edit others' CSS/JSON/JS subpages.
1474 // The checks to exclude deletion/suppression, which cannot be used for
1475 // attacks and should be excluded to avoid the situation where an
1476 // unprivileged user can post abusive content on their subpages
1477 // and only very highly privileged users could remove it,
1478 // are now a part of `getPermissionStatus` and this method isn't called.
1479 if (
1480 $title->isUserCssConfigPage()
1481 && !$this->userHasRight( $user, 'editusercss' )
1482 ) {
1483 $status->fatal( 'customcssprotected', $action );
1484 } elseif (
1485 $title->isUserJsonConfigPage()
1486 && !$this->userHasRight( $user, 'edituserjson' )
1487 ) {
1488 $status->fatal( 'customjsonprotected', $action );
1489 } elseif (
1490 $title->isUserJsConfigPage()
1491 && !$this->userHasRight( $user, 'edituserjs' )
1492 ) {
1493 $status->fatal( 'customjsprotected', $action );
1494 }
1495 }
1496 }
1497
1506 public function userHasRight( UserIdentity $user, $action = '' ): bool {
1507 if ( $action === '' ) {
1508 // In the spirit of DWIM
1509 return true;
1510 }
1511 // Use strict parameter to avoid matching numeric 0 accidentally inserted
1512 // by misconfiguration: 0 == 'foo'
1513 return in_array( $action, $this->getImplicitRights(), true )
1514 || in_array( $action, $this->getUserPermissions( $user ), true );
1515 }
1516
1525 public function userHasAnyRight( UserIdentity $user, ...$actions ): bool {
1526 foreach ( $actions as $action ) {
1527 if ( $this->userHasRight( $user, $action ) ) {
1528 return true;
1529 }
1530 }
1531 return false;
1532 }
1533
1542 public function userHasAllRights( UserIdentity $user, ...$actions ): bool {
1543 foreach ( $actions as $action ) {
1544 if ( !$this->userHasRight( $user, $action ) ) {
1545 return false;
1546 }
1547 }
1548 return true;
1549 }
1550
1558 public function getUserPermissions( UserIdentity $user ): array {
1559 $rightsCacheKey = $this->getRightsCacheKey( $user );
1560 if ( !isset( $this->usersRights[ $rightsCacheKey ] ) ) {
1561 $userObj = $this->userFactory->newFromUserIdentity( $user );
1562 $rights = $this->groupPermissionsLookup->getGroupPermissions(
1563 $this->userGroupManager->getUserEffectiveGroups( $user )
1564 );
1565 // Hook requires a full User object
1566 $this->hookRunner->onUserGetRights( $userObj, $rights );
1567
1568 // Deny any rights denied by the user's session, unless this
1569 // endpoint has no sessions.
1570 if ( !defined( 'MW_NO_SESSION' ) ) {
1571 // FIXME: $userObj->getRequest().. need to be replaced with something else
1572 $allowedRights = $userObj->getRequest()->getSession()->getAllowedUserRights();
1573 if ( $allowedRights !== null ) {
1574 $rights = array_intersect( $rights, $allowedRights );
1575 }
1576 }
1577
1578 // Hook requires a full User object
1579 $this->hookRunner->onUserGetRightsRemove( $userObj, $rights );
1580 // Force reindexation of rights when a hook has unset one of them
1581 $rights = array_values( array_unique( $rights ) );
1582
1583 // If BlockDisablesLogin is true, remove rights that anonymous
1584 // users don't have. This has to be done after the hooks so that
1585 // we know whether the user is exempt. (T129738)
1586 if (
1587 $userObj->isRegistered()
1588 && $this->options->get( MainConfigNames::BlockDisablesLogin )
1589 ) {
1590 // Stash the permissions as they are before triggering any block checks for BlockDisablesLogin
1591 // to avoid a potential infinite loop, since GetUserBlock handlers may themselves check
1592 // permissions on this user. (T384197)
1593 $this->usersRights[ $rightsCacheKey ] = $rights;
1594
1595 $isExempt = in_array( 'ipblock-exempt', $rights, true );
1596 if ( $this->blockManager->getBlock(
1597 $userObj,
1598 $isExempt ? null : $userObj->getRequest()
1599 ) ) {
1600 $anon = $this->userFactory->newAnonymous();
1601 $rights = array_intersect( $rights, $this->getUserPermissions( $anon ) );
1602 }
1603 }
1604
1605 $this->usersRights[ $rightsCacheKey ] = $rights;
1606 } else {
1607 $rights = $this->usersRights[ $rightsCacheKey ];
1608 }
1609 foreach ( $this->temporaryUserRights[ $user->getId() ] ?? [] as $overrides ) {
1610 $rights = array_values( array_unique( array_merge( $rights, $overrides ) ) );
1611 }
1612 return $rights;
1613 }
1614
1622 public function invalidateUsersRightsCache( $user = null ): void {
1623 if ( $user !== null ) {
1624 $rightsCacheKey = $this->getRightsCacheKey( $user );
1625 unset( $this->usersRights[ $rightsCacheKey ] );
1626 } else {
1627 $this->usersRights = [];
1628 }
1629 }
1630
1637 private function getRightsCacheKey( UserIdentity $user ): string {
1638 return $user->isRegistered() ? "u:{$user->getId()}" : "anon:{$user->getName()}";
1639 }
1640
1655 public function isEveryoneAllowed( $right ): bool {
1656 // Use the cached results, except in unit tests which rely on
1657 // being able change the permission mid-request
1658 if ( isset( $this->cachedRights[$right] ) ) {
1659 return $this->cachedRights[$right];
1660 }
1661
1662 if ( !isset( $this->options->get( MainConfigNames::GroupPermissions )['*'][$right] )
1663 || !$this->options->get( MainConfigNames::GroupPermissions )['*'][$right]
1664 ) {
1665 $this->cachedRights[$right] = false;
1666 return false;
1667 }
1668
1669 // If it's revoked anywhere, then everyone doesn't have it
1670 foreach ( $this->options->get( MainConfigNames::RevokePermissions ) as $rights ) {
1671 if ( isset( $rights[$right] ) && $rights[$right] ) {
1672 $this->cachedRights[$right] = false;
1673 return false;
1674 }
1675 }
1676
1677 // Remove any rights that aren't allowed to the global-session user,
1678 // unless there are no sessions for this endpoint.
1679 if ( !defined( 'MW_NO_SESSION' ) ) {
1680 // XXX: think what could be done with the below
1681 $allowedRights = RequestContext::getMain()->getRequest()->getSession()->getAllowedUserRights();
1682 if ( $allowedRights !== null && !in_array( $right, $allowedRights, true ) ) {
1683 $this->cachedRights[$right] = false;
1684 return false;
1685 }
1686 }
1687
1688 // Allow extensions to say false
1689 if ( !$this->hookRunner->onUserIsEveryoneAllowed( $right ) ) {
1690 $this->cachedRights[$right] = false;
1691 return false;
1692 }
1693
1694 $this->cachedRights[$right] = true;
1695 return true;
1696 }
1697
1707 public function getAllPermissions(): array {
1708 if ( $this->allRights === null ) {
1709 if ( count( $this->options->get( MainConfigNames::AvailableRights ) ) ) {
1710 $this->allRights = array_unique( array_merge(
1711 self::CORE_RIGHTS,
1712 $this->options->get( MainConfigNames::AvailableRights )
1713 ) );
1714 } else {
1715 $this->allRights = self::CORE_RIGHTS;
1716 }
1717 $this->hookRunner->onUserGetAllRights( $this->allRights );
1718 }
1719 return $this->allRights;
1720 }
1721
1733 public function getImplicitRights(): array {
1734 if ( $this->implicitRights === null ) {
1735 $rights = array_unique( array_merge(
1736 self::CORE_IMPLICIT_RIGHTS,
1737 $this->options->get( MainConfigNames::ImplicitRights )
1738 ) );
1739
1740 $this->implicitRights = array_diff( $rights, $this->getAllPermissions() );
1741 }
1742 return $this->implicitRights;
1743 }
1744
1752 private function isNamespaceProtected( $index, UserIdentity $user ): bool {
1753 $namespaceProtection = $this->options->get( MainConfigNames::NamespaceProtection );
1754 if ( isset( $namespaceProtection[$index] ) ) {
1755 return !$this->userHasAllRights( $user, ...(array)$namespaceProtection[$index] );
1756 }
1757 return false;
1758 }
1759
1768 public function getNamespaceRestrictionLevels( $index, ?UserIdentity $user = null ): array {
1769 if ( !isset( $this->options->get( MainConfigNames::NamespaceProtection )[$index] ) ) {
1770 // All levels are valid if there's no namespace restriction.
1771 // But still filter by user, if necessary
1772 $levels = $this->options->get( MainConfigNames::RestrictionLevels );
1773 if ( $user ) {
1774 $levels = array_values( array_filter( $levels, function ( $level ) use ( $user ) {
1775 $right = $level;
1776 if ( $right === 'sysop' ) {
1777 $right = 'editprotected'; // BC
1778 }
1779 if ( $right === 'autoconfirmed' ) {
1780 $right = 'editsemiprotected'; // BC
1781 }
1782 return $this->userHasRight( $user, $right );
1783 } ) );
1784 }
1785 return $levels;
1786 }
1787
1788 // $wgNamespaceProtection can require one or more rights to edit the namespace, which
1789 // may be satisfied by membership in multiple groups each giving a subset of those rights.
1790 // A restriction level is redundant if, for any one of the namespace rights, all groups
1791 // giving that right also give the restriction level's right. Or, conversely, a
1792 // restriction level is not redundant if, for every namespace right, there's at least one
1793 // group giving that right without the restriction level's right.
1794 //
1795 // First, for each right, get a list of groups with that right.
1796 $namespaceRightGroups = [];
1797 foreach ( (array)$this->options->get( MainConfigNames::NamespaceProtection )[$index] as $right ) {
1798 if ( $right === 'sysop' ) {
1799 $right = 'editprotected'; // BC
1800 }
1801 if ( $right === 'autoconfirmed' ) {
1802 $right = 'editsemiprotected'; // BC
1803 }
1804 if ( $right != '' ) {
1805 $namespaceRightGroups[$right] = $this->groupPermissionsLookup->getGroupsWithPermission( $right );
1806 }
1807 }
1808
1809 // Now, go through the protection levels one by one.
1810 $usableLevels = [ '' ];
1811 foreach ( $this->options->get( MainConfigNames::RestrictionLevels ) as $level ) {
1812 $right = $level;
1813 if ( $right === 'sysop' ) {
1814 $right = 'editprotected'; // BC
1815 }
1816 if ( $right === 'autoconfirmed' ) {
1817 $right = 'editsemiprotected'; // BC
1818 }
1819
1820 if ( $right != '' &&
1821 !isset( $namespaceRightGroups[$right] ) &&
1822 ( !$user || $this->userHasRight( $user, $right ) )
1823 ) {
1824 // Do any of the namespace rights imply the restriction right? (see explanation above)
1825 foreach ( $namespaceRightGroups as $groups ) {
1826 if ( !array_diff( $groups, $this->groupPermissionsLookup->getGroupsWithPermission( $right ) ) ) {
1827 // Yes, this one does.
1828 continue 2;
1829 }
1830 }
1831 // No, keep the restriction level
1832 $usableLevels[] = $level;
1833 }
1834 }
1835
1836 return $usableLevels;
1837 }
1838
1848 private function userCanEditRawHtmlPage( UserIdentity $user ): bool {
1849 return $this->userHasAllRights( $user, 'editsitecss', 'editsitejs' );
1850 }
1851
1868 #[\NoDiscard]
1869 public function addTemporaryUserRights( UserIdentity $user, $rights ): ScopedCallback {
1870 $userId = $user->getId();
1871 $nextKey = count( $this->temporaryUserRights[$userId] ?? [] );
1872 $this->temporaryUserRights[$userId][$nextKey] = (array)$rights;
1873 return new ScopedCallback( function () use ( $userId, $nextKey ) {
1874 unset( $this->temporaryUserRights[$userId][$nextKey] );
1875 } );
1876 }
1877
1886 public function overrideUserRightsForTesting( $user, $rights = [] ) {
1887 if ( !defined( 'MW_PHPUNIT_TEST' ) ) {
1888 throw new LogicException( __METHOD__ . ' can not be called outside of tests' );
1889 }
1890 $this->usersRights[ $this->getRightsCacheKey( $user ) ] =
1891 is_array( $rights ) ? $rights : [ $rights ];
1892 }
1893
1894}
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:68
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.
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'=> false, '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, 250, 300,], '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, 'DjvuUseBoxedCommand'=> false, 'DjvuDump'=> null, 'DjvuRenderer'=> null, 'DjvuTxt'=> null, 'DjvuPostProcessor'=> 'pnmtojpeg', 'DjvuOutputExtension'=> 'jpg', 'EmergencyContact'=> false, 'PasswordSender'=> false, 'NoReplyAddress'=> false, 'EnableEmail'=> true, 'EnableUserEmail'=> true, 'EnableSpecialMute'=> false, 'EnableUserEmailMuteList'=> false, 'UserEmailUseReplyTo'=> true, 'PasswordReminderResendTime'=> 24, 'NewPasswordExpiry'=> 604800, 'UserEmailConfirmationTokenExpiry'=> 604800, 'UserEmailConfirmationUseHTML'=> false, 'PasswordExpirationDays'=> false, 'PasswordExpireGrace'=> 604800, 'SMTP'=> false, 'AdditionalMailParams'=> null, 'AllowHTMLEmail'=> false, 'EnotifFromEditor'=> false, 'EmailAuthentication'=> true, '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, 'ImageLinksSchemaMigrationStage'=> 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'=> '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,],], 'parsoid-pcache'=>['default'=>['minCpuTime'=> 0,],], 'postproc-pcache'=>['default'=>['minCpuTime'=> 9223372036854775807,],], 'postproc-parsoid-pcache'=>['default'=>['minCpuTime'=> 9223372036854775807,],],], 'ChronologyProtectorSecret'=> '', 'ParserCacheExpireTime'=> 86400, 'ParserCacheAsyncExpireTime'=> 60, 'ParserCacheAsyncRefreshJobs'=> true, 'OldRevisionParserCacheExpireTime'=> 3600, 'ObjectCacheSessionExpiry'=> 3600, 'PHPSessionHandling'=> 'warn', 'SuspiciousIpExpiry'=> false, 'SessionPbkdf2Iterations'=> 10001, 'UseSessionCookieJwt'=> false, '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, ], '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' => [ ], '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, 'pagelang' => true, ], 'editprotected' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'editprotected' => true, ], 'editmycssjs' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'editmyusercss' => true, 'editmyuserjson' => true, 'editmyuserjs' => true, ], 'editmyoptions' => [ 'editmyoptions' => true, 'editmyuserjson' => true, ], 'editinterface' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'editinterface' => true, 'edituserjson' => true, 'editsitejson' => true, ], 'editsiteconfig' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => 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, '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, '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, '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, '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, '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' => [ ], 'RCEngines' => [ 'redis' => 'MediaWiki\\RCFeed\\RedisPubSubFeedEngine', 'udp' => 'MediaWiki\\RCFeed\\UDPRCFeedEngine', ], '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, '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, 'UsePostprocCache' => false, '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', ], 'UserEmailConfirmationUseHTML' => 'boolean', 'SMTP' => [ 'boolean', 'object', ], 'EnotifFromEditor' => 'boolean', 'EnotifRevealEditorAddress' => 'boolean', 'UsersNotifiedOnAllChanges' => 'object', 'DBmwschema' => [ 'string', 'null', ], 'SharedTables' => 'array', 'DBservers' => [ 'boolean', 'array', ], 'LBFactoryConf' => 'object', 'LocalDatabases' => 'array', 'VirtualDomainsMapping' => 'object', 'FileSchemaMigrationStage' => 'integer', 'ImageLinksSchemaMigrationStage' => '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', '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', ], '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', 'RCEngines' => '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', ], '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', 'UsePostprocCache' => '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', ], 'msg' => [ 'type' => 'string', 'description' => 'a message key', ], ], 'required' => [ 'url', ], ], ], '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