MediaWiki master
User.php
Go to the documentation of this file.
1<?php
7namespace MediaWiki\User;
8
9use AllowDynamicProperties;
10use ArrayIterator;
11use BadMethodCallException;
12use InvalidArgumentException;
19use MediaWiki\DAO\WikiAwareEntityTrait;
22use MediaWiki\HookContainer\ProtectedHookAccessorTrait;
28use MediaWiki\MainConfigSchema;
45use RuntimeException;
46use stdClass;
47use Stringable;
48use UnexpectedValueException;
49use Wikimedia\Assert\Assert;
50use Wikimedia\Assert\PreconditionException;
51use Wikimedia\DebugInfo\DebugInfoTrait;
52use Wikimedia\IPUtils;
61use Wikimedia\ScopedCallback;
62use Wikimedia\Timestamp\ConvertibleTimestamp;
63use Wikimedia\Timestamp\TimestampFormat as TS;
64
128#[AllowDynamicProperties]
129class User implements Stringable, Authority, UserIdentity, UserEmailContact {
130 use DebugInfoTrait;
131 use ProtectedHookAccessorTrait;
132 use WikiAwareEntityTrait;
133
137 public const READ_EXCLUSIVE = IDBAccessObject::READ_EXCLUSIVE;
138
142 public const READ_LOCKING = IDBAccessObject::READ_LOCKING;
143
147 public const TOKEN_LENGTH = 32;
148
152 public const INVALID_TOKEN = '*** INVALID ***';
153
158 private const VERSION = 18;
159
164 public const MAINTENANCE_SCRIPT_USER = 'Maintenance script';
165
173 protected static $mCacheVars = [
174 // user table
175 'mId',
176 'mName',
177 'mRealName',
178 'mEmail',
179 'mTouched',
180 'mToken',
181 'mEmailAuthenticated',
182 'mEmailToken',
183 'mEmailTokenExpires',
184 // actor table
185 'mActorId',
186 ];
187
189 // Some of these are public, including for use by the UserFactory, but they generally
190 // should not be set manually
191 // @{
193 public $mId;
195 public $mName;
201 public $mActorId;
204
206 public $mEmail;
208 public $mTouched;
210 protected $mQuickTouched;
212 protected $mToken;
216 protected $mEmailToken;
219 // @}
220
221 // @{
225 protected $mLoadedItems = [];
226 // @}
227
238 public $mFrom;
239
246 protected $mGlobalBlock;
248 protected $mLocked;
249
251 private $mRequest;
252
254 protected $queryFlagsUsed = IDBAccessObject::READ_NORMAL;
255
260 private $mThisAsAuthority;
261
263 private $isTemp;
264
277 public function __construct() {
278 // By default, this is a lightweight constructor representing
279 // an anonymous user from the current web request and IP.
280 $this->clearInstanceCache( 'defaults' );
281 }
282
288 public function getWikiId(): string|false {
289 return self::LOCAL;
290 }
291
295 public function __toString() {
296 return $this->getName();
297 }
298
299 public function &__get( $name ) {
300 // A shortcut for $mRights deprecation phase
301 if ( $name === 'mRights' ) {
302 // hard deprecated since 1.40
303 wfDeprecated( 'User::$mRights', '1.34' );
305 ->getPermissionManager()
306 ->getUserPermissions( $this );
307 return $copy;
308 } elseif ( !property_exists( $this, $name ) ) {
309 // T227688 - do not break $u->foo['bar'] = 1
310 wfLogWarning( 'tried to get non-existent property' );
311 $this->$name = null;
312 return $this->$name;
313 } else {
314 wfLogWarning( 'tried to get non-visible property' );
315 $null = null;
316 return $null;
317 }
318 }
319
320 public function __set( $name, $value ) {
321 // A shortcut for $mRights deprecation phase, only known legitimate use was for
322 // testing purposes, other uses seem bad in principle
323 if ( $name === 'mRights' ) {
324 // hard deprecated since 1.40
325 wfDeprecated( 'User::$mRights', '1.34' );
326 MediaWikiServices::getInstance()->getPermissionManager()->overrideUserRightsForTesting(
327 $this,
328 $value ?? []
329 );
330 } elseif ( !property_exists( $this, $name ) ) {
331 $this->$name = $value;
332 } else {
333 wfLogWarning( 'tried to set non-visible property' );
334 }
335 }
336
337 public function __sleep(): array {
338 return array_diff(
339 array_keys( get_object_vars( $this ) ),
340 [
341 'mThisAsAuthority', // memoization, will be recreated on demand.
342 'mRequest', // contains Session, reloaded when needed, T400549
343 ]
344 );
345 }
346
363 public function isSafeToLoad() {
364 global $wgFullyInitialised;
365
366 // The user is safe to load if:
367 // * MW_NO_SESSION is undefined AND $wgFullyInitialised is true (safe to use session data)
368 // * mLoadedItems === true (already loaded)
369 // * mFrom !== 'session' (sessions not involved at all)
370
371 return ( !defined( 'MW_NO_SESSION' ) && $wgFullyInitialised ) ||
372 $this->mLoadedItems === true || $this->mFrom !== 'session';
373 }
374
380 public function load( $flags = IDBAccessObject::READ_NORMAL ) {
381 global $wgFullyInitialised;
382
383 if ( $this->mLoadedItems === true ) {
384 return;
385 }
386
387 // Set it now to avoid infinite recursion in accessors
388 $oldLoadedItems = $this->mLoadedItems;
389 $this->mLoadedItems = true;
390 $this->queryFlagsUsed = $flags;
391
392 // If this is called too early, things are likely to break.
393 if ( !$wgFullyInitialised && $this->mFrom === 'session' ) {
394 LoggerFactory::getInstance( 'session' )
395 ->warning( 'User::loadFromSession called before the end of Setup.php', [
396 'userId' => $this->mId,
397 'userName' => $this->mName,
398 'exception' => new RuntimeException(
399 'User::loadFromSession called before the end of Setup.php'
400 ),
401 ] );
402 $this->loadDefaults();
403 $this->mLoadedItems = $oldLoadedItems;
404 return;
405 } elseif ( $this->mFrom === 'session'
406 && defined( 'MW_NO_SESSION' ) && MW_NO_SESSION !== 'warn'
407 ) {
408 // Even though we are throwing an exception, make sure the User object is left in a
409 // clean state as sometimes these exceptions are caught and the object accessed again.
410 $this->loadDefaults();
411 $this->mLoadedItems = $oldLoadedItems;
412 $ep = defined( 'MW_ENTRY_POINT' ) ? MW_ENTRY_POINT : 'this';
413 throw new BadMethodCallException( "Sessions are disabled for $ep entry point" );
414 }
415
416 switch ( $this->mFrom ) {
417 case 'defaults':
418 $this->loadDefaults();
419 break;
420 case 'id':
421 // Make sure this thread sees its own changes, if the ID isn't 0
422 if ( $this->mId != 0 ) {
423 $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
424 if ( $lb->hasOrMadeRecentPrimaryChanges() ) {
425 $flags |= IDBAccessObject::READ_LATEST;
426 $this->queryFlagsUsed = $flags;
427 }
428 }
429
430 $this->loadFromId( $flags );
431 break;
432 case 'actor':
433 case 'name':
434 // Make sure this thread sees its own changes
435 $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
436 if ( $lb->hasOrMadeRecentPrimaryChanges() ) {
437 $flags |= IDBAccessObject::READ_LATEST;
438 $this->queryFlagsUsed = $flags;
439 }
440
441 $dbr = DBAccessObjectUtils::getDBFromRecency(
442 MediaWikiServices::getInstance()->getDBLoadBalancerFactory(),
443 $flags
444 );
445
446 $actorStore = MediaWikiServices::getInstance()->getActorStore();
447 if ( $this->mFrom === 'name' ) {
448 $userIdentity = $actorStore->getUserIdentityByName( $this->mName, $flags );
449 } else {
450 $userIdentity = $actorStore->getActorById( $this->mActorId, $dbr );
451 }
452
453 if ( !$userIdentity ) {
454 $this->loadDefaults( $this->mFrom === 'name' ? $this->mName : false );
455 } elseif ( $userIdentity->getId() ) {
456 $this->mId = $userIdentity->getId();
457 $this->loadFromId( $flags );
458 } else {
459 $userName = $userIdentity->getName();
460 $this->loadDefaults( $userName, $actorStore->findActorIdByName( $userName, $dbr ) );
461 }
462 break;
463 case 'session':
464 if ( !$this->loadFromSession() ) {
465 // Loading from session failed. Load defaults.
466 $this->loadDefaults();
467 }
468 $this->getHookRunner()->onUserLoadAfterLoadFromSession( $this );
469 break;
470 default:
471 throw new UnexpectedValueException(
472 "Unrecognised value for User->mFrom: \"{$this->mFrom}\"" );
473 }
474 }
475
481 public function loadFromId( $flags = IDBAccessObject::READ_NORMAL ) {
482 if ( $this->mId == 0 ) {
483 // Anonymous users are not in the database (don't need cache)
484 $this->loadDefaults();
485 return false;
486 }
487
488 // Try cache (unless this needs data from the primary DB).
489 // NOTE: if this thread called saveSettings(), the cache was cleared.
490 $latest = DBAccessObjectUtils::hasFlags( $flags, IDBAccessObject::READ_LATEST );
491 if ( $latest ) {
492 if ( !$this->loadFromDatabase( $flags ) ) {
493 // Can't load from ID
494 return false;
495 }
496 } else {
497 $this->loadFromCache();
498 }
499
500 $this->mLoadedItems = true;
501 $this->queryFlagsUsed = $flags;
502
503 return true;
504 }
505
511 public static function purge( $dbDomain, $userId ) {
512 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
513 $key = $cache->makeGlobalKey( 'user', 'id', $dbDomain, $userId );
514 $cache->delete( $key );
515 }
516
522 protected function getCacheKey( WANObjectCache $cache ) {
523 $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
524
525 return $cache->makeGlobalKey( 'user', 'id',
526 $lbFactory->getLocalDomainID(), $this->mId );
527 }
528
535 protected function loadFromCache() {
536 global $wgFullyInitialised;
537
538 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
539 $data = $cache->getWithSetCallback(
540 $this->getCacheKey( $cache ),
541 $cache::TTL_HOUR,
542 function ( $oldValue, &$ttl ) use ( $wgFullyInitialised ) {
543 wfDebug( "User: cache miss for user {$this->mId}" );
544
545 $this->loadFromDatabase( IDBAccessObject::READ_NORMAL );
546
547 $data = [];
548 foreach ( self::$mCacheVars as $name ) {
549 $data[$name] = $this->$name;
550 }
551 if ( $wgFullyInitialised ) {
552 $groupMemberships = MediaWikiServices::getInstance()
553 ->getUserGroupManager()
554 ->getUserGroupMemberships( $this, $this->queryFlagsUsed );
555
556 // if a user group membership is about to expire, the cache needs to
557 // expire at that time (T163691)
558 foreach ( $groupMemberships as $ugm ) {
559 if ( $ugm->getExpiry() ) {
560 $secondsUntilExpiry =
561 (int)wfTimestamp( TS::UNIX, $ugm->getExpiry() ) - time();
562
563 if ( $secondsUntilExpiry > 0 && $secondsUntilExpiry < $ttl ) {
564 $ttl = $secondsUntilExpiry;
565 }
566 }
567 }
568 }
569
570 return $data;
571 },
572 [ 'pcTTL' => $cache::TTL_PROC_LONG, 'version' => self::VERSION ]
573 );
574
575 // Restore from cache
576 foreach ( self::$mCacheVars as $name ) {
577 $this->$name = $data[$name];
578 }
579
580 return true;
581 }
582
583 /***************************************************************************/
584 // region newFrom*() static factory methods
610 public static function newFromName( $name, $validate = 'valid' ) {
611 // Backwards compatibility with strings / false
612 $validation = match ( $validate ) {
613 'valid' => UserRigorOptions::RIGOR_VALID,
614 'usable' => UserRigorOptions::RIGOR_USABLE,
615 'creatable' => UserRigorOptions::RIGOR_CREATABLE,
616 true => UserRigorOptions::RIGOR_VALID,
617 false => UserRigorOptions::RIGOR_NONE,
618 // Not a recognized value, probably a test for unsupported validation
619 // levels, regardless, just pass it along
620 default => $validate,
621 };
622 return MediaWikiServices::getInstance()->getUserFactory()
623 ->newFromName( (string)$name, $validation ) ?? false;
624 }
625
636 public static function newFromId( $id ) {
637 return MediaWikiServices::getInstance()
638 ->getUserFactory()
639 ->newFromId( (int)$id );
640 }
641
653 public static function newFromActorId( $id ) {
654 return MediaWikiServices::getInstance()
655 ->getUserFactory()
656 ->newFromActorId( (int)$id );
657 }
658
672 public static function newFromIdentity( UserIdentity $identity ) {
673 // Don't use the service if we already have a User object,
674 // so that User::newFromIdentity calls don't break things in unit tests.
675 if ( $identity instanceof User ) {
676 return $identity;
677 }
678
679 return MediaWikiServices::getInstance()
680 ->getUserFactory()
681 ->newFromUserIdentity( $identity );
682 }
683
702 public static function newFromAnyId( $userId, $userName, $actorId, $dbDomain = false ) {
703 return MediaWikiServices::getInstance()
704 ->getUserFactory()
705 ->newFromAnyId( $userId, $userName, $actorId, $dbDomain );
706 }
707
723 public static function newFromConfirmationCode( $code, $flags = IDBAccessObject::READ_NORMAL ) {
724 return MediaWikiServices::getInstance()
725 ->getUserFactory()
726 ->newFromConfirmationCode( (string)$code, $flags );
727 }
728
736 public static function newFromSession( ?WebRequest $request = null ) {
737 $user = new User;
738 $user->mFrom = 'session';
739 $user->mRequest = $request;
740 return $user;
741 }
742
758 public static function newFromRow( $row, $data = null ) {
759 $user = new User;
760 $user->loadFromRow( $row, $data );
761 return $user;
762 }
763
808 public static function newSystemUser( $name, $options = [] ) {
809 $options += [
810 'validate' => UserRigorOptions::RIGOR_VALID,
811 'create' => true,
812 'steal' => false,
813 ];
814
815 // Username validation
816 $validate = $options['validate'];
817 // Backwards compatibility with strings / false
818 $validation = match ( $validate ) {
819 'valid' => UserRigorOptions::RIGOR_VALID,
820 'usable' => UserRigorOptions::RIGOR_USABLE,
821 'creatable' => UserRigorOptions::RIGOR_CREATABLE,
822 false => UserRigorOptions::RIGOR_NONE,
823 // Not a recognized value, probably a test for unsupported validation
824 // levels, regardless, just pass it along
825 default => $validate,
826 };
827
828 if ( $validation !== UserRigorOptions::RIGOR_VALID ) {
830 __METHOD__ . ' options["validation"] parameter must be omitted or set to "valid".',
831 '1.36'
832 );
833 }
834 $services = MediaWikiServices::getInstance();
835 $userNameUtils = $services->getUserNameUtils();
836
837 $name = $userNameUtils->getCanonical( (string)$name, $validation );
838 if ( $name === false ) {
839 return null;
840 }
841
842 $dbProvider = $services->getDBLoadBalancerFactory();
843 $dbr = $dbProvider->getReplicaDatabase();
844
845 $userQuery = self::newQueryBuilder( $dbr )
846 ->where( [ 'user_name' => $name ] )
847 ->caller( __METHOD__ );
848 $row = $userQuery->fetchRow();
849 if ( !$row ) {
850 // Try the primary database
851 $userQuery->connection( $dbProvider->getPrimaryDatabase() );
852 // Lock the row to prevent insertNewUser() returning null due to race conditions
853 $userQuery->forUpdate();
854 $row = $userQuery->fetchRow();
855 }
856
857 if ( !$row ) {
858 // No user. Create it?
859 if ( !$options['create'] ) {
860 // No.
861 return null;
862 }
863
864 // If it's a reserved user that had an anonymous actor created for it at
865 // some point, we need special handling.
866 return self::insertNewUser( static function ( UserIdentity $actor, IDatabase $dbw ) {
867 return MediaWikiServices::getInstance()->getActorStore()
868 ->acquireSystemActorId( $actor, $dbw );
869 }, $name, [ 'token' => self::INVALID_TOKEN ] );
870 }
871
872 $user = self::newFromRow( $row );
873
874 if ( !$user->isSystemUser() ) {
875 // User exists. Steal it?
876 if ( !$options['steal'] ) {
877 return null;
878 }
879
880 $services->getAuthManager()->revokeAccessForUser( $name );
881
882 $user->invalidateEmail();
883 $user->mToken = self::INVALID_TOKEN;
884 $user->saveSettings();
885 $manager = $services->getSessionManager();
886 if ( $manager instanceof SessionManager ) {
887 $manager->preventSessionsForUser( $user->getName() );
888 }
889 }
890
891 return $user;
892 }
893
895 // endregion -- end of newFrom*() static factory methods
896
907 public static function findUsersByGroup( $groups, $limit = 5000, $after = null ) {
908 if ( $groups === [] ) {
909 return UserArrayFromResult::newFromIDs( [] );
910 }
911 $dbr = MediaWikiServices::getInstance()->getConnectionProvider()->getReplicaDatabase();
912 $queryBuilder = $dbr->newSelectQueryBuilder()
913 ->select( 'ug_user' )
914 ->distinct()
915 ->from( 'user_groups' )
916 ->where( [ 'ug_group' => array_unique( (array)$groups ) ] )
917 ->orderBy( 'ug_user' )
918 ->limit( min( 5000, $limit ) );
919
920 if ( $after !== null ) {
921 $queryBuilder->andWhere( $dbr->expr( 'ug_user', '>', (int)$after ) );
922 }
923
924 $ids = $queryBuilder->caller( __METHOD__ )->fetchFieldValues() ?: [];
925 return UserArray::newFromIDs( $ids );
926 }
927
934 public function isValidPassword( $password ) {
935 // simple boolean wrapper for checkPasswordValidity
936 return $this->checkPasswordValidity( $password )->isGood();
937 }
938
960 public function checkPasswordValidity( $password ) {
961 $services = MediaWikiServices::getInstance();
962 $userNameUtils = $services->getUserNameUtils();
963 if ( $userNameUtils->isTemp( $this->getName() ) ) {
964 return Status::newFatal( 'error-temporary-accounts-cannot-have-passwords' );
965 }
966
967 $passwordPolicy = $services->getMainConfig()->get( MainConfigNames::PasswordPolicy );
968
969 $upp = new UserPasswordPolicy(
970 $passwordPolicy['policies'],
971 $passwordPolicy['checks']
972 );
973
974 $status = Status::newGood( [] );
975 $result = false; // init $result to false for the internal checks
976
977 if ( !$this->getHookRunner()->onIsValidPassword( $password, $result, $this ) ) {
978 $status->error( $result );
979 return $status;
980 }
981
982 if ( $result === false ) {
983 $status->merge( $upp->checkUserPassword( $this, $password ), true );
984 return $status;
985 }
986
987 if ( $result === true ) {
988 return $status;
989 }
990
991 $status->error( $result );
992 return $status; // the isValidPassword hook set a string $result and returned true
993 }
994
1004 public function loadDefaults( $name = false, $actorId = null ) {
1005 $this->mId = 0;
1006 $this->mName = $name;
1007 $this->mActorId = $actorId;
1008 $this->mRealName = '';
1009 $this->mEmail = '';
1010 $this->isTemp = null;
1011
1012 $loggedOut = $this->mRequest && !defined( 'MW_NO_SESSION' )
1013 ? $this->mRequest->getSession()->getLoggedOutTimestamp() : 0;
1014 if ( $loggedOut !== 0 ) {
1015 $this->mTouched = wfTimestamp( TS::MW, $loggedOut );
1016 } else {
1017 $this->mTouched = '1'; # Allow any pages to be cached
1018 }
1019
1020 $this->mToken = null; // Don't run cryptographic functions till we need a token
1021 $this->mEmailAuthenticated = null;
1022 $this->mEmailToken = '';
1023 $this->mEmailTokenExpires = null;
1024
1025 $this->getHookRunner()->onUserLoadDefaults( $this, $name );
1026 }
1027
1040 public function isItemLoaded( $item, $all = 'all' ) {
1041 return ( $this->mLoadedItems === true && $all === 'all' ) ||
1042 ( isset( $this->mLoadedItems[$item] ) && $this->mLoadedItems[$item] === true );
1043 }
1044
1052 public function setItemLoaded( $item ) {
1053 if ( is_array( $this->mLoadedItems ) ) {
1054 $this->mLoadedItems[$item] = true;
1055 }
1056 }
1057
1063 private function loadFromSession() {
1064 // MediaWiki\Session\Session already did the necessary authentication of the user
1065 // returned here, so just use it if applicable.
1066 $session = $this->getRequest()->getSession();
1067 $user = $session->getUser();
1068 if ( $user->isRegistered() ) {
1069 $this->loadFromUserObject( $user );
1070
1071 // Other code expects these to be set in the session, so set them.
1072 $session->set( 'wsUserID', $this->getId() );
1073 $session->set( 'wsUserName', $this->getName() );
1074
1075 return true;
1076 }
1077
1078 return false;
1079 }
1080
1088 public function loadFromDatabase( $flags = IDBAccessObject::READ_LATEST ) {
1089 // Paranoia
1090 $this->mId = intval( $this->mId );
1091
1092 if ( !$this->mId ) {
1093 // Anonymous users are not in the database
1094 $this->loadDefaults();
1095 return false;
1096 }
1097
1098 $db = DBAccessObjectUtils::getDBFromRecency(
1099 MediaWikiServices::getInstance()->getDBLoadBalancerFactory(),
1100 $flags
1101 );
1102 $row = self::newQueryBuilder( $db )
1103 ->where( [ 'user_id' => $this->mId ] )
1104 ->recency( $flags )
1105 ->caller( __METHOD__ )
1106 ->fetchRow();
1107
1108 $this->queryFlagsUsed = $flags;
1109
1110 if ( $row !== false ) {
1111 // Initialise user table data
1112 $this->loadFromRow( $row );
1113 return true;
1114 }
1115
1116 // Invalid user_id
1117 $this->mId = 0;
1118 $this->loadDefaults( 'Unknown user' );
1119
1120 return false;
1121 }
1122
1134 protected function loadFromRow( $row, $data = null ) {
1135 if ( !( $row instanceof stdClass ) ) {
1136 throw new InvalidArgumentException( '$row must be an object' );
1137 }
1138
1139 $all = true;
1140
1141 if ( isset( $row->actor_id ) ) {
1142 $this->mActorId = (int)$row->actor_id;
1143 if ( $this->mActorId !== 0 ) {
1144 $this->mFrom = 'actor';
1145 }
1146 $this->setItemLoaded( 'actor' );
1147 } else {
1148 $all = false;
1149 }
1150
1151 if ( isset( $row->user_name ) && $row->user_name !== '' ) {
1152 $this->mName = $row->user_name;
1153 $this->mFrom = 'name';
1154 $this->setItemLoaded( 'name' );
1155 } else {
1156 $all = false;
1157 }
1158
1159 if ( isset( $row->user_real_name ) ) {
1160 $this->mRealName = $row->user_real_name;
1161 $this->setItemLoaded( 'realname' );
1162 } else {
1163 $all = false;
1164 }
1165
1166 if ( isset( $row->user_id ) ) {
1167 $this->mId = intval( $row->user_id );
1168 if ( $this->mId !== 0 ) {
1169 $this->mFrom = 'id';
1170 }
1171 $this->setItemLoaded( 'id' );
1172 } else {
1173 $all = false;
1174 }
1175
1176 if ( isset( $row->user_editcount ) ) {
1177 // Don't try to set edit count for anonymous users
1178 // We check the id here and not in UserEditTracker because calling
1179 // User::getId() can trigger some other loading. This will result in
1180 // discarding the user_editcount field for rows if the id wasn't set.
1181 if ( $this->mId !== null && $this->mId !== 0 ) {
1182 MediaWikiServices::getInstance()
1183 ->getUserEditTracker()
1184 ->setCachedUserEditCount( $this, (int)$row->user_editcount );
1185 }
1186 } else {
1187 $all = false;
1188 }
1189
1190 if ( isset( $row->user_touched ) ) {
1191 $this->mTouched = wfTimestamp( TS::MW, $row->user_touched );
1192 } else {
1193 $all = false;
1194 }
1195
1196 if ( isset( $row->user_token ) ) {
1197 // The definition for the column is binary(32), so trim the NULs
1198 // that appends. The previous definition was char(32), so trim
1199 // spaces too.
1200 $this->mToken = rtrim( $row->user_token, " \0" );
1201 if ( $this->mToken === '' ) {
1202 $this->mToken = null;
1203 }
1204 } else {
1205 $all = false;
1206 }
1207
1208 if ( isset( $row->user_email ) ) {
1209 $this->mEmail = $row->user_email;
1210 $this->mEmailAuthenticated = wfTimestampOrNull( TS::MW, $row->user_email_authenticated );
1211 $this->mEmailToken = $row->user_email_token;
1212 $this->mEmailTokenExpires = wfTimestampOrNull( TS::MW, $row->user_email_token_expires );
1213 $registration = wfTimestampOrNull( TS::MW, $row->user_registration );
1214 MediaWikiServices::getInstance()
1215 ->getUserRegistrationLookup()
1216 ->setCachedRegistration( $this, $registration );
1217 } else {
1218 $all = false;
1219 }
1220
1221 if ( $all ) {
1222 $this->mLoadedItems = true;
1223 }
1224
1225 if ( is_array( $data ) ) {
1226
1227 if ( isset( $data['user_groups'] ) && is_array( $data['user_groups'] ) ) {
1228 MediaWikiServices::getInstance()
1229 ->getUserGroupManager()
1230 ->loadGroupMembershipsFromArray(
1231 $this,
1232 $data['user_groups'],
1233 $this->queryFlagsUsed
1234 );
1235 }
1236 }
1237 }
1238
1244 protected function loadFromUserObject( $user ) {
1245 $user->load();
1246 foreach ( self::$mCacheVars as $var ) {
1247 $this->$var = $user->$var;
1248 }
1249 }
1250
1258 protected function makeUpdateConditions( IReadableDatabase $db ) {
1259 if ( $this->mTouched ) {
1260 // CAS check: only update if the row wasn't changed since it was loaded.
1261 return [ 'user_touched' => $db->timestamp( $this->mTouched ) ];
1262 }
1263 return [];
1264 }
1265
1276 public function checkAndSetTouched() {
1277 $this->load();
1278
1279 if ( !$this->mId ) {
1280 return false; // anon
1281 }
1282
1283 // Get a new user_touched that is higher than the old one
1284 $newTouched = $this->newTouchedTimestamp();
1285
1286 $dbw = MediaWikiServices::getInstance()->getConnectionProvider()->getPrimaryDatabase();
1287 $dbw->newUpdateQueryBuilder()
1288 ->update( 'user' )
1289 ->set( [ 'user_touched' => $dbw->timestamp( $newTouched ) ] )
1290 ->where( [ 'user_id' => $this->mId ] )
1291 ->andWhere( $this->makeUpdateConditions( $dbw ) )
1292 ->caller( __METHOD__ )->execute();
1293 $success = ( $dbw->affectedRows() > 0 );
1294
1295 if ( $success ) {
1296 $this->mTouched = $newTouched;
1297 $this->clearSharedCache( 'changed' );
1298 } else {
1299 // Clears on failure too since that is desired if the cache is stale
1300 $this->clearSharedCache( 'refresh' );
1301 }
1302
1303 return $success;
1304 }
1305
1313 public function clearInstanceCache( $reloadFrom = false ) {
1314 global $wgFullyInitialised;
1315
1316 $this->mDatePreference = null;
1317 $this->mThisAsAuthority = null;
1318
1319 if ( $wgFullyInitialised && $this->mFrom ) {
1320 $services = MediaWikiServices::getInstance();
1321
1322 if ( $services->peekService( 'PermissionManager' ) ) {
1323 $services->getPermissionManager()->invalidateUsersRightsCache( $this );
1324 }
1325
1326 if ( $services->peekService( 'UserOptionsManager' ) ) {
1327 $services->getUserOptionsManager()->clearUserOptionsCache( $this );
1328 }
1329
1330 if ( $services->peekService( 'TalkPageNotificationManager' ) ) {
1331 $services->getTalkPageNotificationManager()->clearInstanceCache( $this );
1332 }
1333
1334 if ( $services->peekService( 'UserGroupManager' ) ) {
1335 $services->getUserGroupManager()->clearCache( $this );
1336 }
1337
1338 if ( $services->peekService( 'UserEditTracker' ) ) {
1339 $services->getUserEditTracker()->clearUserEditCache( $this );
1340 }
1341
1342 if ( $services->peekService( 'BlockManager' ) ) {
1343 $services->getBlockManager()->clearUserCache( $this );
1344 }
1345 }
1346
1347 if ( $reloadFrom ) {
1348 if ( in_array( $reloadFrom, [ 'name', 'id', 'actor' ] ) ) {
1349 $this->mLoadedItems = [ $reloadFrom => true ];
1350 } else {
1351 $this->mLoadedItems = [];
1352 }
1353 $this->mFrom = $reloadFrom;
1354 }
1355 }
1356
1362 public function isPingLimitable() {
1363 $limiter = MediaWikiServices::getInstance()->getRateLimiter();
1364 $subject = $this->toRateLimitSubject();
1365 return !$limiter->isExempt( $subject );
1366 }
1367
1383 public function pingLimiter( $action = 'edit', $incrBy = 1 ) {
1384 return $this->getThisAsAuthority()->limit( $action, $incrBy, null );
1385 }
1386
1392 $flags = [
1393 'exempt' => $this->isAllowed( 'noratelimit' ),
1394 'newbie' => $this->isNewbie(),
1395 ];
1396
1397 return new RateLimitSubject( $this, $this->getRequest()->getIP(), $flags );
1398 }
1399
1412 public function getBlock(
1413 $freshness = IDBAccessObject::READ_NORMAL,
1414 $disableIpBlockExemptChecking = false
1415 ): ?Block {
1416 if ( is_bool( $freshness ) ) {
1417 $fromReplica = $freshness;
1418 } else {
1419 $fromReplica = ( $freshness !== IDBAccessObject::READ_LATEST );
1420 }
1421
1422 if ( $disableIpBlockExemptChecking ) {
1423 $isExempt = false;
1424 } else {
1425 $isExempt = $this->isAllowed( 'ipblock-exempt' );
1426 }
1427
1428 // TODO: Block checking shouldn't really be done from the User object. Block
1429 // checking can involve checking for IP blocks, cookie blocks, and/or XFF blocks,
1430 // which need more knowledge of the request context than the User should have.
1431 // Since we do currently check blocks from the User, we have to do the following
1432 // here:
1433 // - Check if this is the user associated with the main request
1434 // - If so, pass the relevant request information to the block manager
1435 $request = null;
1436 if ( !$isExempt && $this->isGlobalSessionUser() ) {
1437 // This is the global user, so we need to pass the request
1438 $request = $this->getRequest();
1439 }
1440
1441 return MediaWikiServices::getInstance()->getBlockManager()->getBlock(
1442 $this,
1443 $request,
1444 $fromReplica,
1445 );
1446 }
1447
1453 public function isLocked() {
1454 if ( $this->mLocked !== null ) {
1455 return $this->mLocked;
1456 }
1457 // Reset for hook
1458 $this->mLocked = false;
1459 $this->getHookRunner()->onUserIsLocked( $this, $this->mLocked );
1460 return $this->mLocked;
1461 }
1462
1468 public function isHidden() {
1469 $block = $this->getBlock( disableIpBlockExemptChecking: true );
1470 return $block ? $block->getHideName() : false;
1471 }
1472
1478 public function getId( $wikiId = self::LOCAL ): int {
1479 $this->assertWiki( $wikiId );
1480 if ( $this->mId === null && $this->mName !== null ) {
1481 $userNameUtils = MediaWikiServices::getInstance()->getUserNameUtils();
1482 if ( $userNameUtils->isIP( $this->mName ) || ExternalUserNames::isExternal( $this->mName ) ) {
1483 // Special case, we know the user is anonymous
1484 // Note that "external" users are "local" (they have an actor ID that is relative to
1485 // the local wiki).
1486 return 0;
1487 }
1488 }
1489
1490 if ( !$this->isItemLoaded( 'id' ) ) {
1491 // Don't load if this was initialized from an ID
1492 $this->load();
1493 }
1494
1495 return (int)$this->mId;
1496 }
1497
1502 public function setId( $v ) {
1503 $this->mId = $v;
1504 $this->clearInstanceCache( 'id' );
1505 }
1506
1511 public function getName(): string {
1512 if ( $this->isItemLoaded( 'name', 'only' ) ) {
1513 // Special case optimisation
1514 return $this->mName;
1515 }
1516
1517 $this->load();
1518 if ( $this->mName === false ) {
1519 // Clean up IPs
1520 $this->mName = IPUtils::sanitizeIP( $this->getRequest()->getIP() );
1521 }
1522
1523 return $this->mName;
1524 }
1525
1539 public function setName( $str ) {
1540 $this->load();
1541 $this->mName = $str;
1542 }
1543
1557 public function getActorId( $dbwOrWikiId = self::LOCAL ): int {
1558 if ( $dbwOrWikiId ) {
1559 wfDeprecatedMsg( 'Passing a parameter to getActorId() is deprecated', '1.36' );
1560 }
1561
1562 if ( is_string( $dbwOrWikiId ) ) {
1563 $this->assertWiki( $dbwOrWikiId );
1564 }
1565
1566 if ( !$this->isItemLoaded( 'actor' ) ) {
1567 $this->load();
1568 }
1569
1570 if ( !$this->mActorId && $dbwOrWikiId instanceof IDatabase ) {
1571 MediaWikiServices::getInstance()
1572 ->getActorStoreFactory()
1573 ->getActorNormalization( $dbwOrWikiId->getDomainID() )
1574 ->acquireActorId( $this, $dbwOrWikiId );
1575 // acquireActorId will call setActorId on $this
1576 Assert::postcondition(
1577 $this->mActorId !== null,
1578 "Failed to acquire actor ID for user id {$this->mId} name {$this->mName}"
1579 );
1580 }
1581
1582 return (int)$this->mActorId;
1583 }
1584
1594 public function setActorId( int $actorId ) {
1595 $this->mActorId = $actorId;
1596 $this->setItemLoaded( 'actor' );
1597 }
1598
1603 public function getTitleKey(): string {
1604 return str_replace( ' ', '_', $this->getName() );
1605 }
1606
1613 private function newTouchedTimestamp() {
1614 $time = (int)ConvertibleTimestamp::now( TS::UNIX );
1615 if ( $this->mTouched ) {
1616 $time = max( $time, (int)ConvertibleTimestamp::convert( TS::UNIX, $this->mTouched ) + 1 );
1617 }
1618
1619 return ConvertibleTimestamp::convert( TS::MW, $time );
1620 }
1621
1632 public function clearSharedCache( $mode = 'refresh' ) {
1633 if ( !$this->getId() ) {
1634 return;
1635 }
1636
1637 $dbProvider = MediaWikiServices::getInstance()->getConnectionProvider();
1638 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
1639 $key = $this->getCacheKey( $cache );
1640
1641 if ( $mode === 'refresh' ) {
1642 $cache->delete( $key, 1 ); // low tombstone/"hold-off" TTL
1643 } else {
1644 $dbProvider->getPrimaryDatabase()->onTransactionPreCommitOrIdle(
1645 static function () use ( $cache, $key ) {
1646 $cache->delete( $key );
1647 },
1648 __METHOD__
1649 );
1650 }
1651 }
1652
1658 public function invalidateCache() {
1659 $this->touch();
1660 $this->clearSharedCache( 'changed' );
1661 }
1662
1675 public function touch() {
1676 $id = $this->getId();
1677 if ( $id ) {
1678 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
1679 $key = $cache->makeKey( 'user-quicktouched', 'id', $id );
1680 $cache->touchCheckKey( $key );
1681 $this->mQuickTouched = null;
1682 }
1683 }
1684
1690 public function debouncedDBTouch() {
1691 $oldTouched = (int)ConvertibleTimestamp::convert( TS::UNIX, $this->getDBTouched() );
1692 $newTouched = (int)ConvertibleTimestamp::now( TS::UNIX );
1693
1694 if ( ( $newTouched - $oldTouched ) < ( 300 + mt_rand( 1, 20 ) ) ) {
1695 // Touched would be updated too soon, skip this round
1696 // Adding jitter to avoid stampede.
1697 return;
1698 }
1699
1700 // Too old, definitely update.
1701 $this->checkAndSetTouched();
1702 }
1703
1709 public function validateCache( $timestamp ) {
1710 return ( $timestamp >= $this->getTouched() );
1711 }
1712
1721 public function getTouched() {
1722 $this->load();
1723
1724 if ( $this->mId ) {
1725 if ( $this->mQuickTouched === null ) {
1726 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
1727 $key = $cache->makeKey( 'user-quicktouched', 'id', $this->mId );
1728
1729 $this->mQuickTouched = wfTimestamp( TS::MW, $cache->getCheckKeyTime( $key ) );
1730 }
1731
1732 return max( $this->mTouched, $this->mQuickTouched );
1733 }
1734
1735 return $this->mTouched;
1736 }
1737
1743 public function getDBTouched() {
1744 $this->load();
1745
1746 return $this->mTouched;
1747 }
1748
1761 public function changeAuthenticationData( array $data ) {
1762 $manager = MediaWikiServices::getInstance()->getAuthManager();
1763 $reqs = $manager->getAuthenticationRequests( AuthManager::ACTION_CHANGE, $this );
1764 $reqs = AuthenticationRequest::loadRequestsFromSubmission( $reqs, $data );
1765
1766 $status = Status::newGood( 'ignored' );
1767 foreach ( $reqs as $req ) {
1768 $status->merge( $manager->allowsAuthenticationDataChange( $req ), true );
1769 }
1770 if ( $status->getValue() === 'ignored' ) {
1771 $status->warning( 'authenticationdatachange-ignored' );
1772 }
1773
1774 if ( $status->isGood() ) {
1775 foreach ( $reqs as $req ) {
1776 $manager->changeAuthenticationData( $req );
1777 }
1778 }
1779 return $status;
1780 }
1781
1788 public function getToken( $forceCreation = true ) {
1789 $authenticationTokenVersion = MediaWikiServices::getInstance()
1790 ->getMainConfig()->get( MainConfigNames::AuthenticationTokenVersion );
1791
1792 $this->load();
1793 if ( !$this->mToken && $forceCreation ) {
1794 $this->setToken();
1795 }
1796
1797 if ( !$this->mToken ) {
1798 // The user doesn't have a token, return null to indicate that.
1799 return null;
1800 }
1801
1802 if ( $this->mToken === self::INVALID_TOKEN ) {
1803 // We return a random value here so existing token checks are very
1804 // likely to fail.
1805 return MWCryptRand::generateHex( self::TOKEN_LENGTH );
1806 }
1807
1808 if ( $authenticationTokenVersion === null ) {
1809 // $wgAuthenticationTokenVersion not in use, so return the raw secret
1810 return $this->mToken;
1811 }
1812
1813 // $wgAuthenticationTokenVersion in use, so hmac it.
1814 $ret = MWCryptHash::hmac( $authenticationTokenVersion, $this->mToken, false );
1815
1816 // The raw hash can be overly long. Shorten it up.
1817 $len = max( 32, self::TOKEN_LENGTH );
1818 if ( strlen( $ret ) < $len ) {
1819 // Should never happen, even md5 is 128 bits
1820 throw new \UnexpectedValueException( 'Hmac returned less than 128 bits' );
1821 }
1822
1823 return substr( $ret, -$len );
1824 }
1825
1832 public function setToken( $token = false ) {
1833 $this->load();
1834 if ( $this->mToken === self::INVALID_TOKEN ) {
1835 LoggerFactory::getInstance( 'session' )
1836 ->debug( __METHOD__ . ": Ignoring attempt to set token for system user \"$this\"" );
1837 } elseif ( !$token ) {
1838 $this->mToken = MWCryptRand::generateHex( self::TOKEN_LENGTH );
1839 } else {
1840 $this->mToken = $token;
1841 }
1842 }
1843
1848 public function getEmail(): string {
1849 $this->load();
1850 $email = $this->mEmail;
1851 $this->getHookRunner()->onUserGetEmail( $this, $email );
1852 // In case a hook handler returns e.g. null
1853 $this->mEmail = is_string( $email ) ? $email : '';
1854 return $this->mEmail;
1855 }
1856
1862 $this->load();
1863 $this->getHookRunner()->onUserGetEmailAuthenticationTimestamp(
1864 $this, $this->mEmailAuthenticated );
1865 return $this->mEmailAuthenticated;
1866 }
1867
1872 public function setEmail( string $str ) {
1873 $this->load();
1874 if ( $str == $this->getEmail() ) {
1875 return;
1876 }
1877 $this->invalidateEmail();
1878 $this->mEmail = $str;
1879 $this->getHookRunner()->onUserSetEmail( $this, $this->mEmail );
1880 }
1881
1889 public function setEmailWithConfirmation( string $str ) {
1890 $config = MediaWikiServices::getInstance()->getMainConfig();
1891 $enableEmail = $config->get( MainConfigNames::EnableEmail );
1892
1893 if ( !$enableEmail ) {
1894 return Status::newFatal( 'emaildisabled' );
1895 }
1896
1897 $oldaddr = $this->getEmail();
1898 if ( $str === $oldaddr ) {
1899 return Status::newGood( true );
1900 }
1901
1902 $type = $oldaddr != '' ? 'changed' : 'set';
1903 $notificationResult = null;
1904
1905 $emailAuthentication = $config->get( MainConfigNames::EmailAuthentication );
1906
1907 if ( $emailAuthentication && $type === 'changed' && $this->isEmailConfirmed() ) {
1908 $change = $str != '' ? 'changed' : 'removed';
1909 $notificationResult = Status::wrap(
1910 MediaWikiServices::getInstance()->getNotificationEmailSender()->sendNotificationMail(
1911 RequestContext::getMain(),
1912 $this,
1913 $change,
1914 $str
1915 )
1916 );
1917 }
1918
1919 $this->setEmail( $str );
1920
1921 if ( $str !== '' && $emailAuthentication ) {
1922 // Send a confirmation request to the new address if needed
1923 $result = $this->sendConfirmationMail( $type );
1924
1925 if ( $notificationResult !== null ) {
1926 $result->merge( $notificationResult );
1927 }
1928
1929 if ( $result->isGood() ) {
1930 // Say to the caller that a confirmation and notification mail has been sent
1931 $result->value = 'eauth';
1932 }
1933 } else {
1934 $result = Status::newGood( true );
1935 }
1936
1937 return $result;
1938 }
1939
1944 public function getRealName(): string {
1945 if ( !$this->isItemLoaded( 'realname' ) ) {
1946 $this->load();
1947 }
1948
1949 return $this->mRealName;
1950 }
1951
1956 public function setRealName( string $str ) {
1957 $this->load();
1958 $this->mRealName = $str;
1959 }
1960
1971 public function getTokenFromOption( $oname ) {
1972 $hiddenPrefs =
1973 MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::HiddenPrefs );
1974
1975 $id = $this->getId();
1976 if ( !$id || in_array( $oname, $hiddenPrefs ) ) {
1977 return false;
1978 }
1979
1980 $userOptionsLookup = MediaWikiServices::getInstance()
1981 ->getUserOptionsLookup();
1982 $token = $userOptionsLookup->getOption( $this, (string)$oname );
1983 if ( !$token ) {
1984 // Default to a value based on the user token to avoid space
1985 // wasted on storing tokens for all users. When this option
1986 // is set manually by the user, only then is it stored.
1987 $token = hash_hmac( 'sha1', "$oname:$id", $this->getToken() );
1988 }
1989
1990 return $token;
1991 }
1992
2002 public function resetTokenFromOption( $oname ) {
2003 $hiddenPrefs =
2004 MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::HiddenPrefs );
2005 if ( in_array( $oname, $hiddenPrefs ) ) {
2006 return false;
2007 }
2008
2009 $token = MWCryptRand::generateHex( 40 );
2010 MediaWikiServices::getInstance()
2011 ->getUserOptionsManager()
2012 ->setOption( $this, $oname, $token );
2013 return $token;
2014 }
2015
2020 public function getDatePreference() {
2021 // Important migration for old data rows
2022 if ( $this->mDatePreference === null ) {
2023 $lang = RequestContext::getMain()->getLanguage();
2024 $userOptionsLookup = MediaWikiServices::getInstance()
2025 ->getUserOptionsLookup();
2026 $value = $userOptionsLookup->getOption( $this, 'date' ) ?? 'default';
2027 $map = $lang->getDatePreferenceMigrationMap();
2028 if ( isset( $map[$value] ) ) {
2029 $value = $map[$value];
2030 }
2031 $this->mDatePreference = $value;
2032 }
2033 return $this->mDatePreference;
2034 }
2035
2042 public function requiresHTTPS() {
2043 if ( !$this->isRegistered() ) {
2044 return false;
2045 }
2046
2047 $services = MediaWikiServices::getInstance();
2048 $config = $services->getMainConfig();
2049 if ( $config->get( MainConfigNames::ForceHTTPS ) ) {
2050 return true;
2051 }
2052 if ( !$config->get( MainConfigNames::SecureLogin ) ) {
2053 return false;
2054 }
2055 return $services->getUserOptionsLookup()
2056 ->getBoolOption( $this, 'prefershttps' );
2057 }
2058
2063 public function getEditCount() {
2064 return MediaWikiServices::getInstance()
2065 ->getUserEditTracker()
2066 ->getUserEditCount( $this );
2067 }
2068
2077 public function isRegistered(): bool {
2078 return $this->getId() != 0;
2079 }
2080
2085 public function isAnon() {
2086 return !$this->isRegistered();
2087 }
2088
2093 public function isBot() {
2094 $userGroupManager = MediaWikiServices::getInstance()->getUserGroupManager();
2095 if ( in_array( 'bot', $userGroupManager->getUserGroups( $this ) )
2096 && $this->isAllowed( 'bot' )
2097 ) {
2098 return true;
2099 }
2100
2101 $isBot = false;
2102 $this->getHookRunner()->onUserIsBot( $this, $isBot );
2103
2104 return $isBot;
2105 }
2106
2116 public function isSystemUser() {
2117 $this->load();
2118 if ( $this->getEmail() || $this->mToken !== self::INVALID_TOKEN ||
2119 MediaWikiServices::getInstance()->getAuthManager()->userCanAuthenticate( $this->mName )
2120 ) {
2121 return false;
2122 }
2123 return true;
2124 }
2125
2127 public function isAllowedAny( ...$permissions ): bool {
2128 return $this->getThisAsAuthority()->isAllowedAny( ...$permissions );
2129 }
2130
2132 public function isAllowedAll( ...$permissions ): bool {
2133 return $this->getThisAsAuthority()->isAllowedAll( ...$permissions );
2134 }
2135
2136 public function isAllowed( string $permission, ?PermissionStatus $status = null ): bool {
2137 return $this->getThisAsAuthority()->isAllowed( $permission, $status );
2138 }
2139
2144 public function useRCPatrol() {
2145 $useRCPatrol =
2146 MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::UseRCPatrol );
2147 return $useRCPatrol && $this->isAllowedAny( 'patrol', 'patrolmarks' );
2148 }
2149
2154 public function useNPPatrol() {
2155 $useRCPatrol =
2156 MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::UseRCPatrol );
2157 $useNPPatrol =
2158 MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::UseNPPatrol );
2159 return (
2160 ( $useRCPatrol || $useNPPatrol )
2161 && ( $this->isAllowedAny( 'patrol', 'patrolmarks' ) )
2162 );
2163 }
2164
2169 public function useFilePatrol() {
2170 $useRCPatrol =
2171 MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::UseRCPatrol );
2172 $useFilePatrol = MediaWikiServices::getInstance()->getMainConfig()
2173 ->get( MainConfigNames::UseFilePatrol );
2174 return (
2175 ( $useRCPatrol || $useFilePatrol )
2176 && ( $this->isAllowedAny( 'patrol', 'patrolmarks' ) )
2177 );
2178 }
2179
2183 public function getRequest(): WebRequest {
2184 return $this->mRequest ?? RequestContext::getMain()->getRequest();
2185 }
2186
2192 public function getExperienceLevel() {
2193 $mainConfig = MediaWikiServices::getInstance()->getMainConfig();
2194 $learnerEdits = $mainConfig->get( MainConfigNames::LearnerEdits );
2195 $experiencedUserEdits = $mainConfig->get( MainConfigNames::ExperiencedUserEdits );
2196 $learnerMemberSince = $mainConfig->get( MainConfigNames::LearnerMemberSince );
2197 $experiencedUserMemberSince =
2198 $mainConfig->get( MainConfigNames::ExperiencedUserMemberSince );
2199 if ( $this->isAnon() ) {
2200 return false;
2201 }
2202
2203 $editCount = $this->getEditCount();
2204 $registration = $this->getRegistration();
2205 $now = time();
2206 $learnerRegistration = wfTimestamp( TS::MW, $now - $learnerMemberSince * 86400 );
2207 $experiencedRegistration = wfTimestamp( TS::MW, $now - $experiencedUserMemberSince * 86400 );
2208 if ( $registration === null ) {
2209 // for some very old accounts, this information is missing in the database
2210 // treat them as old enough to be 'experienced'
2211 $registration = $experiencedRegistration;
2212 }
2213
2214 if ( $editCount < $learnerEdits ||
2215 $registration > $learnerRegistration ) {
2216 return 'newcomer';
2217 }
2218
2219 if ( $editCount > $experiencedUserEdits &&
2220 $registration <= $experiencedRegistration
2221 ) {
2222 return 'experienced';
2223 }
2224
2225 return 'learner';
2226 }
2227
2236 public function setCookies( $request = null, $secure = null, $rememberMe = false ) {
2237 $this->load();
2238 if ( $this->mId == 0 ) {
2239 return;
2240 }
2241
2242 $session = $this->getRequest()->getSession();
2243 if ( $request && $session->getRequest() !== $request ) {
2244 $session = $session->sessionWithRequest( $request );
2245 }
2246 $delay = $session->delaySave();
2247
2248 if ( !$session->getUser()->equals( $this ) ) {
2249 if ( !$session->canSetUser() ) {
2250 LoggerFactory::getInstance( 'session' )
2251 ->warning( __METHOD__ .
2252 ": Cannot save user \"$this\" to a user " .
2253 "\"{$session->getUser()}\"'s immutable session"
2254 );
2255 return;
2256 }
2257 $session->setUser( $this );
2258 }
2259
2260 $session->setRememberUser( $rememberMe );
2261 if ( $secure !== null ) {
2262 $session->setForceHTTPS( $secure );
2263 }
2264
2265 $session->persist();
2266
2267 ScopedCallback::consume( $delay );
2268 }
2269
2273 public function logout() {
2274 if ( $this->getHookRunner()->onUserLogout( $this ) ) {
2275 $this->doLogout();
2276 }
2277 }
2278
2283 public function doLogout() {
2284 $session = $this->getRequest()->getSession();
2285 $accountType = MediaWikiServices::getInstance()->getUserIdentityUtils()->getShortUserTypeInternal( $this );
2286 if ( !$session->canSetUser() ) {
2287 LoggerFactory::getInstance( 'session' )
2288 ->warning( __METHOD__ . ": Cannot log out of an immutable session" );
2289 $error = 'immutable';
2290 } elseif ( !$session->getUser()->equals( $this ) ) {
2291 LoggerFactory::getInstance( 'session' )
2292 ->warning( __METHOD__ .
2293 ": Cannot log user \"$this\" out of a user \"{$session->getUser()}\"'s session"
2294 );
2295 // But we still may as well make this user object anon
2296 $this->clearInstanceCache( 'defaults' );
2297 $error = 'wronguser';
2298 } else {
2299 $this->clearInstanceCache( 'defaults' );
2300 $delay = $session->delaySave();
2301 $session->unpersist(); // Clear cookies (T127436)
2302 $session->setLoggedOutTimestamp( time() );
2303 $session->setUser( new User );
2304 $session->set( 'wsUserID', 0 ); // Other code expects this
2305 $session->resetAllTokens();
2306 ScopedCallback::consume( $delay );
2307 $error = false;
2308 }
2309 LoggerFactory::getInstance( 'authevents' )->info( 'Logout', [
2310 'event' => 'logout',
2311 'successful' => $error === false,
2312 'status' => $error ?: 'success',
2313 'accountType' => $accountType,
2314 ] );
2315 }
2316
2321 public function saveSettings() {
2322 if ( MediaWikiServices::getInstance()->getReadOnlyMode()->isReadOnly() ) {
2323 // @TODO: caller should deal with this instead!
2324 // This should really just be an exception.
2325 MWExceptionHandler::logException( new DBExpectedError(
2326 null,
2327 "Could not update user with ID '{$this->mId}'; DB is read-only."
2328 ) );
2329 return;
2330 }
2331
2332 $this->load();
2333 if ( $this->mId == 0 ) {
2334 return; // anon
2335 }
2336
2337 // Get a new user_touched that is higher than the old one.
2338 // This will be used for a CAS check as a last-resort safety
2339 // check against race conditions and replica DB lag.
2340 $newTouched = $this->newTouchedTimestamp();
2341
2342 $dbw = MediaWikiServices::getInstance()->getConnectionProvider()->getPrimaryDatabase();
2343 $dbw->doAtomicSection( __METHOD__, function ( IDatabase $dbw, $fname ) use ( $newTouched ) {
2344 $dbw->newUpdateQueryBuilder()
2345 ->update( 'user' )
2346 ->set( [
2347 'user_name' => $this->mName,
2348 'user_real_name' => $this->mRealName,
2349 'user_email' => $this->mEmail,
2350 'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ),
2351 'user_touched' => $dbw->timestamp( $newTouched ),
2352 'user_token' => strval( $this->mToken ),
2353 'user_email_token' => $this->mEmailToken,
2354 'user_email_token_expires' => $dbw->timestampOrNull( $this->mEmailTokenExpires ),
2355 ] )
2356 ->where( [ 'user_id' => $this->mId ] )
2357 ->andWhere( $this->makeUpdateConditions( $dbw ) )
2358 ->caller( $fname )->execute();
2359
2360 if ( !$dbw->affectedRows() ) {
2361 // Maybe the problem was a missed cache update; clear it to be safe
2362 $this->clearSharedCache( 'refresh' );
2363 // User was changed in the meantime or loaded with stale data
2364 $from = ( $this->queryFlagsUsed & IDBAccessObject::READ_LATEST ) ? 'primary' : 'replica';
2365 LoggerFactory::getInstance( 'preferences' )->warning(
2366 "CAS update failed on user_touched for user ID '{user_id}' ({db_flag} read)",
2367 [ 'user_id' => $this->mId, 'db_flag' => $from ]
2368 );
2369 throw new RuntimeException( "CAS update failed on user_touched. " .
2370 "The version of the user to be saved is older than the current version."
2371 );
2372 }
2373
2374 $dbw->newUpdateQueryBuilder()
2375 ->update( 'actor' )
2376 ->set( [ 'actor_name' => $this->mName ] )
2377 ->where( [ 'actor_user' => $this->mId ] )
2378 ->caller( $fname )->execute();
2379 MediaWikiServices::getInstance()->getActorStore()->deleteUserIdentityFromCache( $this );
2380 } );
2381
2382 $this->mTouched = $newTouched;
2383 if ( $this->isNamed() ) {
2384 MediaWikiServices::getInstance()->getUserOptionsManager()->saveOptionsInternal( $this );
2385 }
2386
2387 $this->getHookRunner()->onUserSaveSettings( $this );
2388 $this->clearSharedCache( 'changed' );
2389 $hcu = MediaWikiServices::getInstance()->getHTMLCacheUpdater();
2390 $hcu->purgeTitleUrls( $this->getUserPage(), $hcu::PURGE_INTENT_TXROUND_REFLECTED );
2391 }
2392
2399 public function idForName( $flags = IDBAccessObject::READ_NORMAL ) {
2400 $s = trim( $this->getName() );
2401 if ( $s === '' ) {
2402 return 0;
2403 }
2404
2405 $db = DBAccessObjectUtils::getDBFromRecency(
2406 MediaWikiServices::getInstance()->getDBLoadBalancerFactory(),
2407 $flags
2408 );
2409 $id = $db->newSelectQueryBuilder()
2410 ->select( 'user_id' )
2411 ->from( 'user' )
2412 ->where( [ 'user_name' => $s ] )
2413 ->recency( $flags )
2414 ->caller( __METHOD__ )->fetchField();
2415
2416 return (int)$id;
2417 }
2418
2432 public static function createNew( $name, $params = [] ) {
2433 return self::insertNewUser( static function ( UserIdentity $actor, IDatabase $dbw ) {
2434 return MediaWikiServices::getInstance()->getActorStore()->createNewActor( $actor, $dbw );
2435 }, $name, $params );
2436 }
2437
2445 private static function insertNewUser( callable $insertActor, $name, $params = [] ) {
2446 foreach ( [ 'password', 'newpassword', 'newpass_time', 'password_expires' ] as $field ) {
2447 if ( isset( $params[$field] ) ) {
2448 wfDeprecated( __METHOD__ . " with param '$field'", '1.27' );
2449 unset( $params[$field] );
2450 }
2451 }
2452
2453 $user = new User;
2454 $user->load();
2455 $user->setToken(); // init token
2456 $dbw = MediaWikiServices::getInstance()->getConnectionProvider()->getPrimaryDatabase();
2457
2458 $noPass = PasswordFactory::newInvalidPassword()->toString();
2459
2460 $fields = [
2461 'user_name' => $name,
2462 'user_password' => $noPass,
2463 'user_newpassword' => $noPass,
2464 'user_email' => $user->mEmail,
2465 'user_email_authenticated' => $dbw->timestampOrNull( $user->mEmailAuthenticated ),
2466 'user_real_name' => $user->mRealName,
2467 'user_token' => strval( $user->mToken ),
2468 'user_registration' => $dbw->timestamp(),
2469 'user_editcount' => 0,
2470 'user_touched' => $dbw->timestamp( $user->newTouchedTimestamp() ),
2471 ];
2472 foreach ( $params as $name => $value ) {
2473 $fields["user_$name"] = $value;
2474 }
2475
2476 return $dbw->doAtomicSection( __METHOD__, static function ( IDatabase $dbw, $fname )
2477 use ( $fields, $insertActor )
2478 {
2479 $dbw->newInsertQueryBuilder()
2480 ->insertInto( 'user' )
2481 ->ignore()
2482 ->row( $fields )
2483 ->caller( $fname )->execute();
2484 if ( $dbw->affectedRows() ) {
2485 $newUser = self::newFromId( $dbw->insertId() );
2486 $newUser->mName = $fields['user_name'];
2487 // Don't pass $this, since calling ::getId, ::getName might force ::load
2488 // and this user might not be ready for the yet.
2489 $newUser->mActorId = $insertActor(
2490 new UserIdentityValue( $newUser->mId, $newUser->mName ),
2491 $dbw
2492 );
2493 // Load the user from primary DB to avoid replica lag
2494 $newUser->load( IDBAccessObject::READ_LATEST );
2495 } else {
2496 $newUser = null;
2497 }
2498 return $newUser;
2499 } );
2500 }
2501
2527 public function addToDatabase() {
2528 $this->load();
2529 if ( !$this->mToken ) {
2530 $this->setToken(); // init token
2531 }
2532
2533 if ( !is_string( $this->mName ) ) {
2534 throw new RuntimeException( "User name field is not set." );
2535 }
2536
2537 $this->mTouched = $this->newTouchedTimestamp();
2538
2539 $dbw = MediaWikiServices::getInstance()->getConnectionProvider()->getPrimaryDatabase();
2540 $status = $dbw->doAtomicSection( __METHOD__, function ( IDatabase $dbw, $fname ) {
2541 $noPass = PasswordFactory::newInvalidPassword()->toString();
2542 $dbw->newInsertQueryBuilder()
2543 ->insertInto( 'user' )
2544 ->ignore()
2545 ->row( [
2546 'user_name' => $this->mName,
2547 'user_password' => $noPass,
2548 'user_newpassword' => $noPass,
2549 'user_email' => $this->mEmail,
2550 'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ),
2551 'user_real_name' => $this->mRealName,
2552 'user_token' => strval( $this->mToken ),
2553 'user_registration' => $dbw->timestamp(),
2554 'user_editcount' => 0,
2555 'user_touched' => $dbw->timestamp( $this->mTouched ),
2556 'user_is_temp' => $this->isTemp(),
2557 ] )
2558 ->caller( $fname )->execute();
2559 if ( !$dbw->affectedRows() ) {
2560 // Use locking reads to bypass any REPEATABLE-READ snapshot.
2561 $this->mId = $dbw->newSelectQueryBuilder()
2562 ->select( 'user_id' )
2563 ->lockInShareMode()
2564 ->from( 'user' )
2565 ->where( [ 'user_name' => $this->mName ] )
2566 ->caller( $fname )->fetchField();
2567 $loaded = false;
2568 if ( $this->mId && $this->loadFromDatabase( IDBAccessObject::READ_LOCKING ) ) {
2569 $loaded = true;
2570 }
2571 if ( !$loaded ) {
2572 throw new RuntimeException( $fname . ": hit a key conflict attempting " .
2573 "to insert user '{$this->mName}' row, but it was not present in select!" );
2574 }
2575 return Status::newFatal( 'userexists' );
2576 }
2577 $this->mId = $dbw->insertId();
2578 $this->queryFlagsUsed = IDBAccessObject::READ_LATEST;
2579
2580 // Don't pass $this, since calling ::getId, ::getName might force ::load
2581 // and this user might not be ready for that yet.
2582 $this->mActorId = MediaWikiServices::getInstance()
2583 ->getActorNormalization()
2584 ->acquireActorId( new UserIdentityValue( $this->mId, $this->mName ), $dbw );
2585 return Status::newGood();
2586 } );
2587 if ( !$status->isGood() ) {
2588 return $status;
2589 }
2590
2591 // Clear instance cache other than user table data and actor, which is already accurate
2592 $this->clearInstanceCache();
2593
2594 if ( $this->isNamed() ) {
2595 MediaWikiServices::getInstance()->getUserOptionsManager()->saveOptions( $this );
2596 }
2597 return Status::newGood();
2598 }
2599
2606 public function scheduleSpreadBlock() {
2607 DeferredUpdates::addCallableUpdate( function () {
2608 // Permit master queries in a GET request
2609 $scope = Profiler::instance()->getTransactionProfiler()->silenceForScope();
2610 $this->spreadAnyEditBlock();
2611 ScopedCallback::consume( $scope );
2612 } );
2613 }
2614
2621 public function spreadAnyEditBlock() {
2622 if ( !$this->isRegistered() ) {
2623 return false;
2624 }
2625
2626 $blockWasSpread = false;
2627 $this->getHookRunner()->onSpreadAnyEditBlock( $this, $blockWasSpread );
2628
2629 $block = $this->getBlock();
2630 if ( $block ) {
2631 $blockWasSpread = $blockWasSpread || $this->spreadBlock( $block );
2632 }
2633
2634 return $blockWasSpread;
2635 }
2636
2643 protected function spreadBlock( Block $block ): bool {
2644 wfDebug( __METHOD__ . "()" );
2645 $this->load();
2646 if ( $this->mId == 0 ) {
2647 return false;
2648 }
2649
2650 $blockStore = MediaWikiServices::getInstance()->getDatabaseBlockStore();
2651 foreach ( $block->toArray() as $singleBlock ) {
2652 if ( $singleBlock instanceof DatabaseBlock && $singleBlock->isAutoblocking() ) {
2653 return (bool)$blockStore->doAutoblock( $singleBlock, $this->getRequest()->getIP() );
2654 }
2655 }
2656 return false;
2657 }
2658
2666 public function isBlockedFromEmailuser() {
2667 wfDeprecated( __METHOD__, '1.41' );
2668 $block = $this->getBlock();
2669 return $block && $block->appliesToRight( 'sendemail' );
2670 }
2671
2678 public function isBlockedFromUpload() {
2679 $block = $this->getBlock();
2680 return $block && $block->appliesToRight( 'upload' );
2681 }
2682
2687 public function isAllowedToCreateAccount() {
2688 return $this->getThisAsAuthority()->isDefinitelyAllowed( 'createaccount' );
2689 }
2690
2696 public function getUserPage() {
2697 return Title::makeTitle( NS_USER, $this->getName() );
2698 }
2699
2705 public function getTalkPage() {
2706 $title = $this->getUserPage();
2707 return $title->getTalkPage();
2708 }
2709
2717 public function isNewbie() {
2718 // IP users and temp account users are excluded from the autoconfirmed group.
2719 return !$this->isAllowed( 'autoconfirmed' );
2720 }
2721
2734 public function getEditTokenObject( $salt = '', $request = null ) {
2735 if ( $this->isAnon() ) {
2736 return new LoggedOutEditToken();
2737 }
2738
2739 if ( !$request ) {
2740 $request = $this->getRequest();
2741 }
2742 return $request->getSession()->getToken( $salt );
2743 }
2744
2759 public function getEditToken( $salt = '', $request = null ) {
2760 return $this->getEditTokenObject( $salt, $request )->toString();
2761 }
2762
2776 public function matchEditToken( $val, $salt = '', $request = null, $maxage = null ) {
2777 return $this->getEditTokenObject( $salt, $request )->match( $val, $maxage );
2778 }
2779
2788 public function sendConfirmationMail( $type = 'created' ) {
2789 $emailer = MediaWikiServices::getInstance()->getConfirmEmailSender();
2790 $expiration = null; // gets passed-by-ref and defined in next line
2791 $token = $this->getConfirmationToken( $expiration );
2792 $confirmationUrl = $this->getConfirmationTokenUrl( $token );
2793 $invalidateUrl = $this->getInvalidationTokenUrl( $token );
2794 $this->saveSettings();
2795
2796 return Status::wrap( $emailer->sendConfirmationMail(
2797 RequestContext::getMain(),
2798 $type,
2799 new ConfirmEmailData(
2800 $this->getUser(),
2801 $confirmationUrl,
2802 $invalidateUrl,
2803 $expiration
2804 )
2805 ) );
2806 }
2807
2819 public function sendMail( $subject, $body, $from = null, $replyto = null ) {
2820 $passwordSender = MediaWikiServices::getInstance()->getMainConfig()
2821 ->get( MainConfigNames::PasswordSender );
2822
2823 if ( $from instanceof User ) {
2824 $sender = MailAddress::newFromUser( $from );
2825 } else {
2826 $sender = new MailAddress( $passwordSender,
2827 wfMessage( 'emailsender' )->inContentLanguage()->text() );
2828 }
2829 $to = MailAddress::newFromUser( $this );
2830
2831 if ( is_array( $body ) ) {
2832 $bodyText = $body['text'] ?? '';
2833 $bodyHtml = $body['html'] ?? null;
2834 } else {
2835 $bodyText = $body;
2836 $bodyHtml = null;
2837 }
2838
2839 return Status::wrap( MediaWikiServices::getInstance()->getEmailer()
2840 ->send(
2841 [ $to ],
2842 $sender,
2843 $subject,
2844 $bodyText,
2845 $bodyHtml,
2846 [ 'replyTo' => $replyto ]
2847 ) );
2848 }
2849
2865 public function getConfirmationToken(
2866 ?string &$expiration,
2867 ?int $tokenLifeTimeSeconds = null
2868 ): string {
2869 $tokenLifeTimeSeconds ??= MediaWikiServices::getInstance()
2870 ->getMainConfig()->get( MainConfigNames::UserEmailConfirmationTokenExpiry );
2871 $now = ConvertibleTimestamp::time();
2872
2873 $expires = $now + $tokenLifeTimeSeconds;
2874 $expiration = wfTimestamp( TS::MW, $expires );
2875 $this->load();
2876 $token = MWCryptRand::generateHex( 32 );
2877 $hash = md5( $token );
2878 $this->mEmailToken = $hash;
2879 $this->mEmailTokenExpires = $expiration;
2880 return $token;
2881 }
2882
2889 protected function confirmationToken( &$expiration ) {
2890 return $this->getConfirmationToken( $expiration );
2891 }
2892
2899 public static function isWellFormedConfirmationToken( string $token ): bool {
2900 return preg_match( '/^[a-f0-9]{32}$/', $token );
2901 }
2902
2910 public function getConfirmationTokenUrl( string $token ): string {
2911 return $this->getTokenUrl( 'ConfirmEmail', $token );
2912 }
2913
2921 public function getInvalidationTokenUrl( string $token ): string {
2922 return $this->getTokenUrl( 'InvalidateEmail', $token );
2923 }
2924
2932 protected function invalidationTokenUrl( $token ) {
2933 return $this->getTokenUrl( 'InvalidateEmail', $token );
2934 }
2935
2951 public function getTokenUrl( string $page, string $token ): string {
2952 // Hack to bypass localization of 'Special:'
2953 $title = Title::makeTitle( NS_MAIN, "Special:$page/$token" );
2954 return $title->getCanonicalURL();
2955 }
2956
2964 public function confirmEmail() {
2965 // Check if it's already confirmed, so we don't touch the database
2966 // and fire the ConfirmEmailComplete hook on redundant confirmations.
2967 if ( !$this->isEmailConfirmed() ) {
2968 $this->setEmailAuthenticationTimestamp( wfTimestampNow() );
2969 $this->getHookRunner()->onConfirmEmailComplete( $this );
2970 $logContext = [ 'event' => 'email_confirmed' ];
2971 if ( $this->mEmailTokenExpires !== null ) {
2972 $tokenLifetime = MediaWikiServices::getInstance()->getMainConfig()
2973 ->get( MainConfigNames::UserEmailConfirmationTokenExpiry );
2974 $tokenExpiry = wfTimestamp( TS_UNIX, $this->mEmailTokenExpires );
2975 if ( is_numeric( $tokenLifetime ) && $tokenExpiry !== false ) {
2976 $emailSentAt = (int)$tokenExpiry - (int)$tokenLifetime;
2977 $delaySec = max( 0, (int)ConvertibleTimestamp::time() - $emailSentAt );
2978 $logContext['confirmation_delay_seconds'] = $delaySec;
2979 }
2980 }
2981 LoggerFactory::getInstance( 'confirmemail' )->info( 'Email address confirmed', $logContext );
2982 }
2983 return true;
2984 }
2985
2993 public function invalidateEmail() {
2994 $this->load();
2995 $this->mEmailToken = null;
2996 $this->mEmailTokenExpires = null;
2997 $this->setEmailAuthenticationTimestamp( null );
2998 $this->mEmail = '';
2999 $this->getHookRunner()->onInvalidateEmailComplete( $this );
3000 return true;
3001 }
3002
3007 public function setEmailAuthenticationTimestamp( $timestamp ) {
3008 $this->load();
3009 $this->mEmailAuthenticated = $timestamp;
3010 $this->getHookRunner()->onUserSetEmailAuthenticationTimestamp(
3011 $this, $this->mEmailAuthenticated );
3012 }
3013
3021 public function canSendEmail() {
3022 wfDeprecated( __METHOD__, '1.41' );
3023 $permError = MediaWikiServices::getInstance()->getEmailUserFactory()
3024 ->newEmailUser( $this->getThisAsAuthority() )
3025 ->canSend();
3026 return $permError->isGood();
3027 }
3028
3034 public function canReceiveEmail() {
3035 $userOptionsLookup = MediaWikiServices::getInstance()
3036 ->getUserOptionsLookup();
3037 return $this->isEmailConfirmed() && !$userOptionsLookup->getOption( $this, 'disablemail' );
3038 }
3039
3050 public function isEmailConfirmed(): bool {
3051 $emailAuthentication = MediaWikiServices::getInstance()->getMainConfig()
3052 ->get( MainConfigNames::EmailAuthentication );
3053 $this->load();
3054 $confirmed = true;
3055 if ( $this->getHookRunner()->onEmailConfirmed( $this, $confirmed ) ) {
3056 return !$this->isAnon() &&
3057 Sanitizer::validateEmail( $this->getEmail() ) &&
3058 ( !$emailAuthentication || $this->getEmailAuthenticationTimestamp() );
3059 }
3060
3061 return $confirmed;
3062 }
3063
3068 public function isEmailConfirmationPending() {
3069 $emailAuthentication = MediaWikiServices::getInstance()->getMainConfig()
3070 ->get( MainConfigNames::EmailAuthentication );
3071 return $emailAuthentication &&
3072 !$this->isEmailConfirmed() &&
3073 $this->mEmailToken &&
3074 $this->mEmailTokenExpires > wfTimestamp();
3075 }
3076
3085 public function getRegistration() {
3086 return MediaWikiServices::getInstance()
3087 ->getUserRegistrationLookup()
3088 ->getRegistration( $this );
3089 }
3090
3098 public static function getRightDescription( $right ) {
3099 $key = "right-$right";
3100 $msg = wfMessage( $key );
3101 return $msg->isDisabled() ? $right : $msg->text();
3102 }
3103
3111 public static function getRightDescriptionHtml( $right ) {
3112 return wfMessage( "right-$right" )->parse();
3113 }
3114
3128 public static function getQueryInfo() {
3129 return [
3130 'tables' => [ 'user', 'user_actor' => 'actor' ],
3131 'fields' => [
3132 'user_id',
3133 'user_name',
3134 'user_real_name',
3135 'user_email',
3136 'user_touched',
3137 'user_token',
3138 'user_email_authenticated',
3139 'user_email_token',
3140 'user_email_token_expires',
3141 'user_registration',
3142 'user_editcount',
3143 'user_actor.actor_id',
3144 ],
3145 'joins' => [
3146 'user_actor' => [ 'JOIN', 'user_actor.actor_user = user_id' ],
3147 ],
3148 ];
3149 }
3150
3160 public static function newQueryBuilder( IReadableDatabase $db ) {
3161 return $db->newSelectQueryBuilder()
3162 ->select( [
3163 'user_id',
3164 'user_name',
3165 'user_real_name',
3166 'user_email',
3167 'user_touched',
3168 'user_token',
3169 'user_email_authenticated',
3170 'user_email_token',
3171 'user_email_token_expires',
3172 'user_registration',
3173 'user_editcount',
3174 'user_actor.actor_id',
3175 ] )
3176 ->from( 'user' )
3177 ->join( 'actor', 'user_actor', 'user_actor.actor_user = user_id' );
3178 }
3179
3190 public static function newFatalPermissionDeniedStatus( $permission ) {
3191 return Status::wrap( MediaWikiServices::getInstance()->getPermissionManager()
3192 ->newFatalPermissionDeniedStatus(
3193 $permission,
3194 RequestContext::getMain()
3195 ) );
3196 }
3197
3213 public function getInstanceForUpdate() {
3214 wfDeprecated( __METHOD__, '1.46' );
3215 return $this->getInstanceFromPrimary( IDBAccessObject::READ_EXCLUSIVE );
3216 }
3217
3235 public function getInstanceFromPrimary( int $loadFlags = IDBAccessObject::READ_LATEST ): ?User {
3236 if ( $this->isAnon() ) {
3237 return null;
3238 } elseif ( ( $loadFlags & $this->queryFlagsUsed ) === $loadFlags ) {
3239 return $this;
3240 }
3241
3242 $user = self::newFromId( $this->getId() );
3243 if ( !$user->loadFromId( $loadFlags ) ) {
3244 return null;
3245 }
3246
3247 return $user;
3248 }
3249
3257 public function equals( ?UserIdentity $user ): bool {
3258 if ( !$user ) {
3259 return false;
3260 }
3261 // XXX it's not clear whether central ID providers are supposed to obey this
3262 return $this->getName() === $user->getName();
3263 }
3264
3270 public function getUser(): UserIdentity {
3271 return $this;
3272 }
3273
3281 public function probablyCan(
3282 string $action,
3283 PageIdentity $target,
3284 ?PermissionStatus $status = null
3285 ): bool {
3286 return $this->getThisAsAuthority()->probablyCan( $action, $target, $status );
3287 }
3288
3296 public function definitelyCan(
3297 string $action,
3298 PageIdentity $target,
3299 ?PermissionStatus $status = null
3300 ): bool {
3301 return $this->getThisAsAuthority()->definitelyCan( $action, $target, $status );
3302 }
3303
3312 public function isDefinitelyAllowed( string $action, ?PermissionStatus $status = null ): bool {
3313 return $this->getThisAsAuthority()->isDefinitelyAllowed( $action, $status );
3314 }
3315
3324 public function authorizeAction( string $action, ?PermissionStatus $status = null ): bool {
3325 return $this->getThisAsAuthority()->authorizeAction( $action, $status );
3326 }
3327
3335 public function authorizeRead(
3336 string $action,
3337 PageIdentity $target,
3338 ?PermissionStatus $status = null
3339 ): bool {
3340 return $this->getThisAsAuthority()->authorizeRead( $action, $target, $status );
3341 }
3342
3350 public function authorizeWrite(
3351 string $action, PageIdentity $target,
3352 ?PermissionStatus $status = null
3353 ): bool {
3354 return $this->getThisAsAuthority()->authorizeWrite( $action, $target, $status );
3355 }
3356
3361 private function getThisAsAuthority(): UserAuthority {
3362 if ( !$this->mThisAsAuthority ) {
3363 // TODO: For users that are not User::isGlobalSessionUser,
3364 // creating a UserAuthority here is incorrect, since it depends
3365 // on global WebRequest, but that is what we've used to do before Authority.
3366 // When PermissionManager is refactored into Authority, we need
3367 // to provide base implementation, based on just user groups/rights,
3368 // and use it here.
3369 $request = $this->getRequest();
3370 $uiContext = RequestContext::getMain();
3371
3372 $services = MediaWikiServices::getInstance();
3373 $this->mThisAsAuthority = new UserAuthority(
3374 $this,
3375 $request,
3376 $uiContext,
3377 $services->getPermissionManager(),
3378 $services->getRateLimiter(),
3379 $services->getFormatterFactory()->getBlockErrorFormatter( $uiContext )
3380 );
3381 }
3382
3383 return $this->mThisAsAuthority;
3384 }
3385
3389 private function isGlobalSessionUser(): bool {
3390 // The session user is set up towards the end of Setup.php. Until then,
3391 // assume it's a logged-out user.
3392 $sessionUser = RequestContext::getMain()->getUser();
3393 $globalUserName = $sessionUser->isSafeToLoad()
3394 ? $sessionUser->getName()
3395 : IPUtils::sanitizeIP( $sessionUser->getRequest()->getIP() );
3396
3397 return $this->getName() === $globalUserName;
3398 }
3399
3405 public function isTemp(): bool {
3406 if ( $this->isTemp === null ) {
3407 $this->isTemp = MediaWikiServices::getInstance()->getUserIdentityUtils()
3408 ->isTemp( $this );
3409 }
3410 return $this->isTemp;
3411 }
3412
3419 public function isNamed(): bool {
3420 return $this->isRegistered() && !$this->isTemp();
3421 }
3422}
3423
3425class_alias( User::class, 'User' );
const NS_USER
Definition Defines.php:53
const NS_MAIN
Definition Defines.php:51
wfTimestampOrNull( $outputtype=TS::UNIX, $ts=null)
Return a formatted timestamp, or null if input is null.
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
wfDeprecatedMsg( $msg, $version=false, $component=false, $callerOffset=2)
Log a deprecation warning with arbitrary message text.
wfLogWarning( $msg, $callerOffset=1, $level=E_USER_WARNING)
Send a warning as a PHP error and the debug log.
wfTimestamp( $outputtype=TS::UNIX, $ts=0)
Get a timestamp string in one of various formats.
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
global $wgFullyInitialised
Definition Setup.php:561
if(!defined('MW_SETUP_CALLBACK'))
Definition WebStart.php:71
const MW_ENTRY_POINT
Definition api.php:21
AuthManager is the authentication system in MediaWiki and serves entry point for authentication.
This is a value object for authentication requests.
A DatabaseBlock (unlike a SystemBlock) is stored in the database, may give rise to autoblocks and may...
isAutoblocking( $x=null)
Does the block cause autoblocks to be created?
Group all the pieces relevant to the context of a request into one instance.
Defer callable updates to run later in the PHP process.
Handler class for MWExceptions.
Create PSR-3 logger objects.
Value class wrapping variables present in the confirmation email.
Represent and format a single name and email address pair for SMTP.
A class containing constants representing the names of configuration variables.
Service locator for MediaWiki core services.
static getInstance()
Returns the global default instance of the top level service locator.
HTML sanitizer for MediaWiki.
Definition Sanitizer.php:34
Factory class for creating and checking Password objects.
Check if a user's password complies with any password policies that apply to that user,...
A StatusValue for permission errors.
Represents the subject that rate limits are applied to.
Represents the authority of a given User.
Profiler base class that defines the interface and some shared functionality.
Definition Profiler.php:26
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form,...
This serves as the entry point to the MediaWiki session handling system.
Value object representing a CSRF token.
Definition Token.php:19
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition Status.php:44
Represents a title within MediaWiki.
Definition Title.php:69
getCanonicalURL( $query='')
Get the URL for a canonical link, for use in things like IRC and e-mail notifications.
Definition Title.php:2353
Value object representing a MediaWiki edit token for logged-out users.
getOption(UserIdentity $user, string $oname, $defaultOverride=null, bool $ignoreHidden=false, int $queryFlags=IDBAccessObject::READ_NORMAL)
Get the user's current setting for a given option.
Value object representing a user's identity.
User class for the MediaWiki software.
Definition User.php:129
authorizeAction(string $action, ?PermissionStatus $status=null)
Authorize an action.This should be used immediately before performing the action.Calling this method ...
Definition User.php:3324
isAllowedToCreateAccount()
Get whether the user is allowed to create an account.
Definition User.php:2687
string null $mDatePreference
Lazy-initialized variables, invalidated with clearInstanceCache.
Definition User.php:244
touch()
Update the "touched" timestamp for the user.
Definition User.php:1675
isAllowed(string $permission, ?PermissionStatus $status=null)
Checks whether this authority has the given permission in general.
Definition User.php:2136
static newFromConfirmationCode( $code, $flags=IDBAccessObject::READ_NORMAL)
Factory method to fetch whichever user has a given email confirmation code.
Definition User.php:723
& __get( $name)
Definition User.php:299
logout()
Log this user out.
Definition User.php:2273
getRegistration()
Get the timestamp of account creation.
Definition User.php:3085
getWikiId()
Returns self::LOCAL to indicate the user is associated with the local wiki.
Definition User.php:288
int $queryFlagsUsed
IDBAccessObject::READ_* constant bitfield used to load data.
Definition User.php:254
getInstanceFromPrimary(int $loadFlags=IDBAccessObject::READ_LATEST)
Get an instance of this user that was loaded from the primary DB.
Definition User.php:3235
useRCPatrol()
Check whether to enable recent changes patrol features for this user.
Definition User.php:2144
isAllowedAny(... $permissions)
Checks whether this authority has any of the given permissions in general.Implementations must ensure...
Definition User.php:2127
const READ_EXCLUSIVE
Definition User.php:137
requiresHTTPS()
Determine based on the wiki configuration and the user's options, whether this user must be over HTTP...
Definition User.php:2042
getEditTokenObject( $salt='', $request=null)
Initialize (if necessary) and return a session token value which can be used in edit forms to show th...
Definition User.php:2734
getEditToken( $salt='', $request=null)
Initialize (if necessary) and return a session token value which can be used in edit forms to show th...
Definition User.php:2759
string false $mName
Definition User.php:195
confirmationToken(&$expiration)
Deprecated alias for getConfirmationToken() for CentralAuth.
Definition User.php:2889
static findUsersByGroup( $groups, $limit=5000, $after=null)
Return the users who are members of the given group(s).
Definition User.php:907
setToken( $token=false)
Set the random token (used for persistent authentication) Called from loadDefaults() among other plac...
Definition User.php:1832
canReceiveEmail()
Is this user allowed to receive e-mails within limits of current site configuration?
Definition User.php:3034
getId( $wikiId=self::LOCAL)
Get the user's ID.
Definition User.php:1478
getCacheKey(WANObjectCache $cache)
Definition User.php:522
loadFromCache()
Load user data from shared cache, given mId has already been set.
Definition User.php:535
getDBTouched()
Get the user_touched timestamp field (time of last DB updates)
Definition User.php:1743
clearInstanceCache( $reloadFrom=false)
Clear various cached data stored in this object.
Definition User.php:1313
checkPasswordValidity( $password)
Check if this is a valid password for this user.
Definition User.php:960
static string[] $mCacheVars
List of member variables which are saved to the shared cache (memcached).
Definition User.php:173
authorizeRead(string $action, PageIdentity $target, ?PermissionStatus $status=null)
Definition User.php:3335
getExperienceLevel()
Compute experienced level based on edit count and registration date.
Definition User.php:2192
isNamed()
Is the user a normal non-temporary registered user?
Definition User.php:3419
setEmailWithConfirmation(string $str)
Set the user's e-mail address and send a confirmation mail if needed.
Definition User.php:1889
loadFromUserObject( $user)
Load the data for this user object from another user object.
Definition User.php:1244
getUserPage()
Get this user's personal page title.
Definition User.php:2696
isSafeToLoad()
Test if it's safe to load this User object.
Definition User.php:363
isEmailConfirmed()
Is this user's e-mail address valid-looking and confirmed within limits of the current site configura...
Definition User.php:3050
invalidationTokenUrl( $token)
Deprecated alias for getInvalidationTokenUrl() for CentralAuth.
Definition User.php:2932
AbstractBlock false null $mGlobalBlock
Null when uninitialized, false when there is no block.
Definition User.php:246
isPingLimitable()
Is this user subject to rate limiting?
Definition User.php:1362
confirmEmail()
Mark the e-mail address confirmed.
Definition User.php:2964
getConfirmationToken(?string &$expiration, ?int $tokenLifeTimeSeconds=null)
Generate, store, and return a new e-mail confirmation code.
Definition User.php:2865
isNewbie()
Determine whether the user is a newbie.
Definition User.php:2717
resetTokenFromOption( $oname)
Reset a token stored in the preferences (like the watchlist one).
Definition User.php:2002
isBlockedFromEmailuser()
Get whether the user is blocked from using Special:Emailuser.
Definition User.php:2666
isLocked()
Check if user account is locked.
Definition User.php:1453
isBlockedFromUpload()
Get whether the user is blocked from using Special:Upload.
Definition User.php:2678
static newQueryBuilder(IReadableDatabase $db)
Get a SelectQueryBuilder with the tables, fields and join conditions needed to create a new User obje...
Definition User.php:3160
setCookies( $request=null, $secure=null, $rememberMe=false)
Persist this user's session (e.g.
Definition User.php:2236
int $mId
Cache variables.
Definition User.php:193
addToDatabase()
Add this existing user object to the database.
Definition User.php:2527
getBlock( $freshness=IDBAccessObject::READ_NORMAL, $disableIpBlockExemptChecking=false)
Get the block affecting the user, or null if the user is not blocked.
Definition User.php:1412
getTokenUrl(string $page, string $token)
Function to create a special page URL with a token path parameter.
Definition User.php:2951
setEmail(string $str)
Set the user's e-mail address.
Definition User.php:1872
static newFatalPermissionDeniedStatus( $permission)
Factory function for fatal permission-denied errors.
Definition User.php:3190
static purge( $dbDomain, $userId)
Definition User.php:511
string $mFrom
Initialization data source if mLoadedItems!==true.
Definition User.php:238
probablyCan(string $action, PageIdentity $target, ?PermissionStatus $status=null)
Definition User.php:3281
static newFromActorId( $id)
Static factory method for creation from a given actor ID.
Definition User.php:653
loadFromRow( $row, $data=null)
Initialize this object from a row from the user table.
Definition User.php:1134
checkAndSetTouched()
Bump user_touched if it didn't change since this object was loaded.
Definition User.php:1276
load( $flags=IDBAccessObject::READ_NORMAL)
Load the user table data for this object from the source given by mFrom.
Definition User.php:380
loadFromDatabase( $flags=IDBAccessObject::READ_LATEST)
Load user data from the database.
Definition User.php:1088
getEditCount()
Get the user's edit count.
Definition User.php:2063
doLogout()
Clear the user's session, and reset the instance cache.
Definition User.php:2283
getDatePreference()
Get the user's preferred date format.
Definition User.php:2020
clearSharedCache( $mode='refresh')
Clear user data from memcached.
Definition User.php:1632
static newFromId( $id)
Static factory method for creation from a given user ID.
Definition User.php:636
setActorId(int $actorId)
Sets the actor id.
Definition User.php:1594
setItemLoaded( $item)
Set that an item has been loaded.
Definition User.php:1052
static newSystemUser( $name, $options=[])
Static factory method for creation of a "system" user from username.
Definition User.php:808
loadFromId( $flags=IDBAccessObject::READ_NORMAL)
Load user table data, given mId has already been set.
Definition User.php:481
static newFromRow( $row, $data=null)
Create a new user object from a user row.
Definition User.php:758
isAnon()
Get whether the user is anonymous.
Definition User.php:2085
sendMail( $subject, $body, $from=null, $replyto=null)
Send an e-mail to this user's account.
Definition User.php:2819
changeAuthenticationData(array $data)
Changes credentials of the user.
Definition User.php:1761
static newFromAnyId( $userId, $userName, $actorId, $dbDomain=false)
Static factory method for creation from an ID, name, and/or actor ID.
Definition User.php:702
definitelyCan(string $action, PageIdentity $target, ?PermissionStatus $status=null)
Definition User.php:3296
string $mRealName
Definition User.php:203
isSystemUser()
Get whether the user is a system user.
Definition User.php:2116
getInvalidationTokenUrl(string $token)
Return a URL the user can use to invalidate their email address.
Definition User.php:2921
isItemLoaded( $item, $all='all')
Return whether an item has been loaded.
Definition User.php:1040
const TOKEN_LENGTH
Number of characters required for the user_token field.
Definition User.php:147
setId( $v)
Set the user and reload all fields according to a given ID.
Definition User.php:1502
string null $mToken
Definition User.php:212
isAllowedAll(... $permissions)
Checks whether this authority has any of the given permissions in general.Implementations must ensure...
Definition User.php:2132
getInstanceForUpdate()
Get a new instance of this user that was loaded from the primary DB via a locking read.
Definition User.php:3213
getTokenFromOption( $oname)
Get a token stored in the preferences (like the watchlist one), resetting it if it's empty (and savin...
Definition User.php:1971
isDefinitelyAllowed(string $action, ?PermissionStatus $status=null)
Checks whether this authority is allowed to perform the given action.This method performs a thorough ...
Definition User.php:3312
static getQueryInfo()
Return the tables, fields, and join conditions to be selected to create a new user object.
Definition User.php:3128
equals(?UserIdentity $user)
Checks if two user objects point to the same user.
Definition User.php:3257
isTemp()
Is the user an autocreated temporary user?
Definition User.php:3405
const MAINTENANCE_SCRIPT_USER
Username used for various maintenance scripts.
Definition User.php:164
isEmailConfirmationPending()
Check whether there is an outstanding request for e-mail confirmation.
Definition User.php:3068
getRealName()
Get the user's real name.
Definition User.php:1944
string $mTouched
TS::MW timestamp from the DB.
Definition User.php:208
getTitleKey()
Get the user's name escaped by underscores.
Definition User.php:1603
string null $mEmailTokenExpires
Definition User.php:218
bool null $mLocked
Definition User.php:248
getActorId( $dbwOrWikiId=self::LOCAL)
Get the user's actor ID.
Definition User.php:1557
string null $mQuickTouched
TS::MW timestamp from cache.
Definition User.php:210
validateCache( $timestamp)
Validate the cache for this account.
Definition User.php:1709
getEmailAuthenticationTimestamp()
Get the timestamp of the user's e-mail authentication.
Definition User.php:1861
authorizeWrite(string $action, PageIdentity $target, ?PermissionStatus $status=null)
Definition User.php:3350
__set( $name, $value)
Definition User.php:320
useFilePatrol()
Check whether to enable new files patrol features for this user.
Definition User.php:2169
static newFromSession(?WebRequest $request=null)
Create a new user object using data from session.
Definition User.php:736
canSendEmail()
Is this user allowed to send e-mails within limits of current site configuration?
Definition User.php:3021
static isWellFormedConfirmationToken(string $token)
Check if the given email confirmation token is well-formed (to detect mangling by email clients).
Definition User.php:2899
static createNew( $name, $params=[])
Add a user to the database, return the user object.
Definition User.php:2432
getRequest()
Get the WebRequest object to use with this object.
Definition User.php:2183
saveSettings()
Save this user's settings into the database.
Definition User.php:2321
loadDefaults( $name=false, $actorId=null)
Set cached properties to default.
Definition User.php:1004
useNPPatrol()
Check whether to enable new pages patrol features for this user.
Definition User.php:2154
const INVALID_TOKEN
An invalid string value for the user_token field.
Definition User.php:152
pingLimiter( $action='edit', $incrBy=1)
Primitive rate limits: enforce maximum actions per time period to put a brake on flooding.
Definition User.php:1383
matchEditToken( $val, $salt='', $request=null, $maxage=null)
Check given value against the token value stored in the session.
Definition User.php:2776
invalidateEmail()
Invalidate the user's e-mail confirmation, and unauthenticate the e-mail address if it was already co...
Definition User.php:2993
isRegistered()
Get whether the user is registered.
Definition User.php:2077
getTalkPage()
Get this user's talk page title.
Definition User.php:2705
static newFromIdentity(UserIdentity $identity)
Returns a User object corresponding to the given UserIdentity.
Definition User.php:672
makeUpdateConditions(IReadableDatabase $db)
Build additional update conditions to protect against race conditions using a compare-and-set (CAS) m...
Definition User.php:1258
string null $mEmailToken
Definition User.php:216
int null $mActorId
Switched from protected to public for use in UserFactory.
Definition User.php:201
getToken( $forceCreation=true)
Get the user's current token.
Definition User.php:1788
setEmailAuthenticationTimestamp( $timestamp)
Set the e-mail authentication timestamp.
Definition User.php:3007
isHidden()
Check if user account is hidden.
Definition User.php:1468
static getRightDescription( $right)
Get the description of a given right as wikitext.
Definition User.php:3098
setName( $str)
Set the user name.
Definition User.php:1539
static getRightDescriptionHtml( $right)
Get the description of a given right as rendered HTML.
Definition User.php:3111
string null $mEmailAuthenticated
Definition User.php:214
debouncedDBTouch()
Update the db touched timestamp for the user if it hasn't been updated recently.
Definition User.php:1690
isValidPassword( $password)
Is the input a valid password for this user?
Definition User.php:934
getEmail()
Get the user's e-mail address.
Definition User.php:1848
getName()
Get the user name, or the IP of an anonymous user.
Definition User.php:1511
setRealName(string $str)
Set the user's real name.
Definition User.php:1956
spreadAnyEditBlock()
If this user is logged-in and blocked, block any IP address they've successfully logged in from.
Definition User.php:2621
getTouched()
Get the user touched timestamp.
Definition User.php:1721
static newFromName( $name, $validate='valid')
Definition User.php:610
invalidateCache()
Immediately touch the user data cache for this account.
Definition User.php:1658
scheduleSpreadBlock()
Schedule a deferred update which will block the IP address of the current user, if they are blocked w...
Definition User.php:2606
spreadBlock(Block $block)
If this (non-anonymous) user is blocked, block the IP address they've successfully logged in from.
Definition User.php:2643
getConfirmationTokenUrl(string $token)
Return a URL the user can use to confirm their email address.
Definition User.php:2910
idForName( $flags=IDBAccessObject::READ_NORMAL)
If only this user's username is known, and it exists, return the user ID.
Definition User.php:2399
sendConfirmationMail( $type='created')
Generate a new e-mail confirmation token and send a confirmation/invalidation mail to the user's give...
Definition User.php:2788
array bool $mLoadedItems
Array with already loaded items or true if all items have been loaded.
Definition User.php:225
A cryptographic random generator class used for generating secret keys.
Utility functions for generating hashes.
Multi-datacenter aware caching interface.
makeGlobalKey( $keygroup,... $components)
Base class for the more common types of database errors.
Build SELECT queries with a fluent interface.
return[ 'config-schema-inverse'=>['default'=>['ConfigRegistry'=>['main'=> 'MediaWiki\\Config\\GlobalVarConfig::newInstance',], 'Sitename'=> 'MediaWiki', 'Server'=> false, 'CanonicalServer'=> false, 'ServerName'=> false, 'AssumeProxiesUseDefaultProtocolPorts'=> true, 'HttpsPort'=> 443, 'ForceHTTPS'=> false, 'ScriptPath'=> '/wiki', 'UsePathInfo'=> null, 'Script'=> false, 'LoadScript'=> false, 'RestPath'=> false, 'StylePath'=> false, 'LocalStylePath'=> false, 'ExtensionAssetsPath'=> false, 'ExtensionDirectory'=> null, 'StyleDirectory'=> null, 'ArticlePath'=> false, 'UploadPath'=> false, 'ImgAuthPath'=> false, 'ThumbPath'=> false, 'UploadDirectory'=> false, 'FileCacheDirectory'=> false, 'Logo'=> false, 'Logos'=> false, 'Favicon'=> '/favicon.ico', 'AppleTouchIcon'=> false, 'ReferrerPolicy'=> false, 'TmpDirectory'=> false, 'UploadBaseUrl'=> '', 'UploadStashScalerBaseUrl'=> false, 'ActionPaths'=>[], 'MainPageIsDomainRoot'=> false, 'EnableUploads'=> false, 'UploadStashMaxAge'=> 21600, 'EnableAsyncUploads'=> false, 'EnableAsyncUploadsByURL'=> false, 'EnableChunkedUploads'=> false, 'UploadMaintenance'=> false, 'IllegalFileChars'=> ':\\/\\\\', 'DeletedDirectory'=> false, 'ImgAuthDetails'=> false, 'ImgAuthUrlPathMap'=>[], 'LocalFileRepo'=>['class'=> 'MediaWiki\\FileRepo\\LocalRepo', 'name'=> 'local', 'directory'=> null, 'scriptDirUrl'=> null, 'favicon'=> null, 'url'=> null, 'hashLevels'=> null, 'thumbScriptUrl'=> null, 'transformVia404'=> null, 'deletedDir'=> null, 'deletedHashLevels'=> null, 'updateCompatibleMetadata'=> null, 'reserializeMetadata'=> null,], 'ForeignFileRepos'=>[], 'UseInstantCommons'=> false, 'UseSharedUploads'=> false, 'SharedUploadDirectory'=> null, 'SharedUploadPath'=> null, 'HashedSharedUploadDirectory'=> true, 'RepositoryBaseUrl'=> 'https:'FetchCommonsDescriptions'=> false, 'SharedUploadDBname'=> false, 'SharedUploadDBprefix'=> '', 'SharedUploadDBschema'=> null, 'CacheSharedUploads'=> true, 'ForeignUploadTargets'=>['local',], 'UploadDialog'=>['fields'=>['description'=> true, 'date'=> false, 'categories'=> false,], 'licensemessages'=>['local'=> 'generic-local', 'foreign'=> 'generic-foreign',], 'comment'=>['local'=> '', 'foreign'=> '',], 'format'=>['filepage'=> ' $DESCRIPTION', 'description'=> ' $TEXT', 'ownwork'=> '', 'license'=> '', 'uncategorized'=> '',],], 'FileBackends'=>[], 'LockManagers'=>[], 'DefaultLockManager'=> null, 'ShowEXIF'=> null, 'UpdateCompatibleMetadata'=> false, 'AllowCopyUploads'=> false, 'CopyUploadsDomains'=>[], 'CopyUploadsFromSpecialUpload'=> false, 'CopyUploadProxy'=> false, 'CopyUploadTimeout'=> false, 'CopyUploadAllowOnWikiDomainConfig'=> false, 'MaxUploadSize'=> 104857600, 'MinUploadChunkSize'=> 1024, 'UploadNavigationUrl'=> false, 'UploadMissingFileUrl'=> false, 'ThumbnailScriptPath'=> false, 'SharedThumbnailScriptPath'=> false, 'HashedUploadDirectory'=> true, 'CSPUploadEntryPoint'=> true, 'FileExtensions'=>['png', 'gif', 'jpg', 'jpeg', 'webp',], 'ProhibitedFileExtensions'=>['html', 'htm', 'js', 'jsb', 'mhtml', 'mht', 'xhtml', 'xht', 'php', 'phtml', 'php3', 'php4', 'php5', 'phps', 'phar', 'shtml', 'jhtml', 'pl', 'py', 'cgi', 'exe', 'scr', 'dll', 'msi', 'vbs', 'bat', 'com', 'pif', 'cmd', 'vxd', 'cpl', 'xml',], 'MimeTypeExclusions'=>['text/html', 'application/javascript', 'text/javascript', 'text/x-javascript', 'application/x-shellscript', 'application/x-php', 'text/x-php', 'text/x-python', 'text/x-perl', 'text/x-bash', 'text/x-sh', 'text/x-csh', 'text/scriptlet', 'application/x-msdownload', 'application/x-msmetafile', 'application/java', 'application/xml', 'text/xml',], 'CheckFileExtensions'=> true, 'StrictFileExtensions'=> true, 'DisableUploadScriptChecks'=> false, 'UploadSizeWarning'=> false, 'TrustedMediaFormats'=>['BITMAP', 'AUDIO', 'VIDEO', 'image/svg+xml', 'application/pdf',], 'MediaHandlers'=>[], 'NativeImageLazyLoading'=> true, '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 -l $lang -o $output $input', 'ImagickExt'=>['SvgHandler::rasterizeImagickExt',],], 'SVGConverter'=> 'ImageMagick', 'SVGConverterPath'=> '', 'SVGMaxSize'=> 5120, 'SVGMetadataCutoff'=> 5242880, 'SVGNativeRendering'=> true, 'SVGNativeRenderingSizeLimit'=> 51200, 'MediaInTargetLanguage'=> true, 'MaxImageArea'=> 12500000, 'MaxAnimatedGifArea'=> 12500000, 'MaxAnimatedWebPArea'=> 12500000, 'WebPThumbnailType'=>['webp', 'image/webp',], 'TiffThumbnailType'=>[], 'ThumbnailEpoch'=> '20030516000000', 'AttemptFailureEpoch'=> 1, 'IgnoreImageErrors'=> false, 'GenerateThumbnailOnParse'=> true, 'ShowArchiveThumbnails'=> true, 'EnableAutoRotation'=> null, 'Antivirus'=> null, 'AntivirusSetup'=>['clamav'=>['command'=> 'clamscan --no-summary ', 'codemap'=>[0=> 0, 1=> 1, 52=> -1, ' *'=> false,], 'messagepattern'=> '/.*?:(.*)/sim',],], 'AntivirusRequired'=> true, 'VerifyMimeType'=> true, 'MimeTypeFile'=> 'internal', 'MimeInfoFile'=> 'internal', 'MimeDetectorCommand'=> null, 'TrivialMimeDetection'=> false, 'XMLMimeTypes'=>['http:'svg'=> 'image/svg+xml', 'http:'http:'html'=> 'text/html',], 'ImageLimits'=>[[320, 240,], [640, 480,], [800, 600,], [1024, 768,], [1280, 1024,], [2560, 2048,],], 'ThumbLimits'=>[120, 150, 180, 200, 220, 250, 300, 400,], 'ThumbnailNamespaces'=>[6,], 'ThumbnailSteps'=> null, 'ThumbnailBuckets'=> null, 'ThumbnailMinimumBucketDistance'=> 50, 'UploadThumbnailRenderMap'=>[], 'UploadThumbnailRenderMethod'=> 'jobqueue', 'UploadThumbnailRenderHttpCustomHost'=> false, 'UploadThumbnailRenderHttpCustomDomain'=> false, 'UseTinyRGBForJPGThumbnails'=> false, 'GalleryOptions'=>[], 'ThumbUpright'=> 0.75, 'DirectoryMode'=> 511, 'ResponsiveImages'=> true, 'ImagePreconnect'=> false, 'TrackMediaRequestProvenance'=> false, 'DjvuUseBoxedCommand'=> false, 'DjvuDump'=> null, 'DjvuRenderer'=> null, 'DjvuTxt'=> null, 'DjvuPostProcessor'=> 'pnmtojpeg', 'DjvuOutputExtension'=> 'jpg', 'EmergencyContact'=> false, 'PasswordSender'=> false, 'NoReplyAddress'=> false, 'EnableEmail'=> true, 'EnableUserEmail'=> true, 'UserEmailUseReplyTo'=> true, 'PasswordReminderResendTime'=> 24, 'NewPasswordExpiry'=> 604800, 'UserEmailConfirmationTokenExpiry'=> 604800, 'PasswordExpirationDays'=> false, 'PasswordExpireGrace'=> 604800, 'SMTP'=> false, 'AdditionalMailParams'=> null, 'AllowHTMLEmail'=> false, 'EnotifFromEditor'=> false, 'EmailAuthentication'=> true, 'EmailConfirmationBanner'=> false, 'EnotifWatchlist'=> false, 'EnotifUserTalk'=> false, 'EnotifRevealEditorAddress'=> false, 'EnotifMinorEdits'=> true, 'EnotifUseRealName'=> false, 'UsersNotifiedOnAllChanges'=>[], 'DBname'=> 'my_wiki', 'DBmwschema'=> null, 'DBprefix'=> '', 'DBserver'=> 'localhost', 'DBport'=> 5432, 'DBuser'=> 'wikiuser', 'DBpassword'=> '', 'DBtype'=> 'mysql', 'DBssl'=> false, 'DBcompress'=> false, 'DBStrictWarnings'=> false, 'DBadminuser'=> null, 'DBadminpassword'=> null, 'SearchType'=> null, 'SearchTypeAlternatives'=> null, 'DBTableOptions'=> 'ENGINE=InnoDB, DEFAULT CHARSET=binary', 'SQLMode'=> '', 'SQLiteDataDir'=> '', 'SharedDB'=> null, 'SharedPrefix'=> false, 'SharedTables'=>['user', 'user_properties', 'user_autocreate_serial',], 'SharedSchema'=> false, 'DBservers'=> false, 'LBFactoryConf'=>['class'=> 'Wikimedia\\Rdbms\\LBFactorySimple',], 'DataCenterUpdateStickTTL'=> 10, 'DBerrorLog'=> false, 'DBerrorLogTZ'=> false, 'LocalDatabases'=>[], 'DatabaseReplicaLagWarning'=> 10, 'DatabaseReplicaLagCritical'=> 30, 'MaxExecutionTimeForExpensiveQueries'=> 0, 'VirtualDomainsMapping'=>[], 'RemoteVirtualDomainsMapping'=>[], 'FileSchemaMigrationStage'=> 3, 'ExternalLinksDomainGaps'=>[], 'ContentHandlers'=>['wikitext'=>['class'=> 'MediaWiki\\Content\\WikitextContentHandler', 'services'=>['TitleFactory', 'ParserFactory', 'GlobalIdGenerator', 'LanguageNameUtils', 'LinkRenderer', 'MagicWordFactory', 'ParsoidParserFactory',],], 'javascript'=>['class'=> 'MediaWiki\\Content\\JavaScriptContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'UserOptionsLookup', 'CodeHighlighter',],], 'json'=>['class'=> 'MediaWiki\\Content\\JsonContentHandler', 'services'=>['ParsoidParserFactory', 'TitleFactory',],], 'css'=>['class'=> 'MediaWiki\\Content\\CssContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'UserOptionsLookup', 'CodeHighlighter',],], 'vue'=>['class'=> 'MediaWiki\\Content\\VueContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'CodeHighlighter',],], 'text'=> 'MediaWiki\\Content\\TextContentHandler', 'unknown'=> 'MediaWiki\\Content\\FallbackContentHandler',], 'NamespaceContentModels'=>[], 'TextModelsToParse'=>['wikitext', 'javascript', 'css',], 'CompressRevisions'=> false, 'ExternalStores'=>[], 'ExternalServers'=>[], 'DefaultExternalStore'=> false, 'RevisionCacheExpiry'=> 604800, 'PageLanguageUseDB'=> false, 'DiffEngine'=> null, 'ExternalDiffEngine'=> false, 'Wikidiff2Options'=>[], 'RequestTimeLimit'=> null, 'TransactionalTimeLimit'=> 120, 'CriticalSectionTimeLimit'=> 180.0, 'MiserMode'=> false, 'DisableQueryPages'=> false, 'QueryCacheLimit'=> 1000, 'WantedPagesThreshold'=> 1, 'AllowSlowParserFunctions'=> false, 'AllowSchemaUpdates'=> true, 'MaxArticleSize'=> 2048, 'MemoryLimit'=> '50M', 'PoolCounterConf'=> null, 'PoolCountClientConf'=>['servers'=>['127.0.0.1',], 'timeout'=> 0.1,], 'MaxUserDBWriteDuration'=> false, 'MaxJobDBWriteDuration'=> false, 'LinkHolderBatchSize'=> 1000, 'MaximumMovedPages'=> 100, 'ForceDeferredUpdatesPreSend'=> false, 'MultiShardSiteStats'=> false, 'CacheDirectory'=> false, 'MainCacheType'=> 0, 'MessageCacheType'=> -1, 'ParserCacheType'=> -1, 'SessionCacheType'=> -1, 'AnonSessionCacheType'=> false, 'LanguageConverterCacheType'=> -1, 'ObjectCaches'=>[0=>['class'=> 'Wikimedia\\ObjectCache\\EmptyBagOStuff', 'reportDupes'=> false,], 1=>['class'=> 'MediaWiki\\ObjectCache\\SqlBagOStuff', 'loggroup'=> 'SQLBagOStuff',], 'memcached-php'=>['class'=> 'Wikimedia\\ObjectCache\\MemcachedPhpBagOStuff', 'loggroup'=> 'memcached',], 'memcached-pecl'=>['class'=> 'Wikimedia\\ObjectCache\\MemcachedPeclBagOStuff', 'loggroup'=> 'memcached',], 'hash'=>['class'=> 'Wikimedia\\ObjectCache\\HashBagOStuff', 'reportDupes'=> false,], 'apc'=>['class'=> 'Wikimedia\\ObjectCache\\APCUBagOStuff', 'reportDupes'=> false,], 'apcu'=>['class'=> 'Wikimedia\\ObjectCache\\APCUBagOStuff', 'reportDupes'=> false,],], 'WANObjectCache'=>[], 'MicroStashType'=> -1, 'MainStash'=> 1, 'ParsoidCacheConfig'=>['StashType'=> null, 'StashDuration'=> 86400,], 'ParsoidSelectiveUpdateSampleRate'=> 0, 'SplitParsoidParserCache'=> false, 'ParserCacheFilterConfig'=>['pcache'=>['default'=>['minCpuTime'=> 0,],], 'postproc-pcache'=>['default'=>['minCpuTime'=> 9223372036854775807,],], 'parsoid-pcache'=>['default'=>['minCpuTime'=> 0,],], 'postproc-parsoid-pcache'=>['default'=>['minCpuTime'=> 0,],],], 'ChronologyProtectorSecret'=> '', 'ParserCacheExpireTime'=> 86400, 'ParserCacheAsyncExpireTime'=> 60, 'ParserCacheAsyncRefreshJobs'=> true, 'OldRevisionParserCacheExpireTime'=> 3600, 'ObjectCacheSessionExpiry'=> 3600, 'SuspiciousIpExpiry'=> false, 'SessionPbkdf2Iterations'=> 10001, 'UseSessionCookieJwt'=> false, 'JwtSessionCookieIssuer'=> null, 'MemCachedServers'=>['127.0.0.1:11211',], 'MemCachedPersistent'=> false, 'MemCachedTimeout'=> 500000, 'UseLocalMessageCache'=> false, 'AdaptiveMessageCache'=> false, 'LocalisationCacheConf'=>['class'=> 'MediaWiki\\Language\\LocalisationCache', 'store'=> 'detect', 'storeClass'=> false, 'storeDirectory'=> false, 'storeServer'=>[], 'forceRecache'=> false, 'manualRecache'=> false,], 'CachePages'=> true, 'CacheEpoch'=> '20030516000000', 'GitInfoCacheDirectory'=> false, 'UseFileCache'=> false, 'FileCacheDepth'=> 2, 'RenderHashAppend'=> '', 'EnableSidebarCache'=> false, 'SidebarCacheExpiry'=> 86400, 'UseGzip'=> false, 'InvalidateCacheOnLocalSettingsChange'=> true, 'ExtensionInfoMTime'=> false, 'EnableRemoteBagOStuffTests'=> false, 'UseCdn'=> false, 'VaryOnXFP'=> false, 'InternalServer'=> false, 'CdnMaxAge'=> 18000, 'CdnMaxageLagged'=> 30, 'CdnMaxageStale'=> 10, 'CdnReboundPurgeDelay'=> 0, 'CdnMaxageSubstitute'=> 60, 'ForcedRawSMaxage'=> 300, 'CdnServers'=>[], 'CdnServersNoPurge'=>[], 'HTCPRouting'=>[], 'HTCPMulticastTTL'=> 1, 'UsePrivateIPs'=> false, 'CdnMatchParameterOrder'=> true, 'LanguageCode'=> 'en', 'GrammarForms'=>[], 'InterwikiMagic'=> true, 'HideInterlanguageLinks'=> false, 'ExtraInterlanguageLinkPrefixes'=>[], 'InterlanguageLinkCodeMap'=>[], 'ExtraLanguageNames'=>[], 'ExtraLanguageCodes'=>['bh'=> 'bho', 'no'=> 'nb', 'simple'=> 'en',], 'DummyLanguageCodes'=>[], 'AllUnicodeFixes'=> false, 'LegacyEncoding'=> false, 'AmericanDates'=> false, 'TranslateNumerals'=> true, 'UseDatabaseMessages'=> true, 'MaxMsgCacheEntrySize'=> 10000, 'DisableLangConversion'=> false, 'DisableTitleConversion'=> false, 'DefaultLanguageVariant'=> false, 'UsePigLatinVariant'=> false, 'DisabledVariants'=>[], 'VariantArticlePath'=> false, 'UseXssLanguage'=> false, 'LoginLanguageSelector'=> false, 'ForceUIMsgAsContentMsg'=>[], 'RawHtmlMessages'=>[], 'Localtimezone'=> null, 'LocalTZoffset'=> null, 'OverrideUcfirstCharacters'=>[], 'MimeType'=> 'text/html', 'Html5Version'=> null, 'EditSubmitButtonLabelPublish'=> false, 'XhtmlNamespaces'=>[], 'SiteNotice'=> '', 'BrowserFormatDetection'=> 'telephone=no', 'SkinMetaTags'=>[], 'DefaultSkin'=> 'vector-2022', 'FallbackSkin'=> 'fallback', 'SkipSkins'=>[], 'DisableOutputCompression'=> false, 'FragmentMode'=>['html5', 'legacy',], 'ExternalInterwikiFragmentMode'=> 'legacy', 'FooterIcons'=>['copyright'=>['copyright'=>[],], 'poweredby'=>['mediawiki'=>['src'=> null, 'url'=> 'https:'alt'=> 'Powered by MediaWiki', 'lang'=> 'en',],],], 'EnableSectionShare'=> false, 'PageShareSkinsEnabled'=>[], 'UseCombinedLoginLink'=> false, 'Edititis'=> false, 'Send404Code'=> true, 'ShowRollbackEditCount'=> 10, 'EnableCanonicalServerLink'=> false, 'InterwikiLogoOverride'=>[], 'ResourceModules'=>[], 'ResourceModuleSkinStyles'=>[], 'ResourceLoaderSources'=>[], 'ResourceBasePath'=> null, 'ResourceLoaderMaxage'=>[], 'ResourceLoaderDebug'=> false, 'ResourceLoaderMaxQueryLength'=> false, 'ResourceLoaderValidateJS'=> true, 'ResourceLoaderEnableJSProfiler'=> false, 'ResourceLoaderStorageEnabled'=> true, 'ResourceLoaderStorageVersion'=> 1, 'ResourceLoaderEnableSourceMapLinks'=> true, 'AllowSiteCSSOnRestrictedPages'=> false, 'VueDevelopmentMode'=> false, 'CodexDevelopmentDir'=> null, 'MetaNamespace'=> false, 'MetaNamespaceTalk'=> false, 'CanonicalNamespaceNames'=>[-2=> 'Media', -1=> 'Special', 0=> '', 1=> 'Talk', 2=> 'User', 3=> 'User_talk', 4=> 'Project', 5=> 'Project_talk', 6=> 'File', 7=> 'File_talk', 8=> 'MediaWiki', 9=> 'MediaWiki_talk', 10=> 'Template', 11=> 'Template_talk', 12=> 'Help', 13=> 'Help_talk', 14=> 'Category', 15=> 'Category_talk',], 'ExtraNamespaces'=>[], 'ExtraGenderNamespaces'=>[], 'NamespaceAliases'=>[], 'LegalTitleChars'=> ' %!"$&\'()*,\\-.\\/0-9:;=?@A-Z\\\\^_`a-z~\\x80-\\xFF+', 'CapitalLinks' => true, 'CapitalLinkOverrides' => [ ], 'NamespacesWithSubpages' => [ 1 => true, 2 => true, 3 => true, 4 => true, 5 => true, 7 => true, 8 => true, 9 => true, 10 => true, 11 => true, 12 => true, 13 => true, 15 => true, ], 'NamespacesWithoutAutoSummaries' => [ ], 'ContentNamespaces' => [ 0, ], 'ShortPagesNamespaceExclusions' => [ ], 'ExtraSignatureNamespaces' => [ ], 'InvalidRedirectTargets' => [ 'Filepath', 'Mypage', 'Mytalk', 'Redirect', 'Mylog', ], 'DisableHardRedirects' => false, 'FixDoubleRedirects' => false, 'LocalInterwikis' => [ ], 'InterwikiExpiry' => 10800, 'InterwikiCache' => false, 'InterwikiScopes' => 3, 'InterwikiFallbackSite' => 'wiki', 'RedirectSources' => false, 'SiteTypes' => [ 'mediawiki' => 'MediaWiki\\Site\\MediaWikiSite', ], 'MaxTocLevel' => 999, 'MaxPPNodeCount' => 1000000, 'MaxTemplateDepth' => 100, 'MaxPPExpandDepth' => 100, 'UrlProtocols' => [ 'bitcoin:', 'ftp: 'ftps: 'geo:', 'git: 'gopher: 'http: 'https: 'irc: 'ircs: 'magnet:', 'mailto:', 'matrix:', 'mms: 'news:', 'nntp: 'redis: 'sftp: 'sip:', 'sips:', 'sms:', 'ssh: 'svn: 'tel:', 'telnet: 'urn:', 'wikipedia: 'worldwind: 'xmpp:', ' ], 'CleanSignatures' => true, 'AllowExternalImages' => false, 'AllowExternalImagesFrom' => '', 'EnableImageWhitelist' => false, 'TidyConfig' => [ ], 'ParsoidSettings' => [ 'useSelser' => true, ], 'ParsoidExperimentalParserFunctionOutput' => false, 'RawHtml' => false, 'ExternalLinkTarget' => false, 'NoFollowLinks' => true, 'NoFollowNsExceptions' => [ ], 'NoFollowDomainExceptions' => [ 'mediawiki.org', ], 'RegisterInternalExternals' => false, 'ExternalLinksIgnoreDomains' => [ ], 'AllowDisplayTitle' => true, 'RestrictDisplayTitle' => true, 'ExpensiveParserFunctionLimit' => 100, 'PreprocessorCacheThreshold' => 1000, 'EnableScaryTranscluding' => false, 'TranscludeCacheExpiry' => 3600, 'EnableMagicLinks' => [ 'ISBN' => false, 'PMID' => false, 'RFC' => false, ], 'ParserEnableUserLanguage' => false, 'ArticleCountMethod' => 'link', 'ActiveUserDays' => 30, 'LearnerEdits' => 10, 'LearnerMemberSince' => 4, 'ExperiencedUserEdits' => 500, 'ExperiencedUserMemberSince' => 30, 'ManualRevertSearchRadius' => 15, 'RevertedTagMaxDepth' => 15, 'CentralIdLookupProviders' => [ 'local' => [ 'class' => 'MediaWiki\\User\\CentralId\\LocalIdLookup', 'services' => [ 'MainConfig', 'DBLoadBalancerFactory', 'HideUserUtils', ], ], ], 'CentralIdLookupProvider' => 'local', 'UserRegistrationProviders' => [ 'local' => [ 'class' => 'MediaWiki\\User\\Registration\\LocalUserRegistrationProvider', 'services' => [ 'ConnectionProvider', ], ], ], 'PasswordPolicy' => [ 'policies' => [ 'bureaucrat' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'sysop' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'interface-admin' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'bot' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'default' => [ 'MinimalPasswordLength' => [ 'value' => 8, 'suggestChangeOnLogin' => true, ], 'PasswordCannotBeSubstringInUsername' => [ 'value' => true, 'suggestChangeOnLogin' => true, ], 'PasswordCannotMatchDefaults' => [ 'value' => true, 'suggestChangeOnLogin' => true, ], 'MaximalPasswordLength' => [ 'value' => 4096, 'suggestChangeOnLogin' => true, ], 'PasswordNotInCommonList' => [ 'value' => true, 'suggestChangeOnLogin' => true, ], ], ], 'checks' => [ 'MinimalPasswordLength' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkMinimalPasswordLength', ], 'MinimumPasswordLengthToLogin' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkMinimumPasswordLengthToLogin', ], 'PasswordCannotBeSubstringInUsername' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkPasswordCannotBeSubstringInUsername', ], 'PasswordCannotMatchDefaults' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkPasswordCannotMatchDefaults', ], 'MaximalPasswordLength' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkMaximalPasswordLength', ], 'PasswordNotInCommonList' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkPasswordNotInCommonList', ], ], ], 'AuthManagerConfig' => null, 'AuthManagerAutoConfig' => [ 'preauth' => [ 'MediaWiki\\Auth\\ThrottlePreAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\ThrottlePreAuthenticationProvider', 'sort' => 0, ], 'MediaWiki\\Auth\\PreviouslyRenamedAccountPreAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\PreviouslyRenamedAccountPreAuthenticationProvider', 'services' => [ 'ConnectionProvider', 'UserFactory', ], 'sort' => 0, ], ], 'primaryauth' => [ 'MediaWiki\\Auth\\TemporaryPasswordPrimaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\TemporaryPasswordPrimaryAuthenticationProvider', 'services' => [ 'DBLoadBalancerFactory', 'UserOptionsLookup', ], 'args' => [ [ 'authoritative' => false, ], ], 'sort' => 0, ], 'MediaWiki\\Auth\\LocalPasswordPrimaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\LocalPasswordPrimaryAuthenticationProvider', 'services' => [ 'DBLoadBalancerFactory', ], 'args' => [ [ 'authoritative' => true, ], ], 'sort' => 100, ], ], 'secondaryauth' => [ 'MediaWiki\\Auth\\CheckBlocksSecondaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\CheckBlocksSecondaryAuthenticationProvider', 'sort' => 0, ], 'MediaWiki\\Auth\\ResetPasswordSecondaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\ResetPasswordSecondaryAuthenticationProvider', 'sort' => 100, ], 'MediaWiki\\Auth\\EmailNotificationSecondaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\EmailNotificationSecondaryAuthenticationProvider', 'services' => [ 'DBLoadBalancerFactory', ], 'sort' => 200, ], ], ], 'RememberMe' => 'choose', 'ReauthenticateTime' => [ 'default' => 3600, ], 'ChangeCredentialsBlacklist' => [ 'MediaWiki\\Auth\\TemporaryPasswordAuthenticationRequest', ], 'RemoveCredentialsBlacklist' => [ 'MediaWiki\\Auth\\PasswordAuthenticationRequest', ], 'InvalidPasswordReset' => true, 'PasswordDefault' => 'pbkdf2', 'PasswordConfig' => [ 'A' => [ 'class' => 'MediaWiki\\Password\\MWOldPassword', ], 'B' => [ 'class' => 'MediaWiki\\Password\\MWSaltedPassword', ], 'pbkdf2-legacyA' => [ 'class' => 'MediaWiki\\Password\\LayeredParameterizedPassword', 'types' => [ 'A', 'pbkdf2', ], ], 'pbkdf2-legacyB' => [ 'class' => 'MediaWiki\\Password\\LayeredParameterizedPassword', 'types' => [ 'B', 'pbkdf2', ], ], 'bcrypt' => [ 'class' => 'MediaWiki\\Password\\BcryptPassword', 'cost' => 9, ], 'pbkdf2' => [ 'class' => 'MediaWiki\\Password\\Pbkdf2PasswordUsingOpenSSL', 'algo' => 'sha512', 'cost' => '30000', 'length' => '64', ], 'argon2' => [ 'class' => 'MediaWiki\\Password\\Argon2Password', 'algo' => 'auto', ], ], 'PasswordResetRoutes' => [ 'username' => true, 'email' => true, ], 'MaxSigChars' => 255, 'SignatureValidation' => 'warning', 'SignatureAllowedLintErrors' => [ 'obsolete-tag', ], 'MaxNameChars' => 255, 'ReservedUsernames' => [ 'MediaWiki default', 'Conversion script', 'Maintenance script', 'Template namespace initialisation script', 'ScriptImporter', 'Delete page script', 'Move page script', 'Command line script', 'Unknown user', 'msg:double-redirect-fixer', 'msg:usermessage-editor', 'msg:proxyblocker', 'msg:sorbs', 'msg:spambot_username', 'msg:autochange-username', ], 'DefaultUserOptions' => [ 'ccmeonemails' => 0, 'date' => 'default', 'diffonly' => 0, 'diff-type' => 'table', 'disablemail' => 0, 'editfont' => 'monospace', 'editondblclick' => 0, 'editrecovery' => 0, 'editsectiononrightclick' => 0, 'email-allow-new-users' => 1, 'enotifminoredits' => 0, 'enotifrevealaddr' => 0, 'enotifusertalkpages' => 1, 'enotifwatchlistpages' => 1, 'extendwatchlist' => 1, 'fancysig' => 0, 'forceeditsummary' => 0, 'forcesafemode' => 0, 'gender' => 'unknown', 'hidecategorization' => 1, 'hideminor' => 0, 'hidepatrolled' => 0, 'imagesize' => 2, 'minordefault' => 0, 'newpageshidepatrolled' => 0, 'nickname' => '', 'norollbackdiff' => 0, 'prefershttps' => 1, 'previewonfirst' => 0, 'previewontop' => 1, 'pst-cssjs' => 1, 'rcdays' => 7, 'rcenhancedfilters-disable' => 0, 'rclimit' => 50, 'requireemail' => 0, 'search-match-redirect' => true, 'search-special-page' => 'Search', 'search-thumbnail-extra-namespaces' => true, 'searchlimit' => 20, 'showhiddencats' => 0, 'shownumberswatching' => 1, 'showrollbackconfirmation' => 0, 'skin' => false, 'skin-responsive' => 1, 'thumbsize' => 5, 'underline' => 2, 'useeditwarning' => 1, 'uselivepreview' => 0, 'usenewrc' => 1, 'watchcreations' => 1, 'watchcreations-expiry' => 'infinite', 'watchdefault' => 1, 'watchdefault-expiry' => 'infinite', 'watchdeletion' => 0, 'watchlistdays' => 7, 'watchlisthideanons' => 0, 'watchlisthidebots' => 0, 'watchlisthidecategorization' => 1, 'watchlisthideliu' => 0, 'watchlisthideminor' => 0, 'watchlisthideown' => 0, 'watchlisthidepatrolled' => 0, 'watchlistreloadautomatically' => 0, 'watchlistunwatchlinks' => 0, 'watchmoves' => 0, 'watchrollback' => 0, 'watchuploads' => 1, 'watchrollback-expiry' => 'infinite', 'watchstar-expiry' => 'infinite', 'wlenhancedfilters-disable' => 0, 'wllimit' => 250, ], 'ConditionalUserOptions' => [ ], 'HiddenPrefs' => [ ], 'UserJsPrefLimit' => 100, 'InvalidUsernameCharacters' => '@:>=', 'UserrightsInterwikiDelimiter' => '@', 'SecureLogin' => false, 'AuthenticationTokenVersion' => null, 'SessionProviders' => [ 'MediaWiki\\Session\\CookieSessionProvider' => [ 'class' => 'MediaWiki\\Session\\CookieSessionProvider', 'args' => [ [ 'priority' => 30, ], ], 'services' => [ 'JwtCodec', 'UrlUtils', ], ], 'MediaWiki\\Session\\BotPasswordSessionProvider' => [ 'class' => 'MediaWiki\\Session\\BotPasswordSessionProvider', 'args' => [ [ 'priority' => 75, ], ], 'services' => [ 'GrantsInfo', ], ], ], 'AutoCreateTempUser' => [ 'known' => false, 'enabled' => false, 'actions' => [ 'edit', ], 'genPattern' => '~$1', 'matchPattern' => null, 'reservedPattern' => '~$1', 'serialProvider' => [ 'type' => 'local', 'useYear' => true, ], 'serialMapping' => [ 'type' => 'readable-numeric', ], 'expireAfterDays' => 90, 'notifyBeforeExpirationDays' => 10, ], 'AutoblockExemptions' => [ ], 'AutoblockExpiry' => 86400, 'BlockAllowsUTEdit' => true, 'BlockCIDRLimit' => [ 'IPv4' => 16, 'IPv6' => 19, ], 'BlockDisablesLogin' => false, 'EnableMultiBlocks' => false, 'WhitelistRead' => false, 'WhitelistReadRegexp' => false, 'EmailConfirmToEdit' => false, 'HideIdentifiableRedirects' => true, 'GroupPermissions' => [ '*' => [ 'createaccount' => true, 'autocreateaccount' => true, 'read' => true, 'edit' => true, 'createpage' => true, 'createtalk' => true, 'viewmyprivateinfo' => true, 'editmyprivateinfo' => true, 'editmyoptions' => true, ], 'user' => [ 'move' => true, 'move-subpages' => true, 'move-rootuserpages' => true, 'move-categorypages' => true, 'movefile' => true, 'read' => true, 'edit' => true, 'createpage' => true, 'createtalk' => true, 'upload' => true, 'reupload' => true, 'reupload-shared' => true, 'minoredit' => true, 'editmyusercss' => true, 'editmyuserjson' => true, 'editmyuserjs' => true, 'editmyuserjsredirect' => true, 'sendemail' => true, 'applychangetags' => true, 'changetags' => true, 'viewmywatchlist' => true, 'editmywatchlist' => true, 'createwithcontentmodel' => true, 'logout' => true, ], 'autoconfirmed' => [ 'autoconfirmed' => true, 'editsemiprotected' => true, ], 'bot' => [ 'bot' => true, 'autoconfirmed' => true, 'editsemiprotected' => true, 'nominornewtalk' => true, 'autopatrol' => true, 'suppressredirect' => true, 'apihighlimits' => true, ], 'sysop' => [ 'block' => true, 'createaccount' => true, 'createpreviouslyrenamedaccount' => true, 'delete' => true, 'bigdelete' => true, 'deletedhistory' => true, 'deletedtext' => true, 'undelete' => true, 'editcontentmodel' => true, 'editinterface' => true, 'editsitejson' => true, 'edituserjson' => true, 'import' => true, 'importupload' => true, 'move' => true, 'move-subpages' => true, 'move-rootuserpages' => true, 'move-categorypages' => true, 'patrol' => true, 'autopatrol' => true, 'protect' => true, 'editprotected' => true, 'rollback' => true, 'upload' => true, 'reupload' => true, 'reupload-shared' => true, 'unwatchedpages' => true, 'autoconfirmed' => true, 'editsemiprotected' => true, 'ipblock-exempt' => true, 'blockemail' => true, 'markbotedits' => true, 'apihighlimits' => true, 'browsearchive' => true, 'noratelimit' => true, 'movefile' => true, 'unblockself' => true, 'suppressredirect' => true, 'mergehistory' => true, 'managechangetags' => true, 'deletechangetags' => true, ], 'interface-admin' => [ 'editinterface' => true, 'editsitecss' => true, 'editsitejson' => true, 'editsitejs' => true, 'editusercss' => true, 'edituserjson' => true, 'edituserjs' => true, ], 'bureaucrat' => [ 'userrights' => true, 'noratelimit' => true, 'renameuser' => true, ], 'suppress' => [ 'hideuser' => true, 'suppressrevision' => true, 'viewsuppressed' => true, 'suppressionlog' => true, 'deleterevision' => true, 'deletelogentry' => true, ], ], 'PrivilegedGroups' => [ 'bureaucrat', 'interface-admin', 'suppress', 'sysop', ], 'RevokePermissions' => [ ], 'GroupInheritsPermissions' => [ ], 'ImplicitGroups' => [ '*', 'user', 'autoconfirmed', ], 'GroupsAddToSelf' => [ ], 'GroupsRemoveFromSelf' => [ ], 'RestrictedGroups' => [ ], 'UserRequirementsPrivateConditions' => [ ], 'RestrictionTypes' => [ 'create', 'edit', 'move', 'upload', ], 'RestrictionLevels' => [ '', 'autoconfirmed', 'sysop', ], 'CascadingRestrictionLevels' => [ 'sysop', ], 'SemiprotectedRestrictionLevels' => [ 'autoconfirmed', ], 'NamespaceProtection' => [ ], 'RestrictUserPageEditing' => false, 'NonincludableNamespaces' => [ ], 'AutoConfirmAge' => 0, 'AutoConfirmCount' => 0, 'Autopromote' => [ 'autoconfirmed' => [ '&', [ 1, null, ], [ 2, null, ], ], ], 'AutopromoteOnce' => [ 'onEdit' => [ ], ], 'AutopromoteOnceLogInRC' => true, 'AutopromoteOnceRCExcludedGroups' => [ ], 'AddGroups' => [ ], 'RemoveGroups' => [ ], 'AvailableRights' => [ ], 'ImplicitRights' => [ ], 'DeleteRevisionsLimit' => 0, 'DeleteRevisionsBatchSize' => 1000, 'HideUserContribLimit' => 1000, 'AccountCreationThrottle' => [ [ 'count' => 0, 'seconds' => 86400, ], ], 'TempAccountCreationThrottle' => [ [ 'count' => 1, 'seconds' => 600, ], [ 'count' => 6, 'seconds' => 86400, ], ], 'TempAccountNameAcquisitionThrottle' => [ [ 'count' => 60, 'seconds' => 86400, ], ], 'SpamRegex' => [ ], 'SummarySpamRegex' => [ ], 'EnableDnsBlacklist' => false, 'DnsBlacklistUrls' => [ ], 'ProxyList' => [ ], 'ProxyWhitelist' => [ ], 'SoftBlockRanges' => [ ], 'ApplyIpBlocksToXff' => false, 'RateLimits' => [ 'edit' => [ 'ip' => [ 8, 60, ], 'newbie' => [ 8, 60, ], 'user' => [ 90, 60, ], ], 'move' => [ 'newbie' => [ 2, 120, ], 'user' => [ 8, 60, ], ], 'upload' => [ 'ip' => [ 8, 60, ], 'newbie' => [ 8, 60, ], ], 'rollback' => [ 'user' => [ 10, 60, ], 'newbie' => [ 5, 120, ], ], 'mailpassword' => [ 'ip' => [ 5, 3600, ], ], 'sendemail' => [ 'ip' => [ 5, 86400, ], 'newbie' => [ 5, 86400, ], 'user' => [ 20, 86400, ], ], 'changeemail' => [ 'ip-all' => [ 10, 3600, ], 'user' => [ 4, 86400, ], ], 'confirmemail' => [ 'ip-all' => [ 10, 3600, ], 'user' => [ 4, 86400, ], ], 'purge' => [ 'ip' => [ 30, 60, ], 'user' => [ 30, 60, ], ], 'linkpurge' => [ 'ip' => [ 30, 60, ], 'user' => [ 30, 60, ], ], 'renderfile' => [ 'ip' => [ 700, 30, ], 'user' => [ 700, 30, ], ], 'renderfile-nonstandard' => [ 'ip' => [ 70, 30, ], 'user' => [ 70, 30, ], ], 'stashedit' => [ 'ip' => [ 30, 60, ], 'newbie' => [ 30, 60, ], ], 'stashbasehtml' => [ 'ip' => [ 5, 60, ], 'newbie' => [ 5, 60, ], ], 'changetags' => [ 'ip' => [ 8, 60, ], 'newbie' => [ 8, 60, ], ], 'editcontentmodel' => [ 'newbie' => [ 2, 120, ], 'user' => [ 8, 60, ], ], ], 'RateLimitsExcludedIPs' => [ ], 'PutIPinRC' => true, 'QueryPageDefaultLimit' => 50, 'ExternalQuerySources' => [ ], 'PasswordAttemptThrottle' => [ [ 'count' => 5, 'seconds' => 300, ], [ 'count' => 150, 'seconds' => 172800, ], ], 'GrantPermissions' => [ 'basic' => [ 'autocreateaccount' => true, 'autoconfirmed' => true, 'autopatrol' => true, 'editsemiprotected' => true, 'ipblock-exempt' => true, 'nominornewtalk' => true, 'patrolmarks' => true, 'read' => true, 'unwatchedpages' => true, ], 'highvolume' => [ 'bot' => true, 'apihighlimits' => true, 'noratelimit' => true, 'markbotedits' => true, ], 'import' => [ 'import' => true, 'importupload' => true, ], 'editpage' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'pagelang' => true, ], 'editprotected' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editprotected' => true, ], 'editmycssjs' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editmyusercss' => true, 'editmyuserjson' => true, 'editmyuserjs' => true, ], 'editmyoptions' => [ 'editmyoptions' => true, 'editmyuserjson' => true, ], 'editinterface' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editinterface' => true, 'edituserjson' => true, 'editsitejson' => true, ], 'editsiteconfig' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editinterface' => true, 'edituserjson' => true, 'editsitejson' => true, 'editusercss' => true, 'edituserjs' => true, 'editsitecss' => true, 'editsitejs' => true, ], 'createeditmovepage' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'createpage' => true, 'createtalk' => true, 'delete-redirect' => true, 'move' => true, 'move-rootuserpages' => true, 'move-subpages' => true, 'move-categorypages' => true, 'suppressredirect' => true, ], 'uploadfile' => [ 'upload' => true, 'reupload-own' => true, ], 'uploadeditmovefile' => [ 'upload' => true, 'reupload-own' => true, 'reupload' => true, 'reupload-shared' => true, 'upload_by_url' => true, 'movefile' => true, 'suppressredirect' => true, ], 'patrol' => [ 'patrol' => true, ], 'rollback' => [ 'rollback' => true, ], 'blockusers' => [ 'block' => true, 'blockemail' => true, ], 'viewdeleted' => [ 'browsearchive' => true, 'deletedhistory' => true, 'deletedtext' => true, ], 'viewrestrictedlogs' => [ 'suppressionlog' => true, ], 'delete' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'browsearchive' => true, 'deletedhistory' => true, 'deletedtext' => true, 'delete' => true, 'bigdelete' => true, 'deletelogentry' => true, 'deleterevision' => true, 'undelete' => true, ], 'oversight' => [ 'suppressrevision' => true, 'viewsuppressed' => true, ], 'protect' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editprotected' => true, 'protect' => true, ], 'viewmywatchlist' => [ 'viewmywatchlist' => true, ], 'editmywatchlist' => [ 'editmywatchlist' => true, ], 'sendemail' => [ 'sendemail' => true, ], 'createaccount' => [ 'createaccount' => true, ], 'privateinfo' => [ 'viewmyprivateinfo' => true, ], 'mergehistory' => [ 'mergehistory' => true, ], 'managesessions' => [ 'logout' => true, ], ], 'GrantPermissionGroups' => [ 'basic' => 'hidden', 'editpage' => 'page-interaction', 'createeditmovepage' => 'page-interaction', 'editprotected' => 'page-interaction', 'patrol' => 'page-interaction', 'uploadfile' => 'file-interaction', 'uploadeditmovefile' => 'file-interaction', 'sendemail' => 'email', 'viewmywatchlist' => 'watchlist-interaction', 'editmywatchlist' => 'watchlist-interaction', 'editmycssjs' => 'customization', 'editmyoptions' => 'customization', 'editinterface' => 'administration', 'editsiteconfig' => 'administration', 'rollback' => 'administration', 'blockusers' => 'administration', 'delete' => 'administration', 'viewdeleted' => 'administration', 'viewrestrictedlogs' => 'administration', 'protect' => 'administration', 'oversight' => 'administration', 'createaccount' => 'administration', 'mergehistory' => 'administration', 'import' => 'administration', 'highvolume' => 'high-volume', 'privateinfo' => 'private-information', 'managesessions' => 'private-information', ], 'GrantRiskGroups' => [ 'basic' => 'low', 'editpage' => 'low', 'createeditmovepage' => 'low', 'editprotected' => 'vandalism', 'patrol' => 'low', 'uploadfile' => 'low', 'uploadeditmovefile' => 'low', 'sendemail' => 'security', 'viewmywatchlist' => 'low', 'editmywatchlist' => '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', 'managesessions' => 'low', ], 'EnableBotPasswords' => true, 'BotPasswordsCluster' => false, 'BotPasswordsDatabase' => false, 'BotPasswordsLimit' => 100, 'SecretKey' => false, 'JwtPrivateKey' => false, 'JwtPublicKey' => false, 'AllowUserJs' => false, 'ReauthenticateForActions' => [ 'edituserjs' => 'edituserjscss', 'editusercss' => 'edituserjscss', 'editsitejs' => 'editsitejscss', 'editsitecss' => 'editsitejscss', ], 'AllowUserCss' => false, 'AllowUserCssPrefs' => true, 'UseSiteJs' => true, 'UseSiteCss' => true, 'BreakFrames' => false, 'EditPageFrameOptions' => 'DENY', 'ApiFrameOptions' => 'DENY', 'CSPHeader' => false, 'CSPReportOnlyHeader' => false, 'CSPUseReportURIDirective' => false, 'CSPFalsePositiveUrls' => [ 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'chrome-extension' => true, ], 'AllowCrossOrigin' => false, 'RestAllowCrossOriginCookieAuth' => false, 'SessionSecret' => false, 'CookieExpiration' => 2592000, 'ExtendedLoginCookieExpiration' => 15552000, 'SessionCookieJwtExpiration' => 14400, 'CookieDomain' => '', 'CookiePath' => '/', 'CookieSecure' => 'detect', 'CookiePrefix' => false, 'CookieHttpOnly' => true, 'CookieSameSite' => null, 'CacheVaryCookies' => [ ], 'SessionName' => false, 'CookieSetOnAutoblock' => true, 'CookieSetOnIpBlock' => true, 'DebugLogFile' => '', 'DebugLogPrefix' => '', 'DebugRedirects' => false, 'DebugRawPage' => false, 'DebugComments' => false, 'DebugDumpSql' => false, 'TrxProfilerLimits' => [ 'GET' => [ 'masterConns' => 0, 'writes' => 0, 'readQueryTime' => 5, 'readQueryRows' => 10000, ], 'POST' => [ 'readQueryTime' => 5, 'writeQueryTime' => 1, 'readQueryRows' => 100000, 'maxAffected' => 1000, ], 'POST-nonwrite' => [ 'writes' => 0, 'readQueryTime' => 5, 'readQueryRows' => 10000, ], 'PostSend-GET' => [ 'readQueryTime' => 5, 'writeQueryTime' => 1, 'readQueryRows' => 10000, 'maxAffected' => 1000, 'masterConns' => 0, 'writes' => 0, ], 'PostSend-POST' => [ 'readQueryTime' => 5, 'writeQueryTime' => 1, 'readQueryRows' => 100000, 'maxAffected' => 1000, ], 'JobRunner' => [ 'readQueryTime' => 30, 'writeQueryTime' => 5, 'readQueryRows' => 100000, 'maxAffected' => 500, ], 'Maintenance' => [ 'writeQueryTime' => 5, 'maxAffected' => 1000, ], ], 'DebugLogGroups' => [ ], 'MWLoggerDefaultSpi' => [ 'class' => 'MediaWiki\\Logger\\LegacySpi', ], 'ShowDebug' => false, 'SpecialVersionShowHooks' => false, 'ShowExceptionDetails' => false, 'LogExceptionBacktrace' => true, 'PropagateErrors' => true, 'ShowHostnames' => false, 'OverrideHostname' => false, 'DevelopmentWarnings' => false, 'DeprecationReleaseLimit' => false, 'Profiler' => [ ], 'StatsdServer' => false, 'StatsdMetricPrefix' => 'MediaWiki', 'StatsTarget' => null, 'StatsFormat' => null, 'StatsPrefix' => 'mediawiki', 'OpenTelemetryConfig' => null, 'PageInfoTransclusionLimit' => 50, 'EnableJavaScriptTest' => false, 'DebugToolbar' => false, 'ApiClientErrorSampleRate' => 1.0, 'DisableTextSearch' => false, 'AdvancedSearchHighlighting' => false, 'SearchHighlightBoundaries' => '[\\p{Z}\\p{P}\\p{C}]', 'OpenSearchTemplates' => [ 'application/x-suggestions+json' => false, 'application/x-suggestions+xml' => false, ], 'OpenSearchDefaultLimit' => 10, 'OpenSearchDescriptionLength' => 100, 'SearchSuggestCacheExpiry' => 1200, 'DisableSearchUpdate' => false, 'NamespacesToBeSearchedDefault' => [ true, ], 'DisableInternalSearch' => false, 'SearchForwardUrl' => null, 'SitemapNamespaces' => false, 'SitemapNamespacesPriorities' => false, 'SitemapApiConfig' => [ ], 'SpecialSearchFormOptions' => [ ], 'SearchMatchRedirectPreference' => false, 'SearchRunSuggestedQuery' => true, 'Diff3' => '/usr/bin/diff3', 'Diff' => '/usr/bin/diff', 'PreviewOnOpenNamespaces' => [ 14 => true, ], 'UniversalEditButton' => true, 'UseAutomaticEditSummaries' => true, 'CommandLineDarkBg' => false, 'ReadOnly' => null, 'ReadOnlyWatchedItemStore' => false, 'ReadOnlyFile' => false, 'UpgradeKey' => false, 'GitBin' => '/usr/bin/git', 'GitRepositoryViewers' => [ 'https: 'ssh: 'https: 'git@github\\.com:(.*?)(\\.git)?' => 'https: ], 'InstallerInitialPages' => [ [ 'titlemsg' => 'mainpage', 'text' => '{{subst:int:mainpagetext}}{{subst:int:mainpagedocfooter}}', ], ], 'RCMaxAge' => 7776000, 'WatchersMaxAge' => 15552000, 'UnwatchedPageSecret' => 1, 'RCFilterByAge' => false, 'RCLinkLimits' => [ 50, 100, 250, 500, ], 'RCLinkDays' => [ 1, 3, 7, 14, 30, ], 'RCFeeds' => [ ], 'RCWatchCategoryMembership' => false, 'UseRCPatrol' => true, 'StructuredChangeFiltersLiveUpdatePollingRate' => 3, 'UseNPPatrol' => true, 'UseFilePatrol' => true, 'Feed' => true, 'FeedLimit' => 50, 'FeedCacheTimeout' => 60, 'FeedDiffCutoff' => 32768, 'OverrideSiteFeed' => [ ], 'FeedClasses' => [ 'rss' => 'MediaWiki\\Feed\\RSSFeed', 'atom' => 'MediaWiki\\Feed\\AtomFeed', ], 'AdvertisedFeedTypes' => [ 'atom', ], 'RCShowWatchingUsers' => false, 'RCShowChangedSize' => true, 'RCChangedSizeThreshold' => 500, 'ShowUpdatedMarker' => true, 'DisableAnonTalk' => false, 'UseTagFilter' => true, 'SoftwareTags' => [ 'mw-contentmodelchange' => true, 'mw-new-redirect' => true, 'mw-removed-redirect' => true, 'mw-changed-redirect-target' => true, 'mw-blank' => true, 'mw-replace' => true, 'mw-recreated' => true, 'mw-rollback' => true, 'mw-undo' => true, 'mw-manual-revert' => true, 'mw-reverted' => true, 'mw-server-side-upload' => true, 'mw-ipblock-appeal' => true, 'mw-edited-other-users-js' => true, 'mw-edited-other-users-css' => true, ], 'RestrictedTagViewRights' => [ ], 'UnwatchedPageThreshold' => false, 'RecentChangesFlags' => [ 'newpage' => [ 'letter' => 'newpageletter', 'title' => 'recentchanges-label-newpage', 'legend' => 'recentchanges-legend-newpage', 'grouping' => 'any', ], 'minor' => [ 'letter' => 'minoreditletter', 'title' => 'recentchanges-label-minor', 'legend' => 'recentchanges-legend-minor', 'class' => 'minoredit', 'grouping' => 'all', ], 'bot' => [ 'letter' => 'boteditletter', 'title' => 'recentchanges-label-bot', 'legend' => 'recentchanges-legend-bot', 'class' => 'botedit', 'grouping' => 'all', ], 'unpatrolled' => [ 'letter' => 'unpatrolledletter', 'title' => 'recentchanges-label-unpatrolled', 'legend' => 'recentchanges-legend-unpatrolled', 'grouping' => 'any', ], ], 'WatchlistExpiry' => false, 'EnableWatchstarPopover' => false, 'EnableWatchlistLabels' => false, 'WatchlistLabelsMaxPerUser' => 100, 'WatchlistPurgeRate' => 0.1, 'WatchlistExpiryMaxDuration' => '1 year', 'EnableChangesListQueryPartitioning' => false, 'RightsPage' => null, 'RightsUrl' => null, 'RightsText' => null, 'RightsIcon' => null, 'UseCopyrightUpload' => false, 'MaxCredits' => 0, 'ShowCreditsIfMax' => true, 'ImportSources' => [ ], 'ImportTargetNamespace' => null, 'ExportAllowHistory' => true, 'ExportMaxHistory' => 0, 'ExportAllowListContributors' => false, 'ExportMaxLinkDepth' => 0, 'ExportFromNamespaces' => false, 'ExportAllowAll' => false, 'ExportPagelistLimit' => 5000, 'XmlDumpSchemaVersion' => '0.11', 'WikiFarmSettingsDirectory' => null, 'WikiFarmSettingsExtension' => 'yaml', 'ExtensionFunctions' => [ ], 'ExtensionMessagesFiles' => [ ], 'MessagesDirs' => [ ], 'TranslationAliasesDirs' => [ ], 'ExtensionEntryPointListFiles' => [ ], 'EnableParserLimitReporting' => true, 'ValidSkinNames' => [ ], 'SpecialPages' => [ ], 'ExtensionCredits' => [ ], 'Hooks' => [ ], 'ServiceWiringFiles' => [ ], 'JobClasses' => [ 'deletePage' => 'MediaWiki\\Page\\DeletePageJob', 'refreshLinks' => 'MediaWiki\\JobQueue\\Jobs\\RefreshLinksJob', 'deleteLinks' => 'MediaWiki\\Page\\DeleteLinksJob', 'htmlCacheUpdate' => 'MediaWiki\\JobQueue\\Jobs\\HTMLCacheUpdateJob', 'sendMail' => [ 'class' => 'MediaWiki\\Mail\\EmaillingJob', 'services' => [ 'Emailer', ], ], 'enotifNotify' => [ 'class' => 'MediaWiki\\RecentChanges\\RecentChangeNotifyJob', 'services' => [ 'RecentChangeLookup', ], ], 'fixDoubleRedirect' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\DoubleRedirectJob', 'services' => [ 'RevisionLookup', 'MagicWordFactory', 'WikiPageFactory', ], 'needsPage' => true, ], 'AssembleUploadChunks' => 'MediaWiki\\JobQueue\\Jobs\\AssembleUploadChunksJob', 'PublishStashedFile' => 'MediaWiki\\JobQueue\\Jobs\\PublishStashedFileJob', 'ThumbnailRender' => 'MediaWiki\\JobQueue\\Jobs\\ThumbnailRenderJob', 'UploadFromUrl' => 'MediaWiki\\JobQueue\\Jobs\\UploadFromUrlJob', 'recentChangesUpdate' => 'MediaWiki\\RecentChanges\\RecentChangesUpdateJob', 'refreshLinksPrioritized' => 'MediaWiki\\JobQueue\\Jobs\\RefreshLinksJob', 'refreshLinksDynamic' => 'MediaWiki\\JobQueue\\Jobs\\RefreshLinksJob', 'activityUpdateJob' => 'MediaWiki\\Watchlist\\ActivityUpdateJob', 'categoryMembershipChange' => [ 'class' => 'MediaWiki\\RecentChanges\\CategoryMembershipChangeJob', 'services' => [ 'RecentChangeFactory', ], ], 'CategoryCountUpdateJob' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\CategoryCountUpdateJob', 'services' => [ 'ConnectionProvider', 'NamespaceInfo', ], ], 'clearUserWatchlist' => 'MediaWiki\\Watchlist\\ClearUserWatchlistJob', 'watchlistExpiry' => [ 'class' => 'MediaWiki\\Watchlist\\WatchlistExpiryJob', 'needsPage' => false, ], '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', 'renameUserTable' => [ 'class' => 'MediaWiki\\RenameUser\\Job\\RenameUserTableJob', 'services' => [ 'MainConfig', 'DBLoadBalancerFactory', ], ], 'renameUserDerived' => [ 'class' => 'MediaWiki\\RenameUser\\Job\\RenameUserDerivedJob', 'services' => [ 'RenameUserFactory', 'UserFactory', ], ], 'renameUser' => [ 'class' => 'MediaWiki\\RenameUser\\Job\\RenameUserTableJob', 'services' => [ 'MainConfig', 'DBLoadBalancerFactory', ], ], ], 'JobTypesExcludedFromDefaultQueue' => [ 'AssembleUploadChunks', 'PublishStashedFile', 'UploadFromUrl', ], 'JobBackoffThrottling' => [ ], 'JobTypeConf' => [ 'default' => [ 'class' => 'MediaWiki\\JobQueue\\JobQueueDB', 'order' => 'random', 'claimTTL' => 3600, ], ], 'JobQueueIncludeInMaxLagFactor' => false, 'SpecialPageCacheUpdates' => [ 'Statistics' => [ 'MediaWiki\\Deferred\\SiteStatsUpdate', 'cacheUpdate', ], ], 'PagePropLinkInvalidations' => [ 'hiddencat' => 'categorylinks', ], 'CategoryMagicGallery' => true, 'CategoryPagingLimit' => 200, 'CategoryCollation' => 'uppercase', 'TempCategoryCollations' => [ ], 'SortedCategories' => false, 'TrackingCategories' => [ ], 'LogTypes' => [ '', 'block', 'protect', 'rights', 'delete', 'upload', 'move', 'import', 'interwiki', 'patrol', 'merge', 'suppress', 'tag', 'managetags', 'contentmodel', 'renameuser', ], 'LogRestrictions' => [ 'suppress' => 'suppressionlog', ], 'FilterLogTypes' => [ 'patrol' => true, 'tag' => true, 'newusers' => false, ], 'LogNames' => [ '' => 'all-logs-page', 'block' => 'blocklogpage', 'protect' => 'protectlogpage', 'rights' => 'rightslog', 'delete' => 'dellogpage', 'upload' => 'uploadlogpage', 'move' => 'movelogpage', 'import' => 'importlogpage', 'patrol' => 'patrol-log-page', 'merge' => 'mergelog', 'suppress' => 'suppressionlog', ], 'LogHeaders' => [ '' => 'alllogstext', 'block' => 'blocklogtext', 'delete' => 'dellogpagetext', 'import' => 'importlogpagetext', 'merge' => 'mergelogpagetext', 'move' => 'movelogpagetext', 'patrol' => 'patrol-log-header', 'protect' => 'protectlogtext', 'rights' => 'rightslogtext', 'suppress' => 'suppressionlogtext', 'upload' => 'uploadlogpagetext', ], 'LogActions' => [ ], 'LogActionsHandlers' => [ 'block/block' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'block/reblock' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'block/unblock' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'contentmodel/change' => 'MediaWiki\\Logging\\ContentModelLogFormatter', 'contentmodel/new' => 'MediaWiki\\Logging\\ContentModelLogFormatter', 'delete/delete' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/delete_redir' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/delete_redir2' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/event' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/restore' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/revision' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'import/interwiki' => 'MediaWiki\\Logging\\ImportLogFormatter', 'import/upload' => 'MediaWiki\\Logging\\ImportLogFormatter', 'interwiki/iw_add' => 'MediaWiki\\Logging\\InterwikiLogFormatter', 'interwiki/iw_delete' => 'MediaWiki\\Logging\\InterwikiLogFormatter', 'interwiki/iw_edit' => 'MediaWiki\\Logging\\InterwikiLogFormatter', 'managetags/activate' => 'MediaWiki\\Logging\\LogFormatter', 'managetags/create' => 'MediaWiki\\Logging\\LogFormatter', 'managetags/deactivate' => 'MediaWiki\\Logging\\LogFormatter', 'managetags/delete' => 'MediaWiki\\Logging\\LogFormatter', 'merge/merge' => [ 'class' => 'MediaWiki\\Logging\\MergeLogFormatter', 'services' => [ 'TitleParser', ], ], 'merge/merge-into' => [ 'class' => 'MediaWiki\\Logging\\MergeLogFormatter', 'services' => [ 'TitleParser', ], ], 'move/move' => [ 'class' => 'MediaWiki\\Logging\\MoveLogFormatter', 'services' => [ 'TitleParser', ], ], 'move/move_redir' => [ 'class' => 'MediaWiki\\Logging\\MoveLogFormatter', 'services' => [ 'TitleParser', ], ], 'patrol/patrol' => 'MediaWiki\\Logging\\PatrolLogFormatter', 'patrol/autopatrol' => 'MediaWiki\\Logging\\PatrolLogFormatter', 'protect/modify' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'protect/move_prot' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'protect/protect' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'protect/unprotect' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'renameuser/renameuser' => [ 'class' => 'MediaWiki\\Logging\\RenameuserLogFormatter', 'services' => [ 'TitleParser', ], ], 'rights/autopromote' => 'MediaWiki\\Logging\\RightsLogFormatter', 'rights/rights' => 'MediaWiki\\Logging\\RightsLogFormatter', 'suppress/block' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'suppress/delete' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'suppress/event' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'suppress/reblock' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'suppress/revision' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'tag/update' => 'MediaWiki\\Logging\\TagLogFormatter', 'upload/overwrite' => 'MediaWiki\\Logging\\UploadLogFormatter', 'upload/revert' => 'MediaWiki\\Logging\\UploadLogFormatter', 'upload/upload' => 'MediaWiki\\Logging\\UploadLogFormatter', ], 'ActionFilteredLogs' => [ 'block' => [ 'block' => [ 'block', ], 'reblock' => [ 'reblock', ], 'unblock' => [ 'unblock', ], ], 'contentmodel' => [ 'change' => [ 'change', ], 'new' => [ 'new', ], ], 'delete' => [ 'delete' => [ 'delete', ], 'delete_redir' => [ 'delete_redir', 'delete_redir2', ], 'restore' => [ 'restore', ], 'event' => [ 'event', ], 'revision' => [ 'revision', ], ], 'import' => [ 'interwiki' => [ 'interwiki', ], 'upload' => [ 'upload', ], ], 'managetags' => [ 'create' => [ 'create', ], 'delete' => [ 'delete', ], 'activate' => [ 'activate', ], 'deactivate' => [ 'deactivate', ], ], 'move' => [ 'move' => [ 'move', ], 'move_redir' => [ 'move_redir', ], ], 'newusers' => [ 'create' => [ 'create', 'newusers', ], 'create2' => [ 'create2', ], 'autocreate' => [ 'autocreate', ], 'byemail' => [ 'byemail', ], ], 'protect' => [ 'protect' => [ 'protect', ], 'modify' => [ 'modify', ], 'unprotect' => [ 'unprotect', ], 'move_prot' => [ 'move_prot', ], ], 'rights' => [ 'rights' => [ 'rights', ], 'autopromote' => [ 'autopromote', ], ], 'suppress' => [ 'event' => [ 'event', ], 'revision' => [ 'revision', ], 'delete' => [ 'delete', ], 'block' => [ 'block', ], 'reblock' => [ 'reblock', ], ], 'upload' => [ 'upload' => [ 'upload', ], 'overwrite' => [ 'overwrite', ], 'revert' => [ 'revert', ], ], ], 'NewUserLog' => true, 'PageCreationLog' => true, 'AllowSpecialInclusion' => true, 'DisableQueryPageUpdate' => false, 'CountCategorizedImagesAsUsed' => false, 'MaxRedirectLinksRetrieved' => 500, 'RangeContributionsCIDRLimit' => [ 'IPv4' => 16, 'IPv6' => 32, ], 'Actions' => [ ], 'DefaultRobotPolicy' => 'index,follow', 'NamespaceRobotPolicies' => [ ], 'ArticleRobotPolicies' => [ ], 'ExemptFromUserRobotsControl' => null, 'DebugAPI' => false, 'APIModules' => [ ], 'APIFormatModules' => [ ], 'APIMetaModules' => [ ], 'APIPropModules' => [ ], 'APIListModules' => [ ], 'APIMaxDBRows' => 5000, 'APIMaxResultSize' => 8388608, 'APIMaxUncachedDiffs' => 1, 'APIMaxLagThreshold' => 7, 'APICacheHelpTimeout' => 3600, 'APIUselessQueryPages' => [ 'MIMEsearch', 'LinkSearch', ], 'AjaxLicensePreview' => true, 'CrossSiteAJAXdomains' => [ ], 'CrossSiteAJAXdomainExceptions' => [ ], 'AllowedCorsHeaders' => [ 'Accept', 'Accept-Language', 'Content-Language', 'Content-Type', 'Accept-Encoding', 'DNT', 'Origin', 'User-Agent', 'Api-User-Agent', 'Promise-Non-Write-API-Action', 'Access-Control-Max-Age', 'Authorization', ], 'RestAPIAdditionalRouteFiles' => [ ], 'RestLocalModuleTestBaseUrl' => null, 'RestModuleOverrides' => [ ], 'RestExternalModules' => [ ], 'RestTermsOfServiceUrl' => null, '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, 'HTTPUserAgentContact' => false, 'AsyncHTTPTimeout' => 25, 'HTTPProxy' => '', 'LocalVirtualHosts' => [ ], 'LocalHTTPProxy' => false, 'AllowExternalReqID' => false, 'GenerateReqIDFormat' => 'rand24', 'JobRunRate' => 1, 'RunJobsAsync' => false, 'UpdateRowsPerJob' => 300, 'UpdateRowsPerQuery' => 100, 'RedirectOnLogin' => 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' => [ ], 'UseLeximorph' => false, 'UsePostprocCacheLegacy' => false, 'UsePostprocCacheParsoid' => true, 'ParserOptionsLogUnsafeSampleRate' => 0, 'ReturnExperimentalPFragmentTypes' => [ ], 'UseParsoidLinksUpdate' => null, 'UseParsoidMessages' => false, 'UseParsoidParser' => true, 'SiteLookup' => [ ], ], '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', 'SharedUploadDBschema' => [ 'string', 'null', ], 'CacheSharedUploads' => 'boolean', 'ForeignUploadTargets' => 'array', 'UploadDialog' => 'object', 'FileBackends' => 'object', 'LockManagers' => 'array', 'DefaultLockManager' => [ 'string', 'null', ], 'CopyUploadsDomains' => 'array', 'CopyUploadTimeout' => [ 'boolean', 'integer', ], 'SharedThumbnailScriptPath' => [ 'string', 'boolean', ], 'HashedUploadDirectory' => 'boolean', 'CSPUploadEntryPoint' => 'boolean', 'FileExtensions' => 'array', 'ProhibitedFileExtensions' => 'array', 'MimeTypeExclusions' => 'array', 'TrustedMediaFormats' => 'array', 'MediaHandlers' => 'object', 'NativeImageLazyLoading' => 'boolean', 'ParserTestMediaHandlers' => 'object', 'MaxInterlacingAreas' => 'object', 'SVGConverters' => 'object', 'SVGNativeRendering' => [ 'string', 'boolean', ], 'MaxImageArea' => [ 'string', 'integer', 'boolean', ], 'WebPThumbnailType' => 'array', 'TiffThumbnailType' => 'array', 'GenerateThumbnailOnParse' => 'boolean', 'EnableAutoRotation' => [ 'boolean', 'null', ], 'Antivirus' => [ 'string', 'null', ], 'AntivirusSetup' => 'object', 'MimeDetectorCommand' => [ 'string', 'null', ], 'XMLMimeTypes' => 'object', 'ImageLimits' => 'array', 'ThumbLimits' => 'array', 'ThumbnailNamespaces' => 'array', 'ThumbnailSteps' => [ 'array', 'null', ], 'ThumbnailBuckets' => [ 'array', 'null', ], 'UploadThumbnailRenderMap' => 'object', 'GalleryOptions' => 'object', 'DjvuDump' => [ 'string', 'null', ], 'DjvuRenderer' => [ 'string', 'null', ], 'DjvuTxt' => [ 'string', 'null', ], 'DjvuPostProcessor' => [ 'string', 'null', ], 'SMTP' => [ 'boolean', 'object', ], 'EnotifFromEditor' => 'boolean', 'EmailConfirmationBanner' => 'boolean', 'EnotifRevealEditorAddress' => 'boolean', 'UsersNotifiedOnAllChanges' => 'object', 'DBmwschema' => [ 'string', 'null', ], 'SharedTables' => 'array', 'DBservers' => [ 'boolean', 'array', ], 'LBFactoryConf' => 'object', 'LocalDatabases' => 'array', 'VirtualDomainsMapping' => 'object', 'RemoteVirtualDomainsMapping' => 'object', 'FileSchemaMigrationStage' => 'integer', 'ExternalLinksDomainGaps' => 'object', 'ContentHandlers' => 'object', 'NamespaceContentModels' => 'object', 'TextModelsToParse' => 'array', 'ExternalStores' => 'array', 'ExternalServers' => 'object', 'DefaultExternalStore' => [ 'array', 'boolean', ], 'RevisionCacheExpiry' => 'integer', 'PageLanguageUseDB' => 'boolean', 'DiffEngine' => [ 'string', 'null', ], 'ExternalDiffEngine' => [ 'string', 'boolean', ], 'Wikidiff2Options' => 'object', 'RequestTimeLimit' => [ 'integer', 'null', ], 'CriticalSectionTimeLimit' => 'number', 'PoolCounterConf' => [ 'object', 'null', ], 'PoolCountClientConf' => 'object', 'MaxUserDBWriteDuration' => [ 'integer', 'boolean', ], 'MaxJobDBWriteDuration' => [ 'integer', 'boolean', ], 'MultiShardSiteStats' => 'boolean', 'ObjectCaches' => 'object', 'WANObjectCache' => 'object', 'MicroStashType' => [ 'string', 'integer', ], 'ParsoidCacheConfig' => 'object', 'ParsoidSelectiveUpdateSampleRate' => 'integer', 'SplitParsoidParserCache' => 'boolean', 'ParserCacheFilterConfig' => 'object', 'ChronologyProtectorSecret' => 'string', 'SuspiciousIpExpiry' => [ 'integer', 'boolean', ], 'MemCachedServers' => 'array', 'LocalisationCacheConf' => 'object', 'ExtensionInfoMTime' => [ 'integer', 'boolean', ], 'CdnServers' => 'object', 'CdnServersNoPurge' => 'object', 'HTCPRouting' => 'object', 'GrammarForms' => 'object', 'ExtraInterlanguageLinkPrefixes' => 'array', 'InterlanguageLinkCodeMap' => 'object', 'ExtraLanguageNames' => 'object', 'ExtraLanguageCodes' => 'object', 'DummyLanguageCodes' => 'object', 'DisabledVariants' => 'object', 'ForceUIMsgAsContentMsg' => 'object', 'RawHtmlMessages' => 'array', 'OverrideUcfirstCharacters' => 'object', 'XhtmlNamespaces' => 'object', 'BrowserFormatDetection' => 'string', 'SkinMetaTags' => 'object', 'SkipSkins' => 'object', 'FragmentMode' => 'array', 'FooterIcons' => 'object', 'PageShareSkinsEnabled' => 'array', 'InterwikiLogoOverride' => 'array', 'ResourceModules' => 'object', 'ResourceModuleSkinStyles' => 'object', 'ResourceLoaderSources' => 'object', 'ResourceLoaderMaxage' => 'object', 'ResourceLoaderMaxQueryLength' => [ 'integer', 'boolean', ], 'CanonicalNamespaceNames' => 'object', 'ExtraNamespaces' => 'object', 'ExtraGenderNamespaces' => 'object', 'NamespaceAliases' => 'object', 'CapitalLinkOverrides' => 'object', 'NamespacesWithSubpages' => 'object', 'NamespacesWithoutAutoSummaries' => 'array', 'ContentNamespaces' => 'array', 'ShortPagesNamespaceExclusions' => 'array', 'ExtraSignatureNamespaces' => 'array', 'InvalidRedirectTargets' => 'array', 'LocalInterwikis' => 'array', 'InterwikiCache' => [ 'boolean', 'object', ], 'SiteTypes' => 'object', 'UrlProtocols' => 'array', 'TidyConfig' => 'object', 'ParsoidSettings' => 'object', 'ParsoidExperimentalParserFunctionOutput' => 'boolean', 'NoFollowNsExceptions' => 'array', 'NoFollowDomainExceptions' => 'array', 'ExternalLinksIgnoreDomains' => 'array', 'EnableMagicLinks' => 'object', 'ManualRevertSearchRadius' => 'integer', 'RevertedTagMaxDepth' => 'integer', 'CentralIdLookupProviders' => 'object', 'CentralIdLookupProvider' => 'string', 'UserRegistrationProviders' => 'object', 'PasswordPolicy' => 'object', 'AuthManagerConfig' => [ 'object', 'null', ], 'AuthManagerAutoConfig' => 'object', 'RememberMe' => 'string', 'ReauthenticateTime' => 'object', 'ChangeCredentialsBlacklist' => 'array', 'RemoveCredentialsBlacklist' => 'array', 'PasswordConfig' => 'object', 'PasswordResetRoutes' => 'object', 'SignatureAllowedLintErrors' => 'array', 'ReservedUsernames' => 'array', 'DefaultUserOptions' => 'object', 'ConditionalUserOptions' => 'object', 'HiddenPrefs' => 'array', 'UserJsPrefLimit' => 'integer', 'AuthenticationTokenVersion' => [ 'string', 'null', ], 'SessionProviders' => 'object', 'AutoCreateTempUser' => 'object', 'AutoblockExemptions' => 'array', 'BlockCIDRLimit' => 'object', 'EnableMultiBlocks' => 'boolean', 'GroupPermissions' => 'object', 'PrivilegedGroups' => 'array', 'RevokePermissions' => 'object', 'GroupInheritsPermissions' => 'object', 'ImplicitGroups' => 'array', 'GroupsAddToSelf' => 'object', 'GroupsRemoveFromSelf' => 'object', 'RestrictedGroups' => 'object', 'UserRequirementsPrivateConditions' => 'array', 'RestrictionTypes' => 'array', 'RestrictionLevels' => 'array', 'CascadingRestrictionLevels' => 'array', 'SemiprotectedRestrictionLevels' => 'array', 'NamespaceProtection' => 'object', 'RestrictUserPageEditing' => 'boolean', 'NonincludableNamespaces' => 'object', 'Autopromote' => 'object', 'AutopromoteOnce' => 'object', 'AutopromoteOnceRCExcludedGroups' => 'array', 'AddGroups' => 'object', 'RemoveGroups' => 'object', 'AvailableRights' => 'array', 'ImplicitRights' => 'array', 'AccountCreationThrottle' => [ 'integer', 'array', ], 'TempAccountCreationThrottle' => 'array', 'TempAccountNameAcquisitionThrottle' => 'array', 'SpamRegex' => 'array', 'SummarySpamRegex' => 'array', 'DnsBlacklistUrls' => 'array', 'ProxyList' => [ 'string', 'array', ], 'ProxyWhitelist' => 'array', 'SoftBlockRanges' => 'array', 'RateLimits' => 'object', 'RateLimitsExcludedIPs' => 'array', 'ExternalQuerySources' => 'object', 'PasswordAttemptThrottle' => 'array', 'GrantPermissions' => 'object', 'GrantPermissionGroups' => 'object', 'GrantRiskGroups' => 'object', 'EnableBotPasswords' => 'boolean', 'BotPasswordsCluster' => [ 'string', 'boolean', ], 'BotPasswordsDatabase' => [ 'string', 'boolean', ], 'BotPasswordsLimit' => 'integer', 'ReauthenticateForActions' => 'object', 'CSPHeader' => [ 'boolean', 'object', ], 'CSPReportOnlyHeader' => [ 'boolean', 'object', ], 'CSPUseReportURIDirective' => [ 'boolean', 'object', ], 'CSPFalsePositiveUrls' => 'object', 'AllowCrossOrigin' => 'boolean', 'RestAllowCrossOriginCookieAuth' => 'boolean', 'CookieSameSite' => [ 'string', 'null', ], 'CacheVaryCookies' => 'array', 'TrxProfilerLimits' => 'object', 'DebugLogGroups' => 'object', 'MWLoggerDefaultSpi' => 'object', 'Profiler' => 'object', 'StatsTarget' => [ 'string', 'null', ], 'StatsFormat' => [ 'string', 'null', ], 'StatsPrefix' => 'string', 'OpenTelemetryConfig' => [ 'object', 'null', ], 'OpenSearchTemplates' => 'object', 'NamespacesToBeSearchedDefault' => 'object', 'SitemapNamespaces' => [ 'boolean', 'array', ], 'SitemapNamespacesPriorities' => [ 'boolean', 'object', ], 'SitemapApiConfig' => 'object', 'SpecialSearchFormOptions' => 'object', 'SearchMatchRedirectPreference' => 'boolean', 'SearchRunSuggestedQuery' => 'boolean', 'PreviewOnOpenNamespaces' => 'object', 'ReadOnlyWatchedItemStore' => 'boolean', 'GitRepositoryViewers' => 'object', 'InstallerInitialPages' => 'array', 'RCLinkLimits' => 'array', 'RCLinkDays' => 'array', 'RCFeeds' => 'object', 'OverrideSiteFeed' => 'object', 'FeedClasses' => 'object', 'AdvertisedFeedTypes' => 'array', 'SoftwareTags' => 'object', 'RestrictedTagViewRights' => 'object', 'RecentChangesFlags' => 'object', 'WatchlistExpiry' => 'boolean', 'EnableWatchstarPopover' => 'boolean', 'EnableWatchlistLabels' => 'boolean', 'WatchlistLabelsMaxPerUser' => 'integer', 'WatchlistPurgeRate' => 'number', 'WatchlistExpiryMaxDuration' => [ 'string', 'null', ], 'EnableChangesListQueryPartitioning' => 'boolean', 'ImportSources' => 'object', 'ExtensionFunctions' => 'array', 'ExtensionMessagesFiles' => 'object', 'MessagesDirs' => 'object', 'TranslationAliasesDirs' => 'object', 'ExtensionEntryPointListFiles' => 'object', 'ValidSkinNames' => 'object', 'SpecialPages' => 'object', 'ExtensionCredits' => 'object', 'Hooks' => 'object', 'ServiceWiringFiles' => 'array', 'JobClasses' => 'object', 'JobTypesExcludedFromDefaultQueue' => 'array', 'JobBackoffThrottling' => 'object', 'JobTypeConf' => 'object', 'SpecialPageCacheUpdates' => 'object', 'PagePropLinkInvalidations' => 'object', 'TempCategoryCollations' => 'array', 'SortedCategories' => 'boolean', 'TrackingCategories' => 'array', 'LogTypes' => 'array', 'LogRestrictions' => 'object', 'FilterLogTypes' => 'object', 'LogNames' => 'object', 'LogHeaders' => 'object', 'LogActions' => 'object', 'LogActionsHandlers' => 'object', 'ActionFilteredLogs' => 'object', 'RangeContributionsCIDRLimit' => 'object', 'Actions' => 'object', 'NamespaceRobotPolicies' => 'object', 'ArticleRobotPolicies' => 'object', 'ExemptFromUserRobotsControl' => [ 'array', 'null', ], 'APIModules' => 'object', 'APIFormatModules' => 'object', 'APIMetaModules' => 'object', 'APIPropModules' => 'object', 'APIListModules' => 'object', 'APIUselessQueryPages' => 'array', 'CrossSiteAJAXdomains' => 'object', 'CrossSiteAJAXdomainExceptions' => 'object', 'AllowedCorsHeaders' => 'array', 'RestAPIAdditionalRouteFiles' => 'array', 'RestLocalModuleTestBaseUrl' => [ 'string', 'null', ], 'RestModuleOverrides' => 'object', 'RestExternalModules' => 'object', 'RestTermsOfServiceUrl' => [ 'string', 'null', ], 'ShellRestrictionMethod' => [ 'string', 'boolean', ], 'ShellboxUrls' => 'object', 'ShellboxSecretKey' => [ 'string', 'null', ], 'ShellboxShell' => [ 'string', 'null', ], 'HTTPTimeout' => 'number', 'HTTPConnectTimeout' => 'number', 'HTTPMaxTimeout' => 'number', 'HTTPMaxConnectTimeout' => 'number', 'HTTPUserAgentContact' => [ 'string', 'boolean', ], 'LocalVirtualHosts' => 'object', 'LocalHTTPProxy' => [ 'string', 'boolean', ], 'GenerateReqIDFormat' => 'string', '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', 'UseLeximorph' => 'boolean', 'UsePostprocCacheLegacy' => 'boolean', 'UsePostprocCacheParsoid' => 'boolean', 'ParserOptionsLogUnsafeSampleRate' => 'integer', 'ReturnExperimentalPFragmentTypes' => 'array', 'UseParsoidLinksUpdate' => [ 'boolean', 'null', ], 'UseParsoidMessages' => [ 'boolean', 'null', ], 'UseParsoidParser' => 'boolean', 'SiteLookup' => 'object', ], 'mergeStrategy' => [ 'WebPThumbnailType' => 'replace', 'TiffThumbnailType' => 'replace', 'LBFactoryConf' => 'replace', 'InterwikiCache' => 'replace', 'PasswordPolicy' => 'array_replace_recursive', 'AuthManagerAutoConfig' => 'array_plus_2d', 'GroupPermissions' => 'array_plus_2d', 'RevokePermissions' => 'array_plus_2d', 'AddGroups' => 'array_merge_recursive', 'RemoveGroups' => 'array_merge_recursive', 'RateLimits' => 'array_plus_2d', 'GrantPermissions' => 'array_plus_2d', 'MWLoggerDefaultSpi' => 'replace', 'Profiler' => 'replace', 'Hooks' => 'array_merge_recursive', 'RestModuleOverrides' => 'array_replace_recursive', 'RestExternalModules' => 'array_replace_recursive', ], 'dynamicDefault' => [ 'UsePathInfo' => [ 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultUsePathInfo', ], ], 'Script' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultScript', ], ], 'LoadScript' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLoadScript', ], ], 'RestPath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultRestPath', ], ], 'StylePath' => [ 'use' => [ 'ResourceBasePath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultStylePath', ], ], 'LocalStylePath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLocalStylePath', ], ], 'ExtensionAssetsPath' => [ 'use' => [ 'ResourceBasePath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultExtensionAssetsPath', ], ], 'ArticlePath' => [ 'use' => [ 'Script', 'UsePathInfo', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultArticlePath', ], ], 'UploadPath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultUploadPath', ], ], 'FileCacheDirectory' => [ 'use' => [ 'UploadDirectory', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultFileCacheDirectory', ], ], 'Logo' => [ 'use' => [ 'ResourceBasePath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLogo', ], ], 'DeletedDirectory' => [ 'use' => [ 'UploadDirectory', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultDeletedDirectory', ], ], 'ShowEXIF' => [ 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultShowEXIF', ], ], 'SharedPrefix' => [ 'use' => [ 'DBprefix', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultSharedPrefix', ], ], 'SharedSchema' => [ 'use' => [ 'DBmwschema', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultSharedSchema', ], ], 'DBerrorLogTZ' => [ 'use' => [ 'Localtimezone', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultDBerrorLogTZ', ], ], 'Localtimezone' => [ 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLocaltimezone', ], ], 'LocalTZoffset' => [ 'use' => [ 'Localtimezone', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLocalTZoffset', ], ], 'ResourceBasePath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultResourceBasePath', ], ], 'MetaNamespace' => [ 'use' => [ 'Sitename', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultMetaNamespace', ], ], 'CookieSecure' => [ 'use' => [ 'ForceHTTPS', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultCookieSecure', ], ], 'CookiePrefix' => [ 'use' => [ 'SharedDB', 'SharedPrefix', 'SharedTables', 'DBname', 'DBprefix', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultCookiePrefix', ], ], 'ReadOnlyFile' => [ 'use' => [ 'UploadDirectory', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultReadOnlyFile', ], ], ], ], 'config-schema' => [ 'UploadStashScalerBaseUrl' => [ 'deprecated' => 'since 1.36 Use thumbProxyUrl in $wgLocalFileRepo', ], 'IllegalFileChars' => [ 'deprecated' => 'since 1.41; no longer customizable', ], 'ThumbnailNamespaces' => [ 'items' => [ 'type' => 'integer', ], ], 'LocalDatabases' => [ 'items' => [ 'type' => 'string', ], ], 'ParserCacheFilterConfig' => [ 'additionalProperties' => [ 'type' => 'object', 'description' => 'A map of namespace IDs to filter definitions.', 'additionalProperties' => [ 'type' => 'object', 'description' => 'A map of filter names to values.', 'properties' => [ 'minCpuTime' => [ 'type' => 'number', ], ], ], ], ], 'RawHtmlMessages' => [ 'items' => [ 'type' => 'string', ], ], 'InterwikiLogoOverride' => [ 'items' => [ 'type' => 'string', ], ], 'LegalTitleChars' => [ 'deprecated' => 'since 1.41; use Extension:TitleBlacklist to customize', ], 'ReauthenticateTime' => [ 'additionalProperties' => [ 'type' => [ 'integer', 'array', ], ], ], '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', ], 'skipRedirects' => [ 'type' => 'bool', ], ], ], 'SoftwareTags' => [ 'additionalProperties' => [ 'type' => 'boolean', ], ], 'UseCopyrightUpload' => [ 'deprecated' => 'since 1.47 This feature is being removed.', ], 'JobBackoffThrottling' => [ 'additionalProperties' => [ 'type' => 'number', ], ], 'JobTypeConf' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'class' => [ 'type' => 'string', ], 'order' => [ 'type' => 'string', ], 'claimTTL' => [ 'type' => 'integer', ], ], ], ], 'TrackingCategories' => [ 'deprecated' => 'since 1.25 Extensions should now register tracking categories using the new extension registration system.', ], 'RangeContributionsCIDRLimit' => [ 'additionalProperties' => [ 'type' => 'integer', ], ], 'RestModuleOverrides' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'availability' => [ 'type' => 'string', ], 'groups' => [ 'type' => 'array', ], ], ], ], 'RestExternalModules' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'info' => [ 'type' => 'object', 'properties' => [ 'version' => [ 'type' => 'string', ], 'title' => [ 'type' => 'string', ], 'x-i18n-title' => [ 'type' => 'string', ], 'description' => [ 'type' => 'string', ], 'x-i18n-description' => [ 'type' => 'string', ], ], 'required' => [ 'version', ], ], 'base' => [ 'type' => 'string', 'format' => 'uri', ], 'spec' => [ 'type' => 'string', 'format' => 'uri', ], ], 'required' => [ 'info', 'base', 'spec', ], ], ], 'ShellboxUrls' => [ 'additionalProperties' => [ 'type' => [ 'string', 'boolean', 'null', ], ], ], ], 'obsolete-config' => [ 'MangleFlashPolicy' => 'Since 1.39; no longer has any effect.', 'EnableOpenSearchSuggest' => 'Since 1.35, no longer used', 'AutoloadAttemptLowercase' => 'Since 1.40; no longer has any effect.', ],]
Represents a block that may prevent users from performing specific operations.
Definition Block.php:32
toArray()
Convert a block to an array of blocks.
const LOCAL
Wiki ID value to use with instances that are defined relative to the local wiki.
Interface for objects (potentially) representing an editable wiki page.
This interface represents the authority associated with the current execution context,...
Definition Authority.php:23
Interface for objects representing user identity.
Interface for database access objects.
Interface to a relational database.
Definition IDatabase.php:31
newUpdateQueryBuilder()
Get an UpdateQueryBuilder bound to this connection.
affectedRows()
Get the number of rows affected by the last query method call.
insertId()
Get the sequence-based ID assigned by the last query method call.
newInsertQueryBuilder()
Get an InsertQueryBuilder bound to this connection.
A database connection without write operations.
newSelectQueryBuilder()
Create an empty SelectQueryBuilder which can be used to run queries against this connection.
timestamp( $ts=0)
Convert a timestamp in one of the formats accepted by ConvertibleTimestamp to the format used for ins...
timestampOrNull( $ts=null)
Convert a timestamp in one of the formats accepted by ConvertibleTimestamp to the format used for ins...
const MW_NO_SESSION
Definition load.php:18