MediaWiki master
DefaultPreferencesFactory.php
Go to the documentation of this file.
1<?php
8
28use MediaWiki\Languages\LanguageConverterFactory;
29use MediaWiki\Languages\LanguageNameUtils;
52use OOUI\ButtonWidget;
53use OOUI\FieldLayout;
54use OOUI\HorizontalLayout;
55use OOUI\HtmlSnippet;
56use OOUI\LabelWidget;
57use OOUI\MessageWidget;
58use Psr\Log\LoggerAwareTrait;
59use Psr\Log\NullLogger;
60use UnexpectedValueException;
62
67 use LoggerAwareTrait;
68
70 protected $options;
71
73 protected $contLang;
74
77
79 protected $authManager;
80
82 protected $linkRenderer;
83
85 protected $nsInfo;
86
89
91 private $languageConverter;
92
94 private $hookRunner;
95
98
100 private $languageConverterFactory;
101
103 private $parserFactory;
104
106 private $skinFactory;
107
109 private $userGroupManager;
110
112 private $signatureValidatorFactory;
113
117 public const CONSTRUCTOR_OPTIONS = [
149 ];
150
168 public function __construct(
175 ILanguageConverter $languageConverter,
176 LanguageNameUtils $languageNameUtils,
177 HookContainer $hookContainer,
178 UserOptionsLookup $userOptionsLookup,
179 ?LanguageConverterFactory $languageConverterFactory = null,
180 ?ParserFactory $parserFactory = null,
181 ?SkinFactory $skinFactory = null,
182 ?UserGroupManager $userGroupManager = null,
183 ?SignatureValidatorFactory $signatureValidatorFactory = null
184 ) {
185 $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
186
187 $this->options = $options;
188 $this->contLang = $contLang;
189 $this->authManager = $authManager;
190 $this->linkRenderer = $linkRenderer;
191 $this->nsInfo = $nsInfo;
192
193 // We don't use the PermissionManager anymore, but we need to be careful
194 // removing the parameter since this class is extended by GlobalPreferencesFactory
195 // in the GlobalPreferences extension, and that class uses it
196 $this->permissionManager = $permissionManager;
197
198 $this->logger = new NullLogger();
199 $this->languageConverter = $languageConverter;
200 $this->languageNameUtils = $languageNameUtils;
201 $this->hookRunner = new HookRunner( $hookContainer );
202
203 // Don't break GlobalPreferences, fall back to global state if missing services
204 // or if passed a UserOptionsLookup that isn't UserOptionsManager
205 $services = static function () {
206 // BC hack. Use a closure so this can be unit-tested.
208 };
209 $this->userOptionsManager = ( $userOptionsLookup instanceof UserOptionsManager )
210 ? $userOptionsLookup
211 : $services()->getUserOptionsManager();
212 $this->languageConverterFactory = $languageConverterFactory ?? $services()->getLanguageConverterFactory();
213
214 $this->parserFactory = $parserFactory ?? $services()->getParserFactory();
215 $this->skinFactory = $skinFactory ?? $services()->getSkinFactory();
216 $this->userGroupManager = $userGroupManager ?? $services()->getUserGroupManager();
217 $this->signatureValidatorFactory = $signatureValidatorFactory
218 ?? $services()->getSignatureValidatorFactory();
219 }
220
224 public function getSaveBlacklist() {
225 return [
226 'realname',
227 'emailaddress',
228 ];
229 }
230
236 public function getFormDescriptor( User $user, IContextSource $context ) {
237 $preferences = [];
238
239 OutputPage::setupOOUI(
240 strtolower( $context->getSkin()->getSkinName() ),
241 $context->getLanguage()->getDir()
242 );
243
244 $this->profilePreferences( $user, $context, $preferences );
245 $this->skinPreferences( $user, $context, $preferences );
246 $this->datetimePreferences( $user, $context, $preferences );
247 $this->filesPreferences( $context, $preferences );
248 $this->renderingPreferences( $user, $context, $preferences );
249 $this->editingPreferences( $user, $context, $preferences );
250 $this->rcPreferences( $user, $context, $preferences );
251 $this->watchlistPreferences( $user, $context, $preferences );
252 $this->searchPreferences( $context, $preferences );
253
254 $this->hookRunner->onGetPreferences( $user, $preferences );
255
256 $this->loadPreferenceValues( $user, $context, $preferences );
257 $this->logger->debug( "Created form descriptor for user '{$user->getName()}'" );
258 return $preferences;
259 }
260
267 public static function simplifyFormDescriptor( array $descriptor ) {
268 foreach ( $descriptor as $name => &$params ) {
269 // Info fields are useless and can use complicated closure to provide
270 // text, skip all of them.
271 if ( ( isset( $params['type'] ) && $params['type'] === 'info' ) ||
272 // Checking old alias for compatibility with unchanged extensions
273 ( isset( $params['class'] ) && $params['class'] === \HTMLInfoField::class ) ||
274 ( isset( $params['class'] ) && $params['class'] === HTMLInfoField::class )
275 ) {
276 unset( $descriptor[$name] );
277 continue;
278 }
279 // Message parsing is the heaviest load when constructing the field,
280 // but we just want to validate data.
281 foreach ( $params as $key => $value ) {
282 switch ( $key ) {
283 // Special case, should be kept.
284 case 'options-message':
285 break;
286 // Special case, should be transferred.
287 case 'options-messages':
288 unset( $params[$key] );
289 $params['options'] = $value;
290 break;
291 default:
292 if ( preg_match( '/-messages?$/', $key ) ) {
293 // Unwanted.
294 unset( $params[$key] );
295 }
296 }
297 }
298 }
299 return $descriptor;
300 }
301
309 private function loadPreferenceValues( User $user, IContextSource $context, &$defaultPreferences ) {
310 // Remove preferences that wikis don't want to use
311 foreach ( $this->options->get( MainConfigNames::HiddenPrefs ) as $pref ) {
312 unset( $defaultPreferences[$pref] );
313 }
314
315 // For validation.
316 $simplified = self::simplifyFormDescriptor( $defaultPreferences );
317 $form = new HTMLForm( $simplified, $context );
318
319 $disable = !$user->isAllowed( 'editmyoptions' );
320
321 $defaultOptions = $this->userOptionsManager->getDefaultOptions( $user );
322 $userOptions = $this->userOptionsManager->getOptions( $user );
323 $this->applyFilters( $userOptions, $defaultPreferences, 'filterForForm' );
324 // Add in defaults from the user
325 foreach ( $simplified as $name => $_ ) {
326 $info = &$defaultPreferences[$name];
327 if ( $disable && !in_array( $name, $this->getSaveBlacklist() ) ) {
328 $info['disabled'] = 'disabled';
329 }
330 if ( isset( $info['default'] ) ) {
331 // Already set, no problem
332 continue;
333 }
334 $field = $form->getField( $name );
335 $globalDefault = $defaultOptions[$name] ?? null;
336 $prefFromUser = static::getPreferenceForField( $name, $field, $userOptions );
337
338 // If it validates, set it as the default
339 // FIXME: That's not how the validate() function works! Values of nested fields
340 // (e.g. CheckMatix) would be missing.
341 if ( $prefFromUser !== null && // Make sure we're not just pulling nothing
342 $field->validate( $prefFromUser, $this->userOptionsManager->getOptions( $user ) ) === true ) {
343 $info['default'] = $prefFromUser;
344 } elseif ( $field->validate( $globalDefault, $this->userOptionsManager->getOptions( $user ) ) === true ) {
345 $info['default'] = $globalDefault;
346 } else {
347 $globalDefault = json_encode( $globalDefault );
348 throw new UnexpectedValueException(
349 "Default '$globalDefault' is invalid for preference $name of user " . $user->getName()
350 );
351 }
352 }
353
354 return $defaultPreferences;
355 }
356
367 public static function getPreferenceForField( $name, HTMLFormField $field, array $userOptions ) {
368 $val = $userOptions[$name] ?? null;
369
370 if ( $field instanceof HTMLNestedFilterable ) {
371 $val = [];
372 $prefix = $field->mParams['prefix'] ?? $name;
373 // Fetch all possible preference keys of the given field on this wiki.
374 $keys = array_keys( $field->filterDataForSubmit( [] ) );
375 foreach ( $keys as $key ) {
376 if ( $userOptions[$prefix . $key] ?? false ) {
377 $val[] = $key;
378 }
379 }
380 }
381
382 return $val;
383 }
384
394 protected function getOptionFromUser( $name, $info, array $userOptions ) {
395 $val = $userOptions[$name] ?? null;
396
397 // Handling for multiselect preferences
398 if ( ( isset( $info['type'] ) && $info['type'] == 'multiselect' ) ||
399 // Checking old alias for compatibility with unchanged extensions
400 ( isset( $info['class'] ) && $info['class'] === \HTMLMultiSelectField::class ) ||
401 ( isset( $info['class'] ) && $info['class'] === HTMLMultiSelectField::class )
402 ) {
403 $options = HTMLFormField::flattenOptions( $info['options-messages'] ?? $info['options'] );
404 $prefix = $info['prefix'] ?? $name;
405 $val = [];
406
407 foreach ( $options as $value ) {
408 if ( $userOptions["$prefix$value"] ?? false ) {
409 $val[] = $value;
410 }
411 }
412 }
413
414 // Handling for checkmatrix preferences
415 if ( ( isset( $info['type'] ) && $info['type'] == 'checkmatrix' ) ||
416 // Checking old alias for compatibility with unchanged extensions
417 ( isset( $info['class'] ) && $info['class'] === \HTMLCheckMatrix::class ) ||
418 ( isset( $info['class'] ) && $info['class'] === HTMLCheckMatrix::class )
419 ) {
420 $columns = HTMLFormField::flattenOptions( $info['columns'] );
421 $rows = HTMLFormField::flattenOptions( $info['rows'] );
422 $prefix = $info['prefix'] ?? $name;
423 $val = [];
424
425 foreach ( $columns as $column ) {
426 foreach ( $rows as $row ) {
427 if ( $userOptions["$prefix$column-$row"] ?? false ) {
428 $val[] = "$column-$row";
429 }
430 }
431 }
432 }
433
434 return $val;
435 }
436
444 protected function profilePreferences(
445 User $user, IContextSource $context, &$defaultPreferences
446 ) {
447 // retrieving user name for GENDER and misc.
448 $userName = $user->getName();
449
450 // Information panel
451 $defaultPreferences['username'] = [
452 'type' => 'info',
453 'label-message' => [ 'username', $userName ],
454 'default' => $userName,
455 'section' => 'personal/info',
456 ];
457
458 $lang = $context->getLanguage();
459
460 // Get groups to which the user belongs, Skip the default * group, seems useless here
461 $userEffectiveGroups = array_diff(
462 $this->userGroupManager->getUserEffectiveGroups( $user ),
463 [ '*' ]
464 );
465 $defaultPreferences['usergroups'] = [
466 'type' => 'info',
467 'label-message' => [ 'prefs-memberingroups',
468 Message::numParam( count( $userEffectiveGroups ) ), $userName ],
469 'default' => function () use ( $user, $userEffectiveGroups, $context, $lang, $userName ) {
470 $userGroupMemberships = $this->userGroupManager->getUserGroupMemberships( $user );
471 $userGroups = $userMembers = $userTempGroups = $userTempMembers = [];
472 foreach ( $userEffectiveGroups as $ueg ) {
473 $groupStringOrObject = $userGroupMemberships[$ueg] ?? $ueg;
474
475 $userG = UserGroupMembership::getLinkHTML( $groupStringOrObject, $context );
476 $userM = UserGroupMembership::getLinkHTML( $groupStringOrObject, $context, $userName );
477
478 // Store expiring groups separately, so we can place them before non-expiring
479 // groups in the list. This is to avoid the ambiguity of something like
480 // "administrator, bureaucrat (until X date)" -- users might wonder whether the
481 // expiry date applies to both groups, or just the last one
482 if ( $groupStringOrObject instanceof UserGroupMembership &&
483 $groupStringOrObject->getExpiry()
484 ) {
485 $userTempGroups[] = $userG;
486 $userTempMembers[] = $userM;
487 } else {
488 $userGroups[] = $userG;
489 $userMembers[] = $userM;
490 }
491 }
492 sort( $userGroups );
493 sort( $userMembers );
494 sort( $userTempGroups );
495 sort( $userTempMembers );
496 $userGroups = array_merge( $userTempGroups, $userGroups );
497 $userMembers = array_merge( $userTempMembers, $userMembers );
498 return $context->msg( 'prefs-memberingroups-type' )
499 ->rawParams( $lang->commaList( $userGroups ), $lang->commaList( $userMembers ) )
500 ->escaped();
501 },
502 'raw' => true,
503 'section' => 'personal/info',
504 ];
505
506 $contribTitle = SpecialPage::getTitleFor( "Contributions", $userName );
507 $formattedEditCount = $lang->formatNum( $user->getEditCount() );
508 $editCount = $this->linkRenderer->makeLink( $contribTitle, $formattedEditCount );
509
510 $defaultPreferences['editcount'] = [
511 'type' => 'info',
512 'raw' => true,
513 'label-message' => 'prefs-edits',
514 'default' => $editCount,
515 'section' => 'personal/info',
516 ];
517
518 if ( $user->getRegistration() ) {
519 $displayUser = $context->getUser();
520 $userRegistration = $user->getRegistration();
521 $defaultPreferences['registrationdate'] = [
522 'type' => 'info',
523 'label-message' => 'prefs-registration',
524 'default' => $context->msg(
525 'prefs-registration-date-time',
526 $lang->userTimeAndDate( $userRegistration, $displayUser ),
527 $lang->userDate( $userRegistration, $displayUser ),
528 $lang->userTime( $userRegistration, $displayUser )
529 )->text(),
530 'section' => 'personal/info',
531 ];
532 }
533
534 $canViewPrivateInfo = $user->isAllowed( 'viewmyprivateinfo' );
535 $canEditPrivateInfo = $user->isAllowed( 'editmyprivateinfo' );
536
537 // Actually changeable stuff
538 $defaultPreferences['realname'] = [
539 // (not really "private", but still shouldn't be edited without permission)
540 'type' => $canEditPrivateInfo && $this->authManager->allowsPropertyChange( 'realname' )
541 ? 'text' : 'info',
542 'default' => $user->getRealName(),
543 'section' => 'personal/info',
544 'label-message' => 'yourrealname',
545 'help-message' => 'prefs-help-realname',
546 ];
547
548 if ( $canEditPrivateInfo && $this->authManager->allowsAuthenticationDataChange(
549 new PasswordAuthenticationRequest(), false )->isGood()
550 ) {
551 $defaultPreferences['password'] = [
552 'type' => 'info',
553 'raw' => true,
554 'default' => (string)new ButtonWidget( [
555 'href' => SpecialPage::getTitleFor( 'ChangePassword' )->getLinkURL( [
556 'returnto' => SpecialPage::getTitleFor( 'Preferences' )->getPrefixedText()
557 ] ),
558 'label' => $context->msg( 'prefs-resetpass' )->text(),
559 ] ),
560 'label-message' => 'yourpassword',
561 // email password reset feature only works for users that have an email set up
562 'help-raw' => $user->getEmail()
563 ? $context->msg( 'prefs-help-yourpassword',
564 '[[#mw-prefsection-personal-email|{{int:prefs-email}}]]' )->parse()
565 : '',
566 'section' => 'personal/info',
567 ];
568 }
569 // Only show prefershttps if secure login is turned on
570 if ( !$this->options->get( MainConfigNames::ForceHTTPS )
571 && $this->options->get( MainConfigNames::SecureLogin )
572 ) {
573 $defaultPreferences['prefershttps'] = [
574 'type' => 'toggle',
575 'label-message' => 'tog-prefershttps',
576 'help-message' => 'prefs-help-prefershttps',
577 'section' => 'personal/info'
578 ];
579 }
580
581 $defaultPreferences['downloaduserdata'] = [
582 'type' => 'info',
583 'raw' => true,
584 'label-message' => 'prefs-user-downloaddata-label',
585 'default' => Html::element(
586 'a',
587 [
588 'href' => $this->options->get( MainConfigNames::ScriptPath ) .
589 '/api.php?action=query&meta=userinfo&uiprop=*&formatversion=2',
590 ],
591 $context->msg( 'prefs-user-downloaddata-info' )->text()
592 ),
593 'help-message' => [ 'prefs-user-downloaddata-help-message', urlencode( $user->getTitleKey() ) ],
594 'section' => 'personal/info',
595 ];
596
597 $defaultPreferences['restoreprefs'] = [
598 'type' => 'info',
599 'raw' => true,
600 'label-message' => 'prefs-user-restoreprefs-label',
601 'default' => Html::element(
602 'a',
603 [
604 'href' => SpecialPage::getTitleFor( 'Preferences' )
605 ->getSubpage( 'reset' )->getLocalURL()
606 ],
607 $context->msg( 'prefs-user-restoreprefs-info' )->text()
608 ),
609 'section' => 'personal/info',
610 ];
611
612 $languages = $this->languageNameUtils->getLanguageNames(
613 LanguageNameUtils::AUTONYMS,
614 LanguageNameUtils::SUPPORTED
615 );
616 $languageCode = $this->options->get( MainConfigNames::LanguageCode );
617 if ( !array_key_exists( $languageCode, $languages ) ) {
618 $languages[$languageCode] = $languageCode;
619 // Sort the array again
620 ksort( $languages );
621 }
622
623 $options = [];
624 foreach ( $languages as $code => $name ) {
625 $display = LanguageCode::bcp47( $code ) . ' - ' . $name;
626 $options[$display] = $code;
627 }
628 $defaultPreferences['language'] = [
629 'type' => 'select',
630 'section' => 'personal/i18n',
631 'options' => $options,
632 'label-message' => 'yourlanguage',
633 ];
634
635 $neutralGenderMessage = $context->msg( 'gender-notknown' )->escaped() . (
636 !$context->msg( 'gender-unknown' )->isDisabled()
637 ? "<br>" . $context->msg( 'parentheses' )
638 ->params( $context->msg( 'gender-unknown' )->plain() )
639 ->escaped()
640 : ''
641 );
642
643 $defaultPreferences['gender'] = [
644 'type' => 'radio',
645 'section' => 'personal/i18n',
646 'options' => [
647 $neutralGenderMessage => 'unknown',
648 $context->msg( 'gender-female' )->escaped() => 'female',
649 $context->msg( 'gender-male' )->escaped() => 'male',
650 ],
651 'label-message' => 'yourgender',
652 'help-message' => 'prefs-help-gender',
653 ];
654
655 // see if there are multiple language variants to choose from
656 if ( !$this->languageConverterFactory->isConversionDisabled() ) {
657
658 foreach ( LanguageConverter::$languagesWithVariants as $langCode ) {
659 if ( $langCode == $this->contLang->getCode() ) {
660 if ( !$this->languageConverter->hasVariants() ) {
661 continue;
662 }
663
664 $variants = $this->languageConverter->getVariants();
665 $variantArray = [];
666 foreach ( $variants as $v ) {
667 $v = str_replace( '_', '-', strtolower( $v ) );
668 $variantArray[$v] = $lang->getVariantname( $v, false );
669 }
670
671 $options = [];
672 foreach ( $variantArray as $code => $name ) {
673 $display = LanguageCode::bcp47( $code ) . ' - ' . $name;
674 $options[$display] = $code;
675 }
676
677 $defaultPreferences['variant'] = [
678 'label-message' => 'yourvariant',
679 'type' => 'select',
680 'options' => $options,
681 'section' => 'personal/i18n',
682 'help-message' => 'prefs-help-variant',
683 ];
684 } else {
685 $defaultPreferences["variant-$langCode"] = [
686 'type' => 'api',
687 ];
688 }
689 }
690 }
691
692 // show a preview of the old signature first
693 $oldsigWikiText = $this->parserFactory->getInstance()->preSaveTransform(
694 '~~~',
695 $context->getTitle(),
696 $user,
697 ParserOptions::newFromContext( $context )
698 );
699 $oldsigHTML = Parser::stripOuterParagraph(
700 $context->getOutput()->parseAsContent( $oldsigWikiText )
701 );
702 $signatureFieldConfig = [];
703 // Validate existing signature and show a message about it
704 $signature = $this->userOptionsManager->getOption( $user, 'nickname' );
705 $useFancySig = $this->userOptionsManager->getBoolOption( $user, 'fancysig' );
706 if ( $useFancySig && $signature !== '' ) {
707 $parserOpts = ParserOptions::newFromContext( $context );
708 $validator = $this->signatureValidatorFactory
709 ->newSignatureValidator( $user, $context, $parserOpts );
710 $signatureErrors = $validator->validateSignature( $signature );
711 if ( $signatureErrors ) {
712 $sigValidation = $this->options->get( MainConfigNames::SignatureValidation );
713 $oldsigHTML .= '<p><strong>' .
714 // Messages used here:
715 // * prefs-signature-invalid-warning
716 // * prefs-signature-invalid-new
717 // * prefs-signature-invalid-disallow
718 $context->msg( "prefs-signature-invalid-$sigValidation" )->parse() .
719 '</strong></p>';
720
721 // On initial page load, show the warnings as well
722 // (when posting, you get normal validation errors instead)
723 foreach ( $signatureErrors as &$sigError ) {
724 $sigError = new HtmlSnippet( $sigError );
725 }
726 if ( !$context->getRequest()->wasPosted() ) {
727 $signatureFieldConfig = [
728 'warnings' => $sigValidation !== 'disallow' ? $signatureErrors : null,
729 'errors' => $sigValidation === 'disallow' ? $signatureErrors : null,
730 ];
731 }
732 }
733 }
734 $defaultPreferences['oldsig'] = [
735 'type' => 'info',
736 // Normally HTMLFormFields do not display warnings, so we need to use 'rawrow'
737 // and provide the entire OOUI\FieldLayout here
738 'rawrow' => true,
739 'default' => new FieldLayout(
740 new LabelWidget( [
741 'label' => new HtmlSnippet( $oldsigHTML ),
742 ] ),
743 [
744 'align' => 'top',
745 'label' => new HtmlSnippet( $context->msg( 'tog-oldsig' )->parse() )
746 ] + $signatureFieldConfig
747 ),
748 'section' => 'personal/signature',
749 ];
750 $defaultPreferences['nickname'] = [
751 'type' => $this->authManager->allowsPropertyChange( 'nickname' ) ? 'text' : 'info',
752 'maxlength' => $this->options->get( MainConfigNames::MaxSigChars ),
753 'label-message' => 'yournick',
754 'validation-callback' => function ( $signature, $alldata, HTMLForm $form ) {
755 return $this->validateSignature( $signature, $alldata, $form );
756 },
757 'section' => 'personal/signature',
758 'filter-callback' => function ( $signature, array $alldata, HTMLForm $form ) {
759 return $this->cleanSignature( $signature, $alldata, $form );
760 },
761 ];
762 $defaultPreferences['fancysig'] = [
763 'type' => 'toggle',
764 'label-message' => 'tog-fancysig',
765 // show general help about signature at the bottom of the section
766 'help-message' => 'prefs-help-signature',
767 'section' => 'personal/signature'
768 ];
769
770 // Email preferences
771 if ( $this->options->get( MainConfigNames::EnableEmail ) ) {
772 if ( $canViewPrivateInfo ) {
773 $helpMessages = [];
774 $helpMessages[] = $this->options->get( MainConfigNames::EmailConfirmToEdit )
775 ? 'prefs-help-email-required'
776 : 'prefs-help-email';
777
778 if ( $this->options->get( MainConfigNames::EnableUserEmail ) ) {
779 // additional messages when users can send email to each other
780 $helpMessages[] = 'prefs-help-email-others';
781 }
782
783 $emailAddress = $user->getEmail() ? htmlspecialchars( $user->getEmail() ) : '';
784 if ( $canEditPrivateInfo && $this->authManager->allowsPropertyChange( 'emailaddress' ) ) {
785 $button = new ButtonWidget( [
786 'href' => SpecialPage::getTitleFor( 'ChangeEmail' )->getLinkURL( [
787 'returnto' => SpecialPage::getTitleFor( 'Preferences' )->getPrefixedText()
788 ] ),
789 'label' =>
790 $context->msg( $user->getEmail() ? 'prefs-changeemail' : 'prefs-setemail' )->text(),
791 ] );
792
793 $emailAddress .= $emailAddress == '' ? $button : ( '<br />' . $button );
794 }
795
796 $defaultPreferences['emailaddress'] = [
797 'type' => 'info',
798 'raw' => true,
799 'default' => $emailAddress,
800 'label-message' => 'youremail',
801 'section' => 'personal/email',
802 'help-messages' => $helpMessages,
803 // 'cssclass' chosen below
804 ];
805 }
806
807 $disableEmailPrefs = false;
808
809 $defaultPreferences['requireemail'] = [
810 'type' => 'toggle',
811 'label-message' => 'tog-requireemail',
812 'help-message' => 'prefs-help-requireemail',
813 'section' => 'personal/email',
814 'disabled' => !$user->getEmail(),
815 ];
816
817 if ( $this->options->get( MainConfigNames::EmailAuthentication ) ) {
818 if ( $user->getEmail() ) {
819 if ( $user->getEmailAuthenticationTimestamp() ) {
820 // date and time are separate parameters to facilitate localisation.
821 // $time is kept for backward compat reasons.
822 // 'emailauthenticated' is also used in SpecialConfirmemail.php
823 $displayUser = $context->getUser();
824 $emailTimestamp = $user->getEmailAuthenticationTimestamp();
825 $time = $lang->userTimeAndDate( $emailTimestamp, $displayUser );
826 $d = $lang->userDate( $emailTimestamp, $displayUser );
827 $t = $lang->userTime( $emailTimestamp, $displayUser );
828 $emailauthenticated = $context->msg( 'emailauthenticated',
829 $time, $d, $t )->parse() . '<br />';
830 $emailauthenticationclass = 'mw-email-authenticated';
831 } else {
832 $disableEmailPrefs = true;
833 $emailauthenticated = $context->msg( 'emailnotauthenticated' )->parse() . '<br />' .
834 new ButtonWidget( [
835 'href' => SpecialPage::getTitleFor( 'Confirmemail' )->getLinkURL(),
836 'label' => $context->msg( 'emailconfirmlink' )->text(),
837 ] );
838 $emailauthenticationclass = "mw-email-not-authenticated";
839 }
840 } else {
841 $disableEmailPrefs = true;
842 $emailauthenticated = $context->msg( 'noemailprefs' )->escaped();
843 $emailauthenticationclass = 'mw-email-none';
844 }
845
846 if ( $canViewPrivateInfo ) {
847 $defaultPreferences['emailauthentication'] = [
848 'type' => 'info',
849 'raw' => true,
850 'section' => 'personal/email',
851 'label-message' => 'prefs-emailconfirm-label',
852 'default' => $emailauthenticated,
853 // Apply the same CSS class used on the input to the message:
854 'cssclass' => $emailauthenticationclass,
855 ];
856 }
857 }
858
859 if ( $this->options->get( MainConfigNames::EnableUserEmail ) &&
860 $user->isAllowed( 'sendemail' )
861 ) {
862 $defaultPreferences['disablemail'] = [
863 'id' => 'wpAllowEmail',
864 'type' => 'toggle',
865 'invert' => true,
866 'section' => 'personal/email',
867 'label-message' => 'allowemail',
868 'disabled' => $disableEmailPrefs,
869 ];
870
871 $defaultPreferences['email-allow-new-users'] = [
872 'id' => 'wpAllowEmailFromNewUsers',
873 'type' => 'toggle',
874 'section' => 'personal/email',
875 'label-message' => 'email-allow-new-users-label',
876 'help-message' => 'prefs-help-email-allow-new-users',
877 'disabled' => $disableEmailPrefs,
878 'disable-if' => [ '!==', 'disablemail', '1' ],
879 ];
880
881 $defaultPreferences['ccmeonemails'] = [
882 'type' => 'toggle',
883 'section' => 'personal/email',
884 'label-message' => 'tog-ccmeonemails',
885 'disabled' => $disableEmailPrefs,
886 ];
887
888 $defaultPreferences['email-blacklist'] = [
889 'type' => 'usersmultiselect',
890 'label-message' => 'email-mutelist-label',
891 'section' => 'personal/email',
892 'disabled' => $disableEmailPrefs,
893 'filter' => MultiUsernameFilter::class,
894 'excludetemp' => true,
895 ];
896 }
897
898 if ( $this->options->get( MainConfigNames::EnotifWatchlist ) ) {
899 $defaultPreferences['enotifwatchlistpages'] = [
900 'type' => 'toggle',
901 'section' => 'personal/email',
902 'label-message' => 'tog-enotifwatchlistpages',
903 'disabled' => $disableEmailPrefs,
904 ];
905 }
906 if ( $this->options->get( MainConfigNames::EnotifUserTalk ) ) {
907 $defaultPreferences['enotifusertalkpages'] = [
908 'type' => 'toggle',
909 'section' => 'personal/email',
910 'label-message' => 'tog-enotifusertalkpages',
911 'disabled' => $disableEmailPrefs,
912 ];
913 }
914 if ( $this->options->get( MainConfigNames::EnotifUserTalk ) ||
915 $this->options->get( MainConfigNames::EnotifWatchlist ) ) {
916 if ( $this->options->get( MainConfigNames::EnotifMinorEdits ) ) {
917 $defaultPreferences['enotifminoredits'] = [
918 'type' => 'toggle',
919 'section' => 'personal/email',
920 'label-message' => 'tog-enotifminoredits',
921 'disabled' => $disableEmailPrefs,
922 ];
923 }
924
925 if ( $this->options->get( MainConfigNames::EnotifRevealEditorAddress ) ) {
926 $defaultPreferences['enotifrevealaddr'] = [
927 'type' => 'toggle',
928 'section' => 'personal/email',
929 'label-message' => 'tog-enotifrevealaddr',
930 'disabled' => $disableEmailPrefs,
931 ];
932 }
933 }
934 }
935 }
936
943 protected function skinPreferences( User $user, IContextSource $context, &$defaultPreferences ) {
944 // Skin selector, if there is at least one valid skin
945 $validSkinNames = $this->getValidSkinNames( $user, $context );
946 if ( $validSkinNames ) {
947 $defaultPreferences['skin'] = [
948 // @phan-suppress-next-line SecurityCheck-XSS False +ve, label is escaped in generateSkinOptions()
949 'type' => 'radio',
950 'options' => $this->generateSkinOptions( $user, $context, $validSkinNames ),
951 'section' => 'rendering/skin',
952 ];
953 $hideCond = [ 'AND' ];
954 foreach ( $validSkinNames as $skinName => $_ ) {
955 $options = $this->skinFactory->getSkinOptions( $skinName );
956 if ( $options['responsive'] ?? false ) {
957 $hideCond[] = [ '!==', 'skin', $skinName ];
958 }
959 }
960 if ( $hideCond === [ 'AND' ] ) {
961 $hideCond = [];
962 }
963 $defaultPreferences['skin-responsive'] = [
964 'type' => 'check',
965 'label-message' => 'prefs-skin-responsive',
966 'section' => 'rendering/skin/skin-prefs',
967 'help-message' => 'prefs-help-skin-responsive',
968 'hide-if' => $hideCond,
969 ];
970 }
971
972 $allowUserCss = $this->options->get( MainConfigNames::AllowUserCss );
973 $allowUserJs = $this->options->get( MainConfigNames::AllowUserJs );
974 $safeMode = $this->userOptionsManager->getOption( $user, 'forcesafemode' );
975 // Create links to user CSS/JS pages for all skins.
976 // This code is basically copied from generateSkinOptions().
977 // @todo Refactor this and the similar code in generateSkinOptions().
978 if ( $allowUserCss || $allowUserJs ) {
979 if ( $safeMode ) {
980 $defaultPreferences['customcssjs-safemode'] = [
981 'type' => 'info',
982 'raw' => true,
983 'rawrow' => true,
984 'section' => 'rendering/skin',
985 'default' => new FieldLayout(
986 new MessageWidget( [
987 'label' => new HtmlSnippet( $context->msg( 'prefs-custom-cssjs-safemode' )->parse() ),
988 'type' => 'warning',
989 ] )
990 ),
991 ];
992 } else {
993 $linkTools = [];
994 $userName = $user->getName();
995
996 if ( $allowUserCss ) {
997 $cssPage = Title::makeTitleSafe( NS_USER, $userName . '/common.css' );
998 $cssLinkText = $context->msg( 'prefs-custom-css' )->text();
999 $linkTools[] = $this->linkRenderer->makeLink( $cssPage, $cssLinkText );
1000 }
1001
1002 if ( $allowUserJs ) {
1003 $jsPage = Title::makeTitleSafe( NS_USER, $userName . '/common.js' );
1004 $jsLinkText = $context->msg( 'prefs-custom-js' )->text();
1005 $linkTools[] = $this->linkRenderer->makeLink( $jsPage, $jsLinkText );
1006 }
1007
1008 $defaultPreferences['commoncssjs'] = [
1009 'type' => 'info',
1010 'raw' => true,
1011 'default' => $context->getLanguage()->pipeList( $linkTools ),
1012 'label-message' => 'prefs-common-config',
1013 'section' => 'rendering/skin',
1014 ];
1015 }
1016 }
1017 }
1018
1023 protected function filesPreferences( IContextSource $context, &$defaultPreferences ) {
1024 $defaultPreferences['imagesize'] = [
1025 'type' => 'select',
1026 'options' => $this->getImageSizes( $context ),
1027 'label-message' => 'imagemaxsize',
1028 'section' => 'rendering/files',
1029 ];
1030 $defaultPreferences['thumbsize'] = [
1031 'type' => 'select',
1032 'options' => $this->getThumbSizes( $context ),
1033 'label-message' => 'thumbsize',
1034 'section' => 'rendering/files',
1035 ];
1036 }
1037
1044 protected function datetimePreferences(
1045 User $user, IContextSource $context, &$defaultPreferences
1046 ) {
1047 $dateOptions = $this->getDateOptions( $context );
1048 if ( $dateOptions ) {
1049 $defaultPreferences['date'] = [
1050 'type' => 'radio',
1051 'options' => $dateOptions,
1052 'section' => 'rendering/dateformat',
1053 ];
1054 }
1055
1056 // Info
1057 $now = wfTimestampNow();
1058 $lang = $context->getLanguage();
1059 $nowlocal = Html::element( 'span', [ 'id' => 'wpLocalTime' ],
1060 $lang->userTime( $now, $user ) );
1061 $nowserver = $lang->userTime( $now, $user,
1062 [ 'format' => false, 'timecorrection' => false ] ) .
1063 Html::hidden( 'wpServerTime', (int)substr( $now, 8, 2 ) * 60 + (int)substr( $now, 10, 2 ) );
1064
1065 $defaultPreferences['nowserver'] = [
1066 'type' => 'info',
1067 'raw' => 1,
1068 'label-message' => 'servertime',
1069 'default' => $nowserver,
1070 'section' => 'rendering/timeoffset',
1071 ];
1072
1073 $defaultPreferences['nowlocal'] = [
1074 'type' => 'info',
1075 'raw' => 1,
1076 'label-message' => 'localtime',
1077 'default' => $nowlocal,
1078 'section' => 'rendering/timeoffset',
1079 ];
1080
1081 $userTimeCorrection = (string)$this->userOptionsManager->getOption( $user, 'timecorrection' );
1082 // This value should already be normalized by UserTimeCorrection, so it should always be valid and not
1083 // in the legacy format. However, let's be sure about that and normalize it again.
1084 // Also, recompute the offset because it can change with DST.
1085 $userTimeCorrectionObj = new UserTimeCorrection(
1086 $userTimeCorrection,
1087 null,
1088 $this->options->get( MainConfigNames::LocalTZoffset )
1089 );
1090
1091 if ( $userTimeCorrectionObj->getCorrectionType() === UserTimeCorrection::OFFSET ) {
1092 $tzDefault = UserTimeCorrection::formatTimezoneOffset( $userTimeCorrectionObj->getTimeOffset() );
1093 } else {
1094 $tzDefault = $userTimeCorrectionObj->toString();
1095 }
1096
1097 $defaultPreferences['timecorrection'] = [
1098 'type' => 'timezone',
1099 'label-message' => 'timezonelegend',
1100 'default' => $tzDefault,
1101 'size' => 20,
1102 'section' => 'rendering/timeoffset',
1103 'id' => 'wpTimeCorrection',
1104 'filter' => TimezoneFilter::class,
1105 ];
1106 }
1107
1113 protected function renderingPreferences(
1114 User $user,
1115 MessageLocalizer $l10n,
1116 &$defaultPreferences
1117 ) {
1118 // Diffs
1119 $defaultPreferences['diffonly'] = [
1120 'type' => 'toggle',
1121 'section' => 'rendering/diffs',
1122 'label-message' => 'tog-diffonly',
1123 ];
1124 $defaultPreferences['norollbackdiff'] = [
1125 'type' => 'toggle',
1126 'section' => 'rendering/diffs',
1127 'label-message' => 'tog-norollbackdiff',
1128 ];
1129 $defaultPreferences['diff-type'] = [
1130 'type' => 'api',
1131 ];
1132
1133 // Page Rendering
1134 if ( $this->options->get( MainConfigNames::AllowUserCssPrefs ) ) {
1135 $defaultPreferences['underline'] = [
1136 'type' => 'select',
1137 'options' => [
1138 $l10n->msg( 'underline-never' )->text() => 0,
1139 $l10n->msg( 'underline-always' )->text() => 1,
1140 $l10n->msg( 'underline-default' )->text() => 2,
1141 ],
1142 'label-message' => 'tog-underline',
1143 'section' => 'rendering/advancedrendering',
1144 ];
1145 }
1146
1147 $defaultPreferences['showhiddencats'] = [
1148 'type' => 'toggle',
1149 'section' => 'rendering/advancedrendering',
1150 'label-message' => 'tog-showhiddencats'
1151 ];
1152
1153 if ( $user->isAllowed( 'rollback' ) ) {
1154 $defaultPreferences['showrollbackconfirmation'] = [
1155 'type' => 'toggle',
1156 'section' => 'rendering/advancedrendering',
1157 'label-message' => 'tog-showrollbackconfirmation',
1158 ];
1159 }
1160
1161 $defaultPreferences['forcesafemode'] = [
1162 'type' => 'toggle',
1163 'section' => 'rendering/advancedrendering',
1164 'label-message' => 'tog-forcesafemode',
1165 'help-message' => 'prefs-help-forcesafemode'
1166 ];
1167 }
1168
1174 protected function editingPreferences( User $user, MessageLocalizer $l10n, &$defaultPreferences ) {
1175 $defaultPreferences['editsectiononrightclick'] = [
1176 'type' => 'toggle',
1177 'section' => 'editing/advancedediting',
1178 'label-message' => 'tog-editsectiononrightclick',
1179 ];
1180 $defaultPreferences['editondblclick'] = [
1181 'type' => 'toggle',
1182 'section' => 'editing/advancedediting',
1183 'label-message' => 'tog-editondblclick',
1184 ];
1185
1186 if ( $this->options->get( MainConfigNames::AllowUserCssPrefs ) ) {
1187 $defaultPreferences['editfont'] = [
1188 'type' => 'select',
1189 'section' => 'editing/editor',
1190 'label-message' => 'editfont-style',
1191 'options' => [
1192 $l10n->msg( 'editfont-monospace' )->text() => 'monospace',
1193 $l10n->msg( 'editfont-sansserif' )->text() => 'sans-serif',
1194 $l10n->msg( 'editfont-serif' )->text() => 'serif',
1195 ]
1196 ];
1197 }
1198
1199 if ( $user->isAllowed( 'minoredit' ) ) {
1200 $defaultPreferences['minordefault'] = [
1201 'type' => 'toggle',
1202 'section' => 'editing/editor',
1203 'label-message' => 'tog-minordefault',
1204 ];
1205 }
1206
1207 $defaultPreferences['forceeditsummary'] = [
1208 'type' => 'toggle',
1209 'section' => 'editing/editor',
1210 'label-message' => 'tog-forceeditsummary',
1211 ];
1212
1213 // T350653
1214 if ( $this->options->get( MainConfigNames::EnableEditRecovery ) ) {
1215 $defaultPreferences['editrecovery'] = [
1216 'type' => 'toggle',
1217 'section' => 'editing/editor',
1218 'label-message' => 'tog-editrecovery',
1219 'help-message' => [
1220 'tog-editrecovery-help',
1221 'https://meta.wikimedia.org/wiki/Talk:Community_Wishlist_Survey_2023/Edit-recovery_feature',
1222 ],
1223 ];
1224 }
1225
1226 $defaultPreferences['useeditwarning'] = [
1227 'type' => 'toggle',
1228 'section' => 'editing/editor',
1229 'label-message' => 'tog-useeditwarning',
1230 ];
1231
1232 $defaultPreferences['previewonfirst'] = [
1233 'type' => 'toggle',
1234 'section' => 'editing/preview',
1235 'label-message' => 'tog-previewonfirst',
1236 ];
1237 $defaultPreferences['previewontop'] = [
1238 'type' => 'toggle',
1239 'section' => 'editing/preview',
1240 'label-message' => 'tog-previewontop',
1241 ];
1242 $defaultPreferences['uselivepreview'] = [
1243 'type' => 'toggle',
1244 'section' => 'editing/preview',
1245 'label-message' => 'tog-uselivepreview',
1246 ];
1247 }
1248
1254 protected function rcPreferences( User $user, MessageLocalizer $l10n, &$defaultPreferences ) {
1255 $rcMaxAge = $this->options->get( MainConfigNames::RCMaxAge );
1256 $rcMax = ceil( $rcMaxAge / ( 3600 * 24 ) );
1257 $defaultPreferences['rcdays'] = [
1258 'type' => 'float',
1259 'label-message' => 'recentchangesdays',
1260 'section' => 'rc/displayrc',
1261 'min' => 1 / 24,
1262 'max' => $rcMax,
1263 'help-message' => [ 'recentchangesdays-max', Message::numParam( $rcMax ) ],
1264 ];
1265 $defaultPreferences['rclimit'] = [
1266 'type' => 'int',
1267 'min' => 1,
1268 'max' => 1000,
1269 'label-message' => 'recentchangescount',
1270 'help-message' => 'prefs-help-recentchangescount',
1271 'section' => 'rc/displayrc',
1272 'filter' => IntvalFilter::class,
1273 ];
1274 $defaultPreferences['usenewrc'] = [
1275 'type' => 'toggle',
1276 'label-message' => 'tog-usenewrc',
1277 'section' => 'rc/advancedrc',
1278 ];
1279 $defaultPreferences['hideminor'] = [
1280 'type' => 'toggle',
1281 'label-message' => 'tog-hideminor',
1282 'section' => 'rc/changesrc',
1283 ];
1284 $defaultPreferences['pst-cssjs'] = [
1285 'type' => 'api',
1286 ];
1287 $defaultPreferences['rcfilters-rc-collapsed'] = [
1288 'type' => 'api',
1289 ];
1290 $defaultPreferences['rcfilters-wl-collapsed'] = [
1291 'type' => 'api',
1292 ];
1293 $defaultPreferences['rcfilters-saved-queries'] = [
1294 'type' => 'api',
1295 ];
1296 $defaultPreferences['rcfilters-wl-saved-queries'] = [
1297 'type' => 'api',
1298 ];
1299 // Override RCFilters preferences for RecentChanges 'limit'
1300 $defaultPreferences['rcfilters-limit'] = [
1301 'type' => 'api',
1302 ];
1303 $defaultPreferences['rcfilters-saved-queries-versionbackup'] = [
1304 'type' => 'api',
1305 ];
1306 $defaultPreferences['rcfilters-wl-saved-queries-versionbackup'] = [
1307 'type' => 'api',
1308 ];
1309
1310 if ( $this->options->get( MainConfigNames::RCWatchCategoryMembership ) ) {
1311 $defaultPreferences['hidecategorization'] = [
1312 'type' => 'toggle',
1313 'label-message' => 'tog-hidecategorization',
1314 'section' => 'rc/changesrc',
1315 ];
1316 }
1317
1318 if ( $user->useRCPatrol() ) {
1319 $defaultPreferences['hidepatrolled'] = [
1320 'type' => 'toggle',
1321 'section' => 'rc/changesrc',
1322 'label-message' => 'tog-hidepatrolled',
1323 ];
1324 }
1325
1326 if ( $user->useNPPatrol() ) {
1327 $defaultPreferences['newpageshidepatrolled'] = [
1328 'type' => 'toggle',
1329 'section' => 'rc/changesrc',
1330 'label-message' => 'tog-newpageshidepatrolled',
1331 ];
1332 }
1333
1334 if ( $this->options->get( MainConfigNames::RCShowWatchingUsers ) ) {
1335 $defaultPreferences['shownumberswatching'] = [
1336 'type' => 'toggle',
1337 'section' => 'rc/advancedrc',
1338 'label-message' => 'tog-shownumberswatching',
1339 ];
1340 }
1341
1342 $defaultPreferences['rcenhancedfilters-disable'] = [
1343 'type' => 'toggle',
1344 'section' => 'rc/advancedrc',
1345 'label-message' => 'rcfilters-preference-label',
1346 'help-message' => 'rcfilters-preference-help',
1347 ];
1348 }
1349
1355 protected function watchlistPreferences(
1356 User $user, IContextSource $context, &$defaultPreferences
1357 ) {
1358 $watchlistdaysMax = ceil( $this->options->get( MainConfigNames::RCMaxAge ) / ( 3600 * 24 ) );
1359
1360 if ( $user->isAllowed( 'editmywatchlist' ) ) {
1361 $editWatchlistLinks = [];
1362 $editWatchlistModes = [
1363 'edit' => [ 'subpage' => false, 'flags' => [] ],
1364 'raw' => [ 'subpage' => 'raw', 'flags' => [] ],
1365 'clear' => [ 'subpage' => 'clear', 'flags' => [] ],
1366 ];
1367 foreach ( $editWatchlistModes as $mode => $options ) {
1368 // Messages: prefs-editwatchlist-edit, prefs-editwatchlist-raw, prefs-editwatchlist-clear
1369 $editWatchlistLinks[] =
1370 new ButtonWidget( [
1371 'href' => SpecialPage::getTitleFor( 'EditWatchlist', $options['subpage'] )->getLinkURL(),
1372 'flags' => $options[ 'flags' ],
1373 'label' => new HtmlSnippet(
1374 $context->msg( "prefs-editwatchlist-{$mode}" )->parse()
1375 ),
1376 ] );
1377 }
1378
1379 $defaultPreferences['editwatchlist'] = [
1380 'type' => 'info',
1381 'raw' => true,
1382 // Improve button spacing when they are wrapped (T353365)
1383 'default' => (string)new HorizontalLayout( [ 'items' => $editWatchlistLinks ] ),
1384 'label-message' => 'prefs-editwatchlist-label',
1385 'section' => 'watchlist/editwatchlist',
1386 ];
1387
1388 // If watchlist labels are enabled, show WatchlistLabels button
1389 if ( $this->options->get( MainConfigNames::EnableWatchlistLabels ) ) {
1390 $watchlistLabelsLink = new ButtonWidget( [
1391 'href' => SpecialPage::getTitleFor( 'WatchlistLabels' )->getLinkURL(),
1392 'label' => new HtmlSnippet(
1393 $context->msg( 'prefs-managewatchlistlabels' )->parse()
1394 ),
1395 ] );
1396
1397 $defaultPreferences['editwatchlistlabels'] = [
1398 'type' => 'info',
1399 'raw' => true,
1400 'default' => (string)$watchlistLabelsLink,
1401 'label-message' => 'prefs-editwatchlistlabels-label',
1402 'section' => 'watchlist/editwatchlist',
1403 ];
1404 }
1405
1406 }
1407
1408 $defaultPreferences['watchlistdays'] = [
1409 'type' => 'float',
1410 'min' => 1 / 24,
1411 'max' => $watchlistdaysMax,
1412 'section' => 'watchlist/displaywatchlist',
1413 'help-message' => [ 'prefs-watchlist-days-max', Message::numParam( $watchlistdaysMax ) ],
1414 'label-message' => 'prefs-watchlist-days',
1415 ];
1416 $defaultPreferences['wllimit'] = [
1417 'type' => 'int',
1418 'min' => 1,
1419 'max' => 1000,
1420 'label-message' => 'prefs-watchlist-edits',
1421 'help-message' => 'prefs-watchlist-edits-max',
1422 'section' => 'watchlist/displaywatchlist',
1423 'filter' => IntvalFilter::class,
1424 ];
1425 $defaultPreferences['extendwatchlist'] = [
1426 'type' => 'toggle',
1427 'section' => 'watchlist/advancedwatchlist',
1428 'label-message' => 'tog-extendwatchlist',
1429 ];
1430 $defaultPreferences['watchlisthideminor'] = [
1431 'type' => 'toggle',
1432 'section' => 'watchlist/changeswatchlist',
1433 'label-message' => 'tog-watchlisthideminor',
1434 ];
1435 $defaultPreferences['watchlisthidebots'] = [
1436 'type' => 'toggle',
1437 'section' => 'watchlist/changeswatchlist',
1438 'label-message' => 'tog-watchlisthidebots',
1439 ];
1440 $defaultPreferences['watchlisthideown'] = [
1441 'type' => 'toggle',
1442 'section' => 'watchlist/changeswatchlist',
1443 'label-message' => 'tog-watchlisthideown',
1444 ];
1445 $defaultPreferences['watchlisthideanons'] = [
1446 'type' => 'toggle',
1447 'section' => 'watchlist/changeswatchlist',
1448 'label-message' => 'tog-watchlisthideanons',
1449 ];
1450 $defaultPreferences['watchlisthideliu'] = [
1451 'type' => 'toggle',
1452 'section' => 'watchlist/changeswatchlist',
1453 'label-message' => 'tog-watchlisthideliu',
1454 ];
1455
1457 $defaultPreferences['watchlistreloadautomatically'] = [
1458 'type' => 'toggle',
1459 'section' => 'watchlist/advancedwatchlist',
1460 'label-message' => 'tog-watchlistreloadautomatically',
1461 ];
1462 }
1463
1464 $defaultPreferences['watchlistunwatchlinks'] = [
1465 'type' => 'toggle',
1466 'section' => 'watchlist/advancedwatchlist',
1467 'label-message' => 'tog-watchlistunwatchlinks',
1468 ];
1469
1470 if ( $this->options->get( MainConfigNames::RCWatchCategoryMembership ) ) {
1471 $defaultPreferences['watchlisthidecategorization'] = [
1472 'type' => 'toggle',
1473 'section' => 'watchlist/changeswatchlist',
1474 'label-message' => 'tog-watchlisthidecategorization',
1475 ];
1476 }
1477
1478 if ( $user->useRCPatrol() ) {
1479 $defaultPreferences['watchlisthidepatrolled'] = [
1480 'type' => 'toggle',
1481 'section' => 'watchlist/changeswatchlist',
1482 'label-message' => 'tog-watchlisthidepatrolled',
1483 ];
1484 }
1485
1486 if ( $this->options->get( MainConfigNames::WatchlistExpiry ) ) {
1487 $defaultPreferences["watchstar-expiry"] = [
1488 'type' => 'select',
1489 'options' => WatchAction::getExpiryOptionsFromMessage( $context ),
1490 'label-message' => "tog-watchstar-expiry",
1491 'section' => 'watchlist/pageswatchlist',
1492 ];
1493 }
1494
1495 $watchTypes = [
1496 'edit' => 'watchdefault',
1497 'move' => 'watchmoves',
1498 ];
1499
1500 // Kinda hacky
1501 if ( $user->isAllowedAny( 'createpage', 'createtalk' ) ) {
1502 $watchTypes['read'] = 'watchcreations';
1503 }
1504
1505 // Move uncommon actions to end of list
1506 $watchTypes += [
1507 'rollback' => 'watchrollback',
1508 'upload' => 'watchuploads',
1509 'delete' => 'watchdeletion',
1510 ];
1511
1512 foreach ( $watchTypes as $action => $pref ) {
1513 if ( $user->isAllowed( $action ) ) {
1514 // Messages:
1515 // tog-watchdefault, tog-watchmoves, tog-watchdeletion,
1516 // tog-watchcreations, tog-watchuploads, tog-watchrollback
1517 $defaultPreferences[$pref] = [
1518 'type' => 'toggle',
1519 'section' => 'watchlist/pageswatchlist',
1520 'label-message' => "tog-$pref",
1521 ];
1522
1523 if ( in_array( $action, [ 'edit', 'read', 'rollback' ] ) &&
1524 $this->options->get( MainConfigNames::WatchlistExpiry )
1525 ) {
1526 $defaultPreferences["$pref-expiry"] = [
1527 'type' => 'select',
1528 'options' => WatchAction::getExpiryOptionsFromMessage( $context ),
1529 'label-message' => "tog-watch-expiry",
1530 'section' => 'watchlist/pageswatchlist',
1531 'hide-if' => [ '!==', $pref, '1' ],
1532 'cssclass' => 'mw-prefs-indent',
1533 ];
1534 }
1535 }
1536 }
1537
1538 $defaultPreferences['watchlisttoken'] = [
1539 'type' => 'api',
1540 ];
1541
1542 // T408235
1543 $defaultPreferences['watchlistlabelonboarding'] = [
1544 'type' => 'api',
1545 ];
1546
1547 $tokenButton = new ButtonWidget( [
1548 'href' => SpecialPage::getTitleFor( 'ResetTokens' )->getLinkURL( [
1549 'returnto' => SpecialPage::getTitleFor( 'Preferences' )->getPrefixedText()
1550 ] ),
1551 'label' => $context->msg( 'prefs-watchlist-managetokens' )->text(),
1552 ] );
1553 $defaultPreferences['watchlisttoken-info'] = [
1554 'type' => 'info',
1555 'section' => 'watchlist/tokenwatchlist',
1556 'label-message' => 'prefs-watchlist-token',
1557 'help-message' => 'prefs-help-tokenmanagement',
1558 'raw' => true,
1559 'default' => (string)$tokenButton,
1560 ];
1561
1562 $defaultPreferences['wlenhancedfilters-disable'] = [
1563 'type' => 'toggle',
1564 'section' => 'watchlist/advancedwatchlist',
1565 'label-message' => 'rcfilters-watchlist-preference-label',
1566 'help-message' => 'rcfilters-watchlist-preference-help',
1567 ];
1568 }
1569
1574 protected function searchPreferences( $context, &$defaultPreferences ) {
1575 $defaultPreferences['search-special-page'] = [
1576 'type' => 'api',
1577 ];
1578
1579 foreach ( $this->nsInfo->getValidNamespaces() as $n ) {
1580 $defaultPreferences['searchNs' . $n] = [
1581 'type' => 'api',
1582 ];
1583 }
1584
1585 if ( $this->options->get( MainConfigNames::SearchMatchRedirectPreference ) ) {
1586 $defaultPreferences['search-match-redirect'] = [
1587 'type' => 'toggle',
1588 'section' => 'searchoptions/searchmisc',
1589 'label-message' => 'search-match-redirect-label',
1590 'help-message' => 'search-match-redirect-help',
1591 ];
1592 } else {
1593 $defaultPreferences['search-match-redirect'] = [
1594 'type' => 'api',
1595 ];
1596 }
1597
1598 $defaultPreferences['searchlimit'] = [
1599 'type' => 'int',
1600 'min' => 1,
1601 'max' => 500,
1602 'section' => 'searchoptions/searchmisc',
1603 'label-message' => 'searchlimit-label',
1604 'help-message' => $context->msg( 'searchlimit-help', 500 ),
1605 'filter' => IntvalFilter::class,
1606 ];
1607
1608 // show a preference for thumbnails from namespaces other than NS_FILE,
1609 // only when there they're actually configured to be served
1610 $thumbNamespaces = $this->options->get( MainConfigNames::ThumbnailNamespaces );
1611 $thumbNamespacesFormatted = array_combine(
1612 $thumbNamespaces,
1613 array_map(
1614 static function ( $namespaceId ) use ( $context ) {
1615 return $namespaceId === NS_MAIN
1616 ? $context->msg( 'blanknamespace' )->escaped()
1617 : $context->getLanguage()->getFormattedNsText( $namespaceId );
1618 },
1619 $thumbNamespaces
1620 )
1621 );
1622 $defaultThumbNamespacesFormatted =
1623 array_intersect_key( $thumbNamespacesFormatted, [ NS_FILE => 1 ] ) ?? [];
1624 $extraThumbNamespacesFormatted =
1625 array_diff_key( $thumbNamespacesFormatted, [ NS_FILE => 1 ] );
1626 if ( $extraThumbNamespacesFormatted ) {
1627 $defaultPreferences['search-thumbnail-extra-namespaces'] = [
1628 'type' => 'toggle',
1629 'section' => 'searchoptions/searchmisc',
1630 'label-message' => 'search-thumbnail-extra-namespaces-label',
1631 'help-message' => $context->msg(
1632 'search-thumbnail-extra-namespaces-message',
1633 $context->getLanguage()->listToText( $extraThumbNamespacesFormatted ),
1634 count( $extraThumbNamespacesFormatted ),
1635 $context->getLanguage()->listToText( $defaultThumbNamespacesFormatted ),
1636 count( $defaultThumbNamespacesFormatted )
1637 ),
1638 ];
1639 }
1640 }
1641
1651 private static function sortSkinNames( $a, $b, $currentSkin, $preferredSkins ) {
1652 // Display the current skin first in the list
1653 if ( strcasecmp( $a, $currentSkin ) === 0 ) {
1654 return -1;
1655 }
1656 if ( strcasecmp( $b, $currentSkin ) === 0 ) {
1657 return 1;
1658 }
1659 // Display preferred skins over other skins
1660 if ( count( $preferredSkins ) ) {
1661 $aPreferred = array_search( $a, $preferredSkins );
1662 $bPreferred = array_search( $b, $preferredSkins );
1663 // Cannot use ! operator because array_search returns the
1664 // index of the array item if found (i.e. 0) and false otherwise
1665 if ( $aPreferred !== false && $bPreferred === false ) {
1666 return -1;
1667 }
1668 if ( $aPreferred === false && $bPreferred !== false ) {
1669 return 1;
1670 }
1671 // When both skins are preferred, default to the ordering
1672 // specified by the preferred skins config array
1673 if ( $aPreferred !== false && $bPreferred !== false ) {
1674 return strcasecmp( $aPreferred, $bPreferred );
1675 }
1676 }
1677 // Use normal string comparison if both strings are not preferred
1678 return strcasecmp( $a, $b );
1679 }
1680
1689 private function getValidSkinNames( User $user, IContextSource $context ) {
1690 // Only show skins that aren't disabled
1691 $validSkinNames = $this->skinFactory->getAllowedSkins();
1692 $allInstalledSkins = $this->skinFactory->getInstalledSkins();
1693
1694 // Display the installed skin the user has specifically requested via useskin=….
1695 $useSkin = $context->getRequest()->getRawVal( 'useskin' );
1696 if ( $useSkin !== null && isset( $allInstalledSkins[$useSkin] )
1697 && $context->msg( "skinname-$useSkin" )->exists()
1698 ) {
1699 $validSkinNames[$useSkin] = $useSkin;
1700 }
1701
1702 // Display the skin if the user has set it as a preference already before it was hidden.
1703 $currentUserSkin = $this->userOptionsManager->getOption( $user, 'skin' );
1704 if ( isset( $allInstalledSkins[$currentUserSkin] )
1705 && $context->msg( "skinname-$currentUserSkin" )->exists()
1706 ) {
1707 $validSkinNames[$currentUserSkin] = $currentUserSkin;
1708 }
1709
1710 foreach ( $validSkinNames as $skinkey => &$skinname ) {
1711 $msg = $context->msg( "skinname-{$skinkey}" );
1712 if ( $msg->exists() ) {
1713 $skinname = htmlspecialchars( $msg->text() );
1714 }
1715 }
1716
1717 $preferredSkins = $this->options->get( MainConfigNames::SkinsPreferred );
1718 // Sort by the internal name, so that the ordering is the same for each display language,
1719 // especially if some skin names are translated to use a different alphabet and some are not.
1720 uksort( $validSkinNames, function ( $a, $b ) use ( $currentUserSkin, $preferredSkins ) {
1721 return $this->sortSkinNames( $a, $b, $currentUserSkin, $preferredSkins );
1722 } );
1723
1724 return $validSkinNames;
1725 }
1726
1733 protected function generateSkinOptions( User $user, IContextSource $context, array $validSkinNames ) {
1734 $ret = [];
1735
1736 $mptitle = Title::newMainPage();
1737 $previewtext = $context->msg( 'skin-preview' )->escaped();
1738 $defaultSkin = $this->options->get( MainConfigNames::DefaultSkin );
1739 $allowUserCss = $this->options->get( MainConfigNames::AllowUserCss );
1740 $allowUserJs = $this->options->get( MainConfigNames::AllowUserJs );
1741 $safeMode = $this->userOptionsManager->getOption( $user, 'forcesafemode' );
1742 $foundDefault = false;
1743 foreach ( $validSkinNames as $skinkey => $sn ) {
1744 $linkTools = [];
1745
1746 // Mark the default skin
1747 if ( strcasecmp( $skinkey, $defaultSkin ) === 0 ) {
1748 $linkTools[] = $context->msg( 'default' )->escaped();
1749 $foundDefault = true;
1750 }
1751
1752 // Create talk page link if relevant message exists.
1753 $talkPageMsg = $context->msg( "$skinkey-prefs-talkpage" );
1754 if ( $talkPageMsg->exists() ) {
1755 $linkTools[] = $talkPageMsg->parse();
1756 }
1757
1758 // Create preview link
1759 $mplink = htmlspecialchars( $mptitle->getLocalURL( [ 'useskin' => $skinkey ] ) );
1760 $linkTools[] = "<a target='_blank' href=\"$mplink\">$previewtext</a>";
1761
1762 if ( !$safeMode ) {
1763 // Create links to user CSS/JS pages
1764 // @todo Refactor this and the similar code in skinPreferences().
1765 if ( $allowUserCss ) {
1766 $cssPage = Title::makeTitleSafe( NS_USER, $user->getName() . '/' . $skinkey . '.css' );
1767 $cssLinkText = $context->msg( 'prefs-custom-css' )->text();
1768 $linkTools[] = $this->linkRenderer->makeLink( $cssPage, $cssLinkText );
1769 }
1770
1771 if ( $allowUserJs ) {
1772 $jsPage = Title::makeTitleSafe( NS_USER, $user->getName() . '/' . $skinkey . '.js' );
1773 $jsLinkText = $context->msg( 'prefs-custom-js' )->text();
1774 $linkTools[] = $this->linkRenderer->makeLink( $jsPage, $jsLinkText );
1775 }
1776 }
1777
1778 $display = $sn . ' ' . $context->msg( 'parentheses' )
1779 ->rawParams( $context->getLanguage()->pipeList( $linkTools ) )
1780 ->escaped();
1781 $ret[$display] = $skinkey;
1782 }
1783
1784 if ( !$foundDefault ) {
1785 // If the default skin is not available, things are going to break horribly because the
1786 // default value for skin selector will not be a valid value. Let's just not show it then.
1787 return [];
1788 }
1789
1790 return $ret;
1791 }
1792
1797 protected function getDateOptions( IContextSource $context ) {
1798 $lang = $context->getLanguage();
1799 $dateopts = $lang->getDatePreferences();
1800
1801 $ret = [];
1802
1803 if ( $dateopts ) {
1804 if ( !in_array( 'default', $dateopts ) ) {
1805 $dateopts[] = 'default'; // Make sure default is always valid T21237
1806 }
1807
1808 // FIXME KLUGE: site default might not be valid for user language
1809 global $wgDefaultUserOptions;
1810 if ( !in_array( $wgDefaultUserOptions['date'], $dateopts ) ) {
1811 $wgDefaultUserOptions['date'] = 'default';
1812 }
1813
1814 $epoch = wfTimestampNow();
1815 foreach ( $dateopts as $key ) {
1816 if ( $key == 'default' ) {
1817 $formatted = $context->msg( 'datedefault' )->escaped();
1818 } else {
1819 $formatted = htmlspecialchars( $lang->timeanddate( $epoch, false, $key ) );
1820 }
1821 $ret[$formatted] = $key;
1822 }
1823 }
1824 return $ret;
1825 }
1826
1831 protected function getImageSizes( MessageLocalizer $l10n ) {
1832 $ret = [];
1833 $pixels = $l10n->msg( 'unit-pixel' )->text();
1834
1835 foreach ( $this->options->get( MainConfigNames::ImageLimits ) as $index => $limits ) {
1836 // Note: A left-to-right marker (U+200E) is inserted, see T144386
1837 $display = "{$limits[0]}\u{200E}×{$limits[1]}$pixels";
1838 $ret[$display] = $index;
1839 }
1840
1841 return $ret;
1842 }
1843
1848 protected function getThumbSizes( MessageLocalizer $l10n ) {
1849 $ret = [];
1850 $pixels = $l10n->msg( 'unit-pixel' )->text();
1851
1852 foreach ( $this->options->get( MainConfigNames::ThumbLimits ) as $index => $size ) {
1853 $display = $size . $pixels;
1854 $ret[$display] = $index;
1855 }
1856
1857 return $ret;
1858 }
1859
1866 protected function validateSignature( $signature, $alldata, HTMLForm $form ) {
1867 $sigValidation = $this->options->get( MainConfigNames::SignatureValidation );
1868 $maxSigChars = $this->options->get( MainConfigNames::MaxSigChars );
1869 if ( is_string( $signature ) && mb_strlen( $signature ) > $maxSigChars ) {
1870 return $form->msg( 'badsiglength' )->numParams( $maxSigChars )->escaped();
1871 }
1872
1873 if ( $signature === null || $signature === '' ) {
1874 // Make sure leaving the field empty is valid, since that's used as the default (T288151).
1875 // Code using this preference in Parser::getUserSig() handles this case specially.
1876 return true;
1877 }
1878
1879 // Remaining checks only apply to fancy signatures
1880 if ( !( isset( $alldata['fancysig'] ) && $alldata['fancysig'] ) ) {
1881 return true;
1882 }
1883
1884 // HERE BE DRAGONS:
1885 //
1886 // If this value is already saved as the user's signature, treat it as valid, even if it
1887 // would be invalid to save now, and even if $wgSignatureValidation is set to 'disallow'.
1888 //
1889 // It can become invalid when we introduce new validation, or when the value just transcludes
1890 // some page containing the real signature and that page is edited (which we can't validate),
1891 // or when someone's username is changed.
1892 //
1893 // Otherwise it would be completely removed when the user opens their preferences page, which
1894 // would be very unfriendly.
1895 $user = $form->getUser();
1896 if (
1897 $signature === $this->userOptionsManager->getOption( $user, 'nickname' ) &&
1898 (bool)$alldata['fancysig'] === $this->userOptionsManager->getBoolOption( $user, 'fancysig' )
1899 ) {
1900 return true;
1901 }
1902
1903 if ( $sigValidation === 'new' || $sigValidation === 'disallow' ) {
1904 // Validate everything
1905 $parserOpts = ParserOptions::newFromContext( $form->getContext() );
1906 $validator = $this->signatureValidatorFactory
1907 ->newSignatureValidator( $user, $form->getContext(), $parserOpts );
1908 $errors = $validator->validateSignature( $signature );
1909 if ( $errors ) {
1910 return $errors;
1911 }
1912 }
1913
1914 // Quick check for mismatched HTML tags in the input.
1915 // Note that this is easily fooled by wikitext templates or bold/italic markup.
1916 // We're only keeping this until Parsoid is integrated and guaranteed to be available.
1917 if ( $this->parserFactory->getInstance()->validateSig( $signature ) === false ) {
1918 return $form->msg( 'badsig' )->escaped();
1919 }
1920
1921 return true;
1922 }
1923
1930 protected function cleanSignature( $signature, $alldata, HTMLForm $form ) {
1931 if ( isset( $alldata['fancysig'] ) && $alldata['fancysig'] ) {
1932 $signature = $this->parserFactory->getInstance()->cleanSig( $signature );
1933 } else {
1934 // When no fancy sig used, make sure ~{3,5} get removed.
1935 $signature = Parser::cleanSigInSig( $signature );
1936 }
1937
1938 return $signature;
1939 }
1940
1948 public function getForm(
1949 User $user,
1950 IContextSource $context,
1951 $formClass = PreferencesFormOOUI::class,
1952 array $remove = []
1953 ) {
1954 // We use ButtonWidgets in some of the getPreferences() functions
1955 $context->getOutput()->enableOOUI();
1956
1957 // Note that the $user parameter of getFormDescriptor() is deprecated.
1958 $formDescriptor = $this->getFormDescriptor( $user, $context );
1959 if ( count( $remove ) ) {
1960 $removeKeys = array_fill_keys( $remove, true );
1961 $formDescriptor = array_diff_key( $formDescriptor, $removeKeys );
1962 }
1963
1964 // Remove type=api preferences. They are not intended for rendering in the form.
1965 foreach ( $formDescriptor as $name => $info ) {
1966 if ( isset( $info['type'] ) && $info['type'] === 'api' ) {
1967 unset( $formDescriptor[$name] );
1968 }
1969 }
1970
1974 $htmlForm = new $formClass( $formDescriptor, $context, 'prefs' );
1975
1976 // This allows users to opt-in to hidden skins. While this should be discouraged and is not
1977 // discoverable, this allows users to still use hidden skins while preventing new users from
1978 // adopting unsupported skins. If no useskin=… parameter was provided, it will not show up
1979 // in the resulting URL.
1980 $htmlForm->setAction( $context->getTitle()->getLocalURL( [
1981 'useskin' => $context->getRequest()->getRawVal( 'useskin' )
1982 ] ) );
1983
1984 $htmlForm->setModifiedUser( $user );
1985 $htmlForm->setOptionsEditable( $user->isAllowed( 'editmyoptions' ) );
1986 $htmlForm->setPrivateInfoEditable( $user->isAllowed( 'editmyprivateinfo' ) );
1987 $htmlForm->setId( 'mw-prefs-form' );
1988 $htmlForm->setAutocomplete( 'off' );
1989 $htmlForm->setSubmitTextMsg( 'saveprefs' );
1990 // Used message keys: 'accesskey-preferences-save', 'tooltip-preferences-save'
1991 $htmlForm->setSubmitTooltip( 'preferences-save' );
1992 $htmlForm->setSubmitID( 'prefcontrol' );
1993 $htmlForm->setSubmitCallback(
1994 function ( array $formData, PreferencesFormOOUI $form ) use ( $formDescriptor ) {
1995 return $this->submitForm( $formData, $form, $formDescriptor );
1996 }
1997 );
1998
1999 return $htmlForm;
2000 }
2001
2010 protected function saveFormData( $formData, PreferencesFormOOUI $form, array $formDescriptor ) {
2011 $user = $form->getModifiedUser();
2012 $hiddenPrefs = $this->options->get( MainConfigNames::HiddenPrefs );
2013 $result = true;
2014
2015 if ( !$user->isAllowedAny( 'editmyprivateinfo', 'editmyoptions' ) ) {
2016 return Status::newFatal( 'mypreferencesprotected' );
2017 }
2018
2019 // Filter input
2020 $this->applyFilters( $formData, $formDescriptor, 'filterFromForm' );
2021
2022 // Fortunately, the realname field is MUCH simpler
2023 // (not really "private", but still shouldn't be edited without permission)
2024
2025 if ( !in_array( 'realname', $hiddenPrefs )
2026 && $user->isAllowed( 'editmyprivateinfo' )
2027 && array_key_exists( 'realname', $formData )
2028 ) {
2029 $realName = $formData['realname'];
2030 $user->setRealName( $realName );
2031 }
2032
2033 if ( $user->isAllowed( 'editmyoptions' ) ) {
2034 $oldUserOptions = $this->userOptionsManager->getOptions( $user );
2035
2036 foreach ( $this->getSaveBlacklist() as $b ) {
2037 unset( $formData[$b] );
2038 }
2039
2040 // If users have saved a value for a preference which has subsequently been disabled
2041 // via $wgHiddenPrefs, we don't want to destroy that setting in case the preference
2042 // is subsequently re-enabled
2043 foreach ( $hiddenPrefs as $pref ) {
2044 // If the user has not set a non-default value here, the default will be returned
2045 // and subsequently discarded
2046 $formData[$pref] = $this->userOptionsManager->getOption( $user, $pref, null, true );
2047 }
2048
2049 // If the user changed the rclimit preference, also change the rcfilters-rclimit preference
2050 if (
2051 isset( $formData['rclimit'] ) &&
2052 intval( $formData[ 'rclimit' ] ) !== $this->userOptionsManager->getIntOption( $user, 'rclimit' )
2053 ) {
2054 $formData['rcfilters-limit'] = $formData['rclimit'];
2055 }
2056
2057 // Keep old preferences from interfering due to back-compat code, etc.
2058 $optionsToReset = $this->getOptionNamesForReset( $user, $form->getContext(), 'unused' );
2059 $this->userOptionsManager->resetOptionsByName( $user, $optionsToReset );
2060
2061 foreach ( $formData as $key => $value ) {
2062 // If we're creating a new local override, we need to explicitly pass
2063 // GLOBAL_OVERRIDE to setOption(), otherwise the update would be ignored
2064 // due to the conflicting global option.
2065 $except = !empty( $formData[$key . UserOptionsLookup::LOCAL_EXCEPTION_SUFFIX] );
2066 $this->userOptionsManager->setOption( $user, $key, $value,
2067 $except ? UserOptionsManager::GLOBAL_OVERRIDE : UserOptionsManager::GLOBAL_IGNORE );
2068 }
2069
2070 $this->hookRunner->onPreferencesFormPreSave(
2071 $formData, $form, $user, $result, $oldUserOptions );
2072 }
2073
2074 $user->saveSettings();
2075
2076 return $result;
2077 }
2078
2087 protected function applyFilters( array &$preferences, array $formDescriptor, $verb ) {
2088 foreach ( $formDescriptor as $preference => $desc ) {
2089 if ( !isset( $desc['filter'] ) || !isset( $preferences[$preference] ) ) {
2090 continue;
2091 }
2092 $filterDesc = $desc['filter'];
2093 if ( $filterDesc instanceof Filter ) {
2094 $filter = $filterDesc;
2095 } elseif ( class_exists( $filterDesc ) ) {
2096 $filter = new $filterDesc();
2097 } elseif ( is_callable( $filterDesc ) ) {
2098 $filter = $filterDesc();
2099 } else {
2100 throw new UnexpectedValueException(
2101 "Unrecognized filter type for preference '$preference'"
2102 );
2103 }
2104 $preferences[$preference] = $filter->$verb( $preferences[$preference] );
2105 }
2106 }
2107
2116 protected function submitForm(
2117 array $formData,
2118 PreferencesFormOOUI $form,
2119 array $formDescriptor
2120 ) {
2121 $res = $this->saveFormData( $formData, $form, $formDescriptor );
2122
2123 if ( $res === true ) {
2124 $context = $form->getContext();
2125 $urlOptions = [];
2126
2127 $urlOptions += $form->getExtraSuccessRedirectParameters();
2128
2129 $url = $form->getTitle()->getFullURL( $urlOptions );
2130
2131 // Set session data for the success message
2132 $context->getRequest()->getSession()->set( 'specialPreferencesSaveSuccess', 1 );
2133
2134 $context->getOutput()->redirect( $url );
2135 }
2136
2137 return ( $res === true ? Status::newGood() : $res );
2138 }
2139
2146 public function getResetKinds(
2147 User $user, IContextSource $context, $options = null
2148 ): array {
2149 $options ??= $this->userOptionsManager->loadUserOptions( $user );
2150
2151 $prefs = $this->getFormDescriptor( $user, $context );
2152 $mapping = [];
2153
2154 // Pull out the "special" options, so they don't get converted as
2155 // multiselect or checkmatrix.
2156 $specialOptions = array_fill_keys( $this->getSaveBlacklist(), true );
2157 foreach ( $specialOptions as $name => $value ) {
2158 unset( $prefs[$name] );
2159 }
2160
2161 // Multiselect and checkmatrix options are stored in the database with
2162 // one key per option, each having a boolean value. Extract those keys.
2163 $multiselectOptions = [];
2164 foreach ( $prefs as $name => $info ) {
2165 if ( ( isset( $info['type'] ) && $info['type'] == 'multiselect' ) ||
2166 // Checking old alias for compatibility with unchanged extensions
2167 ( isset( $info['class'] ) && $info['class'] === \HTMLMultiSelectField::class ) ||
2168 ( isset( $info['class'] ) && $info['class'] === HTMLMultiSelectField::class )
2169 ) {
2170 $opts = HTMLFormField::flattenOptions( $info['options'] ?? $info['options-messages'] );
2171 $prefix = $info['prefix'] ?? $name;
2172
2173 foreach ( $opts as $value ) {
2174 $multiselectOptions["$prefix$value"] = true;
2175 }
2176
2177 unset( $prefs[$name] );
2178 }
2179 }
2180 $checkmatrixOptions = [];
2181 foreach ( $prefs as $name => $info ) {
2182 if ( ( isset( $info['type'] ) && $info['type'] == 'checkmatrix' ) ||
2183 // Checking old alias for compatibility with unchanged extensions
2184 ( isset( $info['class'] ) && $info['class'] === \HTMLCheckMatrix::class ) ||
2185 ( isset( $info['class'] ) && $info['class'] === HTMLCheckMatrix::class )
2186 ) {
2187 $columns = HTMLFormField::flattenOptions( $info['columns'] );
2188 $rows = HTMLFormField::flattenOptions( $info['rows'] );
2189 $prefix = $info['prefix'] ?? $name;
2190
2191 foreach ( $columns as $column ) {
2192 foreach ( $rows as $row ) {
2193 $checkmatrixOptions["$prefix$column-$row"] = true;
2194 }
2195 }
2196
2197 unset( $prefs[$name] );
2198 }
2199 }
2200
2201 // $value is ignored
2202 foreach ( $options as $key => $value ) {
2203 if ( isset( $prefs[$key] ) ) {
2204 $mapping[$key] = 'registered';
2205 } elseif ( isset( $multiselectOptions[$key] ) ) {
2206 $mapping[$key] = 'registered-multiselect';
2207 } elseif ( isset( $checkmatrixOptions[$key] ) ) {
2208 $mapping[$key] = 'registered-checkmatrix';
2209 } elseif ( isset( $specialOptions[$key] ) ) {
2210 $mapping[$key] = 'special';
2211 } elseif ( str_starts_with( $key, 'userjs-' ) ) {
2212 $mapping[$key] = 'userjs';
2213 } elseif ( str_starts_with( $key, UserOptionsLookup::LOCAL_EXCEPTION_SUFFIX ) ) {
2214 $mapping[$key] = 'local-exception';
2215 } else {
2216 $mapping[$key] = 'unused';
2217 }
2218 }
2219
2220 return $mapping;
2221 }
2222
2223 public function listResetKinds(): array {
2224 return [
2225 'registered',
2226 'registered-multiselect',
2227 'registered-checkmatrix',
2228 'userjs',
2229 'special',
2230 'unused'
2231 ];
2232 }
2233
2240 public function getOptionNamesForReset( User $user, IContextSource $context, $kinds ) {
2241 $oldOptions = $this->userOptionsManager->loadUserOptions( $user, IDBAccessObject::READ_LATEST );
2242
2243 if ( !is_array( $kinds ) ) {
2244 $kinds = [ $kinds ];
2245 }
2246
2247 if ( in_array( 'all', $kinds ) ) {
2248 return array_keys( $oldOptions );
2249 } else {
2250 $optionKinds = $this->getResetKinds( $user, $context );
2251 $kinds = array_intersect( $kinds, $this->listResetKinds() );
2252 $optionNames = [];
2253
2254 foreach ( $oldOptions as $key => $value ) {
2255 if ( in_array( $optionKinds[$key], $kinds ) ) {
2256 $optionNames[] = $key;
2257 }
2258 }
2259 return $optionNames;
2260 }
2261 }
2262}
const NS_USER
Definition Defines.php:53
const NS_FILE
Definition Defines.php:57
const NS_MAIN
Definition Defines.php:51
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
Page addition to a user's watchlist.
AuthManager is the authentication system in MediaWiki and serves entry point for authentication.
This is a value object for authentication requests with a username and password.
A class for passing options to services.
assertRequiredOptions(array $expectedKeys)
Assert that the list of options provided in this instance exactly match $expectedKeys,...
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
getContext()
Get the base IContextSource object.
A checkbox matrix Operates similarly to HTMLMultiSelectField, but instead of using an array of option...
An information field (text blob), not a proper input.
The parent class to generate form fields.
Object handling generic submission, CSRF protection, layout and other logic for UI forms in a reusabl...
Definition HTMLForm.php:195
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
This class is a collection of static functions that serve two purposes:
Definition Html.php:43
Methods for dealing with language codes.
Base class for multi-variant language conversion.
Base class for language-specific code.
Definition Language.php:68
Class that generates HTML for internal links.
A class containing constants representing the names of configuration variables.
const HiddenPrefs
Name constant for the HiddenPrefs setting, for use with Config::get()
const ForceHTTPS
Name constant for the ForceHTTPS setting, for use with Config::get()
const EnotifWatchlist
Name constant for the EnotifWatchlist setting, for use with Config::get()
const MaxSigChars
Name constant for the MaxSigChars setting, for use with Config::get()
const RCMaxAge
Name constant for the RCMaxAge setting, for use with Config::get()
const DefaultSkin
Name constant for the DefaultSkin setting, for use with Config::get()
const EnotifRevealEditorAddress
Name constant for the EnotifRevealEditorAddress setting, for use with Config::get()
const EnableUserEmail
Name constant for the EnableUserEmail setting, for use with Config::get()
const SkinsPreferred
Name constant for the SkinsPreferred setting, for use with Config::get()
const EnableWatchlistLabels
Name constant for the EnableWatchlistLabels setting, for use with Config::get()
const EnableEditRecovery
Name constant for the EnableEditRecovery setting, for use with Config::get()
const EmailConfirmToEdit
Name constant for the EmailConfirmToEdit setting, for use with Config::get()
const EnableEmail
Name constant for the EnableEmail setting, for use with Config::get()
const LocalTZoffset
Name constant for the LocalTZoffset setting, for use with Config::get()
const RCShowWatchingUsers
Name constant for the RCShowWatchingUsers setting, for use with Config::get()
const WatchlistExpiry
Name constant for the WatchlistExpiry setting, for use with Config::get()
const EnotifUserTalk
Name constant for the EnotifUserTalk setting, for use with Config::get()
const AllowUserJs
Name constant for the AllowUserJs setting, for use with Config::get()
const ImageLimits
Name constant for the ImageLimits setting, for use with Config::get()
const SearchMatchRedirectPreference
Name constant for the SearchMatchRedirectPreference setting, for use with Config::get()
const EnotifMinorEdits
Name constant for the EnotifMinorEdits setting, for use with Config::get()
const ScriptPath
Name constant for the ScriptPath setting, for use with Config::get()
const AllowUserCss
Name constant for the AllowUserCss setting, for use with Config::get()
const ThumbLimits
Name constant for the ThumbLimits setting, for use with Config::get()
const SecureLogin
Name constant for the SecureLogin setting, for use with Config::get()
const LanguageCode
Name constant for the LanguageCode setting, for use with Config::get()
const SignatureValidation
Name constant for the SignatureValidation setting, for use with Config::get()
const AllowUserCssPrefs
Name constant for the AllowUserCssPrefs setting, for use with Config::get()
const RCWatchCategoryMembership
Name constant for the RCWatchCategoryMembership setting, for use with Config::get()
const ThumbnailNamespaces
Name constant for the ThumbnailNamespaces setting, for use with Config::get()
const EmailAuthentication
Name constant for the EmailAuthentication setting, for use with Config::get()
Service locator for MediaWiki core services.
static getInstance()
Returns the global default instance of the top level service locator.
The Message class deals with fetching and processing of interface message into a variety of formats.
Definition Message.php:144
This is one of the Core classes and should be read at least once by any new developers.
Set options of the Parser.
PHP Parser - Processes wiki markup (which uses a more user-friendly syntax, such as "[[link]]" for ma...
Definition Parser.php:135
A service class for checking permissions To obtain an instance, use MediaWikiServices::getInstance()-...
This is the default implementation of PreferencesFactory.
validateSignature( $signature, $alldata, HTMLForm $form)
rcPreferences(User $user, MessageLocalizer $l10n, &$defaultPreferences)
watchlistPreferences(User $user, IContextSource $context, &$defaultPreferences)
listResetKinds()
Return a list of the types of user options currently returned by getResetKinds().
profilePreferences(User $user, IContextSource $context, &$defaultPreferences)
renderingPreferences(User $user, MessageLocalizer $l10n, &$defaultPreferences)
getForm(User $user, IContextSource $context, $formClass=PreferencesFormOOUI::class, array $remove=[])
getOptionNamesForReset(User $user, IContextSource $context, $kinds)
skinPreferences(User $user, IContextSource $context, &$defaultPreferences)
__construct(ServiceOptions $options, Language $contLang, AuthManager $authManager, LinkRenderer $linkRenderer, NamespaceInfo $nsInfo, PermissionManager $permissionManager, ILanguageConverter $languageConverter, LanguageNameUtils $languageNameUtils, HookContainer $hookContainer, UserOptionsLookup $userOptionsLookup, ?LanguageConverterFactory $languageConverterFactory=null, ?ParserFactory $parserFactory=null, ?SkinFactory $skinFactory=null, ?UserGroupManager $userGroupManager=null, ?SignatureValidatorFactory $signatureValidatorFactory=null)
generateSkinOptions(User $user, IContextSource $context, array $validSkinNames)
static simplifyFormDescriptor(array $descriptor)
Simplify form descriptor for validation or something similar.
getResetKinds(User $user, IContextSource $context, $options=null)
editingPreferences(User $user, MessageLocalizer $l10n, &$defaultPreferences)
getOptionFromUser( $name, $info, array $userOptions)
Pull option from a user account.
datetimePreferences(User $user, IContextSource $context, &$defaultPreferences)
cleanSignature( $signature, $alldata, HTMLForm $form)
getSaveBlacklist()
Get the names of preferences that should never be saved (such as 'realname' and 'emailaddress')....
applyFilters(array &$preferences, array $formDescriptor, $verb)
Applies filters to preferences either before or after form usage.
static getPreferenceForField( $name, HTMLFormField $field, array $userOptions)
Get preference values for the 'default' param of html form descriptor, compatible with nested fields.
filesPreferences(IContextSource $context, &$defaultPreferences)
submitForm(array $formData, PreferencesFormOOUI $form, array $formDescriptor)
Save the form data and reload the page.
saveFormData( $formData, PreferencesFormOOUI $form, array $formDescriptor)
Handle the form submission if everything validated properly.
Factory class to create Skin objects.
Parent class for all special pages.
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don't need a full Title object,...
getExtraSuccessRedirectParameters()
Get extra parameters for the query string when redirecting after successful save.
A special page that lists last changes made to the wiki, limited to user-defined list of titles.
static checkStructuredFilterUiEnabled(UserIdentity $user)
Static method to check whether StructuredFilter UI is enabled for the given user.1....
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition Status.php:44
This is a utility class for dealing with namespaces that encodes all the "magic" behaviors of them ba...
Represents a title within MediaWiki.
Definition Title.php:69
Provides access to user options.
A service class to control user options.
Manage user group memberships.
Represents the membership of one user in one user group.
Utility class to parse the TimeCorrection string value.
User class for the MediaWiki software.
Definition User.php:130
isAllowed(string $permission, ?PermissionStatus $status=null)
Checks whether this authority has the given permission in general.
Definition User.php:2150
getRegistration()
Get the timestamp of account creation.
Definition User.php:3087
useRCPatrol()
Check whether to enable recent changes patrol features for this user.
Definition User.php:2158
isAllowedAny(... $permissions)
Checks whether this authority has any of the given permissions in general.Implementations must ensure...
Definition User.php:2141
getEditCount()
Get the user's edit count.
Definition User.php:2077
getRealName()
Get the user's real name.
Definition User.php:1958
getTitleKey()
Get the user's name escaped by underscores.
Definition User.php:1616
getEmailAuthenticationTimestamp()
Get the timestamp of the user's e-mail authentication.
Definition User.php:1874
useNPPatrol()
Check whether to enable new pages patrol features for this user.
Definition User.php:2168
getEmail()
Get the user's e-mail address.
Definition User.php:1861
getName()
Get the user name, or the IP of an anonymous user.
Definition User.php:1524
return[ 'config-schema-inverse'=>['default'=>['ConfigRegistry'=>['main'=> 'MediaWiki\\Config\\GlobalVarConfig::newInstance',], 'Sitename'=> 'MediaWiki', 'Server'=> false, 'CanonicalServer'=> false, 'ServerName'=> false, 'AssumeProxiesUseDefaultProtocolPorts'=> true, 'HttpsPort'=> 443, 'ForceHTTPS'=> false, 'ScriptPath'=> '/wiki', 'UsePathInfo'=> null, 'Script'=> false, 'LoadScript'=> false, 'RestPath'=> false, 'StylePath'=> false, 'LocalStylePath'=> false, 'ExtensionAssetsPath'=> false, 'ExtensionDirectory'=> null, 'StyleDirectory'=> null, 'ArticlePath'=> false, 'UploadPath'=> false, 'ImgAuthPath'=> false, 'ThumbPath'=> false, 'UploadDirectory'=> false, 'FileCacheDirectory'=> false, 'Logo'=> false, 'Logos'=> false, 'Favicon'=> '/favicon.ico', 'AppleTouchIcon'=> false, 'ReferrerPolicy'=> false, 'TmpDirectory'=> false, 'UploadBaseUrl'=> '', 'UploadStashScalerBaseUrl'=> false, 'ActionPaths'=>[], 'MainPageIsDomainRoot'=> false, 'EnableUploads'=> false, 'UploadStashMaxAge'=> 21600, 'EnableAsyncUploads'=> false, 'EnableAsyncUploadsByURL'=> false, 'UploadMaintenance'=> false, 'IllegalFileChars'=> ':\\/\\\\', 'DeletedDirectory'=> false, 'ImgAuthDetails'=> false, 'ImgAuthUrlPathMap'=>[], 'LocalFileRepo'=>['class'=> 'MediaWiki\\FileRepo\\LocalRepo', 'name'=> 'local', 'directory'=> null, 'scriptDirUrl'=> null, 'favicon'=> null, 'url'=> null, 'hashLevels'=> null, 'thumbScriptUrl'=> null, 'transformVia404'=> null, 'deletedDir'=> null, 'deletedHashLevels'=> null, 'updateCompatibleMetadata'=> null, 'reserializeMetadata'=> null,], 'ForeignFileRepos'=>[], 'UseInstantCommons'=> false, 'UseSharedUploads'=> false, 'SharedUploadDirectory'=> null, 'SharedUploadPath'=> null, 'HashedSharedUploadDirectory'=> true, 'RepositoryBaseUrl'=> 'https:'FetchCommonsDescriptions'=> false, 'SharedUploadDBname'=> false, 'SharedUploadDBprefix'=> '', 'CacheSharedUploads'=> true, 'ForeignUploadTargets'=>['local',], 'UploadDialog'=>['fields'=>['description'=> true, 'date'=> false, 'categories'=> false,], 'licensemessages'=>['local'=> 'generic-local', 'foreign'=> 'generic-foreign',], 'comment'=>['local'=> '', 'foreign'=> '',], 'format'=>['filepage'=> ' $DESCRIPTION', 'description'=> ' $TEXT', 'ownwork'=> '', 'license'=> '', 'uncategorized'=> '',],], 'FileBackends'=>[], 'LockManagers'=>[], 'ShowEXIF'=> null, 'UpdateCompatibleMetadata'=> false, 'AllowCopyUploads'=> false, 'CopyUploadsDomains'=>[], 'CopyUploadsFromSpecialUpload'=> false, 'CopyUploadProxy'=> false, 'CopyUploadTimeout'=> false, 'CopyUploadAllowOnWikiDomainConfig'=> false, 'MaxUploadSize'=> 104857600, 'MinUploadChunkSize'=> 1024, 'UploadNavigationUrl'=> false, 'UploadMissingFileUrl'=> false, 'ThumbnailScriptPath'=> false, 'SharedThumbnailScriptPath'=> false, 'HashedUploadDirectory'=> true, 'CSPUploadEntryPoint'=> true, 'FileExtensions'=>['png', 'gif', 'jpg', 'jpeg', 'webp',], 'ProhibitedFileExtensions'=>['html', 'htm', 'js', 'jsb', 'mhtml', 'mht', 'xhtml', 'xht', 'php', 'phtml', 'php3', 'php4', 'php5', 'phps', 'phar', 'shtml', 'jhtml', 'pl', 'py', 'cgi', 'exe', 'scr', 'dll', 'msi', 'vbs', 'bat', 'com', 'pif', 'cmd', 'vxd', 'cpl', 'xml',], 'MimeTypeExclusions'=>['text/html', 'application/javascript', 'text/javascript', 'text/x-javascript', 'application/x-shellscript', 'application/x-php', 'text/x-php', 'text/x-python', 'text/x-perl', 'text/x-bash', 'text/x-sh', 'text/x-csh', 'text/scriptlet', 'application/x-msdownload', 'application/x-msmetafile', 'application/java', 'application/xml', 'text/xml',], 'CheckFileExtensions'=> true, 'StrictFileExtensions'=> true, 'DisableUploadScriptChecks'=> false, 'UploadSizeWarning'=> false, 'TrustedMediaFormats'=>['BITMAP', 'AUDIO', 'VIDEO', 'image/svg+xml', 'application/pdf',], 'MediaHandlers'=>[], 'NativeImageLazyLoading'=> false, 'ParserTestMediaHandlers'=>['image/jpeg'=> 'MockBitmapHandler', 'image/png'=> 'MockBitmapHandler', 'image/gif'=> 'MockBitmapHandler', 'image/tiff'=> 'MockBitmapHandler', 'image/webp'=> 'MockBitmapHandler', 'image/x-ms-bmp'=> 'MockBitmapHandler', 'image/x-bmp'=> 'MockBitmapHandler', 'image/x-xcf'=> 'MockBitmapHandler', 'image/svg+xml'=> 'MockSvgHandler', 'image/vnd.djvu'=> 'MockDjVuHandler',], 'UseImageResize'=> true, 'UseImageMagick'=> false, 'ImageMagickConvertCommand'=> '/usr/bin/convert', 'MaxInterlacingAreas'=>[], 'SharpenParameter'=> '0x0.4', 'SharpenReductionThreshold'=> 0.85, 'ImageMagickTempDir'=> false, 'CustomConvertCommand'=> false, 'JpegTran'=> '/usr/bin/jpegtran', 'JpegPixelFormat'=> 'yuv420', 'JpegQuality'=> 80, 'Exiv2Command'=> '/usr/bin/exiv2', 'Exiftool'=> '/usr/bin/exiftool', 'SVGConverters'=>['ImageMagick'=> ' $path/convert -background "#ffffff00" -thumbnail $widthx$height\\! $input PNG:$output', 'inkscape'=> ' $path/inkscape -w $width -o $output $input', 'batik'=> 'java -Djava.awt.headless=true -jar $path/batik-rasterizer.jar -w $width -d $output $input', 'rsvg'=> ' $path/rsvg-convert -w $width -h $height -o $output $input', 'ImagickExt'=>['SvgHandler::rasterizeImagickExt',],], 'SVGConverter'=> 'ImageMagick', 'SVGConverterPath'=> '', 'SVGMaxSize'=> 5120, 'SVGMetadataCutoff'=> 5242880, 'SVGNativeRendering'=> true, 'SVGNativeRenderingSizeLimit'=> 51200, 'MediaInTargetLanguage'=> true, 'MaxImageArea'=> 12500000, 'MaxAnimatedGifArea'=> 12500000, 'TiffThumbnailType'=>[], 'ThumbnailEpoch'=> '20030516000000', 'AttemptFailureEpoch'=> 1, 'IgnoreImageErrors'=> false, 'GenerateThumbnailOnParse'=> true, 'ShowArchiveThumbnails'=> true, 'EnableAutoRotation'=> null, 'Antivirus'=> null, 'AntivirusSetup'=>['clamav'=>['command'=> 'clamscan --no-summary ', 'codemap'=>[0=> 0, 1=> 1, 52=> -1, ' *'=> false,], 'messagepattern'=> '/.*?:(.*)/sim',],], 'AntivirusRequired'=> true, 'VerifyMimeType'=> true, 'MimeTypeFile'=> 'internal', 'MimeInfoFile'=> 'internal', 'MimeDetectorCommand'=> null, 'TrivialMimeDetection'=> false, 'XMLMimeTypes'=>['http:'svg'=> 'image/svg+xml', 'http:'http:'html'=> 'text/html',], 'ImageLimits'=>[[320, 240,], [640, 480,], [800, 600,], [1024, 768,], [1280, 1024,], [2560, 2048,],], 'ThumbLimits'=>[120, 150, 180, 200, 250, 300,], 'ThumbnailNamespaces'=>[6,], 'ThumbnailSteps'=> null, 'ThumbnailStepsRatio'=> null, 'ThumbnailBuckets'=> null, 'ThumbnailMinimumBucketDistance'=> 50, 'UploadThumbnailRenderMap'=>[], 'UploadThumbnailRenderMethod'=> 'jobqueue', 'UploadThumbnailRenderHttpCustomHost'=> false, 'UploadThumbnailRenderHttpCustomDomain'=> false, 'UseTinyRGBForJPGThumbnails'=> false, 'GalleryOptions'=>[], 'ThumbUpright'=> 0.75, 'DirectoryMode'=> 511, 'ResponsiveImages'=> true, 'ImagePreconnect'=> false, 'DjvuUseBoxedCommand'=> false, 'DjvuDump'=> null, 'DjvuRenderer'=> null, 'DjvuTxt'=> null, 'DjvuPostProcessor'=> 'pnmtojpeg', 'DjvuOutputExtension'=> 'jpg', 'EmergencyContact'=> false, 'PasswordSender'=> false, 'NoReplyAddress'=> false, 'EnableEmail'=> true, 'EnableUserEmail'=> true, 'UserEmailUseReplyTo'=> true, 'PasswordReminderResendTime'=> 24, 'NewPasswordExpiry'=> 604800, 'UserEmailConfirmationTokenExpiry'=> 604800, 'UserEmailConfirmationUseHTML'=> false, 'PasswordExpirationDays'=> false, 'PasswordExpireGrace'=> 604800, 'SMTP'=> false, 'AdditionalMailParams'=> null, 'AllowHTMLEmail'=> false, 'EnotifFromEditor'=> false, 'EmailAuthentication'=> true, 'EnotifWatchlist'=> false, 'EnotifUserTalk'=> false, 'EnotifRevealEditorAddress'=> false, 'EnotifMinorEdits'=> true, 'EnotifUseRealName'=> false, 'UsersNotifiedOnAllChanges'=>[], 'DBname'=> 'my_wiki', 'DBmwschema'=> null, 'DBprefix'=> '', 'DBserver'=> 'localhost', 'DBport'=> 5432, 'DBuser'=> 'wikiuser', 'DBpassword'=> '', 'DBtype'=> 'mysql', 'DBssl'=> false, 'DBcompress'=> false, 'DBStrictWarnings'=> false, 'DBadminuser'=> null, 'DBadminpassword'=> null, 'SearchType'=> null, 'SearchTypeAlternatives'=> null, 'DBTableOptions'=> 'ENGINE=InnoDB, DEFAULT CHARSET=binary', 'SQLMode'=> '', 'SQLiteDataDir'=> '', 'SharedDB'=> null, 'SharedPrefix'=> false, 'SharedTables'=>['user', 'user_properties', 'user_autocreate_serial',], 'SharedSchema'=> false, 'DBservers'=> false, 'LBFactoryConf'=>['class'=> 'Wikimedia\\Rdbms\\LBFactorySimple',], 'DataCenterUpdateStickTTL'=> 10, 'DBerrorLog'=> false, 'DBerrorLogTZ'=> false, 'LocalDatabases'=>[], 'DatabaseReplicaLagWarning'=> 10, 'DatabaseReplicaLagCritical'=> 30, 'MaxExecutionTimeForExpensiveQueries'=> 0, 'VirtualDomainsMapping'=>[], 'FileSchemaMigrationStage'=> 3, 'ImageLinksSchemaMigrationStage'=> 769, 'ExternalLinksDomainGaps'=>[], 'ContentHandlers'=>['wikitext'=>['class'=> 'MediaWiki\\Content\\WikitextContentHandler', 'services'=>['TitleFactory', 'ParserFactory', 'GlobalIdGenerator', 'LanguageNameUtils', 'LinkRenderer', 'MagicWordFactory', 'ParsoidParserFactory',],], 'javascript'=>['class'=> 'MediaWiki\\Content\\JavaScriptContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'UserOptionsLookup',],], 'json'=>['class'=> 'MediaWiki\\Content\\JsonContentHandler', 'services'=>['ParsoidParserFactory', 'TitleFactory',],], 'css'=>['class'=> 'MediaWiki\\Content\\CssContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'UserOptionsLookup',],], 'vue'=>['class'=> 'MediaWiki\\Content\\VueContentHandler', 'services'=>['MainConfig', 'ParserFactory',],], 'text'=> 'MediaWiki\\Content\\TextContentHandler', 'unknown'=> 'MediaWiki\\Content\\FallbackContentHandler',], 'NamespaceContentModels'=>[], 'TextModelsToParse'=>['wikitext', 'javascript', 'css',], 'CompressRevisions'=> false, 'ExternalStores'=>[], 'ExternalServers'=>[], 'DefaultExternalStore'=> false, 'RevisionCacheExpiry'=> 604800, 'PageLanguageUseDB'=> false, 'DiffEngine'=> null, 'ExternalDiffEngine'=> false, 'Wikidiff2Options'=>[], 'RequestTimeLimit'=> null, 'TransactionalTimeLimit'=> 120, 'CriticalSectionTimeLimit'=> 180.0, 'MiserMode'=> false, 'DisableQueryPages'=> false, 'QueryCacheLimit'=> 1000, 'WantedPagesThreshold'=> 1, 'AllowSlowParserFunctions'=> false, 'AllowSchemaUpdates'=> true, 'MaxArticleSize'=> 2048, 'MemoryLimit'=> '50M', 'PoolCounterConf'=> null, 'PoolCountClientConf'=>['servers'=>['127.0.0.1',], 'timeout'=> 0.1,], 'MaxUserDBWriteDuration'=> false, 'MaxJobDBWriteDuration'=> false, 'LinkHolderBatchSize'=> 1000, 'MaximumMovedPages'=> 100, 'ForceDeferredUpdatesPreSend'=> false, 'MultiShardSiteStats'=> false, 'CacheDirectory'=> false, 'MainCacheType'=> 0, 'MessageCacheType'=> -1, 'ParserCacheType'=> -1, 'SessionCacheType'=> -1, 'AnonSessionCacheType'=> false, 'LanguageConverterCacheType'=> -1, 'ObjectCaches'=>[0=>['class'=> 'Wikimedia\\ObjectCache\\EmptyBagOStuff', 'reportDupes'=> false,], 1=>['class'=> 'MediaWiki\\ObjectCache\\SqlBagOStuff', 'loggroup'=> 'SQLBagOStuff',], 'memcached-php'=>['class'=> 'Wikimedia\\ObjectCache\\MemcachedPhpBagOStuff', 'loggroup'=> 'memcached',], 'memcached-pecl'=>['class'=> 'Wikimedia\\ObjectCache\\MemcachedPeclBagOStuff', 'loggroup'=> 'memcached',], 'hash'=>['class'=> 'Wikimedia\\ObjectCache\\HashBagOStuff', 'reportDupes'=> false,], 'apc'=>['class'=> 'Wikimedia\\ObjectCache\\APCUBagOStuff', 'reportDupes'=> false,], 'apcu'=>['class'=> 'Wikimedia\\ObjectCache\\APCUBagOStuff', 'reportDupes'=> false,],], 'WANObjectCache'=>[], 'MicroStashType'=> -1, 'MainStash'=> 1, 'ParsoidCacheConfig'=>['StashType'=> null, 'StashDuration'=> 86400, 'WarmParsoidParserCache'=> false,], 'ParsoidSelectiveUpdateSampleRate'=> 0, 'ParserCacheFilterConfig'=>['pcache'=>['default'=>['minCpuTime'=> 0,],], 'parsoid-pcache'=>['default'=>['minCpuTime'=> 0,],], 'postproc-pcache'=>['default'=>['minCpuTime'=> 9223372036854775807,],], 'postproc-parsoid-pcache'=>['default'=>['minCpuTime'=> 9223372036854775807,],],], 'ChronologyProtectorSecret'=> '', 'ParserCacheExpireTime'=> 86400, 'ParserCacheAsyncExpireTime'=> 60, 'ParserCacheAsyncRefreshJobs'=> true, 'OldRevisionParserCacheExpireTime'=> 3600, 'ObjectCacheSessionExpiry'=> 3600, 'PHPSessionHandling'=> 'warn', 'SuspiciousIpExpiry'=> false, 'SessionPbkdf2Iterations'=> 10001, 'UseSessionCookieJwt'=> false, 'MemCachedServers'=>['127.0.0.1:11211',], 'MemCachedPersistent'=> false, 'MemCachedTimeout'=> 500000, 'UseLocalMessageCache'=> false, 'AdaptiveMessageCache'=> false, 'LocalisationCacheConf'=>['class'=> 'MediaWiki\\Language\\LocalisationCache', 'store'=> 'detect', 'storeClass'=> false, 'storeDirectory'=> false, 'storeServer'=>[], 'forceRecache'=> false, 'manualRecache'=> false,], 'CachePages'=> true, 'CacheEpoch'=> '20030516000000', 'GitInfoCacheDirectory'=> false, 'UseFileCache'=> false, 'FileCacheDepth'=> 2, 'RenderHashAppend'=> '', 'EnableSidebarCache'=> false, 'SidebarCacheExpiry'=> 86400, 'UseGzip'=> false, 'InvalidateCacheOnLocalSettingsChange'=> true, 'ExtensionInfoMTime'=> false, 'EnableRemoteBagOStuffTests'=> false, 'UseCdn'=> false, 'VaryOnXFP'=> false, 'InternalServer'=> false, 'CdnMaxAge'=> 18000, 'CdnMaxageLagged'=> 30, 'CdnMaxageStale'=> 10, 'CdnReboundPurgeDelay'=> 0, 'CdnMaxageSubstitute'=> 60, 'ForcedRawSMaxage'=> 300, 'CdnServers'=>[], 'CdnServersNoPurge'=>[], 'HTCPRouting'=>[], 'HTCPMulticastTTL'=> 1, 'UsePrivateIPs'=> false, 'CdnMatchParameterOrder'=> true, 'LanguageCode'=> 'en', 'GrammarForms'=>[], 'InterwikiMagic'=> true, 'HideInterlanguageLinks'=> false, 'ExtraInterlanguageLinkPrefixes'=>[], 'InterlanguageLinkCodeMap'=>[], 'ExtraLanguageNames'=>[], 'ExtraLanguageCodes'=>['bh'=> 'bho', 'no'=> 'nb', 'simple'=> 'en',], 'DummyLanguageCodes'=>[], 'AllUnicodeFixes'=> false, 'LegacyEncoding'=> false, 'AmericanDates'=> false, 'TranslateNumerals'=> true, 'UseDatabaseMessages'=> true, 'MaxMsgCacheEntrySize'=> 10000, 'DisableLangConversion'=> false, 'DisableTitleConversion'=> false, 'DefaultLanguageVariant'=> false, 'UsePigLatinVariant'=> false, 'DisabledVariants'=>[], 'VariantArticlePath'=> false, 'UseXssLanguage'=> false, 'LoginLanguageSelector'=> false, 'ForceUIMsgAsContentMsg'=>[], 'RawHtmlMessages'=>[], 'Localtimezone'=> null, 'LocalTZoffset'=> null, 'OverrideUcfirstCharacters'=>[], 'MimeType'=> 'text/html', 'Html5Version'=> null, 'EditSubmitButtonLabelPublish'=> false, 'XhtmlNamespaces'=>[], 'SiteNotice'=> '', 'BrowserFormatDetection'=> 'telephone=no', 'SkinMetaTags'=>[], 'DefaultSkin'=> 'vector-2022', 'FallbackSkin'=> 'fallback', 'SkipSkins'=>[], 'DisableOutputCompression'=> false, 'FragmentMode'=>['html5', 'legacy',], 'ExternalInterwikiFragmentMode'=> 'legacy', 'FooterIcons'=>['copyright'=>['copyright'=>[],], 'poweredby'=>['mediawiki'=>['src'=> null, 'url'=> 'https:'alt'=> 'Powered by MediaWiki', 'lang'=> 'en',],],], 'UseCombinedLoginLink'=> false, 'Edititis'=> false, 'Send404Code'=> true, 'ShowRollbackEditCount'=> 10, 'EnableCanonicalServerLink'=> false, 'InterwikiLogoOverride'=>[], 'ResourceModules'=>[], 'ResourceModuleSkinStyles'=>[], 'ResourceLoaderSources'=>[], 'ResourceBasePath'=> null, 'ResourceLoaderMaxage'=>[], 'ResourceLoaderDebug'=> false, 'ResourceLoaderMaxQueryLength'=> false, 'ResourceLoaderValidateJS'=> true, 'ResourceLoaderEnableJSProfiler'=> false, 'ResourceLoaderStorageEnabled'=> true, 'ResourceLoaderStorageVersion'=> 1, 'ResourceLoaderEnableSourceMapLinks'=> true, 'AllowSiteCSSOnRestrictedPages'=> false, 'VueDevelopmentMode'=> false, 'CodexDevelopmentDir'=> null, 'MetaNamespace'=> false, 'MetaNamespaceTalk'=> false, 'CanonicalNamespaceNames'=>[-2=> 'Media', -1=> 'Special', 0=> '', 1=> 'Talk', 2=> 'User', 3=> 'User_talk', 4=> 'Project', 5=> 'Project_talk', 6=> 'File', 7=> 'File_talk', 8=> 'MediaWiki', 9=> 'MediaWiki_talk', 10=> 'Template', 11=> 'Template_talk', 12=> 'Help', 13=> 'Help_talk', 14=> 'Category', 15=> 'Category_talk',], 'ExtraNamespaces'=>[], 'ExtraGenderNamespaces'=>[], 'NamespaceAliases'=>[], 'LegalTitleChars'=> ' %!"$&\'()*,\\-.\\/0-9:;=?@A-Z\\\\^_`a-z~\\x80-\\xFF+', 'CapitalLinks' => true, 'CapitalLinkOverrides' => [ ], 'NamespacesWithSubpages' => [ 1 => true, 2 => true, 3 => true, 4 => true, 5 => true, 7 => true, 8 => true, 9 => true, 10 => true, 11 => true, 12 => true, 13 => true, 15 => true, ], 'ContentNamespaces' => [ 0, ], 'ShortPagesNamespaceExclusions' => [ ], 'ExtraSignatureNamespaces' => [ ], 'InvalidRedirectTargets' => [ 'Filepath', 'Mypage', 'Mytalk', 'Redirect', 'Mylog', ], 'DisableHardRedirects' => false, 'FixDoubleRedirects' => false, 'LocalInterwikis' => [ ], 'InterwikiExpiry' => 10800, 'InterwikiCache' => false, 'InterwikiScopes' => 3, 'InterwikiFallbackSite' => 'wiki', 'RedirectSources' => false, 'SiteTypes' => [ 'mediawiki' => 'MediaWiki\\Site\\MediaWikiSite', ], 'MaxTocLevel' => 999, 'MaxPPNodeCount' => 1000000, 'MaxTemplateDepth' => 100, 'MaxPPExpandDepth' => 100, 'UrlProtocols' => [ 'bitcoin:', 'ftp: 'ftps: 'geo:', 'git: 'gopher: 'http: 'https: 'irc: 'ircs: 'magnet:', 'mailto:', 'matrix:', 'mms: 'news:', 'nntp: 'redis: 'sftp: 'sip:', 'sips:', 'sms:', 'ssh: 'svn: 'tel:', 'telnet: 'urn:', 'wikipedia: 'worldwind: 'xmpp:', ' ], 'CleanSignatures' => true, 'AllowExternalImages' => false, 'AllowExternalImagesFrom' => '', 'EnableImageWhitelist' => false, 'TidyConfig' => [ ], 'ParsoidSettings' => [ 'useSelser' => true, ], 'ParsoidExperimentalParserFunctionOutput' => false, 'UseLegacyMediaStyles' => false, 'RawHtml' => false, 'ExternalLinkTarget' => false, 'NoFollowLinks' => true, 'NoFollowNsExceptions' => [ ], 'NoFollowDomainExceptions' => [ 'mediawiki.org', ], 'RegisterInternalExternals' => false, 'ExternalLinksIgnoreDomains' => [ ], 'AllowDisplayTitle' => true, 'RestrictDisplayTitle' => true, 'ExpensiveParserFunctionLimit' => 100, 'PreprocessorCacheThreshold' => 1000, 'EnableScaryTranscluding' => false, 'TranscludeCacheExpiry' => 3600, 'EnableMagicLinks' => [ 'ISBN' => false, 'PMID' => false, 'RFC' => false, ], 'ParserEnableUserLanguage' => false, 'ArticleCountMethod' => 'link', 'ActiveUserDays' => 30, 'LearnerEdits' => 10, 'LearnerMemberSince' => 4, 'ExperiencedUserEdits' => 500, 'ExperiencedUserMemberSince' => 30, 'ManualRevertSearchRadius' => 15, 'RevertedTagMaxDepth' => 15, 'CentralIdLookupProviders' => [ 'local' => [ 'class' => 'MediaWiki\\User\\CentralId\\LocalIdLookup', 'services' => [ 'MainConfig', 'DBLoadBalancerFactory', 'HideUserUtils', ], ], ], 'CentralIdLookupProvider' => 'local', 'UserRegistrationProviders' => [ 'local' => [ 'class' => 'MediaWiki\\User\\Registration\\LocalUserRegistrationProvider', 'services' => [ 'ConnectionProvider', ], ], ], 'PasswordPolicy' => [ 'policies' => [ 'bureaucrat' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'sysop' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'interface-admin' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'bot' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'default' => [ 'MinimalPasswordLength' => [ 'value' => 8, 'suggestChangeOnLogin' => true, ], 'PasswordCannotBeSubstringInUsername' => [ 'value' => true, 'suggestChangeOnLogin' => true, ], 'PasswordCannotMatchDefaults' => [ 'value' => true, 'suggestChangeOnLogin' => true, ], 'MaximalPasswordLength' => [ 'value' => 4096, 'suggestChangeOnLogin' => true, ], 'PasswordNotInCommonList' => [ 'value' => true, 'suggestChangeOnLogin' => true, ], ], ], 'checks' => [ 'MinimalPasswordLength' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkMinimalPasswordLength', ], 'MinimumPasswordLengthToLogin' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkMinimumPasswordLengthToLogin', ], 'PasswordCannotBeSubstringInUsername' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkPasswordCannotBeSubstringInUsername', ], 'PasswordCannotMatchDefaults' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkPasswordCannotMatchDefaults', ], 'MaximalPasswordLength' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkMaximalPasswordLength', ], 'PasswordNotInCommonList' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkPasswordNotInCommonList', ], ], ], 'AuthManagerConfig' => null, 'AuthManagerAutoConfig' => [ 'preauth' => [ 'MediaWiki\\Auth\\ThrottlePreAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\ThrottlePreAuthenticationProvider', 'sort' => 0, ], ], 'primaryauth' => [ 'MediaWiki\\Auth\\TemporaryPasswordPrimaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\TemporaryPasswordPrimaryAuthenticationProvider', 'services' => [ 'DBLoadBalancerFactory', 'UserOptionsLookup', ], 'args' => [ [ 'authoritative' => false, ], ], 'sort' => 0, ], 'MediaWiki\\Auth\\LocalPasswordPrimaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\LocalPasswordPrimaryAuthenticationProvider', 'services' => [ 'DBLoadBalancerFactory', ], 'args' => [ [ 'authoritative' => true, ], ], 'sort' => 100, ], ], 'secondaryauth' => [ 'MediaWiki\\Auth\\CheckBlocksSecondaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\CheckBlocksSecondaryAuthenticationProvider', 'sort' => 0, ], 'MediaWiki\\Auth\\ResetPasswordSecondaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\ResetPasswordSecondaryAuthenticationProvider', 'sort' => 100, ], 'MediaWiki\\Auth\\EmailNotificationSecondaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\EmailNotificationSecondaryAuthenticationProvider', 'services' => [ 'DBLoadBalancerFactory', ], 'sort' => 200, ], ], ], 'RememberMe' => 'choose', 'ReauthenticateTime' => [ 'default' => 3600, ], 'AllowSecuritySensitiveOperationIfCannotReauthenticate' => [ 'default' => true, ], 'ChangeCredentialsBlacklist' => [ 'MediaWiki\\Auth\\TemporaryPasswordAuthenticationRequest', ], 'RemoveCredentialsBlacklist' => [ 'MediaWiki\\Auth\\PasswordAuthenticationRequest', ], 'InvalidPasswordReset' => true, 'PasswordDefault' => 'pbkdf2', 'PasswordConfig' => [ 'A' => [ 'class' => 'MediaWiki\\Password\\MWOldPassword', ], 'B' => [ 'class' => 'MediaWiki\\Password\\MWSaltedPassword', ], 'pbkdf2-legacyA' => [ 'class' => 'MediaWiki\\Password\\LayeredParameterizedPassword', 'types' => [ 'A', 'pbkdf2', ], ], 'pbkdf2-legacyB' => [ 'class' => 'MediaWiki\\Password\\LayeredParameterizedPassword', 'types' => [ 'B', 'pbkdf2', ], ], 'bcrypt' => [ 'class' => 'MediaWiki\\Password\\BcryptPassword', 'cost' => 9, ], 'pbkdf2' => [ 'class' => 'MediaWiki\\Password\\Pbkdf2PasswordUsingOpenSSL', 'algo' => 'sha512', 'cost' => '30000', 'length' => '64', ], 'argon2' => [ 'class' => 'MediaWiki\\Password\\Argon2Password', 'algo' => 'auto', ], ], 'PasswordResetRoutes' => [ 'username' => true, 'email' => true, ], 'MaxSigChars' => 255, 'SignatureValidation' => 'warning', 'SignatureAllowedLintErrors' => [ 'obsolete-tag', ], 'MaxNameChars' => 255, 'ReservedUsernames' => [ 'MediaWiki default', 'Conversion script', 'Maintenance script', 'Template namespace initialisation script', 'ScriptImporter', 'Delete page script', 'Move page script', 'Command line script', 'Unknown user', 'msg:double-redirect-fixer', 'msg:usermessage-editor', 'msg:proxyblocker', 'msg:sorbs', 'msg:spambot_username', 'msg:autochange-username', ], 'DefaultUserOptions' => [ 'ccmeonemails' => 0, 'date' => 'default', 'diffonly' => 0, 'diff-type' => 'table', 'disablemail' => 0, 'editfont' => 'monospace', 'editondblclick' => 0, 'editrecovery' => 0, 'editsectiononrightclick' => 0, 'email-allow-new-users' => 1, 'enotifminoredits' => 0, 'enotifrevealaddr' => 0, 'enotifusertalkpages' => 1, 'enotifwatchlistpages' => 1, 'extendwatchlist' => 1, 'fancysig' => 0, 'forceeditsummary' => 0, 'forcesafemode' => 0, 'gender' => 'unknown', 'hidecategorization' => 1, 'hideminor' => 0, 'hidepatrolled' => 0, 'imagesize' => 2, 'minordefault' => 0, 'newpageshidepatrolled' => 0, 'nickname' => '', 'norollbackdiff' => 0, 'prefershttps' => 1, 'previewonfirst' => 0, 'previewontop' => 1, 'pst-cssjs' => 1, 'rcdays' => 7, 'rcenhancedfilters-disable' => 0, 'rclimit' => 50, 'requireemail' => 0, 'search-match-redirect' => true, 'search-special-page' => 'Search', 'search-thumbnail-extra-namespaces' => true, 'searchlimit' => 20, 'showhiddencats' => 0, 'shownumberswatching' => 1, 'showrollbackconfirmation' => 0, 'skin' => false, 'skin-responsive' => 1, 'thumbsize' => 5, 'underline' => 2, 'useeditwarning' => 1, 'uselivepreview' => 0, 'usenewrc' => 1, 'watchcreations' => 1, 'watchcreations-expiry' => 'infinite', 'watchdefault' => 1, 'watchdefault-expiry' => 'infinite', 'watchdeletion' => 0, 'watchlistdays' => 7, 'watchlisthideanons' => 0, 'watchlisthidebots' => 0, 'watchlisthidecategorization' => 1, 'watchlisthideliu' => 0, 'watchlisthideminor' => 0, 'watchlisthideown' => 0, 'watchlisthidepatrolled' => 0, 'watchlistreloadautomatically' => 0, 'watchlistunwatchlinks' => 0, 'watchmoves' => 0, 'watchrollback' => 0, 'watchuploads' => 1, 'watchrollback-expiry' => 'infinite', 'watchstar-expiry' => 'infinite', 'wlenhancedfilters-disable' => 0, 'wllimit' => 250, ], 'ConditionalUserOptions' => [ ], 'HiddenPrefs' => [ ], 'UserJsPrefLimit' => 100, 'InvalidUsernameCharacters' => '@:>=', 'UserrightsInterwikiDelimiter' => '@', 'SecureLogin' => false, 'AuthenticationTokenVersion' => null, 'SessionProviders' => [ 'MediaWiki\\Session\\CookieSessionProvider' => [ 'class' => 'MediaWiki\\Session\\CookieSessionProvider', 'args' => [ [ 'priority' => 30, ], ], 'services' => [ 'JwtCodec', 'UrlUtils', ], ], 'MediaWiki\\Session\\BotPasswordSessionProvider' => [ 'class' => 'MediaWiki\\Session\\BotPasswordSessionProvider', 'args' => [ [ 'priority' => 75, ], ], 'services' => [ 'GrantsInfo', ], ], ], 'AutoCreateTempUser' => [ 'known' => false, 'enabled' => false, 'actions' => [ 'edit', ], 'genPattern' => '~$1', 'matchPattern' => null, 'reservedPattern' => '~$1', 'serialProvider' => [ 'type' => 'local', 'useYear' => true, ], 'serialMapping' => [ 'type' => 'readable-numeric', ], 'expireAfterDays' => 90, 'notifyBeforeExpirationDays' => 10, ], 'AutoblockExemptions' => [ ], 'AutoblockExpiry' => 86400, 'BlockAllowsUTEdit' => true, 'BlockCIDRLimit' => [ 'IPv4' => 16, 'IPv6' => 19, ], 'BlockDisablesLogin' => false, 'EnableMultiBlocks' => false, 'WhitelistRead' => false, 'WhitelistReadRegexp' => false, 'EmailConfirmToEdit' => false, 'HideIdentifiableRedirects' => true, 'GroupPermissions' => [ '*' => [ 'createaccount' => true, 'read' => true, 'edit' => true, 'createpage' => true, 'createtalk' => true, 'viewmyprivateinfo' => true, 'editmyprivateinfo' => true, 'editmyoptions' => true, ], 'user' => [ 'move' => true, 'move-subpages' => true, 'move-rootuserpages' => true, 'move-categorypages' => true, 'movefile' => true, 'read' => true, 'edit' => true, 'createpage' => true, 'createtalk' => true, 'upload' => true, 'reupload' => true, 'reupload-shared' => true, 'minoredit' => true, 'editmyusercss' => true, 'editmyuserjson' => true, 'editmyuserjs' => true, 'editmyuserjsredirect' => true, 'sendemail' => true, 'applychangetags' => true, 'changetags' => true, 'viewmywatchlist' => true, 'editmywatchlist' => true, ], 'autoconfirmed' => [ 'autoconfirmed' => true, 'editsemiprotected' => true, ], 'bot' => [ 'bot' => true, 'autoconfirmed' => true, 'editsemiprotected' => true, 'nominornewtalk' => true, 'autopatrol' => true, 'suppressredirect' => true, 'apihighlimits' => true, ], 'sysop' => [ 'block' => true, 'createaccount' => true, 'delete' => true, 'bigdelete' => true, 'deletedhistory' => true, 'deletedtext' => true, 'undelete' => true, 'editcontentmodel' => true, 'editinterface' => true, 'editsitejson' => true, 'edituserjson' => true, 'import' => true, 'importupload' => true, 'move' => true, 'move-subpages' => true, 'move-rootuserpages' => true, 'move-categorypages' => true, 'patrol' => true, 'autopatrol' => true, 'protect' => true, 'editprotected' => true, 'rollback' => true, 'upload' => true, 'reupload' => true, 'reupload-shared' => true, 'unwatchedpages' => true, 'autoconfirmed' => true, 'editsemiprotected' => true, 'ipblock-exempt' => true, 'blockemail' => true, 'markbotedits' => true, 'apihighlimits' => true, 'browsearchive' => true, 'noratelimit' => true, 'movefile' => true, 'unblockself' => true, 'suppressredirect' => true, 'mergehistory' => true, 'managechangetags' => true, 'deletechangetags' => true, ], 'interface-admin' => [ 'editinterface' => true, 'editsitecss' => true, 'editsitejson' => true, 'editsitejs' => true, 'editusercss' => true, 'edituserjson' => true, 'edituserjs' => true, ], 'bureaucrat' => [ 'userrights' => true, 'noratelimit' => true, 'renameuser' => true, ], 'suppress' => [ 'hideuser' => true, 'suppressrevision' => true, 'viewsuppressed' => true, 'suppressionlog' => true, 'deleterevision' => true, 'deletelogentry' => true, ], ], 'PrivilegedGroups' => [ 'bureaucrat', 'interface-admin', 'suppress', 'sysop', ], 'RevokePermissions' => [ ], 'GroupInheritsPermissions' => [ ], 'ImplicitGroups' => [ '*', 'user', 'autoconfirmed', ], 'GroupsAddToSelf' => [ ], 'GroupsRemoveFromSelf' => [ ], 'RestrictedGroups' => [ ], 'UserRequirementsPrivateConditions' => [ ], 'RestrictionTypes' => [ 'create', 'edit', 'move', 'upload', ], 'RestrictionLevels' => [ '', 'autoconfirmed', 'sysop', ], 'CascadingRestrictionLevels' => [ 'sysop', ], 'SemiprotectedRestrictionLevels' => [ 'autoconfirmed', ], 'NamespaceProtection' => [ ], 'NonincludableNamespaces' => [ ], 'AutoConfirmAge' => 0, 'AutoConfirmCount' => 0, 'Autopromote' => [ 'autoconfirmed' => [ '&', [ 1, null, ], [ 2, null, ], ], ], 'AutopromoteOnce' => [ 'onEdit' => [ ], ], 'AutopromoteOnceLogInRC' => true, 'AutopromoteOnceRCExcludedGroups' => [ ], 'AddGroups' => [ ], 'RemoveGroups' => [ ], 'AvailableRights' => [ ], 'ImplicitRights' => [ ], 'DeleteRevisionsLimit' => 0, 'DeleteRevisionsBatchSize' => 1000, 'HideUserContribLimit' => 1000, 'AccountCreationThrottle' => [ [ 'count' => 0, 'seconds' => 86400, ], ], 'TempAccountCreationThrottle' => [ [ 'count' => 1, 'seconds' => 600, ], [ 'count' => 6, 'seconds' => 86400, ], ], 'TempAccountNameAcquisitionThrottle' => [ [ 'count' => 60, 'seconds' => 86400, ], ], 'SpamRegex' => [ ], 'SummarySpamRegex' => [ ], 'EnableDnsBlacklist' => false, 'DnsBlacklistUrls' => [ ], 'ProxyList' => [ ], 'ProxyWhitelist' => [ ], 'SoftBlockRanges' => [ ], 'ApplyIpBlocksToXff' => false, 'RateLimits' => [ 'edit' => [ 'ip' => [ 8, 60, ], 'newbie' => [ 8, 60, ], 'user' => [ 90, 60, ], ], 'move' => [ 'newbie' => [ 2, 120, ], 'user' => [ 8, 60, ], ], 'upload' => [ 'ip' => [ 8, 60, ], 'newbie' => [ 8, 60, ], ], 'rollback' => [ 'user' => [ 10, 60, ], 'newbie' => [ 5, 120, ], ], 'mailpassword' => [ 'ip' => [ 5, 3600, ], ], 'sendemail' => [ 'ip' => [ 5, 86400, ], 'newbie' => [ 5, 86400, ], 'user' => [ 20, 86400, ], ], 'changeemail' => [ 'ip-all' => [ 10, 3600, ], 'user' => [ 4, 86400, ], ], 'confirmemail' => [ 'ip-all' => [ 10, 3600, ], 'user' => [ 4, 86400, ], ], 'purge' => [ 'ip' => [ 30, 60, ], 'user' => [ 30, 60, ], ], 'linkpurge' => [ 'ip' => [ 30, 60, ], 'user' => [ 30, 60, ], ], 'renderfile' => [ 'ip' => [ 700, 30, ], 'user' => [ 700, 30, ], ], 'renderfile-nonstandard' => [ 'ip' => [ 70, 30, ], 'user' => [ 70, 30, ], ], 'stashedit' => [ 'ip' => [ 30, 60, ], 'newbie' => [ 30, 60, ], ], 'stashbasehtml' => [ 'ip' => [ 5, 60, ], 'newbie' => [ 5, 60, ], ], 'changetags' => [ 'ip' => [ 8, 60, ], 'newbie' => [ 8, 60, ], ], 'editcontentmodel' => [ 'newbie' => [ 2, 120, ], 'user' => [ 8, 60, ], ], ], 'RateLimitsExcludedIPs' => [ ], 'PutIPinRC' => true, 'QueryPageDefaultLimit' => 50, 'ExternalQuerySources' => [ ], 'PasswordAttemptThrottle' => [ [ 'count' => 5, 'seconds' => 300, ], [ 'count' => 150, 'seconds' => 172800, ], ], 'GrantPermissions' => [ 'basic' => [ 'autocreateaccount' => true, 'autoconfirmed' => true, 'autopatrol' => true, 'editsemiprotected' => true, 'ipblock-exempt' => true, 'nominornewtalk' => true, 'patrolmarks' => true, 'read' => true, 'unwatchedpages' => true, ], 'highvolume' => [ 'bot' => true, 'apihighlimits' => true, 'noratelimit' => true, 'markbotedits' => true, ], 'import' => [ 'import' => true, 'importupload' => true, ], 'editpage' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'pagelang' => true, ], 'editprotected' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'editprotected' => true, ], 'editmycssjs' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'editmyusercss' => true, 'editmyuserjson' => true, 'editmyuserjs' => true, ], 'editmyoptions' => [ 'editmyoptions' => true, 'editmyuserjson' => true, ], 'editinterface' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'editinterface' => true, 'edituserjson' => true, 'editsitejson' => true, ], 'editsiteconfig' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'editinterface' => true, 'edituserjson' => true, 'editsitejson' => true, 'editusercss' => true, 'edituserjs' => true, 'editsitecss' => true, 'editsitejs' => true, ], 'createeditmovepage' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createpage' => true, 'createtalk' => true, 'delete-redirect' => true, 'move' => true, 'move-rootuserpages' => true, 'move-subpages' => true, 'move-categorypages' => true, 'suppressredirect' => true, ], 'uploadfile' => [ 'upload' => true, 'reupload-own' => true, ], 'uploadeditmovefile' => [ 'upload' => true, 'reupload-own' => true, 'reupload' => true, 'reupload-shared' => true, 'upload_by_url' => true, 'movefile' => true, 'suppressredirect' => true, ], 'patrol' => [ 'patrol' => true, ], 'rollback' => [ 'rollback' => true, ], 'blockusers' => [ 'block' => true, 'blockemail' => true, ], 'viewdeleted' => [ 'browsearchive' => true, 'deletedhistory' => true, 'deletedtext' => true, ], 'viewrestrictedlogs' => [ 'suppressionlog' => true, ], 'delete' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'browsearchive' => true, 'deletedhistory' => true, 'deletedtext' => true, 'delete' => true, 'bigdelete' => true, 'deletelogentry' => true, 'deleterevision' => true, 'undelete' => true, ], 'oversight' => [ 'suppressrevision' => true, 'viewsuppressed' => true, ], 'protect' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'editprotected' => true, 'protect' => true, ], 'viewmywatchlist' => [ 'viewmywatchlist' => true, ], 'editmywatchlist' => [ 'editmywatchlist' => true, ], 'sendemail' => [ 'sendemail' => true, ], 'createaccount' => [ 'createaccount' => true, ], 'privateinfo' => [ 'viewmyprivateinfo' => true, ], 'mergehistory' => [ 'mergehistory' => true, ], ], 'GrantPermissionGroups' => [ 'basic' => 'hidden', 'editpage' => 'page-interaction', 'createeditmovepage' => 'page-interaction', 'editprotected' => 'page-interaction', 'patrol' => 'page-interaction', 'uploadfile' => 'file-interaction', 'uploadeditmovefile' => 'file-interaction', 'sendemail' => 'email', 'viewmywatchlist' => 'watchlist-interaction', 'editviewmywatchlist' => 'watchlist-interaction', 'editmycssjs' => 'customization', 'editmyoptions' => 'customization', 'editinterface' => 'administration', 'editsiteconfig' => 'administration', 'rollback' => 'administration', 'blockusers' => 'administration', 'delete' => 'administration', 'viewdeleted' => 'administration', 'viewrestrictedlogs' => 'administration', 'protect' => 'administration', 'oversight' => 'administration', 'createaccount' => 'administration', 'mergehistory' => 'administration', 'import' => 'administration', 'highvolume' => 'high-volume', 'privateinfo' => 'private-information', ], 'GrantRiskGroups' => [ 'basic' => 'low', 'editpage' => 'low', 'createeditmovepage' => 'low', 'editprotected' => 'vandalism', 'patrol' => 'low', 'uploadfile' => 'low', 'uploadeditmovefile' => 'low', 'sendemail' => 'security', 'viewmywatchlist' => 'low', 'editviewmywatchlist' => 'low', 'editmycssjs' => 'security', 'editmyoptions' => 'security', 'editinterface' => 'vandalism', 'editsiteconfig' => 'security', 'rollback' => 'low', 'blockusers' => 'vandalism', 'delete' => 'vandalism', 'viewdeleted' => 'vandalism', 'viewrestrictedlogs' => 'security', 'protect' => 'vandalism', 'oversight' => 'security', 'createaccount' => 'low', 'mergehistory' => 'vandalism', 'import' => 'security', 'highvolume' => 'low', 'privateinfo' => 'low', ], 'EnableBotPasswords' => true, 'BotPasswordsCluster' => false, 'BotPasswordsDatabase' => false, 'SecretKey' => false, 'JwtPrivateKey' => false, 'JwtPublicKey' => false, 'AllowUserJs' => false, 'AllowUserCss' => false, 'AllowUserCssPrefs' => true, 'UseSiteJs' => true, 'UseSiteCss' => true, 'BreakFrames' => false, 'EditPageFrameOptions' => 'DENY', 'ApiFrameOptions' => 'DENY', 'CSPHeader' => false, 'CSPReportOnlyHeader' => false, 'CSPFalsePositiveUrls' => [ 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'chrome-extension' => true, ], 'AllowCrossOrigin' => false, 'RestAllowCrossOriginCookieAuth' => false, 'SessionSecret' => false, 'CookieExpiration' => 2592000, 'ExtendedLoginCookieExpiration' => 15552000, 'SessionCookieJwtExpiration' => 14400, 'CookieDomain' => '', 'CookiePath' => '/', 'CookieSecure' => 'detect', 'CookiePrefix' => false, 'CookieHttpOnly' => true, 'CookieSameSite' => null, 'CacheVaryCookies' => [ ], 'SessionName' => false, 'CookieSetOnAutoblock' => true, 'CookieSetOnIpBlock' => true, 'DebugLogFile' => '', 'DebugLogPrefix' => '', 'DebugRedirects' => false, 'DebugRawPage' => false, 'DebugComments' => false, 'DebugDumpSql' => false, 'TrxProfilerLimits' => [ 'GET' => [ 'masterConns' => 0, 'writes' => 0, 'readQueryTime' => 5, 'readQueryRows' => 10000, ], 'POST' => [ 'readQueryTime' => 5, 'writeQueryTime' => 1, 'readQueryRows' => 100000, 'maxAffected' => 1000, ], 'POST-nonwrite' => [ 'writes' => 0, 'readQueryTime' => 5, 'readQueryRows' => 10000, ], 'PostSend-GET' => [ 'readQueryTime' => 5, 'writeQueryTime' => 1, 'readQueryRows' => 10000, 'maxAffected' => 1000, 'masterConns' => 0, 'writes' => 0, ], 'PostSend-POST' => [ 'readQueryTime' => 5, 'writeQueryTime' => 1, 'readQueryRows' => 100000, 'maxAffected' => 1000, ], 'JobRunner' => [ 'readQueryTime' => 30, 'writeQueryTime' => 5, 'readQueryRows' => 100000, 'maxAffected' => 500, ], 'Maintenance' => [ 'writeQueryTime' => 5, 'maxAffected' => 1000, ], ], 'DebugLogGroups' => [ ], 'MWLoggerDefaultSpi' => [ 'class' => 'MediaWiki\\Logger\\LegacySpi', ], 'ShowDebug' => false, 'SpecialVersionShowHooks' => false, 'ShowExceptionDetails' => false, 'LogExceptionBacktrace' => true, 'PropagateErrors' => true, 'ShowHostnames' => false, 'OverrideHostname' => false, 'DevelopmentWarnings' => false, 'DeprecationReleaseLimit' => false, 'Profiler' => [ ], 'StatsdServer' => false, 'StatsdMetricPrefix' => 'MediaWiki', 'StatsTarget' => null, 'StatsFormat' => null, 'StatsPrefix' => 'mediawiki', 'OpenTelemetryConfig' => null, 'PageInfoTransclusionLimit' => 50, 'EnableJavaScriptTest' => false, 'CachePrefix' => false, 'DebugToolbar' => false, 'DisableTextSearch' => false, 'AdvancedSearchHighlighting' => false, 'SearchHighlightBoundaries' => '[\\p{Z}\\p{P}\\p{C}]', 'OpenSearchTemplates' => [ 'application/x-suggestions+json' => false, 'application/x-suggestions+xml' => false, ], 'OpenSearchDefaultLimit' => 10, 'OpenSearchDescriptionLength' => 100, 'SearchSuggestCacheExpiry' => 1200, 'DisableSearchUpdate' => false, 'NamespacesToBeSearchedDefault' => [ true, ], 'DisableInternalSearch' => false, 'SearchForwardUrl' => null, 'SitemapNamespaces' => false, 'SitemapNamespacesPriorities' => false, 'SitemapApiConfig' => [ ], 'SpecialSearchFormOptions' => [ ], 'SearchMatchRedirectPreference' => false, 'SearchRunSuggestedQuery' => true, 'Diff3' => '/usr/bin/diff3', 'Diff' => '/usr/bin/diff', 'PreviewOnOpenNamespaces' => [ 14 => true, ], 'UniversalEditButton' => true, 'UseAutomaticEditSummaries' => true, 'CommandLineDarkBg' => false, 'ReadOnly' => null, 'ReadOnlyWatchedItemStore' => false, 'ReadOnlyFile' => false, 'UpgradeKey' => false, 'GitBin' => '/usr/bin/git', 'GitRepositoryViewers' => [ 'https: 'ssh: ], 'InstallerInitialPages' => [ [ 'titlemsg' => 'mainpage', 'text' => '{{subst:int:mainpagetext}}{{subst:int:mainpagedocfooter}}', ], ], 'RCMaxAge' => 7776000, 'WatchersMaxAge' => 15552000, 'UnwatchedPageSecret' => 1, 'RCFilterByAge' => false, 'RCLinkLimits' => [ 50, 100, 250, 500, ], 'RCLinkDays' => [ 1, 3, 7, 14, 30, ], 'RCFeeds' => [ ], 'RCEngines' => [ 'redis' => 'MediaWiki\\RCFeed\\RedisPubSubFeedEngine', 'udp' => 'MediaWiki\\RCFeed\\UDPRCFeedEngine', ], 'RCWatchCategoryMembership' => false, 'UseRCPatrol' => true, 'StructuredChangeFiltersLiveUpdatePollingRate' => 3, 'UseNPPatrol' => true, 'UseFilePatrol' => true, 'Feed' => true, 'FeedLimit' => 50, 'FeedCacheTimeout' => 60, 'FeedDiffCutoff' => 32768, 'OverrideSiteFeed' => [ ], 'FeedClasses' => [ 'rss' => 'MediaWiki\\Feed\\RSSFeed', 'atom' => 'MediaWiki\\Feed\\AtomFeed', ], 'AdvertisedFeedTypes' => [ 'atom', ], 'RCShowWatchingUsers' => false, 'RCShowChangedSize' => true, 'RCChangedSizeThreshold' => 500, 'ShowUpdatedMarker' => true, 'DisableAnonTalk' => false, 'UseTagFilter' => true, 'SoftwareTags' => [ 'mw-contentmodelchange' => true, 'mw-new-redirect' => true, 'mw-removed-redirect' => true, 'mw-changed-redirect-target' => true, 'mw-blank' => true, 'mw-replace' => true, 'mw-recreated' => true, 'mw-rollback' => true, 'mw-undo' => true, 'mw-manual-revert' => true, 'mw-reverted' => true, 'mw-server-side-upload' => true, 'mw-ipblock-appeal' => true, ], 'UnwatchedPageThreshold' => false, 'RecentChangesFlags' => [ 'newpage' => [ 'letter' => 'newpageletter', 'title' => 'recentchanges-label-newpage', 'legend' => 'recentchanges-legend-newpage', 'grouping' => 'any', ], 'minor' => [ 'letter' => 'minoreditletter', 'title' => 'recentchanges-label-minor', 'legend' => 'recentchanges-legend-minor', 'class' => 'minoredit', 'grouping' => 'all', ], 'bot' => [ 'letter' => 'boteditletter', 'title' => 'recentchanges-label-bot', 'legend' => 'recentchanges-legend-bot', 'class' => 'botedit', 'grouping' => 'all', ], 'unpatrolled' => [ 'letter' => 'unpatrolledletter', 'title' => 'recentchanges-label-unpatrolled', 'legend' => 'recentchanges-legend-unpatrolled', 'grouping' => 'any', ], ], 'WatchlistExpiry' => false, 'EnableWatchlistLabels' => false, 'WatchlistLabelsMaxPerUser' => 100, 'WatchlistPurgeRate' => 0.1, 'WatchlistExpiryMaxDuration' => '1 year', 'EnableChangesListQueryPartitioning' => false, 'RightsPage' => null, 'RightsUrl' => null, 'RightsText' => null, 'RightsIcon' => null, 'UseCopyrightUpload' => false, 'MaxCredits' => 0, 'ShowCreditsIfMax' => true, 'ImportSources' => [ ], 'ImportTargetNamespace' => null, 'ExportAllowHistory' => true, 'ExportMaxHistory' => 0, 'ExportAllowListContributors' => false, 'ExportMaxLinkDepth' => 0, 'ExportFromNamespaces' => false, 'ExportAllowAll' => false, 'ExportPagelistLimit' => 5000, 'XmlDumpSchemaVersion' => '0.11', 'WikiFarmSettingsDirectory' => null, 'WikiFarmSettingsExtension' => 'yaml', 'ExtensionFunctions' => [ ], 'ExtensionMessagesFiles' => [ ], 'MessagesDirs' => [ ], 'TranslationAliasesDirs' => [ ], 'ExtensionEntryPointListFiles' => [ ], 'EnableParserLimitReporting' => true, 'ValidSkinNames' => [ ], 'SpecialPages' => [ ], 'ExtensionCredits' => [ ], 'Hooks' => [ ], 'ServiceWiringFiles' => [ ], 'JobClasses' => [ 'deletePage' => 'MediaWiki\\Page\\DeletePageJob', 'refreshLinks' => 'MediaWiki\\JobQueue\\Jobs\\RefreshLinksJob', 'deleteLinks' => 'MediaWiki\\Page\\DeleteLinksJob', 'htmlCacheUpdate' => 'MediaWiki\\JobQueue\\Jobs\\HTMLCacheUpdateJob', 'sendMail' => [ 'class' => 'MediaWiki\\Mail\\EmaillingJob', 'services' => [ 'Emailer', ], ], 'enotifNotify' => [ 'class' => 'MediaWiki\\RecentChanges\\RecentChangeNotifyJob', 'services' => [ 'RecentChangeLookup', ], ], 'fixDoubleRedirect' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\DoubleRedirectJob', 'services' => [ 'RevisionLookup', 'MagicWordFactory', 'WikiPageFactory', ], 'needsPage' => true, ], 'AssembleUploadChunks' => 'MediaWiki\\JobQueue\\Jobs\\AssembleUploadChunksJob', 'PublishStashedFile' => 'MediaWiki\\JobQueue\\Jobs\\PublishStashedFileJob', 'ThumbnailRender' => 'MediaWiki\\JobQueue\\Jobs\\ThumbnailRenderJob', 'UploadFromUrl' => 'MediaWiki\\JobQueue\\Jobs\\UploadFromUrlJob', 'recentChangesUpdate' => 'MediaWiki\\RecentChanges\\RecentChangesUpdateJob', 'refreshLinksPrioritized' => 'MediaWiki\\JobQueue\\Jobs\\RefreshLinksJob', 'refreshLinksDynamic' => 'MediaWiki\\JobQueue\\Jobs\\RefreshLinksJob', 'activityUpdateJob' => 'MediaWiki\\Watchlist\\ActivityUpdateJob', 'categoryMembershipChange' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\CategoryMembershipChangeJob', 'services' => [ 'RecentChangeFactory', ], ], 'CategoryCountUpdateJob' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\CategoryCountUpdateJob', 'services' => [ 'ConnectionProvider', 'NamespaceInfo', ], ], 'clearUserWatchlist' => 'MediaWiki\\Watchlist\\ClearUserWatchlistJob', 'watchlistExpiry' => 'MediaWiki\\Watchlist\\WatchlistExpiryJob', 'cdnPurge' => 'MediaWiki\\JobQueue\\Jobs\\CdnPurgeJob', 'userGroupExpiry' => 'MediaWiki\\User\\UserGroupExpiryJob', 'clearWatchlistNotifications' => 'MediaWiki\\Watchlist\\ClearWatchlistNotificationsJob', 'userOptionsUpdate' => 'MediaWiki\\User\\Options\\UserOptionsUpdateJob', 'revertedTagUpdate' => 'MediaWiki\\JobQueue\\Jobs\\RevertedTagUpdateJob', 'null' => 'MediaWiki\\JobQueue\\Jobs\\NullJob', 'userEditCountInit' => 'MediaWiki\\User\\UserEditCountInitJob', 'parsoidCachePrewarm' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\ParsoidCachePrewarmJob', 'services' => [ 'ParserOutputAccess', 'PageStore', 'RevisionLookup', 'ParsoidSiteConfig', ], 'needsPage' => false, ], 'renameUserTable' => [ 'class' => 'MediaWiki\\RenameUser\\Job\\RenameUserTableJob', 'services' => [ 'MainConfig', 'DBLoadBalancerFactory', ], ], 'renameUserDerived' => [ 'class' => 'MediaWiki\\RenameUser\\Job\\RenameUserDerivedJob', 'services' => [ 'RenameUserFactory', 'UserFactory', ], ], 'renameUser' => [ 'class' => 'MediaWiki\\RenameUser\\Job\\RenameUserTableJob', 'services' => [ 'MainConfig', 'DBLoadBalancerFactory', ], ], ], 'JobTypesExcludedFromDefaultQueue' => [ 'AssembleUploadChunks', 'PublishStashedFile', 'UploadFromUrl', ], 'JobBackoffThrottling' => [ ], 'JobTypeConf' => [ 'default' => [ 'class' => 'MediaWiki\\JobQueue\\JobQueueDB', 'order' => 'random', 'claimTTL' => 3600, ], ], 'JobQueueIncludeInMaxLagFactor' => false, 'SpecialPageCacheUpdates' => [ 'Statistics' => [ 'MediaWiki\\Deferred\\SiteStatsUpdate', 'cacheUpdate', ], ], 'PagePropLinkInvalidations' => [ 'hiddencat' => 'categorylinks', ], 'CategoryMagicGallery' => true, 'CategoryPagingLimit' => 200, 'CategoryCollation' => 'uppercase', 'TempCategoryCollations' => [ ], 'SortedCategories' => false, 'TrackingCategories' => [ ], 'LogTypes' => [ '', 'block', 'protect', 'rights', 'delete', 'upload', 'move', 'import', 'interwiki', 'patrol', 'merge', 'suppress', 'tag', 'managetags', 'contentmodel', 'renameuser', ], 'LogRestrictions' => [ 'suppress' => 'suppressionlog', ], 'FilterLogTypes' => [ 'patrol' => true, 'tag' => true, 'newusers' => false, ], 'LogNames' => [ '' => 'all-logs-page', 'block' => 'blocklogpage', 'protect' => 'protectlogpage', 'rights' => 'rightslog', 'delete' => 'dellogpage', 'upload' => 'uploadlogpage', 'move' => 'movelogpage', 'import' => 'importlogpage', 'patrol' => 'patrol-log-page', 'merge' => 'mergelog', 'suppress' => 'suppressionlog', ], 'LogHeaders' => [ '' => 'alllogstext', 'block' => 'blocklogtext', 'delete' => 'dellogpagetext', 'import' => 'importlogpagetext', 'merge' => 'mergelogpagetext', 'move' => 'movelogpagetext', 'patrol' => 'patrol-log-header', 'protect' => 'protectlogtext', 'rights' => 'rightslogtext', 'suppress' => 'suppressionlogtext', 'upload' => 'uploadlogpagetext', ], 'LogActions' => [ ], 'LogActionsHandlers' => [ 'block/block' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'block/reblock' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'block/unblock' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'contentmodel/change' => 'MediaWiki\\Logging\\ContentModelLogFormatter', 'contentmodel/new' => 'MediaWiki\\Logging\\ContentModelLogFormatter', 'delete/delete' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/delete_redir' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/delete_redir2' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/event' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/restore' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/revision' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'import/interwiki' => 'MediaWiki\\Logging\\ImportLogFormatter', 'import/upload' => 'MediaWiki\\Logging\\ImportLogFormatter', 'interwiki/iw_add' => 'MediaWiki\\Logging\\InterwikiLogFormatter', 'interwiki/iw_delete' => 'MediaWiki\\Logging\\InterwikiLogFormatter', 'interwiki/iw_edit' => 'MediaWiki\\Logging\\InterwikiLogFormatter', 'managetags/activate' => 'MediaWiki\\Logging\\LogFormatter', 'managetags/create' => 'MediaWiki\\Logging\\LogFormatter', 'managetags/deactivate' => 'MediaWiki\\Logging\\LogFormatter', 'managetags/delete' => 'MediaWiki\\Logging\\LogFormatter', 'merge/merge' => [ 'class' => 'MediaWiki\\Logging\\MergeLogFormatter', 'services' => [ 'TitleParser', ], ], 'merge/merge-into' => [ 'class' => 'MediaWiki\\Logging\\MergeLogFormatter', 'services' => [ 'TitleParser', ], ], 'move/move' => [ 'class' => 'MediaWiki\\Logging\\MoveLogFormatter', 'services' => [ 'TitleParser', ], ], 'move/move_redir' => [ 'class' => 'MediaWiki\\Logging\\MoveLogFormatter', 'services' => [ 'TitleParser', ], ], 'patrol/patrol' => 'MediaWiki\\Logging\\PatrolLogFormatter', 'patrol/autopatrol' => 'MediaWiki\\Logging\\PatrolLogFormatter', 'protect/modify' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'protect/move_prot' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'protect/protect' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'protect/unprotect' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'renameuser/renameuser' => [ 'class' => 'MediaWiki\\Logging\\RenameuserLogFormatter', 'services' => [ 'TitleParser', ], ], 'rights/autopromote' => 'MediaWiki\\Logging\\RightsLogFormatter', 'rights/rights' => 'MediaWiki\\Logging\\RightsLogFormatter', 'suppress/block' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'suppress/delete' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'suppress/event' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'suppress/reblock' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'suppress/revision' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'tag/update' => 'MediaWiki\\Logging\\TagLogFormatter', 'upload/overwrite' => 'MediaWiki\\Logging\\UploadLogFormatter', 'upload/revert' => 'MediaWiki\\Logging\\UploadLogFormatter', 'upload/upload' => 'MediaWiki\\Logging\\UploadLogFormatter', ], 'ActionFilteredLogs' => [ 'block' => [ 'block' => [ 'block', ], 'reblock' => [ 'reblock', ], 'unblock' => [ 'unblock', ], ], 'contentmodel' => [ 'change' => [ 'change', ], 'new' => [ 'new', ], ], 'delete' => [ 'delete' => [ 'delete', ], 'delete_redir' => [ 'delete_redir', 'delete_redir2', ], 'restore' => [ 'restore', ], 'event' => [ 'event', ], 'revision' => [ 'revision', ], ], 'import' => [ 'interwiki' => [ 'interwiki', ], 'upload' => [ 'upload', ], ], 'managetags' => [ 'create' => [ 'create', ], 'delete' => [ 'delete', ], 'activate' => [ 'activate', ], 'deactivate' => [ 'deactivate', ], ], 'move' => [ 'move' => [ 'move', ], 'move_redir' => [ 'move_redir', ], ], 'newusers' => [ 'create' => [ 'create', 'newusers', ], 'create2' => [ 'create2', ], 'autocreate' => [ 'autocreate', ], 'byemail' => [ 'byemail', ], ], 'protect' => [ 'protect' => [ 'protect', ], 'modify' => [ 'modify', ], 'unprotect' => [ 'unprotect', ], 'move_prot' => [ 'move_prot', ], ], 'rights' => [ 'rights' => [ 'rights', ], 'autopromote' => [ 'autopromote', ], ], 'suppress' => [ 'event' => [ 'event', ], 'revision' => [ 'revision', ], 'delete' => [ 'delete', ], 'block' => [ 'block', ], 'reblock' => [ 'reblock', ], ], 'upload' => [ 'upload' => [ 'upload', ], 'overwrite' => [ 'overwrite', ], 'revert' => [ 'revert', ], ], ], 'NewUserLog' => true, 'PageCreationLog' => true, 'AllowSpecialInclusion' => true, 'DisableQueryPageUpdate' => false, 'CountCategorizedImagesAsUsed' => false, 'MaxRedirectLinksRetrieved' => 500, 'RangeContributionsCIDRLimit' => [ 'IPv4' => 16, 'IPv6' => 32, ], 'Actions' => [ ], 'DefaultRobotPolicy' => 'index,follow', 'NamespaceRobotPolicies' => [ ], 'ArticleRobotPolicies' => [ ], 'ExemptFromUserRobotsControl' => null, 'DebugAPI' => false, 'APIModules' => [ ], 'APIFormatModules' => [ ], 'APIMetaModules' => [ ], 'APIPropModules' => [ ], 'APIListModules' => [ ], 'APIMaxDBRows' => 5000, 'APIMaxResultSize' => 8388608, 'APIMaxUncachedDiffs' => 1, 'APIMaxLagThreshold' => 7, 'APICacheHelpTimeout' => 3600, 'APIUselessQueryPages' => [ 'MIMEsearch', 'LinkSearch', ], 'AjaxLicensePreview' => true, 'CrossSiteAJAXdomains' => [ ], 'CrossSiteAJAXdomainExceptions' => [ ], 'AllowedCorsHeaders' => [ 'Accept', 'Accept-Language', 'Content-Language', 'Content-Type', 'Accept-Encoding', 'DNT', 'Origin', 'User-Agent', 'Api-User-Agent', 'Access-Control-Max-Age', 'Authorization', ], 'RestAPIAdditionalRouteFiles' => [ ], 'RestSandboxSpecs' => [ ], 'MaxShellMemory' => 307200, 'MaxShellFileSize' => 102400, 'MaxShellTime' => 180, 'MaxShellWallClockTime' => 180, 'ShellCgroup' => false, 'PhpCli' => '/usr/bin/php', 'ShellRestrictionMethod' => 'autodetect', 'ShellboxUrls' => [ 'default' => null, ], 'ShellboxSecretKey' => null, 'ShellboxShell' => '/bin/sh', 'HTTPTimeout' => 25, 'HTTPConnectTimeout' => 5.0, 'HTTPMaxTimeout' => 0, 'HTTPMaxConnectTimeout' => 0, 'HTTPImportTimeout' => 25, 'AsyncHTTPTimeout' => 25, 'HTTPProxy' => '', 'LocalVirtualHosts' => [ ], 'LocalHTTPProxy' => false, 'AllowExternalReqID' => false, 'JobRunRate' => 1, 'RunJobsAsync' => false, 'UpdateRowsPerJob' => 300, 'UpdateRowsPerQuery' => 100, 'RedirectOnLogin' => null, 'VirtualRestConfig' => [ 'paths' => [ ], 'modules' => [ ], 'global' => [ 'timeout' => 360, 'forwardCookies' => false, 'HTTPProxy' => null, ], ], 'EventRelayerConfig' => [ 'default' => [ 'class' => 'Wikimedia\\EventRelayer\\EventRelayerNull', ], ], 'Pingback' => false, 'OriginTrials' => [ ], 'ReportToExpiry' => 86400, 'ReportToEndpoints' => [ ], 'FeaturePolicyReportOnly' => [ ], 'SkinsPreferred' => [ 'vector-2022', 'vector', ], 'SpecialContributeSkinsEnabled' => [ ], 'SpecialContributeNewPageTarget' => null, 'EnableEditRecovery' => false, 'EditRecoveryExpiry' => 2592000, 'UseCodexSpecialBlock' => false, 'ShowLogoutConfirmation' => false, 'EnableProtectionIndicators' => true, 'OutputPipelineStages' => [ ], 'FeatureShutdown' => [ ], 'CloneArticleParserOutput' => true, 'UseLeximorph' => false, 'UsePostprocCache' => false, 'UsePostprocCacheLegacy' => false, 'UsePostprocCacheParsoid' => false, 'ParserOptionsLogUnsafeSampleRate' => 0, ], 'type' => [ 'ConfigRegistry' => 'object', 'AssumeProxiesUseDefaultProtocolPorts' => 'boolean', 'ForceHTTPS' => 'boolean', 'ExtensionDirectory' => [ 'string', 'null', ], 'StyleDirectory' => [ 'string', 'null', ], 'UploadDirectory' => [ 'string', 'boolean', 'null', ], 'Logos' => [ 'object', 'boolean', ], 'ReferrerPolicy' => [ 'array', 'string', 'boolean', ], 'ActionPaths' => 'object', 'MainPageIsDomainRoot' => 'boolean', 'ImgAuthUrlPathMap' => 'object', 'LocalFileRepo' => 'object', 'ForeignFileRepos' => 'array', 'UseSharedUploads' => 'boolean', 'SharedUploadDirectory' => [ 'string', 'null', ], 'SharedUploadPath' => [ 'string', 'null', ], 'HashedSharedUploadDirectory' => 'boolean', 'FetchCommonsDescriptions' => 'boolean', 'SharedUploadDBname' => [ 'boolean', 'string', ], 'SharedUploadDBprefix' => 'string', 'CacheSharedUploads' => 'boolean', 'ForeignUploadTargets' => 'array', 'UploadDialog' => 'object', 'FileBackends' => 'object', 'LockManagers' => 'array', 'CopyUploadsDomains' => 'array', 'CopyUploadTimeout' => [ 'boolean', 'integer', ], 'SharedThumbnailScriptPath' => [ 'string', 'boolean', ], 'HashedUploadDirectory' => 'boolean', 'CSPUploadEntryPoint' => 'boolean', 'FileExtensions' => 'array', 'ProhibitedFileExtensions' => 'array', 'MimeTypeExclusions' => 'array', 'TrustedMediaFormats' => 'array', 'MediaHandlers' => 'object', 'NativeImageLazyLoading' => 'boolean', 'ParserTestMediaHandlers' => 'object', 'MaxInterlacingAreas' => 'object', 'SVGConverters' => 'object', 'SVGNativeRendering' => [ 'string', 'boolean', ], 'MaxImageArea' => [ 'string', 'integer', 'boolean', ], 'TiffThumbnailType' => 'array', 'GenerateThumbnailOnParse' => 'boolean', 'EnableAutoRotation' => [ 'boolean', 'null', ], 'Antivirus' => [ 'string', 'null', ], 'AntivirusSetup' => 'object', 'MimeDetectorCommand' => [ 'string', 'null', ], 'XMLMimeTypes' => 'object', 'ImageLimits' => 'array', 'ThumbLimits' => 'array', 'ThumbnailNamespaces' => 'array', 'ThumbnailSteps' => [ 'array', 'null', ], 'ThumbnailStepsRatio' => [ 'number', 'null', ], 'ThumbnailBuckets' => [ 'array', 'null', ], 'UploadThumbnailRenderMap' => 'object', 'GalleryOptions' => 'object', 'DjvuDump' => [ 'string', 'null', ], 'DjvuRenderer' => [ 'string', 'null', ], 'DjvuTxt' => [ 'string', 'null', ], 'DjvuPostProcessor' => [ 'string', 'null', ], 'UserEmailConfirmationUseHTML' => 'boolean', 'SMTP' => [ 'boolean', 'object', ], 'EnotifFromEditor' => 'boolean', 'EnotifRevealEditorAddress' => 'boolean', 'UsersNotifiedOnAllChanges' => 'object', 'DBmwschema' => [ 'string', 'null', ], 'SharedTables' => 'array', 'DBservers' => [ 'boolean', 'array', ], 'LBFactoryConf' => 'object', 'LocalDatabases' => 'array', 'VirtualDomainsMapping' => 'object', 'FileSchemaMigrationStage' => 'integer', 'ImageLinksSchemaMigrationStage' => 'integer', 'ExternalLinksDomainGaps' => 'object', 'ContentHandlers' => 'object', 'NamespaceContentModels' => 'object', 'TextModelsToParse' => 'array', 'ExternalStores' => 'array', 'ExternalServers' => 'object', 'DefaultExternalStore' => [ 'array', 'boolean', ], 'RevisionCacheExpiry' => 'integer', 'PageLanguageUseDB' => 'boolean', 'DiffEngine' => [ 'string', 'null', ], 'ExternalDiffEngine' => [ 'string', 'boolean', ], 'Wikidiff2Options' => 'object', 'RequestTimeLimit' => [ 'integer', 'null', ], 'CriticalSectionTimeLimit' => 'number', 'PoolCounterConf' => [ 'object', 'null', ], 'PoolCountClientConf' => 'object', 'MaxUserDBWriteDuration' => [ 'integer', 'boolean', ], 'MaxJobDBWriteDuration' => [ 'integer', 'boolean', ], 'MultiShardSiteStats' => 'boolean', 'ObjectCaches' => 'object', 'WANObjectCache' => 'object', 'MicroStashType' => [ 'string', 'integer', ], 'ParsoidCacheConfig' => 'object', 'ParsoidSelectiveUpdateSampleRate' => 'integer', 'ParserCacheFilterConfig' => 'object', 'ChronologyProtectorSecret' => 'string', 'PHPSessionHandling' => 'string', 'SuspiciousIpExpiry' => [ 'integer', 'boolean', ], 'MemCachedServers' => 'array', 'LocalisationCacheConf' => 'object', 'ExtensionInfoMTime' => [ 'integer', 'boolean', ], 'CdnServers' => 'object', 'CdnServersNoPurge' => 'object', 'HTCPRouting' => 'object', 'GrammarForms' => 'object', 'ExtraInterlanguageLinkPrefixes' => 'array', 'InterlanguageLinkCodeMap' => 'object', 'ExtraLanguageNames' => 'object', 'ExtraLanguageCodes' => 'object', 'DummyLanguageCodes' => 'object', 'DisabledVariants' => 'object', 'ForceUIMsgAsContentMsg' => 'object', 'RawHtmlMessages' => 'array', 'OverrideUcfirstCharacters' => 'object', 'XhtmlNamespaces' => 'object', 'BrowserFormatDetection' => 'string', 'SkinMetaTags' => 'object', 'SkipSkins' => 'object', 'FragmentMode' => 'array', 'FooterIcons' => 'object', 'InterwikiLogoOverride' => 'array', 'ResourceModules' => 'object', 'ResourceModuleSkinStyles' => 'object', 'ResourceLoaderSources' => 'object', 'ResourceLoaderMaxage' => 'object', 'ResourceLoaderMaxQueryLength' => [ 'integer', 'boolean', ], 'CanonicalNamespaceNames' => 'object', 'ExtraNamespaces' => 'object', 'ExtraGenderNamespaces' => 'object', 'NamespaceAliases' => 'object', 'CapitalLinkOverrides' => 'object', 'NamespacesWithSubpages' => 'object', 'ContentNamespaces' => 'array', 'ShortPagesNamespaceExclusions' => 'array', 'ExtraSignatureNamespaces' => 'array', 'InvalidRedirectTargets' => 'array', 'LocalInterwikis' => 'array', 'InterwikiCache' => [ 'boolean', 'object', ], 'SiteTypes' => 'object', 'UrlProtocols' => 'array', 'TidyConfig' => 'object', 'ParsoidSettings' => 'object', 'ParsoidExperimentalParserFunctionOutput' => 'boolean', 'NoFollowNsExceptions' => 'array', 'NoFollowDomainExceptions' => 'array', 'ExternalLinksIgnoreDomains' => 'array', 'EnableMagicLinks' => 'object', 'ManualRevertSearchRadius' => 'integer', 'RevertedTagMaxDepth' => 'integer', 'CentralIdLookupProviders' => 'object', 'CentralIdLookupProvider' => 'string', 'UserRegistrationProviders' => 'object', 'PasswordPolicy' => 'object', 'AuthManagerConfig' => [ 'object', 'null', ], 'AuthManagerAutoConfig' => 'object', 'RememberMe' => 'string', 'ReauthenticateTime' => 'object', 'AllowSecuritySensitiveOperationIfCannotReauthenticate' => 'object', 'ChangeCredentialsBlacklist' => 'array', 'RemoveCredentialsBlacklist' => 'array', 'PasswordConfig' => 'object', 'PasswordResetRoutes' => 'object', 'SignatureAllowedLintErrors' => 'array', 'ReservedUsernames' => 'array', 'DefaultUserOptions' => 'object', 'ConditionalUserOptions' => 'object', 'HiddenPrefs' => 'array', 'UserJsPrefLimit' => 'integer', 'AuthenticationTokenVersion' => [ 'string', 'null', ], 'SessionProviders' => 'object', 'AutoCreateTempUser' => 'object', 'AutoblockExemptions' => 'array', 'BlockCIDRLimit' => 'object', 'EnableMultiBlocks' => 'boolean', 'GroupPermissions' => 'object', 'PrivilegedGroups' => 'array', 'RevokePermissions' => 'object', 'GroupInheritsPermissions' => 'object', 'ImplicitGroups' => 'array', 'GroupsAddToSelf' => 'object', 'GroupsRemoveFromSelf' => 'object', 'RestrictedGroups' => 'object', 'UserRequirementsPrivateConditions' => 'array', 'RestrictionTypes' => 'array', 'RestrictionLevels' => 'array', 'CascadingRestrictionLevels' => 'array', 'SemiprotectedRestrictionLevels' => 'array', 'NamespaceProtection' => 'object', 'NonincludableNamespaces' => 'object', 'Autopromote' => 'object', 'AutopromoteOnce' => 'object', 'AutopromoteOnceRCExcludedGroups' => 'array', 'AddGroups' => 'object', 'RemoveGroups' => 'object', 'AvailableRights' => 'array', 'ImplicitRights' => 'array', 'AccountCreationThrottle' => [ 'integer', 'array', ], 'TempAccountCreationThrottle' => 'array', 'TempAccountNameAcquisitionThrottle' => 'array', 'SpamRegex' => 'array', 'SummarySpamRegex' => 'array', 'DnsBlacklistUrls' => 'array', 'ProxyList' => [ 'string', 'array', ], 'ProxyWhitelist' => 'array', 'SoftBlockRanges' => 'array', 'RateLimits' => 'object', 'RateLimitsExcludedIPs' => 'array', 'ExternalQuerySources' => 'object', 'PasswordAttemptThrottle' => 'array', 'GrantPermissions' => 'object', 'GrantPermissionGroups' => 'object', 'GrantRiskGroups' => 'object', 'EnableBotPasswords' => 'boolean', 'BotPasswordsCluster' => [ 'string', 'boolean', ], 'BotPasswordsDatabase' => [ 'string', 'boolean', ], 'CSPHeader' => [ 'boolean', 'object', ], 'CSPReportOnlyHeader' => [ 'boolean', 'object', ], 'CSPFalsePositiveUrls' => 'object', 'AllowCrossOrigin' => 'boolean', 'RestAllowCrossOriginCookieAuth' => 'boolean', 'CookieSameSite' => [ 'string', 'null', ], 'CacheVaryCookies' => 'array', 'TrxProfilerLimits' => 'object', 'DebugLogGroups' => 'object', 'MWLoggerDefaultSpi' => 'object', 'Profiler' => 'object', 'StatsTarget' => [ 'string', 'null', ], 'StatsFormat' => [ 'string', 'null', ], 'StatsPrefix' => 'string', 'OpenTelemetryConfig' => [ 'object', 'null', ], 'OpenSearchTemplates' => 'object', 'NamespacesToBeSearchedDefault' => 'object', 'SitemapNamespaces' => [ 'boolean', 'array', ], 'SitemapNamespacesPriorities' => [ 'boolean', 'object', ], 'SitemapApiConfig' => 'object', 'SpecialSearchFormOptions' => 'object', 'SearchMatchRedirectPreference' => 'boolean', 'SearchRunSuggestedQuery' => 'boolean', 'PreviewOnOpenNamespaces' => 'object', 'ReadOnlyWatchedItemStore' => 'boolean', 'GitRepositoryViewers' => 'object', 'InstallerInitialPages' => 'array', 'RCLinkLimits' => 'array', 'RCLinkDays' => 'array', 'RCFeeds' => 'object', 'RCEngines' => 'object', 'OverrideSiteFeed' => 'object', 'FeedClasses' => 'object', 'AdvertisedFeedTypes' => 'array', 'SoftwareTags' => 'object', 'RecentChangesFlags' => 'object', 'WatchlistExpiry' => 'boolean', 'EnableWatchlistLabels' => 'boolean', 'WatchlistLabelsMaxPerUser' => 'integer', 'WatchlistPurgeRate' => 'number', 'WatchlistExpiryMaxDuration' => [ 'string', 'null', ], 'EnableChangesListQueryPartitioning' => 'boolean', 'ImportSources' => 'object', 'ExtensionFunctions' => 'array', 'ExtensionMessagesFiles' => 'object', 'MessagesDirs' => 'object', 'TranslationAliasesDirs' => 'object', 'ExtensionEntryPointListFiles' => 'object', 'ValidSkinNames' => 'object', 'SpecialPages' => 'object', 'ExtensionCredits' => 'object', 'Hooks' => 'object', 'ServiceWiringFiles' => 'array', 'JobClasses' => 'object', 'JobTypesExcludedFromDefaultQueue' => 'array', 'JobBackoffThrottling' => 'object', 'JobTypeConf' => 'object', 'SpecialPageCacheUpdates' => 'object', 'PagePropLinkInvalidations' => 'object', 'TempCategoryCollations' => 'array', 'SortedCategories' => 'boolean', 'TrackingCategories' => 'array', 'LogTypes' => 'array', 'LogRestrictions' => 'object', 'FilterLogTypes' => 'object', 'LogNames' => 'object', 'LogHeaders' => 'object', 'LogActions' => 'object', 'LogActionsHandlers' => 'object', 'ActionFilteredLogs' => 'object', 'RangeContributionsCIDRLimit' => 'object', 'Actions' => 'object', 'NamespaceRobotPolicies' => 'object', 'ArticleRobotPolicies' => 'object', 'ExemptFromUserRobotsControl' => [ 'array', 'null', ], 'APIModules' => 'object', 'APIFormatModules' => 'object', 'APIMetaModules' => 'object', 'APIPropModules' => 'object', 'APIListModules' => 'object', 'APIUselessQueryPages' => 'array', 'CrossSiteAJAXdomains' => 'object', 'CrossSiteAJAXdomainExceptions' => 'object', 'AllowedCorsHeaders' => 'array', 'RestAPIAdditionalRouteFiles' => 'array', 'RestSandboxSpecs' => 'object', 'ShellRestrictionMethod' => [ 'string', 'boolean', ], 'ShellboxUrls' => 'object', 'ShellboxSecretKey' => [ 'string', 'null', ], 'ShellboxShell' => [ 'string', 'null', ], 'HTTPTimeout' => 'number', 'HTTPConnectTimeout' => 'number', 'HTTPMaxTimeout' => 'number', 'HTTPMaxConnectTimeout' => 'number', 'LocalVirtualHosts' => 'object', 'LocalHTTPProxy' => [ 'string', 'boolean', ], 'VirtualRestConfig' => 'object', 'EventRelayerConfig' => 'object', 'Pingback' => 'boolean', 'OriginTrials' => 'array', 'ReportToExpiry' => 'integer', 'ReportToEndpoints' => 'array', 'FeaturePolicyReportOnly' => 'array', 'SkinsPreferred' => 'array', 'SpecialContributeSkinsEnabled' => 'array', 'SpecialContributeNewPageTarget' => [ 'string', 'null', ], 'EnableEditRecovery' => 'boolean', 'EditRecoveryExpiry' => 'integer', 'UseCodexSpecialBlock' => 'boolean', 'ShowLogoutConfirmation' => 'boolean', 'EnableProtectionIndicators' => 'boolean', 'OutputPipelineStages' => 'object', 'FeatureShutdown' => 'array', 'CloneArticleParserOutput' => 'boolean', 'UseLeximorph' => 'boolean', 'UsePostprocCache' => 'boolean', 'UsePostprocCacheLegacy' => 'boolean', 'UsePostprocCacheParsoid' => 'boolean', 'ParserOptionsLogUnsafeSampleRate' => 'integer', ], 'mergeStrategy' => [ 'TiffThumbnailType' => 'replace', 'LBFactoryConf' => 'replace', 'InterwikiCache' => 'replace', 'PasswordPolicy' => 'array_replace_recursive', 'AuthManagerAutoConfig' => 'array_plus_2d', 'GroupPermissions' => 'array_plus_2d', 'RevokePermissions' => 'array_plus_2d', 'AddGroups' => 'array_merge_recursive', 'RemoveGroups' => 'array_merge_recursive', 'RateLimits' => 'array_plus_2d', 'GrantPermissions' => 'array_plus_2d', 'MWLoggerDefaultSpi' => 'replace', 'Profiler' => 'replace', 'Hooks' => 'array_merge_recursive', 'VirtualRestConfig' => 'array_plus_2d', ], 'dynamicDefault' => [ 'UsePathInfo' => [ 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultUsePathInfo', ], ], 'Script' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultScript', ], ], 'LoadScript' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLoadScript', ], ], 'RestPath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultRestPath', ], ], 'StylePath' => [ 'use' => [ 'ResourceBasePath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultStylePath', ], ], 'LocalStylePath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLocalStylePath', ], ], 'ExtensionAssetsPath' => [ 'use' => [ 'ResourceBasePath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultExtensionAssetsPath', ], ], 'ArticlePath' => [ 'use' => [ 'Script', 'UsePathInfo', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultArticlePath', ], ], 'UploadPath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultUploadPath', ], ], 'FileCacheDirectory' => [ 'use' => [ 'UploadDirectory', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultFileCacheDirectory', ], ], 'Logo' => [ 'use' => [ 'ResourceBasePath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLogo', ], ], 'DeletedDirectory' => [ 'use' => [ 'UploadDirectory', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultDeletedDirectory', ], ], 'ShowEXIF' => [ 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultShowEXIF', ], ], 'SharedPrefix' => [ 'use' => [ 'DBprefix', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultSharedPrefix', ], ], 'SharedSchema' => [ 'use' => [ 'DBmwschema', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultSharedSchema', ], ], 'DBerrorLogTZ' => [ 'use' => [ 'Localtimezone', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultDBerrorLogTZ', ], ], 'Localtimezone' => [ 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLocaltimezone', ], ], 'LocalTZoffset' => [ 'use' => [ 'Localtimezone', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLocalTZoffset', ], ], 'ResourceBasePath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultResourceBasePath', ], ], 'MetaNamespace' => [ 'use' => [ 'Sitename', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultMetaNamespace', ], ], 'CookieSecure' => [ 'use' => [ 'ForceHTTPS', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultCookieSecure', ], ], 'CookiePrefix' => [ 'use' => [ 'SharedDB', 'SharedPrefix', 'SharedTables', 'DBname', 'DBprefix', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultCookiePrefix', ], ], 'ReadOnlyFile' => [ 'use' => [ 'UploadDirectory', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultReadOnlyFile', ], ], ], ], 'config-schema' => [ 'UploadStashScalerBaseUrl' => [ 'deprecated' => 'since 1.36 Use thumbProxyUrl in $wgLocalFileRepo', ], 'IllegalFileChars' => [ 'deprecated' => 'since 1.41; no longer customizable', ], 'ThumbnailNamespaces' => [ 'items' => [ 'type' => 'integer', ], ], 'LocalDatabases' => [ 'items' => [ 'type' => 'string', ], ], 'ParserCacheFilterConfig' => [ 'additionalProperties' => [ 'type' => 'object', 'description' => 'A map of namespace IDs to filter definitions.', 'additionalProperties' => [ 'type' => 'object', 'description' => 'A map of filter names to values.', 'properties' => [ 'minCpuTime' => [ 'type' => 'number', ], ], ], ], ], 'PHPSessionHandling' => [ 'deprecated' => 'since 1.45 Integration with PHP session handling will be removed in the future', ], 'RawHtmlMessages' => [ 'items' => [ 'type' => 'string', ], ], 'InterwikiLogoOverride' => [ 'items' => [ 'type' => 'string', ], ], 'LegalTitleChars' => [ 'deprecated' => 'since 1.41; use Extension:TitleBlacklist to customize', ], 'ReauthenticateTime' => [ 'additionalProperties' => [ 'type' => 'integer', ], ], 'AllowSecuritySensitiveOperationIfCannotReauthenticate' => [ 'additionalProperties' => [ 'type' => 'boolean', ], ], 'ChangeCredentialsBlacklist' => [ 'items' => [ 'type' => 'string', ], ], 'RemoveCredentialsBlacklist' => [ 'items' => [ 'type' => 'string', ], ], 'GroupPermissions' => [ 'additionalProperties' => [ 'type' => 'object', 'additionalProperties' => [ 'type' => 'boolean', ], ], ], 'GroupInheritsPermissions' => [ 'additionalProperties' => [ 'type' => 'string', ], ], 'AvailableRights' => [ 'items' => [ 'type' => 'string', ], ], 'ImplicitRights' => [ 'items' => [ 'type' => 'string', ], ], 'SoftBlockRanges' => [ 'items' => [ 'type' => 'string', ], ], 'ExternalQuerySources' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'enabled' => [ 'type' => 'boolean', 'default' => false, ], 'url' => [ 'type' => 'string', 'format' => 'uri', ], 'timeout' => [ 'type' => 'integer', 'default' => 10, ], ], 'required' => [ 'enabled', 'url', ], 'additionalProperties' => false, ], ], 'GrantPermissions' => [ 'additionalProperties' => [ 'type' => 'object', 'additionalProperties' => [ 'type' => 'boolean', ], ], ], 'GrantPermissionGroups' => [ 'additionalProperties' => [ 'type' => 'string', ], ], 'SitemapNamespacesPriorities' => [ 'deprecated' => 'since 1.45 and ignored', ], 'SitemapApiConfig' => [ 'additionalProperties' => [ 'enabled' => [ 'type' => 'bool', ], 'sitemapsPerIndex' => [ 'type' => 'int', ], 'pagesPerSitemap' => [ 'type' => 'int', ], 'expiry' => [ 'type' => 'int', ], ], ], 'SoftwareTags' => [ 'additionalProperties' => [ 'type' => 'boolean', ], ], 'JobBackoffThrottling' => [ 'additionalProperties' => [ 'type' => 'number', ], ], 'JobTypeConf' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'class' => [ 'type' => 'string', ], 'order' => [ 'type' => 'string', ], 'claimTTL' => [ 'type' => 'integer', ], ], ], ], 'TrackingCategories' => [ 'deprecated' => 'since 1.25 Extensions should now register tracking categories using the new extension registration system.', ], 'RangeContributionsCIDRLimit' => [ 'additionalProperties' => [ 'type' => 'integer', ], ], 'RestSandboxSpecs' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'url' => [ 'type' => 'string', 'format' => 'url', ], 'name' => [ 'type' => 'string', ], 'msg' => [ 'type' => 'string', 'description' => 'a message key', ], ], 'required' => [ 'url', ], ], ], 'ShellboxUrls' => [ 'additionalProperties' => [ 'type' => [ 'string', 'boolean', 'null', ], ], ], ], 'obsolete-config' => [ 'MangleFlashPolicy' => 'Since 1.39; no longer has any effect.', 'EnableOpenSearchSuggest' => 'Since 1.35, no longer used', 'AutoloadAttemptLowercase' => 'Since 1.40; no longer has any effect.', ],]
$wgDefaultUserOptions
Config variable stub for the DefaultUserOptions setting, for use by phpdoc and IDEs.
Interface for objects which can provide a MediaWiki context on request.
The shared interface for all language converters.
Interface for localizing messages in MediaWiki.
msg( $key,... $params)
This is the method for getting translated interface messages.
Base interface for user preference filters that work as a middleware between storage and interface.
Definition Filter.php:13
A PreferencesFactory is a MediaWiki service that provides the definitions of preferences for a given ...
Interface for database access objects.
element(SerializerNode $parent, SerializerNode $node, $contents)