MediaWiki master
PermissionManager.php
Go to the documentation of this file.
1<?php
8
9use InvalidArgumentException;
10use LogicException;
45use StatusValue;
50use Wikimedia\ScopedCallback;
51
59
61 public const RIGOR_QUICK = 'quick';
62
64 public const RIGOR_FULL = 'full';
65
67 public const RIGOR_SECURE = 'secure';
68
72 public const CONSTRUCTOR_OPTIONS = [
87 ];
88
89 private HookRunner $hookRunner;
90
92 private $allRights;
93
95 private $implicitRights;
96
98 private $usersRights = [];
99
104 private $temporaryUserRights = [];
105
107 private $cachedRights = [];
108
115 private const CORE_RIGHTS = [
116 'apihighlimits',
117 'applychangetags',
118 'autoconfirmed',
119 'autocreateaccount',
120 'autopatrol',
121 'bigdelete',
122 'block',
123 'blockemail',
124 'bot',
125 'browsearchive',
126 'changetags',
127 'createaccount',
128 'createpreviouslyrenamedaccount',
129 'createwithcontentmodel',
130 'createpage',
131 'createtalk',
132 'delete',
133 'delete-redirect',
134 'deletechangetags',
135 'deletedhistory',
136 'deletedtext',
137 'deletelogentry',
138 'deleterevision',
139 'edit',
140 'editalluserpages',
141 'editcontentmodel',
142 'editinterface',
143 'editprotected',
144 'editmyoptions',
145 'editmyprivateinfo',
146 'editmyusercss',
147 'editmyuserjson',
148 'editmyuserjs',
149 'editmyuserjsredirect',
150 'editmywatchlist',
151 'editsemiprotected',
152 'editsitecss',
153 'editsitejson',
154 'editsitejs',
155 'editusercss',
156 'edituserjson',
157 'edituserjs',
158 'hideuser',
159 'ignore-restricted-groups',
160 'import',
161 'importupload',
162 'interwiki',
163 'ipblock-exempt',
164 'logentryimport',
165 'logout',
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 private ServiceOptions $options,
226 private SpecialPageFactory $specialPageFactory,
227 private NamespaceInfo $nsInfo,
228 private GroupPermissionsLookup $groupPermissionsLookup,
229 private UserGroupManager $userGroupManager,
230 private BlockManager $blockManager,
231 private BlockErrorFormatter $blockErrorFormatter,
232 HookContainer $hookContainer,
233 private UserIdentityLookup $userIdentityLookup,
234 private RedirectLookup $redirectLookup,
235 private RestrictionStore $restrictionStore,
236 private TitleFormatter $titleFormatter,
237 private TempUserConfig $tempUserConfig,
238 private UserFactory $userFactory,
239 private ActionFactory $actionFactory
240 ) {
241 $this->options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
242 $this->hookRunner = new HookRunner( $hookContainer );
243 }
244
262 public function userCan( $action, User $user, LinkTarget $page, $rigor = self::RIGOR_FULL ): bool {
263 return $this->getPermissionStatus( $action, $user, $page, $rigor, true )->isGood();
264 }
265
281 public function quickUserCan( $action, User $user, LinkTarget $page ): bool {
282 return $this->userCan( $action, $user, $page, self::RIGOR_QUICK );
283 }
284
286 private const BLOCK_CODES = [
287 'blockedtext' => true,
288 'blockedtext-partial' => true,
289 'autoblockedtext' => true,
290 'systemblockedtext' => true,
291 'blockedtext-composite' => true,
292 'blockedtext-tempuser' => true,
293 'autoblockedtext-tempuser' => true,
294 ];
295
321 public function getPermissionErrors(
322 $action,
323 User $user,
324 LinkTarget $page,
325 $rigor = self::RIGOR_SECURE,
326 $ignoreErrors = []
327 ): array {
328 $status = $this->getPermissionStatus( $action, $user, $page, $rigor );
329 $result = [];
330
331 // Produce a result in the weird format used by this function
332 foreach ( $status->getErrors() as [ 'message' => $keyOrMsg, 'params' => $params ] ) {
333 $key = $keyOrMsg instanceof MessageSpecifier ? $keyOrMsg->getKey() : $keyOrMsg;
334 // Remove the errors being ignored.
335 if ( !in_array( $key, $ignoreErrors ) ) {
336 // Remove modern block info that is not expected by users of this legacy API
337 if ( isset( self::BLOCK_CODES[ $key ] ) && $keyOrMsg instanceof MessageSpecifier ) {
338 $params = $keyOrMsg->getParams();
339 $keyOrMsg = $key;
340 }
341 $result[] = [ $keyOrMsg, ...$params ];
342 }
343 }
344 return $result;
345 }
346
362 public function throwPermissionErrors(
363 $action,
364 User $user,
365 LinkTarget $page,
366 $rigor = self::RIGOR_SECURE,
367 $ignoreErrors = []
368 ): void {
369 $status = $this->getPermissionStatus(
370 $action, $user, $page, $rigor );
371 if ( $status->hasMessagesExcept( ...$ignoreErrors ) ) {
372 throw new PermissionsError( $action, $status );
373 }
374 }
375
385 public function isBlockedFrom( User $user, $page, $fromReplica = false ): bool {
386 return (bool)$this->getApplicableBlock(
387 'edit',
388 $user,
389 $fromReplica ? self::RIGOR_FULL : self::RIGOR_SECURE,
390 $page,
391 $user->getRequest()
392 );
393 }
394
413 public function getPermissionStatus(
414 $action,
415 User $user,
416 LinkTarget $page,
417 $rigor = self::RIGOR_SECURE,
418 $short = false
420 if ( !in_array( $rigor, [ self::RIGOR_QUICK, self::RIGOR_FULL, self::RIGOR_SECURE ] ) ) {
421 throw new InvalidArgumentException( "Invalid rigor parameter '$rigor'." );
422 }
423
424 // With RIGOR_QUICK we can assume automatic account creation will
425 // occur. At a higher rigor level, the caller is required to opt
426 // in by either passing in a temp placeholder user or by actually
427 // creating the account.
428 if ( $rigor === self::RIGOR_QUICK
429 && !$user->isRegistered()
430 && $this->tempUserConfig->isAutoCreateAction( $action )
431 ) {
432 $user = $this->userFactory->newTempPlaceholder();
433 }
434
435 # Read has special handling
436 if ( $action === 'read' ) {
437 $checks = [
438 $this->checkPermissionHooks( ... ),
439 $this->checkReadPermissions( ... ),
440 $this->checkUserBlock( ... ), // for wgBlockDisablesLogin
441 ];
442 } elseif ( $action === 'create' ) {
443 # Don't call checkSpecialsAndNSPermissions, checkSiteConfigPermissions
444 # or checkUserConfigPermissions here as it will lead to duplicate
445 # error messages. This is okay to do since anywhere that checks for
446 # create will also check for edit, and those checks are called for edit.
447 $checks = [
448 $this->checkQuickPermissions( ... ),
449 $this->checkPermissionHooks( ... ),
450 $this->checkPageRestrictions( ... ),
451 $this->checkCascadingSourcesRestrictions( ... ),
452 $this->checkActionPermissions( ... ),
453 $this->checkUserBlock( ... ),
454 ];
455 } else {
456 // Exclude checkUserConfigPermissions on actions that cannot change the
457 // content of the configuration pages.
458 $skipUserConfigActions = [
459 // Allow patrolling per T21818
460 'patrol',
461
462 // Allow (un)watch (T373758)
463 'editmywatchlist',
464
465 // Allow admins and oversighters to delete. For user pages we want to avoid the
466 // situation where an unprivileged user can post abusive content on
467 // their subpages and only very highly privileged users could remove it.
468 // See T200176.
469 'delete',
470 'deleterevision',
471 'suppressrevision',
472
473 // Allow admins and oversighters to view deleted content, even if they
474 // cannot restore it. See T202989
475 'deletedhistory',
476 'deletedtext',
477 'viewsuppressed',
478 ];
479
480 $checks = [
481 $this->checkQuickPermissions( ... ),
482 $this->checkPermissionHooks( ... ),
483 $this->checkSpecialsAndNSPermissions( ... ),
484 $this->checkUserPageEditPermissions( ... ),
485 $this->checkSiteConfigPermissions( ... ),
486 ];
487 if ( !in_array( $action, $skipUserConfigActions, true ) ) {
488 $checks[] = $this->checkUserConfigPermissions( ... );
489 }
490 $checks = [
491 ...$checks,
492 $this->checkPageRestrictions( ... ),
493 $this->checkCascadingSourcesRestrictions( ... ),
494 $this->checkActionPermissions( ... ),
495 $this->checkUserBlock( ... )
496 ];
497 }
498
499 $status = PermissionStatus::newEmpty();
500 foreach ( $checks as $callback ) {
501 $callback( $action, $user, $status, $rigor, $short, $page );
502
503 if ( $short && !$status->isGood() ) {
504 break;
505 }
506 }
507
508 // Clone the status to prevent users of this hook from modifying the original
509 $this->hookRunner->onPermissionStatusAudit( $page, $user, $action, $rigor, clone $status );
510
511 return $status;
512 }
513
527 private function checkPermissionHooks(
528 $action,
529 User $user,
530 PermissionStatus $status,
531 $rigor,
532 $short,
533 LinkTarget $page
534 ): void {
535 // TODO: remove when LinkTarget usage will expand further
536 $title = Title::newFromLinkTarget( $page );
537 // Use getUserPermissionsErrors instead
538 $result = '';
539 if ( !$this->hookRunner->onUserCan( $title, $user, $action, $result ) ) {
540 if ( !$result ) {
541 $status->fatal( 'badaccess-group0' );
542 }
543 return;
544 }
545 // Check getUserPermissionsErrors hook
546 if ( !$this->hookRunner->onGetUserPermissionsErrors( $title, $user, $action, $result ) ) {
547 $this->resultToStatus( $status, $result );
548 }
549 // Check getUserPermissionsErrorsExpensive hook
550 if (
551 $rigor !== self::RIGOR_QUICK
552 && !( $short && !$status->isGood() )
553 && !$this->hookRunner->onGetUserPermissionsErrorsExpensive(
554 $title, $user, $action, $result )
555 ) {
556 $this->resultToStatus( $status, $result );
557 }
558 }
559
566 private function resultToStatus( PermissionStatus $status, $result ): void {
567 if ( is_array( $result ) && count( $result ) && !is_array( $result[0] ) ) {
568 // A single array representing an error
569 $status->fatal( ...$result );
570 } elseif ( is_array( $result ) && count( $result ) && is_array( $result[0] ) ) {
571 // A nested array representing multiple errors
572 foreach ( $result as $result1 ) {
573 $this->resultToStatus( $status, $result1 );
574 }
575 } elseif ( is_string( $result ) && $result !== '' ) {
576 // A string representing a message-id
577 $status->fatal( $result );
578 } elseif ( $result instanceof MessageSpecifier ) {
579 // A message specifier representing an error
580 $status->fatal( $result );
581 } elseif ( $result === false ) {
582 // a generic "We don't want them to do that"
583 $status->fatal( 'badaccess-group0' );
584 }
585 // If we got here, $results is the empty array or empty string, which mean no errors.
586 }
587
601 private function checkReadPermissions(
602 $action,
603 User $user,
604 PermissionStatus $status,
605 $rigor,
606 $short,
607 LinkTarget $page
608 ): void {
609 // TODO: remove when LinkTarget usage will expand further
610 $title = Title::newFromLinkTarget( $page );
611
612 $whiteListRead = $this->options->get( MainConfigNames::WhitelistRead );
613 $allowed = false;
614 if ( $this->isEveryoneAllowed( 'read' ) ) {
615 // Shortcut for public wikis, allows skipping quite a bit of code
616 $allowed = true;
617 } elseif ( $this->userHasRight( $user, 'read' ) ) {
618 // If the user is allowed to read pages, they are allowed to read all pages
619 $allowed = true;
620 } elseif ( $this->isSameSpecialPage( 'Userlogin', $page )
621 || $this->isSameSpecialPage( 'PasswordReset', $page )
622 || $this->isSameSpecialPage( 'Userlogout', $page )
623 ) {
624 // Always grant access to the login page.
625 // Even anons need to be able to log in.
626 $allowed = true;
627 } elseif ( $this->isSameSpecialPage( 'RunJobs', $page ) ) {
628 // relies on HMAC key signature alone
629 $allowed = true;
630 } elseif ( is_array( $whiteListRead ) && count( $whiteListRead ) ) {
631 // Time to check the whitelist
632 // Only do these checks if there's something to check against
633 $name = $title->getPrefixedText();
634 $dbName = $title->getPrefixedDBkey();
635
636 // Check for explicit whitelisting with and without underscores
637 if ( in_array( $name, $whiteListRead, true )
638 || in_array( $dbName, $whiteListRead, true )
639 ) {
640 $allowed = true;
641 } elseif ( $page->getNamespace() === NS_MAIN ) {
642 // Old settings might have the title prefixed with
643 // a colon for main-namespace pages
644 if ( in_array( ':' . $name, $whiteListRead ) ) {
645 $allowed = true;
646 }
647 } elseif ( $title->isSpecialPage() ) {
648 // If it's a special page, ditch the subpage bit and check again
649 $name = $title->getDBkey();
650 [ $name, /* $subpage */ ] =
651 $this->specialPageFactory->resolveAlias( $name );
652 if ( $name ) {
653 $pure = SpecialPage::getTitleFor( $name )->getPrefixedText();
654 if ( in_array( $pure, $whiteListRead, true ) ) {
655 $allowed = true;
656 }
657 }
658 }
659 }
660
661 $whitelistReadRegexp = $this->options->get( MainConfigNames::WhitelistReadRegexp );
662 if ( !$allowed && is_array( $whitelistReadRegexp )
663 && $whitelistReadRegexp
664 ) {
665 $name = $title->getPrefixedText();
666 // Check for regex whitelisting
667 foreach ( $whitelistReadRegexp as $listItem ) {
668 if ( preg_match( $listItem, $name ) ) {
669 $allowed = true;
670 break;
671 }
672 }
673 }
674
675 if ( !$allowed ) {
676 // If the title is not allowed, give extensions a chance to do so
677 $this->hookRunner->onTitleReadWhitelist( $title, $user, $allowed );
678 if ( !$allowed ) {
679 $this->missingPermissionError( $action, $short, $status );
680 }
681 }
682 }
683
691 private function missingPermissionError( string $action, bool $short, PermissionStatus $status ): void {
692 // We avoid expensive display logic for quickUserCan's and such
693 if ( $short ) {
694 $status->fatal( 'badaccess-group0' );
695 return;
696 }
697
698 // TODO: it would be a good idea to replace the method below with something else like
699 // maybe callback injection
700 $context = RequestContext::getMain();
701 $fatalStatus = $this->newFatalPermissionDeniedStatus( $action, $context );
702 $status->merge( $fatalStatus );
703 }
704
715 public function newFatalPermissionDeniedStatus( $permission, IContextSource $context ): StatusValue {
716 $groupsWithPermission = $this->groupPermissionsLookup->getGroupsWithPermission( $permission );
717 if ( !$groupsWithPermission ) {
718 // Nobody has the right
719 $status = PermissionStatus::newFatal( 'badaccess-group0' );
720 $status->setPermission( $permission );
721 return $status;
722 }
723
724 $groupLinks = array_map(
725 static fn ( $group ) => UserGroupMembership::getLinkWiki( $group, $context ),
726 $groupsWithPermission
727 );
728
729 $userDisabledGroups = $this->userGroupManager->getUserDisabledGroups( $context->getUser() );
730 $disabledGroupsWithPermission = array_intersect( $groupsWithPermission, $userDisabledGroups );
731 if ( $disabledGroupsWithPermission !== [] ) {
732 // One of the groups you are in has the right, however you don't
733 // meet the restrictions to be in that group
734 $disabledGroupsWithPermissionNames = array_map(
735 static fn ( $group ) => $context->getLanguage()->getGroupName( $group ),
736 $disabledGroupsWithPermission
737 );
738 $status = PermissionStatus::newFatal(
739 'badaccess-groups-disabled',
740 Message::listParam( $groupLinks, ListType::COMMA ),
741 count( $groupLinks ),
742 Message::listParam( $disabledGroupsWithPermissionNames, ListType::COMMA ),
743 count( $disabledGroupsWithPermissionNames )
744 );
745 $status->setPermission( $permission );
746 return $status;
747 }
748
749 $user = $context->getUser();
750 $userGroups = $this->userGroupManager->getUserEffectiveGroups( $user );
751 if ( array_intersect( $userGroups, $groupsWithPermission ) ) {
752 // You are in a group that should have this right, but don't for some reason
753 // and we don't know why.
754 // (Possible causes of this include $wgRevokePermissions or an extension hook
755 // that modiied the permission structure)
756 $status = PermissionStatus::newFatal( 'badaccess-group0' );
757 $status->setPermission( $permission );
758 return $status;
759 }
760 // You aren't in any groups that have this right (this is the most commn case)
761 $status = PermissionStatus::newFatal(
762 'badaccess-groups',
763 Message::listParam( $groupLinks, ListType::COMMA ),
764 count( $groupLinks )
765 );
766 $status->setPermission( $permission );
767 return $status;
768 }
769
777 private function isSameSpecialPage( $name, LinkTarget $page ): bool {
778 if ( $page->getNamespace() === NS_SPECIAL ) {
779 [ $pageName ] = $this->specialPageFactory->resolveAlias( $page->getDBkey() );
780 if ( $name === $pageName ) {
781 return true;
782 }
783 }
784 return false;
785 }
786
800 private function checkUserBlock(
801 $action,
802 User $user,
803 PermissionStatus $status,
804 $rigor,
805 $short,
806 LinkTarget $page
807 ): void {
808 $block = $this->getApplicableBlock(
809 $action,
810 $user,
811 $rigor,
812 $page,
813 $user->getRequest()
814 );
815
816 if ( $block ) {
817 $status->setBlock( $block );
818
819 // @todo FIXME: Pass the relevant context into this function.
820 $context = RequestContext::getMain();
821 $messages = $this->blockErrorFormatter->getMessages(
822 $block,
823 $user,
824 $context->getRequest()->getIP()
825 );
826
827 foreach ( $messages as $message ) {
828 $status->fatal( $message );
829 }
830 }
831 }
832
849 public function getApplicableBlock(
850 string $action,
851 User $user,
852 string $rigor,
853 $page,
855 ): ?Block {
856 // Unblocking handled in SpecialUnblock
857 if ( $rigor === self::RIGOR_QUICK || in_array( $action, [ 'unblock' ] ) ) {
858 return null;
859 }
860
861 // Optimize for a very common case
862 if ( $action === 'read' && !$this->options->get( MainConfigNames::BlockDisablesLogin ) ) {
863 return null;
864 }
865
866 // Implicit rights aren't blockable (T350117, T350202).
867 if ( in_array( $action, $this->getImplicitRights(), true ) ) {
868 return null;
869 }
870
871 $useReplica = $rigor !== self::RIGOR_SECURE;
872 $isExempt = $this->userHasRight( $user, 'ipblock-exempt' );
873 $requestIfNotExempt = $isExempt ? null : $request;
874
875 // Create account blocks are implemented separately due to weird IP exemption rules
876 if ( in_array( $action, [ 'createaccount', 'autocreateaccount' ], true ) ) {
877 return $this->blockManager->getCreateAccountBlock(
878 $user,
879 $requestIfNotExempt,
880 $useReplica
881 );
882 }
883
884 $block = $this->blockManager->getBlock( $user, $requestIfNotExempt, $useReplica );
885 if ( !$block ) {
886 return null;
887 }
888 $userIsHidden = $block->getHideName();
889
890 // Remove elements from the block that explicitly allow the action
891 // (like "read" or "upload").
892 $block = $this->blockManager->filter(
893 $block,
894 static function ( AbstractBlock $originalBlock ) use ( $action ) {
895 // Remove the block if it explicitly allows the action
896 return $originalBlock->appliesToRight( $action ) !== false;
897 }
898 );
899 if ( !$block ) {
900 return null;
901 }
902
903 // Convert the input page to a Title
904 $targetTitle = null;
905 if ( $page ) {
906 $targetTitle = $page instanceof PageReference ?
907 Title::castFromPageReference( $page ) :
908 Title::castFromLinkTarget( $page );
909
910 if ( !$targetTitle->canExist() ) {
911 $targetTitle = null;
912 }
913 }
914
915 // What gets passed into this method is a user right, not an action name.
916 // There is no way to instantiate an action by restriction. However, this
917 // will get the action where the restriction is the same. This may result
918 // in actions being blocked that shouldn't be.
919 $actionInfo = $this->actionFactory->getActionInfo( $action, $targetTitle );
920
921 // Ensure that the retrieved action matches the restriction.
922 if ( $actionInfo && $actionInfo->getRestriction() !== $action ) {
923 $actionInfo = null;
924 }
925
926 // Return null if the action does not require an unblocked user.
927 // If no ActionInfo is returned, assume that the action requires unblock
928 // which is the default.
929 // NOTE: We may get null here even for known actions, if a wiki's main page
930 // is set to a special page, e.g. Special:MyLanguage/Main_Page (T348451, T346036).
931 if ( $actionInfo && !$actionInfo->requiresUnblock() ) {
932 return null;
933 }
934
935 // Remove elements from the block that do not apply to the specific page
936 if ( $targetTitle ) {
937 $targetIsUserTalk = !$userIsHidden && $targetTitle->equals( $user->getTalkPage() );
938 $block = $this->blockManager->filter(
939 $block,
940 static function ( AbstractBlock $originalBlock )
941 use ( $action, $targetTitle, $targetIsUserTalk ) {
942 if ( $originalBlock->appliesToRight( $action ) ) {
943 // An action block takes precedence over appliesToTitle().
944 // Block::appliesToRight('edit') always returns null,
945 // allowing title-based exemptions to take effect.
946 return true;
947 } elseif ( $targetIsUserTalk ) {
948 // Special handling for a user's own talk page. The block is not aware
949 // of the user, so this must be done here.
950 return $originalBlock->appliesToUsertalk( $targetTitle );
951 } else {
952 return $originalBlock->appliesToTitle( $targetTitle );
953 }
954 }
955 );
956 }
957
958 if ( $targetTitle && $block instanceof AbstractBlock ) {
959 // Allow extensions to let a blocked user access a particular page
960 $allowUsertalk = $block->isUsertalkEditAllowed();
961 $blocked = true;
962 $this->hookRunner->onUserIsBlockedFrom( $user, $targetTitle, $blocked, $allowUsertalk );
963 if ( !$blocked ) {
964 $block = null;
965 }
966 }
967 return $block;
968 }
969
983 private function checkQuickPermissions(
984 $action,
985 User $user,
986 PermissionStatus $status,
987 $rigor,
988 $short,
989 LinkTarget $page
990 ): void {
991 // TODO: remove when LinkTarget usage will expand further
992 $title = Title::newFromLinkTarget( $page );
993
994 // This method is always called first, so $status is guaranteed to be empty, so we can
995 // just pass an empty $errors array, instead of converting it to the legacy format and back.
996 $errors = [];
997 if ( !$this->hookRunner->onTitleQuickPermissions( $title, $user, $action,
998 $errors, $rigor !== self::RIGOR_QUICK, $short )
999 ) {
1000 // $errors is an array of results, not a result, but resultToStatus() handles
1001 // arrays of arrays with recursion so this will work
1002 $this->resultToStatus( $status, $errors );
1003 return;
1004 }
1005
1006 $isSubPage =
1007 $this->nsInfo->hasSubpages( $title->getNamespace() ) &&
1008 str_contains( $title->getText(), '/' );
1009
1010 if ( $action === 'create' ) {
1011 $right = $this->nsInfo->isTalk( $title->getNamespace() ) ? 'createtalk' : 'createpage';
1012 $errorMsgKey = $user->isNamed() ? 'nocreate-loggedin' : 'nocreatetext';
1013 $this->mergeUserRightStatus( $status, $user, $right, $rigor, !$short, $errorMsgKey );
1014 } elseif ( $action === 'move' ) {
1015 if ( $title->getNamespace() === NS_USER && !$isSubPage ) {
1016 $this->mergeUserRightStatus( $status, $user, 'move-rootuserpages', $rigor, !$short,
1017 'cant-move-user-page' );
1018 }
1019
1020 // Check if user is allowed to move files if it's a file
1021 if ( $title->getNamespace() === NS_FILE ) {
1022 $this->mergeUserRightStatus( $status, $user, 'movefile', $rigor, !$short, 'movenotallowedfile' );
1023 }
1024
1025 // Check if user is allowed to move category pages if it's a category page
1026 if ( $title->getNamespace() === NS_CATEGORY ) {
1027 $this->mergeUserRightStatus( $status, $user, 'move-categorypages', $rigor, !$short,
1028 'cant-move-category-page' );
1029 }
1030
1031 $moveStatus = $this->getUserRightStatus( $user, 'move', $rigor, !$short );
1032 if ( !$moveStatus->isOK() ) {
1033 // User can't move anything
1034 $userCanMove = $this->groupPermissionsLookup
1035 ->groupHasPermission( 'user', 'move' );
1036 $autoconfirmedCanMove = $this->groupPermissionsLookup
1037 ->groupHasPermission( 'autoconfirmed', 'move' );
1038 if ( $user->isAnon()
1039 && ( $userCanMove || $autoconfirmedCanMove )
1040 ) {
1041 // custom message if logged-in users without any special rights can move
1042 $status->fatal( 'movenologintext' );
1043 } elseif ( $user->isTemp() && $autoconfirmedCanMove ) {
1044 // Temp user may be able to move if they log in as a proper account
1045 $status->fatal( 'movenologintext' );
1046 } else {
1047 $status->fatal( 'movenotallowed' );
1048 }
1049 if ( !$short ) {
1050 $status->merge( $moveStatus );
1051 }
1052 }
1053 } elseif ( $action === 'move-target' ) {
1054 if ( !$this->mergeUserRightStatus( $status, $user, 'move', $rigor, !$short, 'movenotallowed' ) ) {
1055 // User can't move anything, don't check the conditions below
1056 } elseif ( $title->getNamespace() === NS_USER && !$isSubPage ) {
1057 // Show user page-specific message only if the user can move other pages
1058 $this->mergeUserRightStatus( $status, $user, 'move-rootuserpages', $rigor, !$short,
1059 'cant-move-to-user-page' );
1060 } elseif ( $title->getNamespace() === NS_CATEGORY ) {
1061 // Show category page-specific message only if the user can move other pages
1062 $this->mergeUserRightStatus( $status, $user, 'move-categorypages', $rigor, !$short,
1063 'cant-move-to-category-page' );
1064 }
1065 } elseif ( $action === 'autocreateaccount' ) {
1066 // createaccount implies autocreateaccount
1067 $this->mergeUserRightStatus( $status, $user, [ 'autocreateaccount', 'createaccount' ], $rigor, !$short );
1068 } else {
1069 $this->mergeUserRightStatus( $status, $user, $action, $rigor, !$short );
1070 }
1071 }
1072
1089 private function checkPageRestrictions(
1090 $action,
1091 UserIdentity $user,
1092 PermissionStatus $status,
1093 $rigor,
1094 $short,
1095 LinkTarget $page
1096 ): void {
1097 // TODO: remove & rework upon further use of LinkTarget
1098 $title = Title::newFromLinkTarget( $page );
1099 foreach ( $this->restrictionStore->getRestrictions( $title, $action ) as $level ) {
1100 // Messages: restriction-level-sysop, restriction-level-autoconfirmed
1101 $levelMsg = MessageValue::new( "restriction-level-$level" );
1102
1103 $right = $level;
1104 // Backwards compatibility, rewrite sysop -> editprotected
1105 if ( $right === 'sysop' ) {
1106 $right = 'editprotected';
1107 }
1108 // Backwards compatibility, rewrite autoconfirmed -> editsemiprotected
1109 if ( $right === 'autoconfirmed' ) {
1110 $right = 'editsemiprotected';
1111 }
1112 if ( $right == '' ) {
1113 continue;
1114 }
1115 if ( !$this->userHasRight( $user, $right ) ) {
1116 // The parameters are not used by the default message text,
1117 // but they're available to be used in on-wiki overrides
1118 $status->fatal( 'protectedpagetext', $right, $action, $levelMsg );
1119 } elseif ( $this->restrictionStore->areRestrictionsCascading( $title ) &&
1120 !$this->userHasRight( $user, 'protect' )
1121 ) {
1122 // The parameters are not used by the default message text,
1123 // but they're available to be used in on-wiki overrides
1124 $status->fatal( 'protectedpagetext', 'protect', $action, $levelMsg );
1125 }
1126 }
1127 }
1128
1142 private function checkCascadingSourcesRestrictions(
1143 $action,
1144 UserIdentity $user,
1145 PermissionStatus $status,
1146 $rigor,
1147 $short,
1148 LinkTarget $page
1149 ): void {
1150 // TODO: remove & rework upon further use of LinkTarget
1151 $title = Title::newFromLinkTarget( $page );
1152
1153 if ( $rigor !== self::RIGOR_QUICK && !$title->isUserConfigPage() ) {
1154 [ $sources, $restrictions, $tlSources, $ilSources ] = $this->restrictionStore
1155 ->getCascadeProtectionSources( $title );
1156
1157 // If the file Wikitext isn't transcluded then we
1158 // don't care about edit cascade restrictions for edit action
1159 if ( $action === 'edit' && $page->getNamespace() === NS_FILE && !$tlSources ) {
1160 return;
1161 }
1162
1163 // For the purposes of cascading protection, edit restrictions should apply to uploads or moves
1164 // Thus remap upload and move to edit
1165 // Unless the file content itself is not transcluded
1166 if ( $ilSources && ( $action === 'upload' || $action === 'move' ) ) {
1167 $restrictedAction = 'edit';
1168 } else {
1169 $restrictedAction = $action;
1170 }
1171
1172 // Cascading protection depends on more than this page...
1173 // Several cascading protected pages may include this page...
1174 // Check each cascading level
1175 // This is only for protection restrictions, not for all actions
1176 if ( isset( $restrictions[$restrictedAction] ) ) {
1177 foreach ( $restrictions[$restrictedAction] as $right ) {
1178 // Backwards compatibility, rewrite sysop -> editprotected
1179 if ( $right === 'sysop' ) {
1180 $right = 'editprotected';
1181 }
1182 // Backwards compatibility, rewrite autoconfirmed -> editsemiprotected
1183 if ( $right === 'autoconfirmed' ) {
1184 $right = 'editsemiprotected';
1185 }
1186 if ( $right != '' && !$this->userHasAllRights( $user, 'protect', $right ) ) {
1187 $wikiPages = '';
1188 foreach ( $sources as $pageIdentity ) {
1189 $wikiPages .= '* [[:' . $this->titleFormatter->getPrefixedText( $pageIdentity ) . "]]\n";
1190 }
1191 $status->fatal( 'cascadeprotected', count( $sources ), $wikiPages, $action );
1192 }
1193 }
1194 }
1195 }
1196 }
1197
1211 private function checkActionPermissions(
1212 $action,
1213 User $user,
1214 PermissionStatus $status,
1215 $rigor,
1216 $short,
1217 LinkTarget $page
1218 ): void {
1219 // TODO: remove & rework upon further use of LinkTarget
1220 $title = Title::newFromLinkTarget( $page );
1221
1222 if ( $rigor !== self::RIGOR_QUICK && !defined( 'MW_NO_SESSION' ) ) {
1223 $sessionRestrictions = $user->getRequest()->getSession()->getRestrictions();
1224 if ( $sessionRestrictions ) {
1225 $userCan = $sessionRestrictions->userCan( $title );
1226 if ( !$userCan->isOK() ) {
1227 $status->merge( $userCan );
1228 }
1229 }
1230 }
1231
1232 if ( $action === 'protect' ) {
1233 if ( !$this->getPermissionStatus( 'edit', $user, $title, $rigor, true )->isGood() ) {
1234 // If they can't edit, they shouldn't protect.
1235 $status->fatal( 'protect-cantedit' );
1236 }
1237 } elseif ( $action === 'create' ) {
1238 $createProtection = $this->restrictionStore->getCreateProtection( $title );
1239 if ( $createProtection ) {
1240 if ( $createProtection['permission'] == ''
1241 || !$this->userHasRight( $user, $createProtection['permission'] )
1242 ) {
1243 $protectUserIdentity = $this->userIdentityLookup
1244 ->getUserIdentityByUserId( $createProtection['user'] );
1245 $status->fatal(
1246 'titleprotected',
1247 $protectUserIdentity ? $protectUserIdentity->getName() : '',
1248 $createProtection['reason']
1249 );
1250 }
1251 }
1252 } elseif ( $action === 'move' ) {
1253 // Check for immobile pages
1254 if ( !$this->nsInfo->isMovable( $title->getNamespace() ) ) {
1255 // Specific message for this case
1256 $nsText = $title->getNsText();
1257 if ( $nsText === '' ) {
1258 $nsText = wfMessage( 'blanknamespace' )->text();
1259 }
1260 $status->fatal( 'immobile-source-namespace', $nsText );
1261 } elseif ( !$title->isMovable() ) {
1262 // Less specific message for rarer cases
1263 $status->fatal( 'immobile-source-page' );
1264 }
1265 } elseif ( $action === 'move-target' ) {
1266 if ( !$this->nsInfo->isMovable( $title->getNamespace() ) ) {
1267 $nsText = $title->getNsText();
1268 if ( $nsText === '' ) {
1269 $nsText = wfMessage( 'blanknamespace' )->text();
1270 }
1271 $status->fatal( 'immobile-target-namespace', $nsText );
1272 } elseif ( !$title->isMovable() ) {
1273 $status->fatal( 'immobile-target-page' );
1274 }
1275 } elseif ( $action === 'delete' || $action === 'delete-redirect' ) {
1276 $tempStatus = PermissionStatus::newEmpty();
1277 $this->checkPageRestrictions( 'edit', $user, $tempStatus, $rigor, true, $title );
1278 if ( $tempStatus->isGood() ) {
1279 $this->checkCascadingSourcesRestrictions( 'edit',
1280 $user, $tempStatus, $rigor, true, $title );
1281 }
1282 if ( !$tempStatus->isGood() ) {
1283 // If protection keeps them from editing, they shouldn't be able to delete.
1284 $status->fatal( 'deleteprotected' );
1285 }
1286 if ( $rigor !== self::RIGOR_QUICK
1287 && $action === 'delete'
1288 && $this->options->get( MainConfigNames::DeleteRevisionsLimit )
1289 && !$this->userCan( 'bigdelete', $user, $title )
1290 && $title->isBigDeletion()
1291 ) {
1292 // NOTE: This check is deprecated since 1.37, see T288759
1293 $status->fatal(
1294 'delete-toobig',
1295 Message::numParam( $this->options->get( MainConfigNames::DeleteRevisionsLimit ) )
1296 );
1297 }
1298 } elseif ( $action === 'undelete' ) {
1299 if ( !$this->getPermissionStatus( 'edit', $user, $title, $rigor, true )->isGood() ) {
1300 // Undeleting implies editing
1301 $status->fatal( 'undelete-cantedit' );
1302 }
1303 if ( !$title->exists()
1304 && !$this->getPermissionStatus( 'create', $user, $title, $rigor, true )->isGood()
1305 ) {
1306 // Undeleting where nothing currently exists implies creating
1307 $status->fatal( 'undelete-cantcreate' );
1308 }
1309 } elseif ( $action === 'edit' ) {
1310 if ( $this->options->get( MainConfigNames::EmailConfirmToEdit )
1311 && !$user->isEmailConfirmed()
1312 ) {
1313 $status->fatal( 'confirmedittext' );
1314 }
1315
1316 if ( !$title->exists() ) {
1317 $status->merge(
1318 $this->getPermissionStatus( 'create', $user, $title, $rigor, $short )
1319 );
1320 }
1321 }
1322 }
1323
1337 private function checkSpecialsAndNSPermissions(
1338 $action,
1339 UserIdentity $user,
1340 PermissionStatus $status,
1341 $rigor,
1342 $short,
1343 LinkTarget $page
1344 ): void {
1345 // TODO: remove & rework upon further use of LinkTarget
1346 $title = Title::newFromLinkTarget( $page );
1347
1348 // Only 'createaccount' can be performed on special pages,
1349 // which don't actually exist in the DB.
1350 if ( $title->getNamespace() === NS_SPECIAL
1351 && !in_array( $action, [ 'createaccount', 'autocreateaccount' ], true )
1352 ) {
1353 $status->fatal( 'ns-specialprotected' );
1354 }
1355
1356 // Check $wgNamespaceProtection for restricted namespaces
1357 if ( $this->isNamespaceProtected( $title->getNamespace(), $user )
1358 // Allow admins and oversighters to view deleted content, even if they
1359 // cannot restore it. See T362536. Allow (un)watch too (T373758)
1360 && !in_array( $action, [ 'deletedhistory', 'deletedtext', 'viewsuppressed', 'editmywatchlist' ], true )
1361 ) {
1362 $ns = $title->getNamespace() === NS_MAIN ?
1363 wfMessage( 'nstab-main' )->text() : $title->getNsText();
1364 if ( $title->getNamespace() === NS_MEDIAWIKI ) {
1365 $status->fatal( 'protectedinterface', $action );
1366 } else {
1367 $status->fatal( 'namespaceprotected', $ns, $action );
1368 }
1369 }
1370 }
1371
1385 private function checkSiteConfigPermissions(
1386 $action,
1387 UserIdentity $user,
1388 PermissionStatus $status,
1389 $rigor,
1390 $short,
1391 LinkTarget $page
1392 ): void {
1393 // TODO: remove & rework upon further use of LinkTarget
1394 $title = Title::newFromLinkTarget( $page );
1395
1396 if ( $action === 'patrol' || $action === 'editmywatchlist' ) {
1397 return;
1398 }
1399
1400 if ( in_array( $action, [ 'deletedhistory', 'deletedtext', 'viewsuppressed' ], true ) ) {
1401 // Allow admins and oversighters to view deleted content, even if they
1402 // cannot restore it. See T202989
1403 // Not using the same handling in `getPermissionStatus` as the checks
1404 // for skipping `checkUserConfigPermissions` since normal admins can delete
1405 // user scripts, but not sitewide scripts
1406 return;
1407 }
1408
1409 // Sitewide CSS/JSON/JS/RawHTML changes, like all NS_MEDIAWIKI changes, also require the
1410 // editinterface right. That's implemented as a restriction so no check needed here.
1411 if ( $title->isSiteCssConfigPage() ) {
1412 $this->mergeUserRightStatus( $status, $user, 'editsitecss', $rigor, !$short,
1413 'sitecssprotected', $action );
1414 } elseif ( $title->isSiteJsonConfigPage() ) {
1415 $this->mergeUserRightStatus( $status, $user, 'editsitejson', $rigor, !$short,
1416 'sitejsonprotected', $action );
1417 } elseif ( $title->isSiteJsConfigPage() ) {
1418 $this->mergeUserRightStatus( $status, $user, 'editsitejs', $rigor, !$short,
1419 'sitejsprotected', $action );
1420 } elseif ( $title->isRawHtmlMessage() ) {
1421 // Editing raw HTML messages requires both editsitejs AND editsitecss
1422 if (
1423 $this->mergeUserRightStatus( $status, $user, 'editsitejs', $rigor, !$short,
1424 'siterawhtmlprotected', $action )
1425 ) {
1426 $this->mergeUserRightStatus( $status, $user, 'editsitecss', $rigor, !$short,
1427 'siterawhtmlprotected', $action );
1428 }
1429 }
1430 }
1431
1445 private function checkUserPageEditPermissions(
1446 $action,
1447 UserIdentity $user,
1448 PermissionStatus $status,
1449 $rigor,
1450 $short,
1451 LinkTarget $page
1452 ): void {
1453 if ( !$this->options->get( MainConfigNames::RestrictUserPageEditing ) ) {
1454 return;
1455 }
1456
1457 if ( !in_array( $action, [ 'edit', 'move', 'move-target' ], true ) ) {
1458 return;
1459 }
1460
1461 // TODO: remove & rework upon further use of LinkTarget
1462 $title = Title::newFromLinkTarget( $page );
1463
1464 if (
1465 $title->getNamespace() === NS_USER
1466 && $title->getRootText() !== $user->getName()
1467 && !$this->userHasRight( $user, 'editalluserpages' )
1468 ) {
1469 $this->missingPermissionError( 'editalluserpages', $short, $status );
1470 }
1471 }
1472
1486 private function checkUserConfigPermissions(
1487 $action,
1488 UserIdentity $user,
1489 PermissionStatus $status,
1490 $rigor,
1491 $short,
1492 LinkTarget $page
1493 ): void {
1494 // TODO: remove & rework upon further use of LinkTarget
1495 $title = Title::newFromLinkTarget( $page );
1496
1497 // Protect css/json/js subpages of user pages
1498 // XXX: this might be better using restrictions
1499 if ( preg_match( '/^' . preg_quote( $user->getName(), '/' ) . '\//', $title->getText() ) ) {
1500 // Users need editmyuser* to edit their own CSS/JSON/JS subpages.
1501 if ( $title->isUserCssConfigPage() ) {
1502 $this->mergeUserRightStatus( $status, $user, [ 'editmyusercss', 'editusercss' ], $rigor, !$short,
1503 'mycustomcssprotected', $action );
1504 } elseif ( $title->isUserJsonConfigPage() ) {
1505 $this->mergeUserRightStatus( $status, $user, [ 'editmyuserjson', 'edituserjson' ], $rigor, !$short,
1506 'mycustomjsonprotected', $action );
1507 } elseif ( $title->isUserJsConfigPage() ) {
1508 if (
1509 $this->mergeUserRightStatus( $status, $user, [ 'editmyuserjs', 'edituserjs' ], $rigor, !$short,
1510 'mycustomjsprotected', $action )
1511 ) {
1512 // T207750 - do not allow users to edit a redirect if they couldn't edit the target
1513 $target = $this->redirectLookup->getRedirectTarget( $title );
1514 if ( $target && (
1515 !$target->inNamespace( NS_USER )
1516 || !preg_match( '/^' . preg_quote( $user->getName(), '/' ) . '\//', $target->getText() )
1517 ) ) {
1518 // The target is not a user JS page belonging to the same user
1519 // Only allow editing if the user has either editmyuserjsredirect or edituserjs
1520 $this->mergeUserRightStatus( $status, $user, [ 'editmyuserjsredirect', 'edituserjs' ],
1521 $rigor, !$short, 'mycustomjsredirectprotected', $action );
1522 }
1523 }
1524 }
1525 } else {
1526 // Users need edituser* to edit others' CSS/JSON/JS subpages.
1527 // The checks to exclude deletion/suppression, which cannot be used for
1528 // attacks and should be excluded to avoid the situation where an
1529 // unprivileged user can post abusive content on their subpages
1530 // and only very highly privileged users could remove it,
1531 // are now a part of `getPermissionStatus` and this method isn't called.
1532 if ( $title->isUserCssConfigPage() ) {
1533 $this->mergeUserRightStatus( $status, $user, 'editusercss', $rigor, !$short,
1534 'customcssprotected', $action );
1535 } elseif ( $title->isUserJsonConfigPage() ) {
1536 $this->mergeUserRightStatus( $status, $user, 'edituserjson', $rigor, !$short,
1537 'customjsonprotected', $action );
1538 } elseif ( $title->isUserJsConfigPage() ) {
1539 $this->mergeUserRightStatus( $status, $user, 'edituserjs', $rigor, !$short,
1540 'customjsprotected', $action );
1541 }
1542 }
1543 }
1544
1566 public function getUserRightStatus(
1567 UserIdentity $user,
1568 string $right,
1569 string $rigor = self::RIGOR_SECURE,
1570 bool $detailedPermissionErrors = true,
1571 ): PermissionStatus {
1572 $status = PermissionStatus::newEmpty();
1573
1574 // For compatibility with userHasRight(), allow the empty action
1575 if ( $right === '' ) {
1576 return $status;
1577 }
1578
1579 // Use strict parameter to avoid matching numeric 0 accidentally inserted
1580 // by misconfiguration: 0 == 'foo'
1581 if (
1582 !in_array( $right, $this->getImplicitRights(), true )
1583 && !in_array( $right, $this->getUserPermissions( $user ), true )
1584 ) {
1585 $this->missingPermissionError( $right, !$detailedPermissionErrors, $status );
1586 return $status;
1587 }
1588
1589 // Deny actions that require reauthentication if the user hasn't recently reauthenticated
1590 $operation = $this->options->get( MainConfigNames::ReauthenticateForActions )[ $right ] ?? false;
1591 if ( $operation !== false ) {
1592 // securitySensitiveOperationStatus() can only check for the currently logged-in user
1593 // If $user is not that user, we can't check whether they've reauthenticated, so behave
1594 // as if they haven't.
1595 // FIXME move securitySensitiveOperationStatus to Session or SessionBackend, so that we
1596 // can use it here. We can't dependency-inject AuthManager because of a circular dependency.
1597 $authManager = MediaWikiServices::getInstance()->getAuthManager();
1598 $reauth = $authManager->getRequest()->getSession()->getUser()->equals( $user ) ?
1599 $authManager->securitySensitiveOperationStatus( $operation ) :
1600 AuthManager::SEC_FAIL;
1601
1602 if ( $reauth === AuthManager::SEC_REAUTH ) {
1603 $status->setReauthOperation( $operation );
1604 if ( $rigor === self::RIGOR_SECURE ) {
1605 $context = RequestContext::getMain();
1606 $title = $context->getTitle();
1607 $returnToParams = $title !== null
1608 ? SkinComponentUtils::getReturnToParam(
1609 $title,
1610 $context->getRequest(),
1611 $context->getAuthority()
1612 )
1613 : [];
1614 $loginUrl = SpecialPage::getSafeTitleFor( 'Userlogin' )
1615 ?->getFullURL( [ 'force' => $operation ] + $returnToParams );
1616 // The operation is passed as $1 for compatibility with older translations
1617 // that still use it inside a {{fullurl:...}} to build the login link. New
1618 // translations should use $2 (the full URL) directly.
1619 $status->fatal( ApiMessage::create(
1620 [ 'badaccess-reauthenticate', $operation, $loginUrl ],
1621 'reauthenticate',
1622 [ 'operation' => $operation ]
1623 ) );
1624 }
1625 } elseif ( $reauth === AuthManager::SEC_FAIL ) {
1626 $status->setReauthOperation( $operation );
1627 // If the user cannot reauthenticate, that is fatal regardless of $rigor
1628 $status->fatal( 'badaccess-cannotreauthenticate', $operation );
1629 }
1630 }
1631
1632 return $status;
1633 }
1634
1654 private function mergeUserRightStatus(
1655 PermissionStatus $status,
1656 UserIdentity $user,
1657 string|array $right,
1658 string $rigor = self::RIGOR_SECURE,
1659 bool $detailedPermissionErrors = true,
1660 ?string $extraError = null,
1661 ...$extraParams
1662 ): bool {
1663 $rightStatus = null;
1664 foreach ( (array)$right as $r ) {
1665 $rightStatus = $this->getUserRightStatus( $user, $r, $rigor, $detailedPermissionErrors );
1666 if ( $rightStatus->isOK() ) {
1667 // Success, stop here
1668 $status->merge( $rightStatus );
1669 return true;
1670 }
1671 }
1672
1673 // If we got here, the user doesn't have any of the requested rights
1674 // First add $extraError, if it exists...
1675 if ( $extraError !== null ) {
1676 $status->fatal( $extraError, ...$extraParams );
1677 }
1678 // ...then merge in the last permission error. But don't combine an $extraError with a
1679 // non-detailed "permission denied" error
1680 if ( $rightStatus && ( $extraError === null || $detailedPermissionErrors ) ) {
1681 $status->merge( $rightStatus );
1682 }
1683 return false;
1684 }
1685
1696 public function userHasRight( UserIdentity $user, $action = '' ): bool {
1697 if ( $action === '' ) {
1698 // In the spirit of DWIM
1699 return true;
1700 }
1701
1702 return $this->getUserRightStatus( $user, $action, self::RIGOR_SECURE, false )->isOK();
1703 }
1704
1713 public function userHasAnyRight( UserIdentity $user, ...$actions ): bool {
1714 foreach ( $actions as $action ) {
1715 if ( $this->userHasRight( $user, $action ) ) {
1716 return true;
1717 }
1718 }
1719 return false;
1720 }
1721
1730 public function userHasAllRights( UserIdentity $user, ...$actions ): bool {
1731 foreach ( $actions as $action ) {
1732 if ( !$this->userHasRight( $user, $action ) ) {
1733 return false;
1734 }
1735 }
1736 return true;
1737 }
1738
1748 public function getUserPermissions( UserIdentity $user, bool $includePrivateInfo = true ): array {
1749 $rightsCacheKey = $this->getRightsCacheKey( $user, $includePrivateInfo );
1750 if ( !isset( $this->usersRights[ $rightsCacheKey ] ) ) {
1751 $userObj = $this->userFactory->newFromUserIdentity( $user );
1752 $effectiveGroups = $this->userGroupManager->getUserEffectiveGroups(
1753 $user, IDBAccessObject::READ_NORMAL, false, $includePrivateInfo );
1754 $rights = $this->groupPermissionsLookup->getGroupPermissions( $effectiveGroups );
1755 // Hook requires a full User object
1756 $this->hookRunner->onUserGetRights( $userObj, $rights );
1757
1758 // Deny any rights denied by the user's session, unless this
1759 // endpoint has no sessions.
1760 if ( !defined( 'MW_NO_SESSION' ) ) {
1761 // FIXME: $userObj->getRequest().. need to be replaced with something else
1762 $allowedRights = $userObj->getRequest()->getSession()->getAllowedUserRights();
1763 if ( $allowedRights !== null ) {
1764 $rights = array_intersect( $rights, $allowedRights );
1765 }
1766 }
1767
1768 // Hook requires a full User object
1769 $this->hookRunner->onUserGetRightsRemove( $userObj, $rights );
1770 // Force reindexation of rights when a hook has unset one of them
1771 $rights = array_values( array_unique( $rights ) );
1772
1773 // If BlockDisablesLogin is true, remove rights that anonymous
1774 // users don't have. This has to be done after the hooks so that
1775 // we know whether the user is exempt. (T129738)
1776 if (
1777 $userObj->isRegistered()
1778 && $this->options->get( MainConfigNames::BlockDisablesLogin )
1779 ) {
1780 // Stash the permissions as they are before triggering any block checks for BlockDisablesLogin
1781 // to avoid a potential infinite loop, since GetUserBlock handlers may themselves check
1782 // permissions on this user. (T384197)
1783 $this->usersRights[ $rightsCacheKey ] = $rights;
1784
1785 $isExempt = in_array( 'ipblock-exempt', $rights, true );
1786 if ( $this->blockManager->getBlock(
1787 $userObj,
1788 $isExempt ? null : $userObj->getRequest()
1789 ) ) {
1790 $anon = $this->userFactory->newAnonymous();
1791 $rights = array_intersect( $rights, $this->getUserPermissions( $anon ) );
1792 }
1793 }
1794
1795 $this->usersRights[ $rightsCacheKey ] = $rights;
1796 } else {
1797 $rights = $this->usersRights[ $rightsCacheKey ];
1798 }
1799 foreach ( $this->temporaryUserRights[ $user->getId() ] ?? [] as $overrides ) {
1800 $rights = array_values( array_unique( array_merge( $rights, $overrides ) ) );
1801 }
1802 return $rights;
1803 }
1804
1812 public function invalidateUsersRightsCache( $user = null ): void {
1813 if ( $user !== null ) {
1814 $rightsCacheKey = $this->getRightsCacheKey( $user, false );
1815 unset( $this->usersRights[ $rightsCacheKey ] );
1816 $rightsCacheKey = $this->getRightsCacheKey( $user, true );
1817 unset( $this->usersRights[ $rightsCacheKey ] );
1818 } else {
1819 $this->usersRights = [];
1820 }
1821 }
1822
1826 private function getRightsCacheKey( UserIdentity $user, bool $includePrivateInfo ): string {
1827 $key = $user->isRegistered() ? "u:{$user->getId()}" : "anon:{$user->getName()}";
1828 if ( $includePrivateInfo ) {
1829 $key .= ':private';
1830 }
1831 return $key;
1832 }
1833
1848 public function isEveryoneAllowed( $right ): bool {
1849 // Use the cached results, except in unit tests which rely on
1850 // being able change the permission mid-request
1851 if ( isset( $this->cachedRights[$right] ) ) {
1852 return $this->cachedRights[$right];
1853 }
1854
1855 if ( !isset( $this->options->get( MainConfigNames::GroupPermissions )['*'][$right] )
1856 || !$this->options->get( MainConfigNames::GroupPermissions )['*'][$right]
1857 ) {
1858 $this->cachedRights[$right] = false;
1859 return false;
1860 }
1861
1862 // If it's revoked anywhere, then everyone doesn't have it
1863 foreach ( $this->options->get( MainConfigNames::RevokePermissions ) as $rights ) {
1864 if ( isset( $rights[$right] ) && $rights[$right] ) {
1865 $this->cachedRights[$right] = false;
1866 return false;
1867 }
1868 }
1869
1870 // Remove any rights that aren't allowed to the global-session user,
1871 // unless there are no sessions for this endpoint.
1872 if ( !defined( 'MW_NO_SESSION' ) ) {
1873 // XXX: think what could be done with the below
1874 $allowedRights = RequestContext::getMain()->getRequest()->getSession()->getAllowedUserRights();
1875 if ( $allowedRights !== null && !in_array( $right, $allowedRights, true ) ) {
1876 $this->cachedRights[$right] = false;
1877 return false;
1878 }
1879 }
1880
1881 // Allow extensions to say false
1882 if ( !$this->hookRunner->onUserIsEveryoneAllowed( $right ) ) {
1883 $this->cachedRights[$right] = false;
1884 return false;
1885 }
1886
1887 $this->cachedRights[$right] = true;
1888 return true;
1889 }
1890
1900 public function getAllPermissions(): array {
1901 if ( $this->allRights === null ) {
1902 if ( count( $this->options->get( MainConfigNames::AvailableRights ) ) ) {
1903 $this->allRights = array_unique( array_merge(
1904 self::CORE_RIGHTS,
1905 $this->options->get( MainConfigNames::AvailableRights )
1906 ) );
1907 } else {
1908 $this->allRights = self::CORE_RIGHTS;
1909 }
1910 $this->hookRunner->onUserGetAllRights( $this->allRights );
1911 }
1912 return $this->allRights;
1913 }
1914
1926 public function getImplicitRights(): array {
1927 if ( $this->implicitRights === null ) {
1928 $rights = array_unique( array_merge(
1929 self::CORE_IMPLICIT_RIGHTS,
1930 $this->options->get( MainConfigNames::ImplicitRights )
1931 ) );
1932
1933 $this->implicitRights = array_diff( $rights, $this->getAllPermissions() );
1934 }
1935 return $this->implicitRights;
1936 }
1937
1945 private function isNamespaceProtected( $index, UserIdentity $user ): bool {
1946 $namespaceProtection = $this->options->get( MainConfigNames::NamespaceProtection );
1947 if ( isset( $namespaceProtection[$index] ) ) {
1948 return !$this->userHasAllRights( $user, ...(array)$namespaceProtection[$index] );
1949 }
1950 return false;
1951 }
1952
1961 public function getNamespaceRestrictionLevels( $index, ?UserIdentity $user = null ): array {
1962 if ( !isset( $this->options->get( MainConfigNames::NamespaceProtection )[$index] ) ) {
1963 // All levels are valid if there's no namespace restriction.
1964 // But still filter by user, if necessary
1965 $levels = $this->options->get( MainConfigNames::RestrictionLevels );
1966 if ( $user ) {
1967 $levels = array_values( array_filter( $levels, function ( $level ) use ( $user ) {
1968 $right = $level;
1969 if ( $right === 'sysop' ) {
1970 $right = 'editprotected'; // BC
1971 }
1972 if ( $right === 'autoconfirmed' ) {
1973 $right = 'editsemiprotected'; // BC
1974 }
1975 return $this->userHasRight( $user, $right );
1976 } ) );
1977 }
1978 return $levels;
1979 }
1980
1981 // $wgNamespaceProtection can require one or more rights to edit the namespace, which
1982 // may be satisfied by membership in multiple groups each giving a subset of those rights.
1983 // A restriction level is redundant if, for any one of the namespace rights, all groups
1984 // giving that right also give the restriction level's right. Or, conversely, a
1985 // restriction level is not redundant if, for every namespace right, there's at least one
1986 // group giving that right without the restriction level's right.
1987 //
1988 // First, for each right, get a list of groups with that right.
1989 $namespaceRightGroups = [];
1990 foreach ( (array)$this->options->get( MainConfigNames::NamespaceProtection )[$index] as $right ) {
1991 if ( $right === 'sysop' ) {
1992 $right = 'editprotected'; // BC
1993 }
1994 if ( $right === 'autoconfirmed' ) {
1995 $right = 'editsemiprotected'; // BC
1996 }
1997 if ( $right != '' ) {
1998 $namespaceRightGroups[$right] = $this->groupPermissionsLookup->getGroupsWithPermission( $right );
1999 }
2000 }
2001
2002 // Now, go through the protection levels one by one.
2003 $usableLevels = [ '' ];
2004 foreach ( $this->options->get( MainConfigNames::RestrictionLevels ) as $level ) {
2005 $right = $level;
2006 if ( $right === 'sysop' ) {
2007 $right = 'editprotected'; // BC
2008 }
2009 if ( $right === 'autoconfirmed' ) {
2010 $right = 'editsemiprotected'; // BC
2011 }
2012
2013 if ( $right != '' &&
2014 !isset( $namespaceRightGroups[$right] ) &&
2015 ( !$user || $this->userHasRight( $user, $right ) )
2016 ) {
2017 // Do any of the namespace rights imply the restriction right? (see explanation above)
2018 foreach ( $namespaceRightGroups as $groups ) {
2019 if ( !array_diff( $groups, $this->groupPermissionsLookup->getGroupsWithPermission( $right ) ) ) {
2020 // Yes, this one does.
2021 continue 2;
2022 }
2023 }
2024 // No, keep the restriction level
2025 $usableLevels[] = $level;
2026 }
2027 }
2028
2029 return $usableLevels;
2030 }
2031
2048 #[\NoDiscard]
2049 public function addTemporaryUserRights( UserIdentity $user, $rights ): ScopedCallback {
2050 $userId = $user->getId();
2051 $nextKey = count( $this->temporaryUserRights[$userId] ?? [] );
2052 $this->temporaryUserRights[$userId][$nextKey] = (array)$rights;
2053 return new ScopedCallback( function () use ( $userId, $nextKey ) {
2054 unset( $this->temporaryUserRights[$userId][$nextKey] );
2055 } );
2056 }
2057
2066 public function overrideUserRightsForTesting( $user, $rights = [] ) {
2067 if ( !defined( 'MW_PHPUNIT_TEST' ) ) {
2068 throw new LogicException( __METHOD__ . ' can not be called outside of tests' );
2069 }
2070 $this->usersRights[ $this->getRightsCacheKey( $user, false ) ] =
2071 is_array( $rights ) ? $rights : [ $rights ];
2072 $this->usersRights[ $this->getRightsCacheKey( $user, true ) ] =
2073 is_array( $rights ) ? $rights : [ $rights ];
2074 }
2075
2076}
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:71
Extension of Message implementing IApiMessage.
AuthManager is the authentication system in MediaWiki and serves entry point for authentication.
A service class for getting formatted information about a block.
A service class for checking blocks.
A class for passing options to services.
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 ReauthenticateForActions
Name constant for the ReauthenticateForActions 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 RestrictUserPageEditing
Name constant for the RestrictUserPageEditing 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()
Service locator for MediaWiki core services.
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?
__construct(private ServiceOptions $options, private SpecialPageFactory $specialPageFactory, private NamespaceInfo $nsInfo, private GroupPermissionsLookup $groupPermissionsLookup, private UserGroupManager $userGroupManager, private BlockManager $blockManager, private BlockErrorFormatter $blockErrorFormatter, HookContainer $hookContainer, private UserIdentityLookup $userIdentityLookup, private RedirectLookup $redirectLookup, private RestrictionStore $restrictionStore, private TitleFormatter $titleFormatter, private TempUserConfig $tempUserConfig, private UserFactory $userFactory, private ActionFactory $actionFactory)
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.
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.
getAllPermissions()
Get a list of all permissions that can be managed through group permissions.
getUserRightStatus(UserIdentity $user, string $right, string $rigor=self::RIGOR_SECURE, bool $detailedPermissionErrors=true,)
Check whether the user is generally allowed to perform the given action.
getUserPermissions(UserIdentity $user, bool $includePrivateInfo=true)
Get the permissions this user has.
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:129
isRegistered()
Get whether the user is registered.
Definition User.php:2087
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Value object representing a message for i18n.
return[ 'config-schema-inverse'=>['default'=>['ConfigRegistry'=>['main'=> 'MediaWiki\\Config\\GlobalVarConfig::newInstance',], 'Sitename'=> 'MediaWiki', 'Server'=> false, 'CanonicalServer'=> false, 'ServerName'=> false, 'AssumeProxiesUseDefaultProtocolPorts'=> true, 'HttpsPort'=> 443, 'ForceHTTPS'=> false, 'ScriptPath'=> '/wiki', 'UsePathInfo'=> null, 'Script'=> false, 'LoadScript'=> false, 'RestPath'=> false, 'StylePath'=> false, 'LocalStylePath'=> false, 'ExtensionAssetsPath'=> false, 'ExtensionDirectory'=> null, 'StyleDirectory'=> null, 'ArticlePath'=> false, 'UploadPath'=> false, 'ImgAuthPath'=> false, 'ThumbPath'=> false, 'UploadDirectory'=> false, 'FileCacheDirectory'=> false, 'Logo'=> false, 'Logos'=> false, 'Favicon'=> '/favicon.ico', 'AppleTouchIcon'=> false, 'ReferrerPolicy'=> false, 'TmpDirectory'=> false, 'UploadBaseUrl'=> '', 'UploadStashScalerBaseUrl'=> false, 'ActionPaths'=>[], 'MainPageIsDomainRoot'=> false, 'EnableUploads'=> false, 'UploadStashMaxAge'=> 21600, 'EnableAsyncUploads'=> false, 'EnableAsyncUploadsByURL'=> false, 'EnableChunkedUploads'=> false, 'UploadMaintenance'=> false, 'IllegalFileChars'=> ':\\/\\\\', 'DeletedDirectory'=> false, 'ImgAuthDetails'=> false, 'ImgAuthUrlPathMap'=>[], 'LocalFileRepo'=>['class'=> 'MediaWiki\\FileRepo\\LocalRepo', 'name'=> 'local', 'directory'=> null, 'scriptDirUrl'=> null, 'favicon'=> null, 'url'=> null, 'hashLevels'=> null, 'thumbScriptUrl'=> null, 'transformVia404'=> null, 'deletedDir'=> null, 'deletedHashLevels'=> null, 'updateCompatibleMetadata'=> null, 'reserializeMetadata'=> null,], 'ForeignFileRepos'=>[], 'UseInstantCommons'=> false, 'UseSharedUploads'=> false, 'SharedUploadDirectory'=> null, 'SharedUploadPath'=> null, 'HashedSharedUploadDirectory'=> true, 'RepositoryBaseUrl'=> 'https:'FetchCommonsDescriptions'=> false, 'SharedUploadDBname'=> false, 'SharedUploadDBprefix'=> '', 'CacheSharedUploads'=> true, 'ForeignUploadTargets'=>['local',], 'UploadDialog'=>['fields'=>['description'=> true, 'date'=> false, 'categories'=> false,], 'licensemessages'=>['local'=> 'generic-local', 'foreign'=> 'generic-foreign',], 'comment'=>['local'=> '', 'foreign'=> '',], 'format'=>['filepage'=> ' $DESCRIPTION', 'description'=> ' $TEXT', 'ownwork'=> '', 'license'=> '', 'uncategorized'=> '',],], 'FileBackends'=>[], 'LockManagers'=>[], 'DefaultLockManager'=> null, 'ShowEXIF'=> null, 'UpdateCompatibleMetadata'=> false, 'AllowCopyUploads'=> false, 'CopyUploadsDomains'=>[], 'CopyUploadsFromSpecialUpload'=> false, 'CopyUploadProxy'=> false, 'CopyUploadTimeout'=> false, 'CopyUploadAllowOnWikiDomainConfig'=> false, 'MaxUploadSize'=> 104857600, 'MinUploadChunkSize'=> 1024, 'UploadNavigationUrl'=> false, 'UploadMissingFileUrl'=> false, 'ThumbnailScriptPath'=> false, 'SharedThumbnailScriptPath'=> false, 'HashedUploadDirectory'=> true, 'CSPUploadEntryPoint'=> true, 'FileExtensions'=>['png', 'gif', 'jpg', 'jpeg', 'webp',], 'ProhibitedFileExtensions'=>['html', 'htm', 'js', 'jsb', 'mhtml', 'mht', 'xhtml', 'xht', 'php', 'phtml', 'php3', 'php4', 'php5', 'phps', 'phar', 'shtml', 'jhtml', 'pl', 'py', 'cgi', 'exe', 'scr', 'dll', 'msi', 'vbs', 'bat', 'com', 'pif', 'cmd', 'vxd', 'cpl', 'xml',], 'MimeTypeExclusions'=>['text/html', 'application/javascript', 'text/javascript', 'text/x-javascript', 'application/x-shellscript', 'application/x-php', 'text/x-php', 'text/x-python', 'text/x-perl', 'text/x-bash', 'text/x-sh', 'text/x-csh', 'text/scriptlet', 'application/x-msdownload', 'application/x-msmetafile', 'application/java', 'application/xml', 'text/xml',], 'CheckFileExtensions'=> true, 'StrictFileExtensions'=> true, 'DisableUploadScriptChecks'=> false, 'UploadSizeWarning'=> false, 'TrustedMediaFormats'=>['BITMAP', 'AUDIO', 'VIDEO', 'image/svg+xml', 'application/pdf',], 'MediaHandlers'=>[], 'NativeImageLazyLoading'=> false, 'ParserTestMediaHandlers'=>['image/jpeg'=> 'MockBitmapHandler', 'image/png'=> 'MockBitmapHandler', 'image/gif'=> 'MockBitmapHandler', 'image/tiff'=> 'MockBitmapHandler', 'image/webp'=> 'MockBitmapHandler', 'image/x-ms-bmp'=> 'MockBitmapHandler', 'image/x-bmp'=> 'MockBitmapHandler', 'image/x-xcf'=> 'MockBitmapHandler', 'image/svg+xml'=> 'MockSvgHandler', 'image/vnd.djvu'=> 'MockDjVuHandler',], 'UseImageResize'=> true, 'UseImageMagick'=> false, 'ImageMagickConvertCommand'=> '/usr/bin/convert', 'MaxInterlacingAreas'=>[], 'SharpenParameter'=> '0x0.4', 'SharpenReductionThreshold'=> 0.85, 'ImageMagickTempDir'=> false, 'CustomConvertCommand'=> false, 'JpegTran'=> '/usr/bin/jpegtran', 'JpegPixelFormat'=> 'yuv420', 'JpegQuality'=> 80, 'Exiv2Command'=> '/usr/bin/exiv2', 'Exiftool'=> '/usr/bin/exiftool', 'SVGConverters'=>['ImageMagick'=> ' $path/convert -background "#ffffff00" -thumbnail $widthx$height\\! $input PNG:$output', 'inkscape'=> ' $path/inkscape -w $width -o $output $input', 'batik'=> 'java -Djava.awt.headless=true -jar $path/batik-rasterizer.jar -w $width -d $output $input', 'rsvg'=> ' $path/rsvg-convert -w $width -h $height -o $output $input', 'ImagickExt'=>['SvgHandler::rasterizeImagickExt',],], 'SVGConverter'=> 'ImageMagick', 'SVGConverterPath'=> '', 'SVGMaxSize'=> 5120, 'SVGMetadataCutoff'=> 5242880, 'SVGNativeRendering'=> true, 'SVGNativeRenderingSizeLimit'=> 51200, 'MediaInTargetLanguage'=> true, 'MaxImageArea'=> 12500000, 'MaxAnimatedGifArea'=> 12500000, 'TiffThumbnailType'=>[], 'ThumbnailEpoch'=> '20030516000000', 'AttemptFailureEpoch'=> 1, 'IgnoreImageErrors'=> false, 'GenerateThumbnailOnParse'=> true, 'ShowArchiveThumbnails'=> true, 'EnableAutoRotation'=> null, 'Antivirus'=> null, 'AntivirusSetup'=>['clamav'=>['command'=> 'clamscan --no-summary ', 'codemap'=>[0=> 0, 1=> 1, 52=> -1, ' *'=> false,], 'messagepattern'=> '/.*?:(.*)/sim',],], 'AntivirusRequired'=> true, 'VerifyMimeType'=> true, 'MimeTypeFile'=> 'internal', 'MimeInfoFile'=> 'internal', 'MimeDetectorCommand'=> null, 'TrivialMimeDetection'=> false, 'XMLMimeTypes'=>['http:'svg'=> 'image/svg+xml', 'http:'http:'html'=> 'text/html',], 'ImageLimits'=>[[320, 240,], [640, 480,], [800, 600,], [1024, 768,], [1280, 1024,], [2560, 2048,],], 'ThumbLimits'=>[120, 150, 180, 200, 220, 250, 300, 400,], 'ThumbnailNamespaces'=>[6,], 'ThumbnailSteps'=> null, 'ThumbnailBuckets'=> null, 'ThumbnailMinimumBucketDistance'=> 50, 'UploadThumbnailRenderMap'=>[], 'UploadThumbnailRenderMethod'=> 'jobqueue', 'UploadThumbnailRenderHttpCustomHost'=> false, 'UploadThumbnailRenderHttpCustomDomain'=> false, 'UseTinyRGBForJPGThumbnails'=> false, 'GalleryOptions'=>[], 'ThumbUpright'=> 0.75, 'DirectoryMode'=> 511, 'ResponsiveImages'=> true, 'ImagePreconnect'=> false, 'TrackMediaRequestProvenance'=> false, 'DjvuUseBoxedCommand'=> false, 'DjvuDump'=> null, 'DjvuRenderer'=> null, 'DjvuTxt'=> null, 'DjvuPostProcessor'=> 'pnmtojpeg', 'DjvuOutputExtension'=> 'jpg', 'EmergencyContact'=> false, 'RestTermsOfServiceUrl'=> null, 'PasswordSender'=> false, 'NoReplyAddress'=> false, 'EnableEmail'=> true, 'EnableUserEmail'=> true, 'UserEmailUseReplyTo'=> true, 'PasswordReminderResendTime'=> 24, 'NewPasswordExpiry'=> 604800, 'UserEmailConfirmationTokenExpiry'=> 604800, 'PasswordExpirationDays'=> false, 'PasswordExpireGrace'=> 604800, 'SMTP'=> false, 'AdditionalMailParams'=> null, 'AllowHTMLEmail'=> false, 'EnotifFromEditor'=> false, 'EmailAuthentication'=> true, 'EmailConfirmationBanner'=> false, 'EnotifWatchlist'=> false, 'EnotifUserTalk'=> false, 'EnotifRevealEditorAddress'=> false, 'EnotifMinorEdits'=> true, 'EnotifUseRealName'=> false, 'UsersNotifiedOnAllChanges'=>[], 'DBname'=> 'my_wiki', 'DBmwschema'=> null, 'DBprefix'=> '', 'DBserver'=> 'localhost', 'DBport'=> 5432, 'DBuser'=> 'wikiuser', 'DBpassword'=> '', 'DBtype'=> 'mysql', 'DBssl'=> false, 'DBcompress'=> false, 'DBStrictWarnings'=> false, 'DBadminuser'=> null, 'DBadminpassword'=> null, 'SearchType'=> null, 'SearchTypeAlternatives'=> null, 'DBTableOptions'=> 'ENGINE=InnoDB, DEFAULT CHARSET=binary', 'SQLMode'=> '', 'SQLiteDataDir'=> '', 'SharedDB'=> null, 'SharedPrefix'=> false, 'SharedTables'=>['user', 'user_properties', 'user_autocreate_serial',], 'SharedSchema'=> false, 'DBservers'=> false, 'LBFactoryConf'=>['class'=> 'Wikimedia\\Rdbms\\LBFactorySimple',], 'DataCenterUpdateStickTTL'=> 10, 'DBerrorLog'=> false, 'DBerrorLogTZ'=> false, 'LocalDatabases'=>[], 'DatabaseReplicaLagWarning'=> 10, 'DatabaseReplicaLagCritical'=> 30, 'MaxExecutionTimeForExpensiveQueries'=> 0, 'VirtualDomainsMapping'=>[], 'FileSchemaMigrationStage'=> 3, 'ExternalLinksDomainGaps'=>[], 'ContentHandlers'=>['wikitext'=>['class'=> 'MediaWiki\\Content\\WikitextContentHandler', 'services'=>['TitleFactory', 'ParserFactory', 'GlobalIdGenerator', 'LanguageNameUtils', 'LinkRenderer', 'MagicWordFactory', 'ParsoidParserFactory',],], 'javascript'=>['class'=> 'MediaWiki\\Content\\JavaScriptContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'UserOptionsLookup', 'CodeHighlighter',],], 'json'=>['class'=> 'MediaWiki\\Content\\JsonContentHandler', 'services'=>['ParsoidParserFactory', 'TitleFactory',],], 'css'=>['class'=> 'MediaWiki\\Content\\CssContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'UserOptionsLookup', 'CodeHighlighter',],], 'vue'=>['class'=> 'MediaWiki\\Content\\VueContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'CodeHighlighter',],], 'text'=> 'MediaWiki\\Content\\TextContentHandler', 'unknown'=> 'MediaWiki\\Content\\FallbackContentHandler',], 'NamespaceContentModels'=>[], 'TextModelsToParse'=>['wikitext', 'javascript', 'css',], 'CompressRevisions'=> false, 'ExternalStores'=>[], 'ExternalServers'=>[], 'DefaultExternalStore'=> false, 'RevisionCacheExpiry'=> 604800, 'PageLanguageUseDB'=> false, 'DiffEngine'=> null, 'ExternalDiffEngine'=> false, 'Wikidiff2Options'=>[], 'RequestTimeLimit'=> null, 'TransactionalTimeLimit'=> 120, 'CriticalSectionTimeLimit'=> 180.0, 'MiserMode'=> false, 'DisableQueryPages'=> false, 'QueryCacheLimit'=> 1000, 'WantedPagesThreshold'=> 1, 'AllowSlowParserFunctions'=> false, 'AllowSchemaUpdates'=> true, 'MaxArticleSize'=> 2048, 'MemoryLimit'=> '50M', 'PoolCounterConf'=> null, 'PoolCountClientConf'=>['servers'=>['127.0.0.1',], 'timeout'=> 0.1,], 'MaxUserDBWriteDuration'=> false, 'MaxJobDBWriteDuration'=> false, 'LinkHolderBatchSize'=> 1000, 'MaximumMovedPages'=> 100, 'ForceDeferredUpdatesPreSend'=> false, 'MultiShardSiteStats'=> false, 'CacheDirectory'=> false, 'MainCacheType'=> 0, 'MessageCacheType'=> -1, 'ParserCacheType'=> -1, 'SessionCacheType'=> -1, 'AnonSessionCacheType'=> false, 'LanguageConverterCacheType'=> -1, 'ObjectCaches'=>[0=>['class'=> 'Wikimedia\\ObjectCache\\EmptyBagOStuff', 'reportDupes'=> false,], 1=>['class'=> 'MediaWiki\\ObjectCache\\SqlBagOStuff', 'loggroup'=> 'SQLBagOStuff',], 'memcached-php'=>['class'=> 'Wikimedia\\ObjectCache\\MemcachedPhpBagOStuff', 'loggroup'=> 'memcached',], 'memcached-pecl'=>['class'=> 'Wikimedia\\ObjectCache\\MemcachedPeclBagOStuff', 'loggroup'=> 'memcached',], 'hash'=>['class'=> 'Wikimedia\\ObjectCache\\HashBagOStuff', 'reportDupes'=> false,], 'apc'=>['class'=> 'Wikimedia\\ObjectCache\\APCUBagOStuff', 'reportDupes'=> false,], 'apcu'=>['class'=> 'Wikimedia\\ObjectCache\\APCUBagOStuff', 'reportDupes'=> false,],], 'WANObjectCache'=>[], 'MicroStashType'=> -1, 'MainStash'=> 1, 'ParsoidCacheConfig'=>['StashType'=> null, 'StashDuration'=> 86400, 'WarmParsoidParserCache'=> false,], 'ParsoidSelectiveUpdateSampleRate'=> 0, 'ParserCacheFilterConfig'=>['pcache'=>['default'=>['minCpuTime'=> 0,],], 'postproc-pcache'=>['default'=>['minCpuTime'=> 9223372036854775807,],], 'parsoid-pcache'=>['default'=>['minCpuTime'=> 0,],], 'postproc-parsoid-pcache'=>['default'=>['minCpuTime'=> 0,],],], 'ChronologyProtectorSecret'=> '', 'ParserCacheExpireTime'=> 86400, 'ParserCacheAsyncExpireTime'=> 60, 'ParserCacheAsyncRefreshJobs'=> true, 'OldRevisionParserCacheExpireTime'=> 3600, 'ObjectCacheSessionExpiry'=> 3600, 'SuspiciousIpExpiry'=> false, 'SessionPbkdf2Iterations'=> 10001, 'UseSessionCookieJwt'=> false, 'JwtSessionCookieIssuer'=> null, 'MemCachedServers'=>['127.0.0.1:11211',], 'MemCachedPersistent'=> false, 'MemCachedTimeout'=> 500000, 'UseLocalMessageCache'=> false, 'AdaptiveMessageCache'=> false, 'LocalisationCacheConf'=>['class'=> 'MediaWiki\\Language\\LocalisationCache', 'store'=> 'detect', 'storeClass'=> false, 'storeDirectory'=> false, 'storeServer'=>[], 'forceRecache'=> false, 'manualRecache'=> false,], 'CachePages'=> true, 'CacheEpoch'=> '20030516000000', 'GitInfoCacheDirectory'=> false, 'UseFileCache'=> false, 'FileCacheDepth'=> 2, 'RenderHashAppend'=> '', 'EnableSidebarCache'=> false, 'SidebarCacheExpiry'=> 86400, 'UseGzip'=> false, 'InvalidateCacheOnLocalSettingsChange'=> true, 'ExtensionInfoMTime'=> false, 'EnableRemoteBagOStuffTests'=> false, 'UseCdn'=> false, 'VaryOnXFP'=> false, 'InternalServer'=> false, 'CdnMaxAge'=> 18000, 'CdnMaxageLagged'=> 30, 'CdnMaxageStale'=> 10, 'CdnReboundPurgeDelay'=> 0, 'CdnMaxageSubstitute'=> 60, 'ForcedRawSMaxage'=> 300, 'CdnServers'=>[], 'CdnServersNoPurge'=>[], 'HTCPRouting'=>[], 'HTCPMulticastTTL'=> 1, 'UsePrivateIPs'=> false, 'CdnMatchParameterOrder'=> true, 'LanguageCode'=> 'en', 'GrammarForms'=>[], 'InterwikiMagic'=> true, 'HideInterlanguageLinks'=> false, 'ExtraInterlanguageLinkPrefixes'=>[], 'InterlanguageLinkCodeMap'=>[], 'ExtraLanguageNames'=>[], 'ExtraLanguageCodes'=>['bh'=> 'bho', 'no'=> 'nb', 'simple'=> 'en',], 'DummyLanguageCodes'=>[], 'AllUnicodeFixes'=> false, 'LegacyEncoding'=> false, 'AmericanDates'=> false, 'TranslateNumerals'=> true, 'UseDatabaseMessages'=> true, 'MaxMsgCacheEntrySize'=> 10000, 'DisableLangConversion'=> false, 'DisableTitleConversion'=> false, 'DefaultLanguageVariant'=> false, 'UsePigLatinVariant'=> false, 'DisabledVariants'=>[], 'VariantArticlePath'=> false, 'UseXssLanguage'=> false, 'LoginLanguageSelector'=> false, 'ForceUIMsgAsContentMsg'=>[], 'RawHtmlMessages'=>[], 'Localtimezone'=> null, 'LocalTZoffset'=> null, 'OverrideUcfirstCharacters'=>[], 'MimeType'=> 'text/html', 'Html5Version'=> null, 'EditSubmitButtonLabelPublish'=> false, 'XhtmlNamespaces'=>[], 'SiteNotice'=> '', 'BrowserFormatDetection'=> 'telephone=no', 'SkinMetaTags'=>[], 'DefaultSkin'=> 'vector-2022', 'FallbackSkin'=> 'fallback', 'SkipSkins'=>[], 'DisableOutputCompression'=> false, 'FragmentMode'=>['html5', 'legacy',], 'ExternalInterwikiFragmentMode'=> 'legacy', 'FooterIcons'=>['copyright'=>['copyright'=>[],], 'poweredby'=>['mediawiki'=>['src'=> null, 'url'=> 'https:'alt'=> 'Powered by MediaWiki', 'lang'=> 'en',],],], 'EnableSectionShare'=> false, 'UseCombinedLoginLink'=> false, 'Edititis'=> false, 'Send404Code'=> true, 'ShowRollbackEditCount'=> 10, 'EnableCanonicalServerLink'=> false, 'InterwikiLogoOverride'=>[], 'ResourceModules'=>[], 'ResourceModuleSkinStyles'=>[], 'ResourceLoaderSources'=>[], 'ResourceBasePath'=> null, 'ResourceLoaderMaxage'=>[], 'ResourceLoaderDebug'=> false, 'ResourceLoaderMaxQueryLength'=> false, 'ResourceLoaderValidateJS'=> true, 'ResourceLoaderEnableJSProfiler'=> false, 'ResourceLoaderStorageEnabled'=> true, 'ResourceLoaderStorageVersion'=> 1, 'ResourceLoaderEnableSourceMapLinks'=> true, 'AllowSiteCSSOnRestrictedPages'=> false, 'VueDevelopmentMode'=> false, 'CodexDevelopmentDir'=> null, 'MetaNamespace'=> false, 'MetaNamespaceTalk'=> false, 'CanonicalNamespaceNames'=>[-2=> 'Media', -1=> 'Special', 0=> '', 1=> 'Talk', 2=> 'User', 3=> 'User_talk', 4=> 'Project', 5=> 'Project_talk', 6=> 'File', 7=> 'File_talk', 8=> 'MediaWiki', 9=> 'MediaWiki_talk', 10=> 'Template', 11=> 'Template_talk', 12=> 'Help', 13=> 'Help_talk', 14=> 'Category', 15=> 'Category_talk',], 'ExtraNamespaces'=>[], 'ExtraGenderNamespaces'=>[], 'NamespaceAliases'=>[], 'LegalTitleChars'=> ' %!"$&\'()*,\\-.\\/0-9:;=?@A-Z\\\\^_`a-z~\\x80-\\xFF+', 'CapitalLinks' => true, 'CapitalLinkOverrides' => [ ], 'NamespacesWithSubpages' => [ 1 => true, 2 => true, 3 => true, 4 => true, 5 => true, 7 => true, 8 => true, 9 => true, 10 => true, 11 => true, 12 => true, 13 => true, 15 => true, ], 'NamespacesWithoutAutoSummaries' => [ ], 'ContentNamespaces' => [ 0, ], 'ShortPagesNamespaceExclusions' => [ ], 'ExtraSignatureNamespaces' => [ ], 'InvalidRedirectTargets' => [ 'Filepath', 'Mypage', 'Mytalk', 'Redirect', 'Mylog', ], 'DisableHardRedirects' => false, 'FixDoubleRedirects' => false, 'LocalInterwikis' => [ ], 'InterwikiExpiry' => 10800, 'InterwikiCache' => false, 'InterwikiScopes' => 3, 'InterwikiFallbackSite' => 'wiki', 'RedirectSources' => false, 'SiteTypes' => [ 'mediawiki' => 'MediaWiki\\Site\\MediaWikiSite', ], 'MaxTocLevel' => 999, 'MaxPPNodeCount' => 1000000, 'MaxTemplateDepth' => 100, 'MaxPPExpandDepth' => 100, 'UrlProtocols' => [ 'bitcoin:', 'ftp: 'ftps: 'geo:', 'git: 'gopher: 'http: 'https: 'irc: 'ircs: 'magnet:', 'mailto:', 'matrix:', 'mms: 'news:', 'nntp: 'redis: 'sftp: 'sip:', 'sips:', 'sms:', 'ssh: 'svn: 'tel:', 'telnet: 'urn:', 'wikipedia: 'worldwind: 'xmpp:', ' ], 'CleanSignatures' => true, 'AllowExternalImages' => false, 'AllowExternalImagesFrom' => '', 'EnableImageWhitelist' => false, 'TidyConfig' => [ ], 'ParsoidSettings' => [ 'useSelser' => true, ], 'ParsoidExperimentalParserFunctionOutput' => false, 'RawHtml' => false, 'ExternalLinkTarget' => false, 'NoFollowLinks' => true, 'NoFollowNsExceptions' => [ ], 'NoFollowDomainExceptions' => [ 'mediawiki.org', ], 'RegisterInternalExternals' => false, 'ExternalLinksIgnoreDomains' => [ ], 'AllowDisplayTitle' => true, 'RestrictDisplayTitle' => true, 'ExpensiveParserFunctionLimit' => 100, 'PreprocessorCacheThreshold' => 1000, 'EnableScaryTranscluding' => false, 'TranscludeCacheExpiry' => 3600, 'EnableMagicLinks' => [ 'ISBN' => false, 'PMID' => false, 'RFC' => false, ], 'ParserEnableUserLanguage' => false, 'ArticleCountMethod' => 'link', 'ActiveUserDays' => 30, 'LearnerEdits' => 10, 'LearnerMemberSince' => 4, 'ExperiencedUserEdits' => 500, 'ExperiencedUserMemberSince' => 30, 'ManualRevertSearchRadius' => 15, 'RevertedTagMaxDepth' => 15, 'CentralIdLookupProviders' => [ 'local' => [ 'class' => 'MediaWiki\\User\\CentralId\\LocalIdLookup', 'services' => [ 'MainConfig', 'DBLoadBalancerFactory', 'HideUserUtils', ], ], ], 'CentralIdLookupProvider' => 'local', 'UserRegistrationProviders' => [ 'local' => [ 'class' => 'MediaWiki\\User\\Registration\\LocalUserRegistrationProvider', 'services' => [ 'ConnectionProvider', ], ], ], 'PasswordPolicy' => [ 'policies' => [ 'bureaucrat' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'sysop' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'interface-admin' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'bot' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'default' => [ 'MinimalPasswordLength' => [ 'value' => 8, 'suggestChangeOnLogin' => true, ], 'PasswordCannotBeSubstringInUsername' => [ 'value' => true, 'suggestChangeOnLogin' => true, ], 'PasswordCannotMatchDefaults' => [ 'value' => true, 'suggestChangeOnLogin' => true, ], 'MaximalPasswordLength' => [ 'value' => 4096, 'suggestChangeOnLogin' => true, ], 'PasswordNotInCommonList' => [ 'value' => true, 'suggestChangeOnLogin' => true, ], ], ], 'checks' => [ 'MinimalPasswordLength' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkMinimalPasswordLength', ], 'MinimumPasswordLengthToLogin' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkMinimumPasswordLengthToLogin', ], 'PasswordCannotBeSubstringInUsername' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkPasswordCannotBeSubstringInUsername', ], 'PasswordCannotMatchDefaults' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkPasswordCannotMatchDefaults', ], 'MaximalPasswordLength' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkMaximalPasswordLength', ], 'PasswordNotInCommonList' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkPasswordNotInCommonList', ], ], ], 'AuthManagerConfig' => null, 'AuthManagerAutoConfig' => [ 'preauth' => [ 'MediaWiki\\Auth\\ThrottlePreAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\ThrottlePreAuthenticationProvider', 'sort' => 0, ], 'MediaWiki\\Auth\\PreviouslyRenamedAccountPreAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\PreviouslyRenamedAccountPreAuthenticationProvider', 'services' => [ 'ConnectionProvider', 'UserFactory', ], 'sort' => 0, ], ], 'primaryauth' => [ 'MediaWiki\\Auth\\TemporaryPasswordPrimaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\TemporaryPasswordPrimaryAuthenticationProvider', 'services' => [ 'DBLoadBalancerFactory', 'UserOptionsLookup', ], 'args' => [ [ 'authoritative' => false, ], ], 'sort' => 0, ], 'MediaWiki\\Auth\\LocalPasswordPrimaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\LocalPasswordPrimaryAuthenticationProvider', 'services' => [ 'DBLoadBalancerFactory', ], 'args' => [ [ 'authoritative' => true, ], ], 'sort' => 100, ], ], 'secondaryauth' => [ 'MediaWiki\\Auth\\CheckBlocksSecondaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\CheckBlocksSecondaryAuthenticationProvider', 'sort' => 0, ], 'MediaWiki\\Auth\\ResetPasswordSecondaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\ResetPasswordSecondaryAuthenticationProvider', 'sort' => 100, ], 'MediaWiki\\Auth\\EmailNotificationSecondaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\EmailNotificationSecondaryAuthenticationProvider', 'services' => [ 'DBLoadBalancerFactory', ], 'sort' => 200, ], ], ], 'RememberMe' => 'choose', 'ReauthenticateTime' => [ 'default' => 3600, ], 'ChangeCredentialsBlacklist' => [ 'MediaWiki\\Auth\\TemporaryPasswordAuthenticationRequest', ], 'RemoveCredentialsBlacklist' => [ 'MediaWiki\\Auth\\PasswordAuthenticationRequest', ], 'InvalidPasswordReset' => true, 'PasswordDefault' => 'pbkdf2', 'PasswordConfig' => [ 'A' => [ 'class' => 'MediaWiki\\Password\\MWOldPassword', ], 'B' => [ 'class' => 'MediaWiki\\Password\\MWSaltedPassword', ], 'pbkdf2-legacyA' => [ 'class' => 'MediaWiki\\Password\\LayeredParameterizedPassword', 'types' => [ 'A', 'pbkdf2', ], ], 'pbkdf2-legacyB' => [ 'class' => 'MediaWiki\\Password\\LayeredParameterizedPassword', 'types' => [ 'B', 'pbkdf2', ], ], 'bcrypt' => [ 'class' => 'MediaWiki\\Password\\BcryptPassword', 'cost' => 9, ], 'pbkdf2' => [ 'class' => 'MediaWiki\\Password\\Pbkdf2PasswordUsingOpenSSL', 'algo' => 'sha512', 'cost' => '30000', 'length' => '64', ], 'argon2' => [ 'class' => 'MediaWiki\\Password\\Argon2Password', 'algo' => 'auto', ], ], 'PasswordResetRoutes' => [ 'username' => true, 'email' => true, ], 'MaxSigChars' => 255, 'SignatureValidation' => 'warning', 'SignatureAllowedLintErrors' => [ 'obsolete-tag', ], 'MaxNameChars' => 255, 'ReservedUsernames' => [ 'MediaWiki default', 'Conversion script', 'Maintenance script', 'Template namespace initialisation script', 'ScriptImporter', 'Delete page script', 'Move page script', 'Command line script', 'Unknown user', 'msg:double-redirect-fixer', 'msg:usermessage-editor', 'msg:proxyblocker', 'msg:sorbs', 'msg:spambot_username', 'msg:autochange-username', ], 'DefaultUserOptions' => [ 'ccmeonemails' => 0, 'date' => 'default', 'diffonly' => 0, 'diff-type' => 'table', 'disablemail' => 0, 'editfont' => 'monospace', 'editondblclick' => 0, 'editrecovery' => 0, 'editsectiononrightclick' => 0, 'email-allow-new-users' => 1, 'enotifminoredits' => 0, 'enotifrevealaddr' => 0, 'enotifusertalkpages' => 1, 'enotifwatchlistpages' => 1, 'extendwatchlist' => 1, 'fancysig' => 0, 'forceeditsummary' => 0, 'forcesafemode' => 0, 'gender' => 'unknown', 'hidecategorization' => 1, 'hideminor' => 0, 'hidepatrolled' => 0, 'imagesize' => 2, 'minordefault' => 0, 'newpageshidepatrolled' => 0, 'nickname' => '', 'norollbackdiff' => 0, 'prefershttps' => 1, 'previewonfirst' => 0, 'previewontop' => 1, 'pst-cssjs' => 1, 'rcdays' => 7, 'rcenhancedfilters-disable' => 0, 'rclimit' => 50, 'requireemail' => 0, 'search-match-redirect' => true, 'search-special-page' => 'Search', 'search-thumbnail-extra-namespaces' => true, 'searchlimit' => 20, 'showhiddencats' => 0, 'shownumberswatching' => 1, 'showrollbackconfirmation' => 0, 'skin' => false, 'skin-responsive' => 1, 'thumbsize' => 5, 'underline' => 2, 'useeditwarning' => 1, 'uselivepreview' => 0, 'usenewrc' => 1, 'watchcreations' => 1, 'watchcreations-expiry' => 'infinite', 'watchdefault' => 1, 'watchdefault-expiry' => 'infinite', 'watchdeletion' => 0, 'watchlistdays' => 7, 'watchlisthideanons' => 0, 'watchlisthidebots' => 0, 'watchlisthidecategorization' => 1, 'watchlisthideliu' => 0, 'watchlisthideminor' => 0, 'watchlisthideown' => 0, 'watchlisthidepatrolled' => 0, 'watchlistreloadautomatically' => 0, 'watchlistunwatchlinks' => 0, 'watchmoves' => 0, 'watchrollback' => 0, 'watchuploads' => 1, 'watchrollback-expiry' => 'infinite', 'watchstar-expiry' => 'infinite', 'wlenhancedfilters-disable' => 0, 'wllimit' => 250, ], 'ConditionalUserOptions' => [ ], 'HiddenPrefs' => [ ], 'UserJsPrefLimit' => 100, 'InvalidUsernameCharacters' => '@:>=', 'UserrightsInterwikiDelimiter' => '@', 'SecureLogin' => false, 'AuthenticationTokenVersion' => null, 'SessionProviders' => [ 'MediaWiki\\Session\\CookieSessionProvider' => [ 'class' => 'MediaWiki\\Session\\CookieSessionProvider', 'args' => [ [ 'priority' => 30, ], ], 'services' => [ 'JwtCodec', 'UrlUtils', ], ], 'MediaWiki\\Session\\BotPasswordSessionProvider' => [ 'class' => 'MediaWiki\\Session\\BotPasswordSessionProvider', 'args' => [ [ 'priority' => 75, ], ], 'services' => [ 'GrantsInfo', ], ], ], 'AutoCreateTempUser' => [ 'known' => false, 'enabled' => false, 'actions' => [ 'edit', ], 'genPattern' => '~$1', 'matchPattern' => null, 'reservedPattern' => '~$1', 'serialProvider' => [ 'type' => 'local', 'useYear' => true, ], 'serialMapping' => [ 'type' => 'readable-numeric', ], 'expireAfterDays' => 90, 'notifyBeforeExpirationDays' => 10, ], 'AutoblockExemptions' => [ ], 'AutoblockExpiry' => 86400, 'BlockAllowsUTEdit' => true, 'BlockCIDRLimit' => [ 'IPv4' => 16, 'IPv6' => 19, ], 'BlockDisablesLogin' => false, 'EnableMultiBlocks' => false, 'WhitelistRead' => false, 'WhitelistReadRegexp' => false, 'EmailConfirmToEdit' => false, 'HideIdentifiableRedirects' => true, 'GroupPermissions' => [ '*' => [ 'createaccount' => true, 'autocreateaccount' => true, 'read' => true, 'edit' => true, 'createpage' => true, 'createtalk' => true, 'viewmyprivateinfo' => true, 'editmyprivateinfo' => true, 'editmyoptions' => true, ], 'user' => [ 'move' => true, 'move-subpages' => true, 'move-rootuserpages' => true, 'move-categorypages' => true, 'movefile' => true, 'read' => true, 'edit' => true, 'createpage' => true, 'createtalk' => true, 'upload' => true, 'reupload' => true, 'reupload-shared' => true, 'minoredit' => true, 'editmyusercss' => true, 'editmyuserjson' => true, 'editmyuserjs' => true, 'editmyuserjsredirect' => true, 'sendemail' => true, 'applychangetags' => true, 'changetags' => true, 'viewmywatchlist' => true, 'editmywatchlist' => true, 'createwithcontentmodel' => true, 'logout' => true, ], 'autoconfirmed' => [ 'autoconfirmed' => true, 'editsemiprotected' => true, ], 'bot' => [ 'bot' => true, 'autoconfirmed' => true, 'editsemiprotected' => true, 'nominornewtalk' => true, 'autopatrol' => true, 'suppressredirect' => true, 'apihighlimits' => true, ], 'sysop' => [ 'block' => true, 'createaccount' => true, 'createpreviouslyrenamedaccount' => true, 'delete' => true, 'bigdelete' => true, 'deletedhistory' => true, 'deletedtext' => true, 'undelete' => true, 'editcontentmodel' => true, 'editinterface' => true, 'editsitejson' => true, 'edituserjson' => true, 'import' => true, 'importupload' => true, 'move' => true, 'move-subpages' => true, 'move-rootuserpages' => true, 'move-categorypages' => true, 'patrol' => true, 'autopatrol' => true, 'protect' => true, 'editprotected' => true, 'rollback' => true, 'upload' => true, 'reupload' => true, 'reupload-shared' => true, 'unwatchedpages' => true, 'autoconfirmed' => true, 'editsemiprotected' => true, 'ipblock-exempt' => true, 'blockemail' => true, 'markbotedits' => true, 'apihighlimits' => true, 'browsearchive' => true, 'noratelimit' => true, 'movefile' => true, 'unblockself' => true, 'suppressredirect' => true, 'mergehistory' => true, 'managechangetags' => true, 'deletechangetags' => true, ], 'interface-admin' => [ 'editinterface' => true, 'editsitecss' => true, 'editsitejson' => true, 'editsitejs' => true, 'editusercss' => true, 'edituserjson' => true, 'edituserjs' => true, ], 'bureaucrat' => [ 'userrights' => true, 'noratelimit' => true, 'renameuser' => true, ], 'suppress' => [ 'hideuser' => true, 'suppressrevision' => true, 'viewsuppressed' => true, 'suppressionlog' => true, 'deleterevision' => true, 'deletelogentry' => true, ], ], 'PrivilegedGroups' => [ 'bureaucrat', 'interface-admin', 'suppress', 'sysop', ], 'RevokePermissions' => [ ], 'GroupInheritsPermissions' => [ ], 'ImplicitGroups' => [ '*', 'user', 'autoconfirmed', ], 'GroupsAddToSelf' => [ ], 'GroupsRemoveFromSelf' => [ ], 'RestrictedGroups' => [ ], 'UserRequirementsPrivateConditions' => [ ], 'RestrictionTypes' => [ 'create', 'edit', 'move', 'upload', ], 'RestrictionLevels' => [ '', 'autoconfirmed', 'sysop', ], 'CascadingRestrictionLevels' => [ 'sysop', ], 'SemiprotectedRestrictionLevels' => [ 'autoconfirmed', ], 'NamespaceProtection' => [ ], 'RestrictUserPageEditing' => false, 'NonincludableNamespaces' => [ ], 'AutoConfirmAge' => 0, 'AutoConfirmCount' => 0, 'Autopromote' => [ 'autoconfirmed' => [ '&', [ 1, null, ], [ 2, null, ], ], ], 'AutopromoteOnce' => [ 'onEdit' => [ ], ], 'AutopromoteOnceLogInRC' => true, 'AutopromoteOnceRCExcludedGroups' => [ ], 'AddGroups' => [ ], 'RemoveGroups' => [ ], 'AvailableRights' => [ ], 'ImplicitRights' => [ ], 'DeleteRevisionsLimit' => 0, 'DeleteRevisionsBatchSize' => 1000, 'HideUserContribLimit' => 1000, 'AccountCreationThrottle' => [ [ 'count' => 0, 'seconds' => 86400, ], ], 'TempAccountCreationThrottle' => [ [ 'count' => 1, 'seconds' => 600, ], [ 'count' => 6, 'seconds' => 86400, ], ], 'TempAccountNameAcquisitionThrottle' => [ [ 'count' => 60, 'seconds' => 86400, ], ], 'SpamRegex' => [ ], 'SummarySpamRegex' => [ ], 'EnableDnsBlacklist' => false, 'DnsBlacklistUrls' => [ ], 'ProxyList' => [ ], 'ProxyWhitelist' => [ ], 'SoftBlockRanges' => [ ], 'ApplyIpBlocksToXff' => false, 'RateLimits' => [ 'edit' => [ 'ip' => [ 8, 60, ], 'newbie' => [ 8, 60, ], 'user' => [ 90, 60, ], ], 'move' => [ 'newbie' => [ 2, 120, ], 'user' => [ 8, 60, ], ], 'upload' => [ 'ip' => [ 8, 60, ], 'newbie' => [ 8, 60, ], ], 'rollback' => [ 'user' => [ 10, 60, ], 'newbie' => [ 5, 120, ], ], 'mailpassword' => [ 'ip' => [ 5, 3600, ], ], 'sendemail' => [ 'ip' => [ 5, 86400, ], 'newbie' => [ 5, 86400, ], 'user' => [ 20, 86400, ], ], 'changeemail' => [ 'ip-all' => [ 10, 3600, ], 'user' => [ 4, 86400, ], ], 'confirmemail' => [ 'ip-all' => [ 10, 3600, ], 'user' => [ 4, 86400, ], ], 'purge' => [ 'ip' => [ 30, 60, ], 'user' => [ 30, 60, ], ], 'linkpurge' => [ 'ip' => [ 30, 60, ], 'user' => [ 30, 60, ], ], 'renderfile' => [ 'ip' => [ 700, 30, ], 'user' => [ 700, 30, ], ], 'renderfile-nonstandard' => [ 'ip' => [ 70, 30, ], 'user' => [ 70, 30, ], ], 'stashedit' => [ 'ip' => [ 30, 60, ], 'newbie' => [ 30, 60, ], ], 'stashbasehtml' => [ 'ip' => [ 5, 60, ], 'newbie' => [ 5, 60, ], ], 'changetags' => [ 'ip' => [ 8, 60, ], 'newbie' => [ 8, 60, ], ], 'editcontentmodel' => [ 'newbie' => [ 2, 120, ], 'user' => [ 8, 60, ], ], ], 'RateLimitsExcludedIPs' => [ ], 'PutIPinRC' => true, 'QueryPageDefaultLimit' => 50, 'ExternalQuerySources' => [ ], 'PasswordAttemptThrottle' => [ [ 'count' => 5, 'seconds' => 300, ], [ 'count' => 150, 'seconds' => 172800, ], ], 'GrantPermissions' => [ 'basic' => [ 'autocreateaccount' => true, 'autoconfirmed' => true, 'autopatrol' => true, 'editsemiprotected' => true, 'ipblock-exempt' => true, 'nominornewtalk' => true, 'patrolmarks' => true, 'read' => true, 'unwatchedpages' => true, ], 'highvolume' => [ 'bot' => true, 'apihighlimits' => true, 'noratelimit' => true, 'markbotedits' => true, ], 'import' => [ 'import' => true, 'importupload' => true, ], 'editpage' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'pagelang' => true, ], 'editprotected' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editprotected' => true, ], 'editmycssjs' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editmyusercss' => true, 'editmyuserjson' => true, 'editmyuserjs' => true, ], 'editmyoptions' => [ 'editmyoptions' => true, 'editmyuserjson' => true, ], 'editinterface' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editinterface' => true, 'edituserjson' => true, 'editsitejson' => true, ], 'editsiteconfig' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editinterface' => true, 'edituserjson' => true, 'editsitejson' => true, 'editusercss' => true, 'edituserjs' => true, 'editsitecss' => true, 'editsitejs' => true, ], 'createeditmovepage' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'createpage' => true, 'createtalk' => true, 'delete-redirect' => true, 'move' => true, 'move-rootuserpages' => true, 'move-subpages' => true, 'move-categorypages' => true, 'suppressredirect' => true, ], 'uploadfile' => [ 'upload' => true, 'reupload-own' => true, ], 'uploadeditmovefile' => [ 'upload' => true, 'reupload-own' => true, 'reupload' => true, 'reupload-shared' => true, 'upload_by_url' => true, 'movefile' => true, 'suppressredirect' => true, ], 'patrol' => [ 'patrol' => true, ], 'rollback' => [ 'rollback' => true, ], 'blockusers' => [ 'block' => true, 'blockemail' => true, ], 'viewdeleted' => [ 'browsearchive' => true, 'deletedhistory' => true, 'deletedtext' => true, ], 'viewrestrictedlogs' => [ 'suppressionlog' => true, ], 'delete' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'browsearchive' => true, 'deletedhistory' => true, 'deletedtext' => true, 'delete' => true, 'bigdelete' => true, 'deletelogentry' => true, 'deleterevision' => true, 'undelete' => true, ], 'oversight' => [ 'suppressrevision' => true, 'viewsuppressed' => true, ], 'protect' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editprotected' => true, 'protect' => true, ], 'viewmywatchlist' => [ 'viewmywatchlist' => true, ], 'editmywatchlist' => [ 'editmywatchlist' => true, ], 'sendemail' => [ 'sendemail' => true, ], 'createaccount' => [ 'createaccount' => true, ], 'privateinfo' => [ 'viewmyprivateinfo' => true, ], 'mergehistory' => [ 'mergehistory' => true, ], 'managesessions' => [ 'logout' => true, ], ], 'GrantPermissionGroups' => [ 'basic' => 'hidden', 'editpage' => 'page-interaction', 'createeditmovepage' => 'page-interaction', 'editprotected' => 'page-interaction', 'patrol' => 'page-interaction', 'uploadfile' => 'file-interaction', 'uploadeditmovefile' => 'file-interaction', 'sendemail' => 'email', 'viewmywatchlist' => 'watchlist-interaction', 'editviewmywatchlist' => 'watchlist-interaction', 'editmycssjs' => 'customization', 'editmyoptions' => 'customization', 'editinterface' => 'administration', 'editsiteconfig' => 'administration', 'rollback' => 'administration', 'blockusers' => 'administration', 'delete' => 'administration', 'viewdeleted' => 'administration', 'viewrestrictedlogs' => 'administration', 'protect' => 'administration', 'oversight' => 'administration', 'createaccount' => 'administration', 'mergehistory' => 'administration', 'import' => 'administration', 'highvolume' => 'high-volume', 'privateinfo' => 'private-information', 'managesessions' => 'private-information', ], 'GrantRiskGroups' => [ 'basic' => 'low', 'editpage' => 'low', 'createeditmovepage' => 'low', 'editprotected' => 'vandalism', 'patrol' => 'low', 'uploadfile' => 'low', 'uploadeditmovefile' => 'low', 'sendemail' => 'security', 'viewmywatchlist' => 'low', 'editviewmywatchlist' => 'low', 'editmycssjs' => 'security', 'editmyoptions' => 'security', 'editinterface' => 'vandalism', 'editsiteconfig' => 'security', 'rollback' => 'low', 'blockusers' => 'vandalism', 'delete' => 'vandalism', 'viewdeleted' => 'vandalism', 'viewrestrictedlogs' => 'security', 'protect' => 'vandalism', 'oversight' => 'security', 'createaccount' => 'low', 'mergehistory' => 'vandalism', 'import' => 'security', 'highvolume' => 'low', 'privateinfo' => 'low', ], 'EnableBotPasswords' => true, 'BotPasswordsCluster' => false, 'BotPasswordsDatabase' => false, 'BotPasswordsLimit' => 100, 'SecretKey' => false, 'JwtPrivateKey' => false, 'JwtPublicKey' => false, 'AllowUserJs' => false, 'ReauthenticateForActions' => [ 'edituserjs' => 'edituserjscss', 'editusercss' => 'edituserjscss', 'editsitejs' => 'editsitejscss', 'editsitecss' => 'editsitejscss', ], 'AllowUserCss' => false, 'AllowUserCssPrefs' => true, 'UseSiteJs' => true, 'UseSiteCss' => true, 'BreakFrames' => false, 'EditPageFrameOptions' => 'DENY', 'ApiFrameOptions' => 'DENY', 'CSPHeader' => false, 'CSPReportOnlyHeader' => false, 'CSPUseReportURIDirective' => false, 'CSPFalsePositiveUrls' => [ 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'chrome-extension' => true, ], 'AllowCrossOrigin' => false, 'RestAllowCrossOriginCookieAuth' => false, 'SessionSecret' => false, 'CookieExpiration' => 2592000, 'ExtendedLoginCookieExpiration' => 15552000, 'SessionCookieJwtExpiration' => 14400, 'CookieDomain' => '', 'CookiePath' => '/', 'CookieSecure' => 'detect', 'CookiePrefix' => false, 'CookieHttpOnly' => true, 'CookieSameSite' => null, 'CacheVaryCookies' => [ ], 'SessionName' => false, 'CookieSetOnAutoblock' => true, 'CookieSetOnIpBlock' => true, 'DebugLogFile' => '', 'DebugLogPrefix' => '', 'DebugRedirects' => false, 'DebugRawPage' => false, 'DebugComments' => false, 'DebugDumpSql' => false, 'TrxProfilerLimits' => [ 'GET' => [ 'masterConns' => 0, 'writes' => 0, 'readQueryTime' => 5, 'readQueryRows' => 10000, ], 'POST' => [ 'readQueryTime' => 5, 'writeQueryTime' => 1, 'readQueryRows' => 100000, 'maxAffected' => 1000, ], 'POST-nonwrite' => [ 'writes' => 0, 'readQueryTime' => 5, 'readQueryRows' => 10000, ], 'PostSend-GET' => [ 'readQueryTime' => 5, 'writeQueryTime' => 1, 'readQueryRows' => 10000, 'maxAffected' => 1000, 'masterConns' => 0, 'writes' => 0, ], 'PostSend-POST' => [ 'readQueryTime' => 5, 'writeQueryTime' => 1, 'readQueryRows' => 100000, 'maxAffected' => 1000, ], 'JobRunner' => [ 'readQueryTime' => 30, 'writeQueryTime' => 5, 'readQueryRows' => 100000, 'maxAffected' => 500, ], 'Maintenance' => [ 'writeQueryTime' => 5, 'maxAffected' => 1000, ], ], 'DebugLogGroups' => [ ], 'MWLoggerDefaultSpi' => [ 'class' => 'MediaWiki\\Logger\\LegacySpi', ], 'ShowDebug' => false, 'SpecialVersionShowHooks' => false, 'ShowExceptionDetails' => false, 'LogExceptionBacktrace' => true, 'PropagateErrors' => true, 'ShowHostnames' => false, 'OverrideHostname' => false, 'DevelopmentWarnings' => false, 'DeprecationReleaseLimit' => false, 'Profiler' => [ ], 'StatsdServer' => false, 'StatsdMetricPrefix' => 'MediaWiki', 'StatsTarget' => null, 'StatsFormat' => null, 'StatsPrefix' => 'mediawiki', 'OpenTelemetryConfig' => null, 'PageInfoTransclusionLimit' => 50, 'EnableJavaScriptTest' => false, 'CachePrefix' => false, 'DebugToolbar' => false, 'ApiClientErrorSampleRate' => 1.0, 'DisableTextSearch' => false, 'AdvancedSearchHighlighting' => false, 'SearchHighlightBoundaries' => '[\\p{Z}\\p{P}\\p{C}]', 'OpenSearchTemplates' => [ 'application/x-suggestions+json' => false, 'application/x-suggestions+xml' => false, ], 'OpenSearchDefaultLimit' => 10, 'OpenSearchDescriptionLength' => 100, 'SearchSuggestCacheExpiry' => 1200, 'DisableSearchUpdate' => false, 'NamespacesToBeSearchedDefault' => [ true, ], 'DisableInternalSearch' => false, 'SearchForwardUrl' => null, 'SitemapNamespaces' => false, 'SitemapNamespacesPriorities' => false, 'SitemapApiConfig' => [ ], 'SpecialSearchFormOptions' => [ ], 'SearchMatchRedirectPreference' => false, 'SearchRunSuggestedQuery' => true, 'Diff3' => '/usr/bin/diff3', 'Diff' => '/usr/bin/diff', 'PreviewOnOpenNamespaces' => [ 14 => true, ], 'UniversalEditButton' => true, 'UseAutomaticEditSummaries' => true, 'CommandLineDarkBg' => false, 'ReadOnly' => null, 'ReadOnlyWatchedItemStore' => false, 'ReadOnlyFile' => false, 'UpgradeKey' => false, 'GitBin' => '/usr/bin/git', 'GitRepositoryViewers' => [ 'https: 'ssh: 'https: 'git@github\\.com:(.*?)(\\.git)?' => 'https: ], 'InstallerInitialPages' => [ [ 'titlemsg' => 'mainpage', 'text' => '{{subst:int:mainpagetext}}{{subst:int:mainpagedocfooter}}', ], ], 'RCMaxAge' => 7776000, 'WatchersMaxAge' => 15552000, 'UnwatchedPageSecret' => 1, 'RCFilterByAge' => false, 'RCLinkLimits' => [ 50, 100, 250, 500, ], 'RCLinkDays' => [ 1, 3, 7, 14, 30, ], 'RCFeeds' => [ ], 'RCWatchCategoryMembership' => false, 'UseRCPatrol' => true, 'StructuredChangeFiltersLiveUpdatePollingRate' => 3, 'UseNPPatrol' => true, 'UseFilePatrol' => true, 'Feed' => true, 'FeedLimit' => 50, 'FeedCacheTimeout' => 60, 'FeedDiffCutoff' => 32768, 'OverrideSiteFeed' => [ ], 'FeedClasses' => [ 'rss' => 'MediaWiki\\Feed\\RSSFeed', 'atom' => 'MediaWiki\\Feed\\AtomFeed', ], 'AdvertisedFeedTypes' => [ 'atom', ], 'RCShowWatchingUsers' => false, 'RCShowChangedSize' => true, 'RCChangedSizeThreshold' => 500, 'ShowUpdatedMarker' => true, 'DisableAnonTalk' => false, 'UseTagFilter' => true, 'SoftwareTags' => [ 'mw-contentmodelchange' => true, 'mw-new-redirect' => true, 'mw-removed-redirect' => true, 'mw-changed-redirect-target' => true, 'mw-blank' => true, 'mw-replace' => true, 'mw-recreated' => true, 'mw-rollback' => true, 'mw-undo' => true, 'mw-manual-revert' => true, 'mw-reverted' => true, 'mw-server-side-upload' => true, 'mw-ipblock-appeal' => true, 'mw-edited-other-users-js' => true, 'mw-edited-other-users-css' => true, ], 'RestrictedTagViewRights' => [ ], 'UnwatchedPageThreshold' => false, 'RecentChangesFlags' => [ 'newpage' => [ 'letter' => 'newpageletter', 'title' => 'recentchanges-label-newpage', 'legend' => 'recentchanges-legend-newpage', 'grouping' => 'any', ], 'minor' => [ 'letter' => 'minoreditletter', 'title' => 'recentchanges-label-minor', 'legend' => 'recentchanges-legend-minor', 'class' => 'minoredit', 'grouping' => 'all', ], 'bot' => [ 'letter' => 'boteditletter', 'title' => 'recentchanges-label-bot', 'legend' => 'recentchanges-legend-bot', 'class' => 'botedit', 'grouping' => 'all', ], 'unpatrolled' => [ 'letter' => 'unpatrolledletter', 'title' => 'recentchanges-label-unpatrolled', 'legend' => 'recentchanges-legend-unpatrolled', 'grouping' => 'any', ], ], 'WatchlistExpiry' => false, 'EnableWatchstarPopover' => false, 'EnableWatchlistLabels' => false, 'WatchlistLabelsMaxPerUser' => 100, 'WatchlistPurgeRate' => 0.1, 'WatchlistExpiryMaxDuration' => '1 year', 'EnableChangesListQueryPartitioning' => false, 'RightsPage' => null, 'RightsUrl' => null, 'RightsText' => null, 'RightsIcon' => null, 'UseCopyrightUpload' => false, 'MaxCredits' => 0, 'ShowCreditsIfMax' => true, 'ImportSources' => [ ], 'ImportTargetNamespace' => null, 'ExportAllowHistory' => true, 'ExportMaxHistory' => 0, 'ExportAllowListContributors' => false, 'ExportMaxLinkDepth' => 0, 'ExportFromNamespaces' => false, 'ExportAllowAll' => false, 'ExportPagelistLimit' => 5000, 'XmlDumpSchemaVersion' => '0.11', 'WikiFarmSettingsDirectory' => null, 'WikiFarmSettingsExtension' => 'yaml', 'ExtensionFunctions' => [ ], 'ExtensionMessagesFiles' => [ ], 'MessagesDirs' => [ ], 'TranslationAliasesDirs' => [ ], 'ExtensionEntryPointListFiles' => [ ], 'EnableParserLimitReporting' => true, 'ValidSkinNames' => [ ], 'SpecialPages' => [ ], 'ExtensionCredits' => [ ], 'Hooks' => [ ], 'ServiceWiringFiles' => [ ], 'JobClasses' => [ 'deletePage' => 'MediaWiki\\Page\\DeletePageJob', 'refreshLinks' => 'MediaWiki\\JobQueue\\Jobs\\RefreshLinksJob', 'deleteLinks' => 'MediaWiki\\Page\\DeleteLinksJob', 'htmlCacheUpdate' => 'MediaWiki\\JobQueue\\Jobs\\HTMLCacheUpdateJob', 'sendMail' => [ 'class' => 'MediaWiki\\Mail\\EmaillingJob', 'services' => [ 'Emailer', ], ], 'enotifNotify' => [ 'class' => 'MediaWiki\\RecentChanges\\RecentChangeNotifyJob', 'services' => [ 'RecentChangeLookup', ], ], 'fixDoubleRedirect' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\DoubleRedirectJob', 'services' => [ 'RevisionLookup', 'MagicWordFactory', 'WikiPageFactory', ], 'needsPage' => true, ], 'AssembleUploadChunks' => 'MediaWiki\\JobQueue\\Jobs\\AssembleUploadChunksJob', 'PublishStashedFile' => 'MediaWiki\\JobQueue\\Jobs\\PublishStashedFileJob', 'ThumbnailRender' => 'MediaWiki\\JobQueue\\Jobs\\ThumbnailRenderJob', 'UploadFromUrl' => 'MediaWiki\\JobQueue\\Jobs\\UploadFromUrlJob', 'recentChangesUpdate' => 'MediaWiki\\RecentChanges\\RecentChangesUpdateJob', 'refreshLinksPrioritized' => 'MediaWiki\\JobQueue\\Jobs\\RefreshLinksJob', 'refreshLinksDynamic' => 'MediaWiki\\JobQueue\\Jobs\\RefreshLinksJob', 'activityUpdateJob' => 'MediaWiki\\Watchlist\\ActivityUpdateJob', 'categoryMembershipChange' => [ 'class' => 'MediaWiki\\RecentChanges\\CategoryMembershipChangeJob', 'services' => [ 'RecentChangeFactory', ], ], 'CategoryCountUpdateJob' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\CategoryCountUpdateJob', 'services' => [ 'ConnectionProvider', 'NamespaceInfo', ], ], 'clearUserWatchlist' => 'MediaWiki\\Watchlist\\ClearUserWatchlistJob', 'watchlistExpiry' => 'MediaWiki\\Watchlist\\WatchlistExpiryJob', 'cdnPurge' => 'MediaWiki\\JobQueue\\Jobs\\CdnPurgeJob', 'userGroupExpiry' => 'MediaWiki\\User\\UserGroupExpiryJob', 'clearWatchlistNotifications' => 'MediaWiki\\Watchlist\\ClearWatchlistNotificationsJob', 'userOptionsUpdate' => 'MediaWiki\\User\\Options\\UserOptionsUpdateJob', 'revertedTagUpdate' => 'MediaWiki\\JobQueue\\Jobs\\RevertedTagUpdateJob', 'null' => 'MediaWiki\\JobQueue\\Jobs\\NullJob', 'userEditCountInit' => 'MediaWiki\\User\\UserEditCountInitJob', 'parsoidCachePrewarm' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\ParsoidCachePrewarmJob', 'services' => [ 'ParserOutputAccess', 'PageStore', 'RevisionLookup', 'ParsoidSiteConfig', ], 'needsPage' => false, ], 'renameUserTable' => [ 'class' => 'MediaWiki\\RenameUser\\Job\\RenameUserTableJob', 'services' => [ 'MainConfig', 'DBLoadBalancerFactory', ], ], 'renameUserDerived' => [ 'class' => 'MediaWiki\\RenameUser\\Job\\RenameUserDerivedJob', 'services' => [ 'RenameUserFactory', 'UserFactory', ], ], 'renameUser' => [ 'class' => 'MediaWiki\\RenameUser\\Job\\RenameUserTableJob', 'services' => [ 'MainConfig', 'DBLoadBalancerFactory', ], ], ], 'JobTypesExcludedFromDefaultQueue' => [ 'AssembleUploadChunks', 'PublishStashedFile', 'UploadFromUrl', ], 'JobBackoffThrottling' => [ ], 'JobTypeConf' => [ 'default' => [ 'class' => 'MediaWiki\\JobQueue\\JobQueueDB', 'order' => 'random', 'claimTTL' => 3600, ], ], 'JobQueueIncludeInMaxLagFactor' => false, 'SpecialPageCacheUpdates' => [ 'Statistics' => [ 'MediaWiki\\Deferred\\SiteStatsUpdate', 'cacheUpdate', ], ], 'PagePropLinkInvalidations' => [ 'hiddencat' => 'categorylinks', ], 'CategoryMagicGallery' => true, 'CategoryPagingLimit' => 200, 'CategoryCollation' => 'uppercase', 'TempCategoryCollations' => [ ], 'SortedCategories' => false, 'TrackingCategories' => [ ], 'LogTypes' => [ '', 'block', 'protect', 'rights', 'delete', 'upload', 'move', 'import', 'interwiki', 'patrol', 'merge', 'suppress', 'tag', 'managetags', 'contentmodel', 'renameuser', ], 'LogRestrictions' => [ 'suppress' => 'suppressionlog', ], 'FilterLogTypes' => [ 'patrol' => true, 'tag' => true, 'newusers' => false, ], 'LogNames' => [ '' => 'all-logs-page', 'block' => 'blocklogpage', 'protect' => 'protectlogpage', 'rights' => 'rightslog', 'delete' => 'dellogpage', 'upload' => 'uploadlogpage', 'move' => 'movelogpage', 'import' => 'importlogpage', 'patrol' => 'patrol-log-page', 'merge' => 'mergelog', 'suppress' => 'suppressionlog', ], 'LogHeaders' => [ '' => 'alllogstext', 'block' => 'blocklogtext', 'delete' => 'dellogpagetext', 'import' => 'importlogpagetext', 'merge' => 'mergelogpagetext', 'move' => 'movelogpagetext', 'patrol' => 'patrol-log-header', 'protect' => 'protectlogtext', 'rights' => 'rightslogtext', 'suppress' => 'suppressionlogtext', 'upload' => 'uploadlogpagetext', ], 'LogActions' => [ ], 'LogActionsHandlers' => [ 'block/block' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'block/reblock' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'block/unblock' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'contentmodel/change' => 'MediaWiki\\Logging\\ContentModelLogFormatter', 'contentmodel/new' => 'MediaWiki\\Logging\\ContentModelLogFormatter', 'delete/delete' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/delete_redir' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/delete_redir2' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/event' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/restore' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/revision' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'import/interwiki' => 'MediaWiki\\Logging\\ImportLogFormatter', 'import/upload' => 'MediaWiki\\Logging\\ImportLogFormatter', 'interwiki/iw_add' => 'MediaWiki\\Logging\\InterwikiLogFormatter', 'interwiki/iw_delete' => 'MediaWiki\\Logging\\InterwikiLogFormatter', 'interwiki/iw_edit' => 'MediaWiki\\Logging\\InterwikiLogFormatter', 'managetags/activate' => 'MediaWiki\\Logging\\LogFormatter', 'managetags/create' => 'MediaWiki\\Logging\\LogFormatter', 'managetags/deactivate' => 'MediaWiki\\Logging\\LogFormatter', 'managetags/delete' => 'MediaWiki\\Logging\\LogFormatter', 'merge/merge' => [ 'class' => 'MediaWiki\\Logging\\MergeLogFormatter', 'services' => [ 'TitleParser', ], ], 'merge/merge-into' => [ 'class' => 'MediaWiki\\Logging\\MergeLogFormatter', 'services' => [ 'TitleParser', ], ], 'move/move' => [ 'class' => 'MediaWiki\\Logging\\MoveLogFormatter', 'services' => [ 'TitleParser', ], ], 'move/move_redir' => [ 'class' => 'MediaWiki\\Logging\\MoveLogFormatter', 'services' => [ 'TitleParser', ], ], 'patrol/patrol' => 'MediaWiki\\Logging\\PatrolLogFormatter', 'patrol/autopatrol' => 'MediaWiki\\Logging\\PatrolLogFormatter', 'protect/modify' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'protect/move_prot' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'protect/protect' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'protect/unprotect' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'renameuser/renameuser' => [ 'class' => 'MediaWiki\\Logging\\RenameuserLogFormatter', 'services' => [ 'TitleParser', ], ], 'rights/autopromote' => 'MediaWiki\\Logging\\RightsLogFormatter', 'rights/rights' => 'MediaWiki\\Logging\\RightsLogFormatter', 'suppress/block' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'suppress/delete' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'suppress/event' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'suppress/reblock' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'suppress/revision' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'tag/update' => 'MediaWiki\\Logging\\TagLogFormatter', 'upload/overwrite' => 'MediaWiki\\Logging\\UploadLogFormatter', 'upload/revert' => 'MediaWiki\\Logging\\UploadLogFormatter', 'upload/upload' => 'MediaWiki\\Logging\\UploadLogFormatter', ], 'ActionFilteredLogs' => [ 'block' => [ 'block' => [ 'block', ], 'reblock' => [ 'reblock', ], 'unblock' => [ 'unblock', ], ], 'contentmodel' => [ 'change' => [ 'change', ], 'new' => [ 'new', ], ], 'delete' => [ 'delete' => [ 'delete', ], 'delete_redir' => [ 'delete_redir', 'delete_redir2', ], 'restore' => [ 'restore', ], 'event' => [ 'event', ], 'revision' => [ 'revision', ], ], 'import' => [ 'interwiki' => [ 'interwiki', ], 'upload' => [ 'upload', ], ], 'managetags' => [ 'create' => [ 'create', ], 'delete' => [ 'delete', ], 'activate' => [ 'activate', ], 'deactivate' => [ 'deactivate', ], ], 'move' => [ 'move' => [ 'move', ], 'move_redir' => [ 'move_redir', ], ], 'newusers' => [ 'create' => [ 'create', 'newusers', ], 'create2' => [ 'create2', ], 'autocreate' => [ 'autocreate', ], 'byemail' => [ 'byemail', ], ], 'protect' => [ 'protect' => [ 'protect', ], 'modify' => [ 'modify', ], 'unprotect' => [ 'unprotect', ], 'move_prot' => [ 'move_prot', ], ], 'rights' => [ 'rights' => [ 'rights', ], 'autopromote' => [ 'autopromote', ], ], 'suppress' => [ 'event' => [ 'event', ], 'revision' => [ 'revision', ], 'delete' => [ 'delete', ], 'block' => [ 'block', ], 'reblock' => [ 'reblock', ], ], 'upload' => [ 'upload' => [ 'upload', ], 'overwrite' => [ 'overwrite', ], 'revert' => [ 'revert', ], ], ], 'NewUserLog' => true, 'PageCreationLog' => true, 'AllowSpecialInclusion' => true, 'DisableQueryPageUpdate' => false, 'CountCategorizedImagesAsUsed' => false, 'MaxRedirectLinksRetrieved' => 500, 'RangeContributionsCIDRLimit' => [ 'IPv4' => 16, 'IPv6' => 32, ], 'Actions' => [ ], 'DefaultRobotPolicy' => 'index,follow', 'NamespaceRobotPolicies' => [ ], 'ArticleRobotPolicies' => [ ], 'ExemptFromUserRobotsControl' => null, 'DebugAPI' => false, 'APIModules' => [ ], 'APIFormatModules' => [ ], 'APIMetaModules' => [ ], 'APIPropModules' => [ ], 'APIListModules' => [ ], 'APIMaxDBRows' => 5000, 'APIMaxResultSize' => 8388608, 'APIMaxUncachedDiffs' => 1, 'APIMaxLagThreshold' => 7, 'APICacheHelpTimeout' => 3600, 'APIUselessQueryPages' => [ 'MIMEsearch', 'LinkSearch', ], 'AjaxLicensePreview' => true, 'CrossSiteAJAXdomains' => [ ], 'CrossSiteAJAXdomainExceptions' => [ ], 'AllowedCorsHeaders' => [ 'Accept', 'Accept-Language', 'Content-Language', 'Content-Type', 'Accept-Encoding', 'DNT', 'Origin', 'User-Agent', 'Api-User-Agent', 'Promise-Non-Write-API-Action', 'Access-Control-Max-Age', 'Authorization', ], 'RestAPIAdditionalRouteFiles' => [ ], 'RestSandboxSpecs' => [ ], 'RestLocalModuleTestBaseUrl' => null, 'RestModuleOverrides' => [ ], 'RestExternalModules' => [ ], 'MaxShellMemory' => 307200, 'MaxShellFileSize' => 102400, 'MaxShellTime' => 180, 'MaxShellWallClockTime' => 180, 'ShellCgroup' => false, 'PhpCli' => '/usr/bin/php', 'ShellRestrictionMethod' => 'autodetect', 'ShellboxUrls' => [ 'default' => null, ], 'ShellboxSecretKey' => null, 'ShellboxShell' => '/bin/sh', 'HTTPTimeout' => 25, 'HTTPConnectTimeout' => 5.0, 'HTTPMaxTimeout' => 0, 'HTTPMaxConnectTimeout' => 0, 'HTTPImportTimeout' => 25, 'AsyncHTTPTimeout' => 25, 'HTTPProxy' => '', 'LocalVirtualHosts' => [ ], 'LocalHTTPProxy' => false, 'AllowExternalReqID' => false, 'GenerateReqIDFormat' => 'rand24', 'JobRunRate' => 1, 'RunJobsAsync' => false, 'UpdateRowsPerJob' => 300, 'UpdateRowsPerQuery' => 100, 'RedirectOnLogin' => null, 'VirtualRestConfig' => [ 'paths' => [ ], 'modules' => [ ], 'global' => [ 'timeout' => 360, 'forwardCookies' => false, 'HTTPProxy' => null, ], ], 'EventRelayerConfig' => [ 'default' => [ 'class' => 'Wikimedia\\EventRelayer\\EventRelayerNull', ], ], 'Pingback' => false, 'OriginTrials' => [ ], 'ReportToExpiry' => 86400, 'ReportToEndpoints' => [ ], 'FeaturePolicyReportOnly' => [ ], 'SkinsPreferred' => [ 'vector-2022', 'vector', ], 'SpecialContributeSkinsEnabled' => [ ], 'SpecialContributeNewPageTarget' => null, 'EnableEditRecovery' => false, 'EditRecoveryExpiry' => 2592000, 'UseCodexSpecialBlock' => false, 'ShowLogoutConfirmation' => false, 'EnableProtectionIndicators' => true, 'OutputPipelineStages' => [ ], 'FeatureShutdown' => [ ], 'CloneArticleParserOutput' => true, 'UseLeximorph' => false, 'UsePostprocCacheLegacy' => false, 'UsePostprocCacheParsoid' => true, 'ParserOptionsLogUnsafeSampleRate' => 0, 'ReturnExperimentalPFragmentTypes' => [ ], ], 'type' => [ 'ConfigRegistry' => 'object', 'AssumeProxiesUseDefaultProtocolPorts' => 'boolean', 'ForceHTTPS' => 'boolean', 'ExtensionDirectory' => [ 'string', 'null', ], 'StyleDirectory' => [ 'string', 'null', ], 'UploadDirectory' => [ 'string', 'boolean', 'null', ], 'Logos' => [ 'object', 'boolean', ], 'ReferrerPolicy' => [ 'array', 'string', 'boolean', ], 'ActionPaths' => 'object', 'MainPageIsDomainRoot' => 'boolean', 'ImgAuthUrlPathMap' => 'object', 'LocalFileRepo' => 'object', 'ForeignFileRepos' => 'array', 'UseSharedUploads' => 'boolean', 'SharedUploadDirectory' => [ 'string', 'null', ], 'SharedUploadPath' => [ 'string', 'null', ], 'HashedSharedUploadDirectory' => 'boolean', 'FetchCommonsDescriptions' => 'boolean', 'SharedUploadDBname' => [ 'boolean', 'string', ], 'SharedUploadDBprefix' => 'string', 'CacheSharedUploads' => 'boolean', 'ForeignUploadTargets' => 'array', 'UploadDialog' => 'object', 'FileBackends' => 'object', 'LockManagers' => 'array', 'DefaultLockManager' => [ 'string', 'null', ], 'CopyUploadsDomains' => 'array', 'CopyUploadTimeout' => [ 'boolean', 'integer', ], 'SharedThumbnailScriptPath' => [ 'string', 'boolean', ], 'HashedUploadDirectory' => 'boolean', 'CSPUploadEntryPoint' => 'boolean', 'FileExtensions' => 'array', 'ProhibitedFileExtensions' => 'array', 'MimeTypeExclusions' => 'array', 'TrustedMediaFormats' => 'array', 'MediaHandlers' => 'object', 'NativeImageLazyLoading' => 'boolean', 'ParserTestMediaHandlers' => 'object', 'MaxInterlacingAreas' => 'object', 'SVGConverters' => 'object', 'SVGNativeRendering' => [ 'string', 'boolean', ], 'MaxImageArea' => [ 'string', 'integer', 'boolean', ], 'TiffThumbnailType' => 'array', 'GenerateThumbnailOnParse' => 'boolean', 'EnableAutoRotation' => [ 'boolean', 'null', ], 'Antivirus' => [ 'string', 'null', ], 'AntivirusSetup' => 'object', 'MimeDetectorCommand' => [ 'string', 'null', ], 'XMLMimeTypes' => 'object', 'ImageLimits' => 'array', 'ThumbLimits' => 'array', 'ThumbnailNamespaces' => 'array', 'ThumbnailSteps' => [ 'array', 'null', ], 'ThumbnailBuckets' => [ 'array', 'null', ], 'UploadThumbnailRenderMap' => 'object', 'GalleryOptions' => 'object', 'DjvuDump' => [ 'string', 'null', ], 'DjvuRenderer' => [ 'string', 'null', ], 'DjvuTxt' => [ 'string', 'null', ], 'DjvuPostProcessor' => [ 'string', 'null', ], 'RestTermsOfServiceUrl' => [ 'string', 'null', ], 'SMTP' => [ 'boolean', 'object', ], 'EnotifFromEditor' => 'boolean', 'EmailConfirmationBanner' => 'boolean', 'EnotifRevealEditorAddress' => 'boolean', 'UsersNotifiedOnAllChanges' => 'object', 'DBmwschema' => [ 'string', 'null', ], 'SharedTables' => 'array', 'DBservers' => [ 'boolean', 'array', ], 'LBFactoryConf' => 'object', 'LocalDatabases' => 'array', 'VirtualDomainsMapping' => 'object', 'FileSchemaMigrationStage' => 'integer', 'ExternalLinksDomainGaps' => 'object', 'ContentHandlers' => 'object', 'NamespaceContentModels' => 'object', 'TextModelsToParse' => 'array', 'ExternalStores' => 'array', 'ExternalServers' => 'object', 'DefaultExternalStore' => [ 'array', 'boolean', ], 'RevisionCacheExpiry' => 'integer', 'PageLanguageUseDB' => 'boolean', 'DiffEngine' => [ 'string', 'null', ], 'ExternalDiffEngine' => [ 'string', 'boolean', ], 'Wikidiff2Options' => 'object', 'RequestTimeLimit' => [ 'integer', 'null', ], 'CriticalSectionTimeLimit' => 'number', 'PoolCounterConf' => [ 'object', 'null', ], 'PoolCountClientConf' => 'object', 'MaxUserDBWriteDuration' => [ 'integer', 'boolean', ], 'MaxJobDBWriteDuration' => [ 'integer', 'boolean', ], 'MultiShardSiteStats' => 'boolean', 'ObjectCaches' => 'object', 'WANObjectCache' => 'object', 'MicroStashType' => [ 'string', 'integer', ], 'ParsoidCacheConfig' => 'object', 'ParsoidSelectiveUpdateSampleRate' => 'integer', 'ParserCacheFilterConfig' => 'object', 'ChronologyProtectorSecret' => 'string', 'SuspiciousIpExpiry' => [ 'integer', 'boolean', ], 'MemCachedServers' => 'array', 'LocalisationCacheConf' => 'object', 'ExtensionInfoMTime' => [ 'integer', 'boolean', ], 'CdnServers' => 'object', 'CdnServersNoPurge' => 'object', 'HTCPRouting' => 'object', 'GrammarForms' => 'object', 'ExtraInterlanguageLinkPrefixes' => 'array', 'InterlanguageLinkCodeMap' => 'object', 'ExtraLanguageNames' => 'object', 'ExtraLanguageCodes' => 'object', 'DummyLanguageCodes' => 'object', 'DisabledVariants' => 'object', 'ForceUIMsgAsContentMsg' => 'object', 'RawHtmlMessages' => 'array', 'OverrideUcfirstCharacters' => 'object', 'XhtmlNamespaces' => 'object', 'BrowserFormatDetection' => 'string', 'SkinMetaTags' => 'object', 'SkipSkins' => 'object', 'FragmentMode' => 'array', 'FooterIcons' => 'object', 'InterwikiLogoOverride' => 'array', 'ResourceModules' => 'object', 'ResourceModuleSkinStyles' => 'object', 'ResourceLoaderSources' => 'object', 'ResourceLoaderMaxage' => 'object', 'ResourceLoaderMaxQueryLength' => [ 'integer', 'boolean', ], 'CanonicalNamespaceNames' => 'object', 'ExtraNamespaces' => 'object', 'ExtraGenderNamespaces' => 'object', 'NamespaceAliases' => 'object', 'CapitalLinkOverrides' => 'object', 'NamespacesWithSubpages' => 'object', 'NamespacesWithoutAutoSummaries' => 'array', 'ContentNamespaces' => 'array', 'ShortPagesNamespaceExclusions' => 'array', 'ExtraSignatureNamespaces' => 'array', 'InvalidRedirectTargets' => 'array', 'LocalInterwikis' => 'array', 'InterwikiCache' => [ 'boolean', 'object', ], 'SiteTypes' => 'object', 'UrlProtocols' => 'array', 'TidyConfig' => 'object', 'ParsoidSettings' => 'object', 'ParsoidExperimentalParserFunctionOutput' => 'boolean', 'NoFollowNsExceptions' => 'array', 'NoFollowDomainExceptions' => 'array', 'ExternalLinksIgnoreDomains' => 'array', 'EnableMagicLinks' => 'object', 'ManualRevertSearchRadius' => 'integer', 'RevertedTagMaxDepth' => 'integer', 'CentralIdLookupProviders' => 'object', 'CentralIdLookupProvider' => 'string', 'UserRegistrationProviders' => 'object', 'PasswordPolicy' => 'object', 'AuthManagerConfig' => [ 'object', 'null', ], 'AuthManagerAutoConfig' => 'object', 'RememberMe' => 'string', 'ReauthenticateTime' => 'object', 'ChangeCredentialsBlacklist' => 'array', 'RemoveCredentialsBlacklist' => 'array', 'PasswordConfig' => 'object', 'PasswordResetRoutes' => 'object', 'SignatureAllowedLintErrors' => 'array', 'ReservedUsernames' => 'array', 'DefaultUserOptions' => 'object', 'ConditionalUserOptions' => 'object', 'HiddenPrefs' => 'array', 'UserJsPrefLimit' => 'integer', 'AuthenticationTokenVersion' => [ 'string', 'null', ], 'SessionProviders' => 'object', 'AutoCreateTempUser' => 'object', 'AutoblockExemptions' => 'array', 'BlockCIDRLimit' => 'object', 'EnableMultiBlocks' => 'boolean', 'GroupPermissions' => 'object', 'PrivilegedGroups' => 'array', 'RevokePermissions' => 'object', 'GroupInheritsPermissions' => 'object', 'ImplicitGroups' => 'array', 'GroupsAddToSelf' => 'object', 'GroupsRemoveFromSelf' => 'object', 'RestrictedGroups' => 'object', 'UserRequirementsPrivateConditions' => 'array', 'RestrictionTypes' => 'array', 'RestrictionLevels' => 'array', 'CascadingRestrictionLevels' => 'array', 'SemiprotectedRestrictionLevels' => 'array', 'NamespaceProtection' => 'object', 'RestrictUserPageEditing' => 'boolean', 'NonincludableNamespaces' => 'object', 'Autopromote' => 'object', 'AutopromoteOnce' => 'object', 'AutopromoteOnceRCExcludedGroups' => 'array', 'AddGroups' => 'object', 'RemoveGroups' => 'object', 'AvailableRights' => 'array', 'ImplicitRights' => 'array', 'AccountCreationThrottle' => [ 'integer', 'array', ], 'TempAccountCreationThrottle' => 'array', 'TempAccountNameAcquisitionThrottle' => 'array', 'SpamRegex' => 'array', 'SummarySpamRegex' => 'array', 'DnsBlacklistUrls' => 'array', 'ProxyList' => [ 'string', 'array', ], 'ProxyWhitelist' => 'array', 'SoftBlockRanges' => 'array', 'RateLimits' => 'object', 'RateLimitsExcludedIPs' => 'array', 'ExternalQuerySources' => 'object', 'PasswordAttemptThrottle' => 'array', 'GrantPermissions' => 'object', 'GrantPermissionGroups' => 'object', 'GrantRiskGroups' => 'object', 'EnableBotPasswords' => 'boolean', 'BotPasswordsCluster' => [ 'string', 'boolean', ], 'BotPasswordsDatabase' => [ 'string', 'boolean', ], 'BotPasswordsLimit' => 'integer', 'ReauthenticateForActions' => 'object', 'CSPHeader' => [ 'boolean', 'object', ], 'CSPReportOnlyHeader' => [ 'boolean', 'object', ], 'CSPUseReportURIDirective' => [ 'boolean', 'object', ], 'CSPFalsePositiveUrls' => 'object', 'AllowCrossOrigin' => 'boolean', 'RestAllowCrossOriginCookieAuth' => 'boolean', 'CookieSameSite' => [ 'string', 'null', ], 'CacheVaryCookies' => 'array', 'TrxProfilerLimits' => 'object', 'DebugLogGroups' => 'object', 'MWLoggerDefaultSpi' => 'object', 'Profiler' => 'object', 'StatsTarget' => [ 'string', 'null', ], 'StatsFormat' => [ 'string', 'null', ], 'StatsPrefix' => 'string', 'OpenTelemetryConfig' => [ 'object', 'null', ], 'OpenSearchTemplates' => 'object', 'NamespacesToBeSearchedDefault' => 'object', 'SitemapNamespaces' => [ 'boolean', 'array', ], 'SitemapNamespacesPriorities' => [ 'boolean', 'object', ], 'SitemapApiConfig' => 'object', 'SpecialSearchFormOptions' => 'object', 'SearchMatchRedirectPreference' => 'boolean', 'SearchRunSuggestedQuery' => 'boolean', 'PreviewOnOpenNamespaces' => 'object', 'ReadOnlyWatchedItemStore' => 'boolean', 'GitRepositoryViewers' => 'object', 'InstallerInitialPages' => 'array', 'RCLinkLimits' => 'array', 'RCLinkDays' => 'array', 'RCFeeds' => 'object', 'OverrideSiteFeed' => 'object', 'FeedClasses' => 'object', 'AdvertisedFeedTypes' => 'array', 'SoftwareTags' => 'object', 'RestrictedTagViewRights' => 'object', 'RecentChangesFlags' => 'object', 'WatchlistExpiry' => 'boolean', 'EnableWatchstarPopover' => 'boolean', 'EnableWatchlistLabels' => 'boolean', 'WatchlistLabelsMaxPerUser' => 'integer', 'WatchlistPurgeRate' => 'number', 'WatchlistExpiryMaxDuration' => [ 'string', 'null', ], 'EnableChangesListQueryPartitioning' => 'boolean', 'ImportSources' => 'object', 'ExtensionFunctions' => 'array', 'ExtensionMessagesFiles' => 'object', 'MessagesDirs' => 'object', 'TranslationAliasesDirs' => 'object', 'ExtensionEntryPointListFiles' => 'object', 'ValidSkinNames' => 'object', 'SpecialPages' => 'object', 'ExtensionCredits' => 'object', 'Hooks' => 'object', 'ServiceWiringFiles' => 'array', 'JobClasses' => 'object', 'JobTypesExcludedFromDefaultQueue' => 'array', 'JobBackoffThrottling' => 'object', 'JobTypeConf' => 'object', 'SpecialPageCacheUpdates' => 'object', 'PagePropLinkInvalidations' => 'object', 'TempCategoryCollations' => 'array', 'SortedCategories' => 'boolean', 'TrackingCategories' => 'array', 'LogTypes' => 'array', 'LogRestrictions' => 'object', 'FilterLogTypes' => 'object', 'LogNames' => 'object', 'LogHeaders' => 'object', 'LogActions' => 'object', 'LogActionsHandlers' => 'object', 'ActionFilteredLogs' => 'object', 'RangeContributionsCIDRLimit' => 'object', 'Actions' => 'object', 'NamespaceRobotPolicies' => 'object', 'ArticleRobotPolicies' => 'object', 'ExemptFromUserRobotsControl' => [ 'array', 'null', ], 'APIModules' => 'object', 'APIFormatModules' => 'object', 'APIMetaModules' => 'object', 'APIPropModules' => 'object', 'APIListModules' => 'object', 'APIUselessQueryPages' => 'array', 'CrossSiteAJAXdomains' => 'object', 'CrossSiteAJAXdomainExceptions' => 'object', 'AllowedCorsHeaders' => 'array', 'RestAPIAdditionalRouteFiles' => 'array', 'RestSandboxSpecs' => 'object', 'RestLocalModuleTestBaseUrl' => [ 'string', 'null', ], 'RestModuleOverrides' => 'object', 'RestExternalModules' => 'object', 'ShellRestrictionMethod' => [ 'string', 'boolean', ], 'ShellboxUrls' => 'object', 'ShellboxSecretKey' => [ 'string', 'null', ], 'ShellboxShell' => [ 'string', 'null', ], 'HTTPTimeout' => 'number', 'HTTPConnectTimeout' => 'number', 'HTTPMaxTimeout' => 'number', 'HTTPMaxConnectTimeout' => 'number', 'LocalVirtualHosts' => 'object', 'LocalHTTPProxy' => [ 'string', 'boolean', ], 'GenerateReqIDFormat' => 'string', 'VirtualRestConfig' => 'object', 'EventRelayerConfig' => 'object', 'Pingback' => 'boolean', 'OriginTrials' => 'array', 'ReportToExpiry' => 'integer', 'ReportToEndpoints' => 'array', 'FeaturePolicyReportOnly' => 'array', 'SkinsPreferred' => 'array', 'SpecialContributeSkinsEnabled' => 'array', 'SpecialContributeNewPageTarget' => [ 'string', 'null', ], 'EnableEditRecovery' => 'boolean', 'EditRecoveryExpiry' => 'integer', 'UseCodexSpecialBlock' => 'boolean', 'ShowLogoutConfirmation' => 'boolean', 'EnableProtectionIndicators' => 'boolean', 'OutputPipelineStages' => 'object', 'FeatureShutdown' => 'array', 'CloneArticleParserOutput' => 'boolean', 'UseLeximorph' => 'boolean', 'UsePostprocCacheLegacy' => 'boolean', 'UsePostprocCacheParsoid' => 'boolean', 'ParserOptionsLogUnsafeSampleRate' => 'integer', 'ReturnExperimentalPFragmentTypes' => 'array', ], 'mergeStrategy' => [ 'TiffThumbnailType' => 'replace', 'LBFactoryConf' => 'replace', 'InterwikiCache' => 'replace', 'PasswordPolicy' => 'array_replace_recursive', 'AuthManagerAutoConfig' => 'array_plus_2d', 'GroupPermissions' => 'array_plus_2d', 'RevokePermissions' => 'array_plus_2d', 'AddGroups' => 'array_merge_recursive', 'RemoveGroups' => 'array_merge_recursive', 'RateLimits' => 'array_plus_2d', 'GrantPermissions' => 'array_plus_2d', 'MWLoggerDefaultSpi' => 'replace', 'Profiler' => 'replace', 'Hooks' => 'array_merge_recursive', 'RestModuleOverrides' => 'array_replace_recursive', 'RestExternalModules' => 'array_replace_recursive', 'VirtualRestConfig' => 'array_plus_2d', ], 'dynamicDefault' => [ 'UsePathInfo' => [ 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultUsePathInfo', ], ], 'Script' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultScript', ], ], 'LoadScript' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLoadScript', ], ], 'RestPath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultRestPath', ], ], 'StylePath' => [ 'use' => [ 'ResourceBasePath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultStylePath', ], ], 'LocalStylePath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLocalStylePath', ], ], 'ExtensionAssetsPath' => [ 'use' => [ 'ResourceBasePath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultExtensionAssetsPath', ], ], 'ArticlePath' => [ 'use' => [ 'Script', 'UsePathInfo', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultArticlePath', ], ], 'UploadPath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultUploadPath', ], ], 'FileCacheDirectory' => [ 'use' => [ 'UploadDirectory', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultFileCacheDirectory', ], ], 'Logo' => [ 'use' => [ 'ResourceBasePath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLogo', ], ], 'DeletedDirectory' => [ 'use' => [ 'UploadDirectory', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultDeletedDirectory', ], ], 'ShowEXIF' => [ 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultShowEXIF', ], ], 'SharedPrefix' => [ 'use' => [ 'DBprefix', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultSharedPrefix', ], ], 'SharedSchema' => [ 'use' => [ 'DBmwschema', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultSharedSchema', ], ], 'DBerrorLogTZ' => [ 'use' => [ 'Localtimezone', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultDBerrorLogTZ', ], ], 'Localtimezone' => [ 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLocaltimezone', ], ], 'LocalTZoffset' => [ 'use' => [ 'Localtimezone', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLocalTZoffset', ], ], 'ResourceBasePath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultResourceBasePath', ], ], 'MetaNamespace' => [ 'use' => [ 'Sitename', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultMetaNamespace', ], ], 'CookieSecure' => [ 'use' => [ 'ForceHTTPS', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultCookieSecure', ], ], 'CookiePrefix' => [ 'use' => [ 'SharedDB', 'SharedPrefix', 'SharedTables', 'DBname', 'DBprefix', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultCookiePrefix', ], ], 'ReadOnlyFile' => [ 'use' => [ 'UploadDirectory', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultReadOnlyFile', ], ], ], ], 'config-schema' => [ 'UploadStashScalerBaseUrl' => [ 'deprecated' => 'since 1.36 Use thumbProxyUrl in $wgLocalFileRepo', ], 'IllegalFileChars' => [ 'deprecated' => 'since 1.41; no longer customizable', ], 'ThumbnailNamespaces' => [ 'items' => [ 'type' => 'integer', ], ], 'LocalDatabases' => [ 'items' => [ 'type' => 'string', ], ], 'ParserCacheFilterConfig' => [ 'additionalProperties' => [ 'type' => 'object', 'description' => 'A map of namespace IDs to filter definitions.', 'additionalProperties' => [ 'type' => 'object', 'description' => 'A map of filter names to values.', 'properties' => [ 'minCpuTime' => [ 'type' => 'number', ], ], ], ], ], 'RawHtmlMessages' => [ 'items' => [ 'type' => 'string', ], ], 'InterwikiLogoOverride' => [ 'items' => [ 'type' => 'string', ], ], 'LegalTitleChars' => [ 'deprecated' => 'since 1.41; use Extension:TitleBlacklist to customize', ], 'ReauthenticateTime' => [ 'additionalProperties' => [ 'type' => 'integer', ], ], 'ChangeCredentialsBlacklist' => [ 'items' => [ 'type' => 'string', ], ], 'RemoveCredentialsBlacklist' => [ 'items' => [ 'type' => 'string', ], ], 'GroupPermissions' => [ 'additionalProperties' => [ 'type' => 'object', 'additionalProperties' => [ 'type' => 'boolean', ], ], ], 'GroupInheritsPermissions' => [ 'additionalProperties' => [ 'type' => 'string', ], ], 'AvailableRights' => [ 'items' => [ 'type' => 'string', ], ], 'ImplicitRights' => [ 'items' => [ 'type' => 'string', ], ], 'SoftBlockRanges' => [ 'items' => [ 'type' => 'string', ], ], 'ExternalQuerySources' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'enabled' => [ 'type' => 'boolean', 'default' => false, ], 'url' => [ 'type' => 'string', 'format' => 'uri', ], 'timeout' => [ 'type' => 'integer', 'default' => 10, ], ], 'required' => [ 'enabled', 'url', ], 'additionalProperties' => false, ], ], 'GrantPermissions' => [ 'additionalProperties' => [ 'type' => 'object', 'additionalProperties' => [ 'type' => 'boolean', ], ], ], 'GrantPermissionGroups' => [ 'additionalProperties' => [ 'type' => 'string', ], ], 'SitemapNamespacesPriorities' => [ 'deprecated' => 'since 1.45 and ignored', ], 'SitemapApiConfig' => [ 'additionalProperties' => [ 'enabled' => [ 'type' => 'bool', ], 'sitemapsPerIndex' => [ 'type' => 'int', ], 'pagesPerSitemap' => [ 'type' => 'int', ], 'expiry' => [ 'type' => 'int', ], ], ], 'SoftwareTags' => [ 'additionalProperties' => [ 'type' => 'boolean', ], ], 'UseCopyrightUpload' => [ 'deprecated' => 'since 1.47 This feature is being removed.', ], 'JobBackoffThrottling' => [ 'additionalProperties' => [ 'type' => 'number', ], ], 'JobTypeConf' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'class' => [ 'type' => 'string', ], 'order' => [ 'type' => 'string', ], 'claimTTL' => [ 'type' => 'integer', ], ], ], ], 'TrackingCategories' => [ 'deprecated' => 'since 1.25 Extensions should now register tracking categories using the new extension registration system.', ], 'RangeContributionsCIDRLimit' => [ 'additionalProperties' => [ 'type' => 'integer', ], ], 'RestSandboxSpecs' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'url' => [ 'type' => 'string', 'format' => 'url', ], 'name' => [ 'type' => 'string', ], 'file' => [ 'type' => 'string', ], 'msg' => [ 'type' => 'string', 'description' => 'a message key', ], ], ], ], 'RestModuleOverrides' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'mode' => [ 'type' => 'string', ], ], 'required' => [ 'mode', ], ], ], 'RestExternalModules' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'info' => [ 'type' => 'object', 'properties' => [ 'version' => [ 'type' => 'string', ], 'title' => [ 'type' => 'string', ], 'x-i18n-title' => [ 'type' => 'string', ], 'description' => [ 'type' => 'string', ], 'x-i18n-description' => [ 'type' => 'string', ], ], 'required' => [ 'version', ], ], 'base' => [ 'type' => 'string', 'format' => 'uri', ], 'spec' => [ 'type' => 'string', 'format' => 'uri', ], ], 'required' => [ 'info', 'base', 'spec', ], ], ], 'ShellboxUrls' => [ 'additionalProperties' => [ 'type' => [ 'string', 'boolean', 'null', ], ], ], ], 'obsolete-config' => [ 'MangleFlashPolicy' => 'Since 1.39; no longer has any effect.', 'EnableOpenSearchSuggest' => 'Since 1.35, no longer used', 'AutoloadAttemptLowercase' => 'Since 1.40; no longer has any effect.', ],]
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.
Interface for database access objects.
ListType
The constants used to specify list types.
Definition ListType.php:9