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;
43use MWCryptHash;
44use MWCryptRand;
45use Profiler;
46use RuntimeException;
47use stdClass;
48use Stringable;
49use UnexpectedValueException;
50use Wikimedia\Assert\Assert;
51use Wikimedia\Assert\PreconditionException;
52use Wikimedia\DebugInfo\DebugInfoTrait;
53use Wikimedia\IPUtils;
62use Wikimedia\ScopedCallback;
63use Wikimedia\Timestamp\ConvertibleTimestamp;
64use Wikimedia\Timestamp\TimestampFormat as TS;
65
109#[AllowDynamicProperties]
110class User implements Stringable, Authority, UserIdentity, UserEmailContact {
111 use DebugInfoTrait;
112 use ProtectedHookAccessorTrait;
113 use WikiAwareEntityTrait;
114
118 public const READ_EXCLUSIVE = IDBAccessObject::READ_EXCLUSIVE;
119
123 public const READ_LOCKING = IDBAccessObject::READ_LOCKING;
124
128 public const TOKEN_LENGTH = 32;
129
133 public const INVALID_TOKEN = '*** INVALID ***';
134
139 private const VERSION = 18;
140
145 public const MAINTENANCE_SCRIPT_USER = 'Maintenance script';
146
154 protected static $mCacheVars = [
155 // user table
156 'mId',
157 'mName',
158 'mRealName',
159 'mEmail',
160 'mTouched',
161 'mToken',
162 'mEmailAuthenticated',
163 'mEmailToken',
164 'mEmailTokenExpires',
165 // actor table
166 'mActorId',
167 ];
168
170 // Some of these are public, including for use by the UserFactory, but they generally
171 // should not be set manually
172 // @{
174 public $mId;
176 public $mName;
182 public $mActorId;
185
187 public $mEmail;
189 public $mTouched;
191 protected $mQuickTouched;
193 protected $mToken;
197 protected $mEmailToken;
200 // @}
201
202 // @{
206 protected $mLoadedItems = [];
207 // @}
208
219 public $mFrom;
220
227 protected $mGlobalBlock;
229 protected $mLocked;
230
232 private $mRequest;
233
235 protected $queryFlagsUsed = IDBAccessObject::READ_NORMAL;
236
241 private $mThisAsAuthority;
242
244 private $isTemp;
245
258 public function __construct() {
259 // By default, this is a lightweight constructor representing
260 // an anonymous user from the current web request and IP.
261 $this->clearInstanceCache( 'defaults' );
262 }
263
269 public function getWikiId(): string|false {
270 return self::LOCAL;
271 }
272
276 public function __toString() {
277 return $this->getName();
278 }
279
280 public function &__get( $name ) {
281 // A shortcut for $mRights deprecation phase
282 if ( $name === 'mRights' ) {
283 // hard deprecated since 1.40
284 wfDeprecated( 'User::$mRights', '1.34' );
286 ->getPermissionManager()
287 ->getUserPermissions( $this );
288 return $copy;
289 } elseif ( !property_exists( $this, $name ) ) {
290 // T227688 - do not break $u->foo['bar'] = 1
291 wfLogWarning( 'tried to get non-existent property' );
292 $this->$name = null;
293 return $this->$name;
294 } else {
295 wfLogWarning( 'tried to get non-visible property' );
296 $null = null;
297 return $null;
298 }
299 }
300
301 public function __set( $name, $value ) {
302 // A shortcut for $mRights deprecation phase, only known legitimate use was for
303 // testing purposes, other uses seem bad in principle
304 if ( $name === 'mRights' ) {
305 // hard deprecated since 1.40
306 wfDeprecated( 'User::$mRights', '1.34' );
307 MediaWikiServices::getInstance()->getPermissionManager()->overrideUserRightsForTesting(
308 $this,
309 $value ?? []
310 );
311 } elseif ( !property_exists( $this, $name ) ) {
312 $this->$name = $value;
313 } else {
314 wfLogWarning( 'tried to set non-visible property' );
315 }
316 }
317
318 public function __sleep(): array {
319 return array_diff(
320 array_keys( get_object_vars( $this ) ),
321 [
322 'mThisAsAuthority', // memoization, will be recreated on demand.
323 'mRequest', // contains Session, reloaded when needed, T400549
324 ]
325 );
326 }
327
342 public function isSafeToLoad() {
343 global $wgFullyInitialised;
344
345 // The user is safe to load if:
346 // * MW_NO_SESSION is undefined AND $wgFullyInitialised is true (safe to use session data)
347 // * mLoadedItems === true (already loaded)
348 // * mFrom !== 'session' (sessions not involved at all)
349
350 return ( !defined( 'MW_NO_SESSION' ) && $wgFullyInitialised ) ||
351 $this->mLoadedItems === true || $this->mFrom !== 'session';
352 }
353
359 public function load( $flags = IDBAccessObject::READ_NORMAL ) {
360 global $wgFullyInitialised;
361
362 if ( $this->mLoadedItems === true ) {
363 return;
364 }
365
366 // Set it now to avoid infinite recursion in accessors
367 $oldLoadedItems = $this->mLoadedItems;
368 $this->mLoadedItems = true;
369 $this->queryFlagsUsed = $flags;
370
371 // If this is called too early, things are likely to break.
372 if ( !$wgFullyInitialised && $this->mFrom === 'session' ) {
373 LoggerFactory::getInstance( 'session' )
374 ->warning( 'User::loadFromSession called before the end of Setup.php', [
375 'userId' => $this->mId,
376 'userName' => $this->mName,
377 'exception' => new RuntimeException(
378 'User::loadFromSession called before the end of Setup.php'
379 ),
380 ] );
381 $this->loadDefaults();
382 $this->mLoadedItems = $oldLoadedItems;
383 return;
384 } elseif ( $this->mFrom === 'session'
385 && defined( 'MW_NO_SESSION' ) && MW_NO_SESSION !== 'warn'
386 ) {
387 // Even though we are throwing an exception, make sure the User object is left in a
388 // clean state as sometimes these exceptions are caught and the object accessed again.
389 $this->loadDefaults();
390 $this->mLoadedItems = $oldLoadedItems;
391 $ep = defined( 'MW_ENTRY_POINT' ) ? MW_ENTRY_POINT : 'this';
392 throw new BadMethodCallException( "Sessions are disabled for $ep entry point" );
393 }
394
395 switch ( $this->mFrom ) {
396 case 'defaults':
397 $this->loadDefaults();
398 break;
399 case 'id':
400 // Make sure this thread sees its own changes, if the ID isn't 0
401 if ( $this->mId != 0 ) {
402 $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
403 if ( $lb->hasOrMadeRecentPrimaryChanges() ) {
404 $flags |= IDBAccessObject::READ_LATEST;
405 $this->queryFlagsUsed = $flags;
406 }
407 }
408
409 $this->loadFromId( $flags );
410 break;
411 case 'actor':
412 case 'name':
413 // Make sure this thread sees its own changes
414 $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
415 if ( $lb->hasOrMadeRecentPrimaryChanges() ) {
416 $flags |= IDBAccessObject::READ_LATEST;
417 $this->queryFlagsUsed = $flags;
418 }
419
420 $dbr = DBAccessObjectUtils::getDBFromRecency(
421 MediaWikiServices::getInstance()->getDBLoadBalancerFactory(),
422 $flags
423 );
424 $queryBuilder = $dbr->newSelectQueryBuilder()
425 ->select( [ 'actor_id', 'actor_user', 'actor_name' ] )
426 ->from( 'actor' )
427 ->recency( $flags );
428 if ( $this->mFrom === 'name' ) {
429 // make sure to use normalized form of IP for anonymous users
430 $queryBuilder->where( [ 'actor_name' => IPUtils::sanitizeIP( $this->mName ) ] );
431 } else {
432 $queryBuilder->where( [ 'actor_id' => $this->mActorId ] );
433 }
434 $row = $queryBuilder->caller( __METHOD__ )->fetchRow();
435
436 if ( !$row ) {
437 // Ugh.
438 $this->loadDefaults( $this->mFrom === 'name' ? $this->mName : false );
439 } elseif ( $row->actor_user ) {
440 $this->mId = $row->actor_user;
441 $this->loadFromId( $flags );
442 } else {
443 $this->loadDefaults( $row->actor_name, $row->actor_id );
444 }
445 break;
446 case 'session':
447 if ( !$this->loadFromSession() ) {
448 // Loading from session failed. Load defaults.
449 $this->loadDefaults();
450 }
451 $this->getHookRunner()->onUserLoadAfterLoadFromSession( $this );
452 break;
453 default:
454 throw new UnexpectedValueException(
455 "Unrecognised value for User->mFrom: \"{$this->mFrom}\"" );
456 }
457 }
458
464 public function loadFromId( $flags = IDBAccessObject::READ_NORMAL ) {
465 if ( $this->mId == 0 ) {
466 // Anonymous users are not in the database (don't need cache)
467 $this->loadDefaults();
468 return false;
469 }
470
471 // Try cache (unless this needs data from the primary DB).
472 // NOTE: if this thread called saveSettings(), the cache was cleared.
473 $latest = DBAccessObjectUtils::hasFlags( $flags, IDBAccessObject::READ_LATEST );
474 if ( $latest ) {
475 if ( !$this->loadFromDatabase( $flags ) ) {
476 // Can't load from ID
477 return false;
478 }
479 } else {
480 $this->loadFromCache();
481 }
482
483 $this->mLoadedItems = true;
484 $this->queryFlagsUsed = $flags;
485
486 return true;
487 }
488
494 public static function purge( $dbDomain, $userId ) {
495 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
496 $key = $cache->makeGlobalKey( 'user', 'id', $dbDomain, $userId );
497 $cache->delete( $key );
498 }
499
505 protected function getCacheKey( WANObjectCache $cache ) {
506 $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
507
508 return $cache->makeGlobalKey( 'user', 'id',
509 $lbFactory->getLocalDomainID(), $this->mId );
510 }
511
518 protected function loadFromCache() {
519 global $wgFullyInitialised;
520
521 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
522 $data = $cache->getWithSetCallback(
523 $this->getCacheKey( $cache ),
524 $cache::TTL_HOUR,
525 function ( $oldValue, &$ttl, array &$setOpts ) use ( $cache, $wgFullyInitialised ) {
526 $setOpts += Database::getCacheSetOptions(
527 MediaWikiServices::getInstance()->getConnectionProvider()->getReplicaDatabase()
528 );
529 wfDebug( "User: cache miss for user {$this->mId}" );
530
531 $this->loadFromDatabase( IDBAccessObject::READ_NORMAL );
532
533 $data = [];
534 foreach ( self::$mCacheVars as $name ) {
535 $data[$name] = $this->$name;
536 }
537
538 $ttl = $cache->adaptiveTTL(
539 (int)wfTimestamp( TS::UNIX, $this->mTouched ),
540 $ttl
541 );
542
543 if ( $wgFullyInitialised ) {
544 $groupMemberships = MediaWikiServices::getInstance()
545 ->getUserGroupManager()
546 ->getUserGroupMemberships( $this, $this->queryFlagsUsed );
547
548 // if a user group membership is about to expire, the cache needs to
549 // expire at that time (T163691)
550 foreach ( $groupMemberships as $ugm ) {
551 if ( $ugm->getExpiry() ) {
552 $secondsUntilExpiry =
553 (int)wfTimestamp( TS::UNIX, $ugm->getExpiry() ) - time();
554
555 if ( $secondsUntilExpiry > 0 && $secondsUntilExpiry < $ttl ) {
556 $ttl = $secondsUntilExpiry;
557 }
558 }
559 }
560 }
561
562 return $data;
563 },
564 [ 'pcTTL' => $cache::TTL_PROC_LONG, 'version' => self::VERSION ]
565 );
566
567 // Restore from cache
568 foreach ( self::$mCacheVars as $name ) {
569 $this->$name = $data[$name];
570 }
571
572 return true;
573 }
574
575 /***************************************************************************/
576 // region newFrom*() static factory methods
602 public static function newFromName( $name, $validate = 'valid' ) {
603 // Backwards compatibility with strings / false
604 $validation = match ( $validate ) {
605 'valid' => UserRigorOptions::RIGOR_VALID,
606 'usable' => UserRigorOptions::RIGOR_USABLE,
607 'creatable' => UserRigorOptions::RIGOR_CREATABLE,
608 true => UserRigorOptions::RIGOR_VALID,
609 false => UserRigorOptions::RIGOR_NONE,
610 // Not a recognized value, probably a test for unsupported validation
611 // levels, regardless, just pass it along
612 default => $validate,
613 };
614 return MediaWikiServices::getInstance()->getUserFactory()
615 ->newFromName( (string)$name, $validation ) ?? false;
616 }
617
628 public static function newFromId( $id ) {
629 return MediaWikiServices::getInstance()
630 ->getUserFactory()
631 ->newFromId( (int)$id );
632 }
633
645 public static function newFromActorId( $id ) {
646 return MediaWikiServices::getInstance()
647 ->getUserFactory()
648 ->newFromActorId( (int)$id );
649 }
650
664 public static function newFromIdentity( UserIdentity $identity ) {
665 // Don't use the service if we already have a User object,
666 // so that User::newFromIdentity calls don't break things in unit tests.
667 if ( $identity instanceof User ) {
668 return $identity;
669 }
670
671 return MediaWikiServices::getInstance()
672 ->getUserFactory()
673 ->newFromUserIdentity( $identity );
674 }
675
694 public static function newFromAnyId( $userId, $userName, $actorId, $dbDomain = false ) {
695 return MediaWikiServices::getInstance()
696 ->getUserFactory()
697 ->newFromAnyId( $userId, $userName, $actorId, $dbDomain );
698 }
699
715 public static function newFromConfirmationCode( $code, $flags = IDBAccessObject::READ_NORMAL ) {
716 return MediaWikiServices::getInstance()
717 ->getUserFactory()
718 ->newFromConfirmationCode( (string)$code, $flags );
719 }
720
728 public static function newFromSession( ?WebRequest $request = null ) {
729 $user = new User;
730 $user->mFrom = 'session';
731 $user->mRequest = $request;
732 return $user;
733 }
734
750 public static function newFromRow( $row, $data = null ) {
751 $user = new User;
752 $user->loadFromRow( $row, $data );
753 return $user;
754 }
755
800 public static function newSystemUser( $name, $options = [] ) {
801 $options += [
802 'validate' => UserRigorOptions::RIGOR_VALID,
803 'create' => true,
804 'steal' => false,
805 ];
806
807 // Username validation
808 $validate = $options['validate'];
809 // Backwards compatibility with strings / false
810 $validation = match ( $validate ) {
811 'valid' => UserRigorOptions::RIGOR_VALID,
812 'usable' => UserRigorOptions::RIGOR_USABLE,
813 'creatable' => UserRigorOptions::RIGOR_CREATABLE,
814 false => UserRigorOptions::RIGOR_NONE,
815 // Not a recognized value, probably a test for unsupported validation
816 // levels, regardless, just pass it along
817 default => $validate,
818 };
819
820 if ( $validation !== UserRigorOptions::RIGOR_VALID ) {
822 __METHOD__ . ' options["validation"] parameter must be omitted or set to "valid".',
823 '1.36'
824 );
825 }
826 $services = MediaWikiServices::getInstance();
827 $userNameUtils = $services->getUserNameUtils();
828
829 $name = $userNameUtils->getCanonical( (string)$name, $validation );
830 if ( $name === false ) {
831 return null;
832 }
833
834 $dbProvider = $services->getDBLoadBalancerFactory();
835 $dbr = $dbProvider->getReplicaDatabase();
836
837 $userQuery = self::newQueryBuilder( $dbr )
838 ->where( [ 'user_name' => $name ] )
839 ->caller( __METHOD__ );
840 $row = $userQuery->fetchRow();
841 if ( !$row ) {
842 // Try the primary database
843 $userQuery->connection( $dbProvider->getPrimaryDatabase() );
844 // Lock the row to prevent insertNewUser() returning null due to race conditions
845 $userQuery->forUpdate();
846 $row = $userQuery->fetchRow();
847 }
848
849 if ( !$row ) {
850 // No user. Create it?
851 if ( !$options['create'] ) {
852 // No.
853 return null;
854 }
855
856 // If it's a reserved user that had an anonymous actor created for it at
857 // some point, we need special handling.
858 return self::insertNewUser( static function ( UserIdentity $actor, IDatabase $dbw ) {
859 return MediaWikiServices::getInstance()->getActorStore()
860 ->acquireSystemActorId( $actor, $dbw );
861 }, $name, [ 'token' => self::INVALID_TOKEN ] );
862 }
863
864 $user = self::newFromRow( $row );
865
866 if ( !$user->isSystemUser() ) {
867 // User exists. Steal it?
868 if ( !$options['steal'] ) {
869 return null;
870 }
871
872 $services->getAuthManager()->revokeAccessForUser( $name );
873
874 $user->invalidateEmail();
875 $user->mToken = self::INVALID_TOKEN;
876 $user->saveSettings();
877 $manager = $services->getSessionManager();
878 if ( $manager instanceof SessionManager ) {
879 $manager->preventSessionsForUser( $user->getName() );
880 }
881 }
882
883 return $user;
884 }
885
887 // endregion -- end of newFrom*() static factory methods
888
899 public static function findUsersByGroup( $groups, $limit = 5000, $after = null ) {
900 if ( $groups === [] ) {
901 return UserArrayFromResult::newFromIDs( [] );
902 }
903 $dbr = MediaWikiServices::getInstance()->getConnectionProvider()->getReplicaDatabase();
904 $queryBuilder = $dbr->newSelectQueryBuilder()
905 ->select( 'ug_user' )
906 ->distinct()
907 ->from( 'user_groups' )
908 ->where( [ 'ug_group' => array_unique( (array)$groups ) ] )
909 ->orderBy( 'ug_user' )
910 ->limit( min( 5000, $limit ) );
911
912 if ( $after !== null ) {
913 $queryBuilder->andWhere( $dbr->expr( 'ug_user', '>', (int)$after ) );
914 }
915
916 $ids = $queryBuilder->caller( __METHOD__ )->fetchFieldValues() ?: [];
917 return UserArray::newFromIDs( $ids );
918 }
919
926 public function isValidPassword( $password ) {
927 // simple boolean wrapper for checkPasswordValidity
928 return $this->checkPasswordValidity( $password )->isGood();
929 }
930
952 public function checkPasswordValidity( $password ) {
953 $services = MediaWikiServices::getInstance();
954 $userNameUtils = $services->getUserNameUtils();
955 if ( $userNameUtils->isTemp( $this->getName() ) ) {
956 return Status::newFatal( 'error-temporary-accounts-cannot-have-passwords' );
957 }
958
959 $passwordPolicy = $services->getMainConfig()->get( MainConfigNames::PasswordPolicy );
960
961 $upp = new UserPasswordPolicy(
962 $passwordPolicy['policies'],
963 $passwordPolicy['checks']
964 );
965
966 $status = Status::newGood( [] );
967 $result = false; // init $result to false for the internal checks
968
969 if ( !$this->getHookRunner()->onIsValidPassword( $password, $result, $this ) ) {
970 $status->error( $result );
971 return $status;
972 }
973
974 if ( $result === false ) {
975 $status->merge( $upp->checkUserPassword( $this, $password ), true );
976 return $status;
977 }
978
979 if ( $result === true ) {
980 return $status;
981 }
982
983 $status->error( $result );
984 return $status; // the isValidPassword hook set a string $result and returned true
985 }
986
996 public function loadDefaults( $name = false, $actorId = null ) {
997 $this->mId = 0;
998 $this->mName = $name;
999 $this->mActorId = $actorId;
1000 $this->mRealName = '';
1001 $this->mEmail = '';
1002 $this->isTemp = null;
1003
1004 $loggedOut = $this->mRequest && !defined( 'MW_NO_SESSION' )
1005 ? $this->mRequest->getSession()->getLoggedOutTimestamp() : 0;
1006 if ( $loggedOut !== 0 ) {
1007 $this->mTouched = wfTimestamp( TS::MW, $loggedOut );
1008 } else {
1009 $this->mTouched = '1'; # Allow any pages to be cached
1010 }
1011
1012 $this->mToken = null; // Don't run cryptographic functions till we need a token
1013 $this->mEmailAuthenticated = null;
1014 $this->mEmailToken = '';
1015 $this->mEmailTokenExpires = null;
1016
1017 $this->getHookRunner()->onUserLoadDefaults( $this, $name );
1018 }
1019
1032 public function isItemLoaded( $item, $all = 'all' ) {
1033 return ( $this->mLoadedItems === true && $all === 'all' ) ||
1034 ( isset( $this->mLoadedItems[$item] ) && $this->mLoadedItems[$item] === true );
1035 }
1036
1044 public function setItemLoaded( $item ) {
1045 if ( is_array( $this->mLoadedItems ) ) {
1046 $this->mLoadedItems[$item] = true;
1047 }
1048 }
1049
1055 private function loadFromSession() {
1056 // MediaWiki\Session\Session already did the necessary authentication of the user
1057 // returned here, so just use it if applicable.
1058 $session = $this->getRequest()->getSession();
1059 $user = $session->getUser();
1060 if ( $user->isRegistered() ) {
1061 $this->loadFromUserObject( $user );
1062
1063 // Other code expects these to be set in the session, so set them.
1064 $session->set( 'wsUserID', $this->getId() );
1065 $session->set( 'wsUserName', $this->getName() );
1066 $session->set( 'wsToken', $this->getToken() );
1067
1068 return true;
1069 }
1070
1071 return false;
1072 }
1073
1081 public function loadFromDatabase( $flags = IDBAccessObject::READ_LATEST ) {
1082 // Paranoia
1083 $this->mId = intval( $this->mId );
1084
1085 if ( !$this->mId ) {
1086 // Anonymous users are not in the database
1087 $this->loadDefaults();
1088 return false;
1089 }
1090
1091 $db = DBAccessObjectUtils::getDBFromRecency(
1092 MediaWikiServices::getInstance()->getDBLoadBalancerFactory(),
1093 $flags
1094 );
1095 $row = self::newQueryBuilder( $db )
1096 ->where( [ 'user_id' => $this->mId ] )
1097 ->recency( $flags )
1098 ->caller( __METHOD__ )
1099 ->fetchRow();
1100
1101 $this->queryFlagsUsed = $flags;
1102
1103 if ( $row !== false ) {
1104 // Initialise user table data
1105 $this->loadFromRow( $row );
1106 return true;
1107 }
1108
1109 // Invalid user_id
1110 $this->mId = 0;
1111 $this->loadDefaults( 'Unknown user' );
1112
1113 return false;
1114 }
1115
1127 protected function loadFromRow( $row, $data = null ) {
1128 if ( !( $row instanceof stdClass ) ) {
1129 throw new InvalidArgumentException( '$row must be an object' );
1130 }
1131
1132 $all = true;
1133
1134 if ( isset( $row->actor_id ) ) {
1135 $this->mActorId = (int)$row->actor_id;
1136 if ( $this->mActorId !== 0 ) {
1137 $this->mFrom = 'actor';
1138 }
1139 $this->setItemLoaded( 'actor' );
1140 } else {
1141 $all = false;
1142 }
1143
1144 if ( isset( $row->user_name ) && $row->user_name !== '' ) {
1145 $this->mName = $row->user_name;
1146 $this->mFrom = 'name';
1147 $this->setItemLoaded( 'name' );
1148 } else {
1149 $all = false;
1150 }
1151
1152 if ( isset( $row->user_real_name ) ) {
1153 $this->mRealName = $row->user_real_name;
1154 $this->setItemLoaded( 'realname' );
1155 } else {
1156 $all = false;
1157 }
1158
1159 if ( isset( $row->user_id ) ) {
1160 $this->mId = intval( $row->user_id );
1161 if ( $this->mId !== 0 ) {
1162 $this->mFrom = 'id';
1163 }
1164 $this->setItemLoaded( 'id' );
1165 } else {
1166 $all = false;
1167 }
1168
1169 if ( isset( $row->user_editcount ) ) {
1170 // Don't try to set edit count for anonymous users
1171 // We check the id here and not in UserEditTracker because calling
1172 // User::getId() can trigger some other loading. This will result in
1173 // discarding the user_editcount field for rows if the id wasn't set.
1174 if ( $this->mId !== null && $this->mId !== 0 ) {
1175 MediaWikiServices::getInstance()
1176 ->getUserEditTracker()
1177 ->setCachedUserEditCount( $this, (int)$row->user_editcount );
1178 }
1179 } else {
1180 $all = false;
1181 }
1182
1183 if ( isset( $row->user_touched ) ) {
1184 $this->mTouched = wfTimestamp( TS::MW, $row->user_touched );
1185 } else {
1186 $all = false;
1187 }
1188
1189 if ( isset( $row->user_token ) ) {
1190 // The definition for the column is binary(32), so trim the NULs
1191 // that appends. The previous definition was char(32), so trim
1192 // spaces too.
1193 $this->mToken = rtrim( $row->user_token, " \0" );
1194 if ( $this->mToken === '' ) {
1195 $this->mToken = null;
1196 }
1197 } else {
1198 $all = false;
1199 }
1200
1201 if ( isset( $row->user_email ) ) {
1202 $this->mEmail = $row->user_email;
1203 $this->mEmailAuthenticated = wfTimestampOrNull( TS::MW, $row->user_email_authenticated );
1204 $this->mEmailToken = $row->user_email_token;
1205 $this->mEmailTokenExpires = wfTimestampOrNull( TS::MW, $row->user_email_token_expires );
1206 $registration = wfTimestampOrNull( TS::MW, $row->user_registration );
1207 MediaWikiServices::getInstance()
1208 ->getUserRegistrationLookup()
1209 ->setCachedRegistration( $this, $registration );
1210 } else {
1211 $all = false;
1212 }
1213
1214 if ( $all ) {
1215 $this->mLoadedItems = true;
1216 }
1217
1218 if ( is_array( $data ) ) {
1219
1220 if ( isset( $data['user_groups'] ) && is_array( $data['user_groups'] ) ) {
1221 MediaWikiServices::getInstance()
1222 ->getUserGroupManager()
1223 ->loadGroupMembershipsFromArray(
1224 $this,
1225 $data['user_groups'],
1226 $this->queryFlagsUsed
1227 );
1228 }
1229 }
1230 }
1231
1237 protected function loadFromUserObject( $user ) {
1238 $user->load();
1239 foreach ( self::$mCacheVars as $var ) {
1240 $this->$var = $user->$var;
1241 }
1242 }
1243
1251 protected function makeUpdateConditions( IReadableDatabase $db ) {
1252 if ( $this->mTouched ) {
1253 // CAS check: only update if the row wasn't changed since it was loaded.
1254 return [ 'user_touched' => $db->timestamp( $this->mTouched ) ];
1255 }
1256 return [];
1257 }
1258
1269 public function checkAndSetTouched() {
1270 $this->load();
1271
1272 if ( !$this->mId ) {
1273 return false; // anon
1274 }
1275
1276 // Get a new user_touched that is higher than the old one
1277 $newTouched = $this->newTouchedTimestamp();
1278
1279 $dbw = MediaWikiServices::getInstance()->getConnectionProvider()->getPrimaryDatabase();
1280 $dbw->newUpdateQueryBuilder()
1281 ->update( 'user' )
1282 ->set( [ 'user_touched' => $dbw->timestamp( $newTouched ) ] )
1283 ->where( [ 'user_id' => $this->mId ] )
1284 ->andWhere( $this->makeUpdateConditions( $dbw ) )
1285 ->caller( __METHOD__ )->execute();
1286 $success = ( $dbw->affectedRows() > 0 );
1287
1288 if ( $success ) {
1289 $this->mTouched = $newTouched;
1290 $this->clearSharedCache( 'changed' );
1291 } else {
1292 // Clears on failure too since that is desired if the cache is stale
1293 $this->clearSharedCache( 'refresh' );
1294 }
1295
1296 return $success;
1297 }
1298
1306 public function clearInstanceCache( $reloadFrom = false ) {
1307 global $wgFullyInitialised;
1308
1309 $this->mDatePreference = null;
1310 $this->mThisAsAuthority = null;
1311
1312 if ( $wgFullyInitialised && $this->mFrom ) {
1313 $services = MediaWikiServices::getInstance();
1314
1315 if ( $services->peekService( 'PermissionManager' ) ) {
1316 $services->getPermissionManager()->invalidateUsersRightsCache( $this );
1317 }
1318
1319 if ( $services->peekService( 'UserOptionsManager' ) ) {
1320 $services->getUserOptionsManager()->clearUserOptionsCache( $this );
1321 }
1322
1323 if ( $services->peekService( 'TalkPageNotificationManager' ) ) {
1324 $services->getTalkPageNotificationManager()->clearInstanceCache( $this );
1325 }
1326
1327 if ( $services->peekService( 'UserGroupManager' ) ) {
1328 $services->getUserGroupManager()->clearCache( $this );
1329 }
1330
1331 if ( $services->peekService( 'UserEditTracker' ) ) {
1332 $services->getUserEditTracker()->clearUserEditCache( $this );
1333 }
1334
1335 if ( $services->peekService( 'BlockManager' ) ) {
1336 $services->getBlockManager()->clearUserCache( $this );
1337 }
1338 }
1339
1340 if ( $reloadFrom ) {
1341 if ( in_array( $reloadFrom, [ 'name', 'id', 'actor' ] ) ) {
1342 $this->mLoadedItems = [ $reloadFrom => true ];
1343 } else {
1344 $this->mLoadedItems = [];
1345 }
1346 $this->mFrom = $reloadFrom;
1347 }
1348 }
1349
1355 public function isPingLimitable() {
1356 $limiter = MediaWikiServices::getInstance()->getRateLimiter();
1357 $subject = $this->toRateLimitSubject();
1358 return !$limiter->isExempt( $subject );
1359 }
1360
1376 public function pingLimiter( $action = 'edit', $incrBy = 1 ) {
1377 return $this->getThisAsAuthority()->limit( $action, $incrBy, null );
1378 }
1379
1385 $flags = [
1386 'exempt' => $this->isAllowed( 'noratelimit' ),
1387 'newbie' => $this->isNewbie(),
1388 ];
1389
1390 return new RateLimitSubject( $this, $this->getRequest()->getIP(), $flags );
1391 }
1392
1405 public function getBlock(
1406 $freshness = IDBAccessObject::READ_NORMAL,
1407 $disableIpBlockExemptChecking = false
1408 ): ?Block {
1409 if ( is_bool( $freshness ) ) {
1410 $fromReplica = $freshness;
1411 } else {
1412 $fromReplica = ( $freshness !== IDBAccessObject::READ_LATEST );
1413 }
1414
1415 if ( $disableIpBlockExemptChecking ) {
1416 $isExempt = false;
1417 } else {
1418 $isExempt = $this->isAllowed( 'ipblock-exempt' );
1419 }
1420
1421 // TODO: Block checking shouldn't really be done from the User object. Block
1422 // checking can involve checking for IP blocks, cookie blocks, and/or XFF blocks,
1423 // which need more knowledge of the request context than the User should have.
1424 // Since we do currently check blocks from the User, we have to do the following
1425 // here:
1426 // - Check if this is the user associated with the main request
1427 // - If so, pass the relevant request information to the block manager
1428 $request = null;
1429 if ( !$isExempt && $this->isGlobalSessionUser() ) {
1430 // This is the global user, so we need to pass the request
1431 $request = $this->getRequest();
1432 }
1433
1434 return MediaWikiServices::getInstance()->getBlockManager()->getBlock(
1435 $this,
1436 $request,
1437 $fromReplica,
1438 );
1439 }
1440
1446 public function isLocked() {
1447 if ( $this->mLocked !== null ) {
1448 return $this->mLocked;
1449 }
1450 // Reset for hook
1451 $this->mLocked = false;
1452 $this->getHookRunner()->onUserIsLocked( $this, $this->mLocked );
1453 return $this->mLocked;
1454 }
1455
1461 public function isHidden() {
1462 $block = $this->getBlock();
1463 return $block ? $block->getHideName() : false;
1464 }
1465
1471 public function getId( $wikiId = self::LOCAL ): int {
1472 $this->assertWiki( $wikiId );
1473 if ( $this->mId === null && $this->mName !== null ) {
1474 $userNameUtils = MediaWikiServices::getInstance()->getUserNameUtils();
1475 if ( $userNameUtils->isIP( $this->mName ) || ExternalUserNames::isExternal( $this->mName ) ) {
1476 // Special case, we know the user is anonymous
1477 // Note that "external" users are "local" (they have an actor ID that is relative to
1478 // the local wiki).
1479 return 0;
1480 }
1481 }
1482
1483 if ( !$this->isItemLoaded( 'id' ) ) {
1484 // Don't load if this was initialized from an ID
1485 $this->load();
1486 }
1487
1488 return (int)$this->mId;
1489 }
1490
1495 public function setId( $v ) {
1496 $this->mId = $v;
1497 $this->clearInstanceCache( 'id' );
1498 }
1499
1504 public function getName(): string {
1505 if ( $this->isItemLoaded( 'name', 'only' ) ) {
1506 // Special case optimisation
1507 return $this->mName;
1508 }
1509
1510 $this->load();
1511 if ( $this->mName === false ) {
1512 // Clean up IPs
1513 $this->mName = IPUtils::sanitizeIP( $this->getRequest()->getIP() );
1514 }
1515
1516 return $this->mName;
1517 }
1518
1532 public function setName( $str ) {
1533 $this->load();
1534 $this->mName = $str;
1535 }
1536
1550 public function getActorId( $dbwOrWikiId = self::LOCAL ): int {
1551 if ( $dbwOrWikiId ) {
1552 wfDeprecatedMsg( 'Passing a parameter to getActorId() is deprecated', '1.36' );
1553 }
1554
1555 if ( is_string( $dbwOrWikiId ) ) {
1556 $this->assertWiki( $dbwOrWikiId );
1557 }
1558
1559 if ( !$this->isItemLoaded( 'actor' ) ) {
1560 $this->load();
1561 }
1562
1563 if ( !$this->mActorId && $dbwOrWikiId instanceof IDatabase ) {
1564 MediaWikiServices::getInstance()
1565 ->getActorStoreFactory()
1566 ->getActorNormalization( $dbwOrWikiId->getDomainID() )
1567 ->acquireActorId( $this, $dbwOrWikiId );
1568 // acquireActorId will call setActorId on $this
1569 Assert::postcondition(
1570 $this->mActorId !== null,
1571 "Failed to acquire actor ID for user id {$this->mId} name {$this->mName}"
1572 );
1573 }
1574
1575 return (int)$this->mActorId;
1576 }
1577
1587 public function setActorId( int $actorId ) {
1588 $this->mActorId = $actorId;
1589 $this->setItemLoaded( 'actor' );
1590 }
1591
1596 public function getTitleKey(): string {
1597 return str_replace( ' ', '_', $this->getName() );
1598 }
1599
1606 private function newTouchedTimestamp() {
1607 $time = (int)ConvertibleTimestamp::now( TS::UNIX );
1608 if ( $this->mTouched ) {
1609 $time = max( $time, (int)ConvertibleTimestamp::convert( TS::UNIX, $this->mTouched ) + 1 );
1610 }
1611
1612 return ConvertibleTimestamp::convert( TS::MW, $time );
1613 }
1614
1625 public function clearSharedCache( $mode = 'refresh' ) {
1626 if ( !$this->getId() ) {
1627 return;
1628 }
1629
1630 $dbProvider = MediaWikiServices::getInstance()->getConnectionProvider();
1631 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
1632 $key = $this->getCacheKey( $cache );
1633
1634 if ( $mode === 'refresh' ) {
1635 $cache->delete( $key, 1 ); // low tombstone/"hold-off" TTL
1636 } else {
1637 $dbProvider->getPrimaryDatabase()->onTransactionPreCommitOrIdle(
1638 static function () use ( $cache, $key ) {
1639 $cache->delete( $key );
1640 },
1641 __METHOD__
1642 );
1643 }
1644 }
1645
1651 public function invalidateCache() {
1652 $this->touch();
1653 $this->clearSharedCache( 'changed' );
1654 }
1655
1668 public function touch() {
1669 $id = $this->getId();
1670 if ( $id ) {
1671 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
1672 $key = $cache->makeKey( 'user-quicktouched', 'id', $id );
1673 $cache->touchCheckKey( $key );
1674 $this->mQuickTouched = null;
1675 }
1676 }
1677
1683 public function debouncedDBTouch() {
1684 $oldTouched = (int)ConvertibleTimestamp::convert( TS::UNIX, $this->getDBTouched() );
1685 $newTouched = (int)ConvertibleTimestamp::now( TS::UNIX );
1686
1687 if ( ( $newTouched - $oldTouched ) < ( 300 + mt_rand( 1, 20 ) ) ) {
1688 // Touched would be updated too soon, skip this round
1689 // Adding jitter to avoid stampede.
1690 return;
1691 }
1692
1693 // Too old, definitely update.
1694 $this->checkAndSetTouched();
1695 }
1696
1702 public function validateCache( $timestamp ) {
1703 return ( $timestamp >= $this->getTouched() );
1704 }
1705
1714 public function getTouched() {
1715 $this->load();
1716
1717 if ( $this->mId ) {
1718 if ( $this->mQuickTouched === null ) {
1719 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
1720 $key = $cache->makeKey( 'user-quicktouched', 'id', $this->mId );
1721
1722 $this->mQuickTouched = wfTimestamp( TS::MW, $cache->getCheckKeyTime( $key ) );
1723 }
1724
1725 return max( $this->mTouched, $this->mQuickTouched );
1726 }
1727
1728 return $this->mTouched;
1729 }
1730
1736 public function getDBTouched() {
1737 $this->load();
1738
1739 return $this->mTouched;
1740 }
1741
1754 public function changeAuthenticationData( array $data ) {
1755 $manager = MediaWikiServices::getInstance()->getAuthManager();
1756 $reqs = $manager->getAuthenticationRequests( AuthManager::ACTION_CHANGE, $this );
1757 $reqs = AuthenticationRequest::loadRequestsFromSubmission( $reqs, $data );
1758
1759 $status = Status::newGood( 'ignored' );
1760 foreach ( $reqs as $req ) {
1761 $status->merge( $manager->allowsAuthenticationDataChange( $req ), true );
1762 }
1763 if ( $status->getValue() === 'ignored' ) {
1764 $status->warning( 'authenticationdatachange-ignored' );
1765 }
1766
1767 if ( $status->isGood() ) {
1768 foreach ( $reqs as $req ) {
1769 $manager->changeAuthenticationData( $req );
1770 }
1771 }
1772 return $status;
1773 }
1774
1781 public function getToken( $forceCreation = true ) {
1782 $authenticationTokenVersion = MediaWikiServices::getInstance()
1783 ->getMainConfig()->get( MainConfigNames::AuthenticationTokenVersion );
1784
1785 $this->load();
1786 if ( !$this->mToken && $forceCreation ) {
1787 $this->setToken();
1788 }
1789
1790 if ( !$this->mToken ) {
1791 // The user doesn't have a token, return null to indicate that.
1792 return null;
1793 }
1794
1795 if ( $this->mToken === self::INVALID_TOKEN ) {
1796 // We return a random value here so existing token checks are very
1797 // likely to fail.
1798 return MWCryptRand::generateHex( self::TOKEN_LENGTH );
1799 }
1800
1801 if ( $authenticationTokenVersion === null ) {
1802 // $wgAuthenticationTokenVersion not in use, so return the raw secret
1803 return $this->mToken;
1804 }
1805
1806 // $wgAuthenticationTokenVersion in use, so hmac it.
1807 $ret = MWCryptHash::hmac( $authenticationTokenVersion, $this->mToken, false );
1808
1809 // The raw hash can be overly long. Shorten it up.
1810 $len = max( 32, self::TOKEN_LENGTH );
1811 if ( strlen( $ret ) < $len ) {
1812 // Should never happen, even md5 is 128 bits
1813 throw new \UnexpectedValueException( 'Hmac returned less than 128 bits' );
1814 }
1815
1816 return substr( $ret, -$len );
1817 }
1818
1825 public function setToken( $token = false ) {
1826 $this->load();
1827 if ( $this->mToken === self::INVALID_TOKEN ) {
1828 LoggerFactory::getInstance( 'session' )
1829 ->debug( __METHOD__ . ": Ignoring attempt to set token for system user \"$this\"" );
1830 } elseif ( !$token ) {
1831 $this->mToken = MWCryptRand::generateHex( self::TOKEN_LENGTH );
1832 } else {
1833 $this->mToken = $token;
1834 }
1835 }
1836
1841 public function getEmail(): string {
1842 $this->load();
1843 $email = $this->mEmail;
1844 $this->getHookRunner()->onUserGetEmail( $this, $email );
1845 // In case a hook handler returns e.g. null
1846 $this->mEmail = is_string( $email ) ? $email : '';
1847 return $this->mEmail;
1848 }
1849
1855 $this->load();
1856 $this->getHookRunner()->onUserGetEmailAuthenticationTimestamp(
1857 $this, $this->mEmailAuthenticated );
1858 return $this->mEmailAuthenticated;
1859 }
1860
1865 public function setEmail( string $str ) {
1866 $this->load();
1867 if ( $str == $this->getEmail() ) {
1868 return;
1869 }
1870 $this->invalidateEmail();
1871 $this->mEmail = $str;
1872 $this->getHookRunner()->onUserSetEmail( $this, $this->mEmail );
1873 }
1874
1882 public function setEmailWithConfirmation( string $str ) {
1883 $config = MediaWikiServices::getInstance()->getMainConfig();
1884 $enableEmail = $config->get( MainConfigNames::EnableEmail );
1885
1886 if ( !$enableEmail ) {
1887 return Status::newFatal( 'emaildisabled' );
1888 }
1889
1890 $oldaddr = $this->getEmail();
1891 if ( $str === $oldaddr ) {
1892 return Status::newGood( true );
1893 }
1894
1895 $type = $oldaddr != '' ? 'changed' : 'set';
1896 $notificationResult = null;
1897
1898 $emailAuthentication = $config->get( MainConfigNames::EmailAuthentication );
1899
1900 if ( $emailAuthentication && $type === 'changed' && $this->isEmailConfirmed() ) {
1901 // Send the user an email notifying the user of the change in registered
1902 // email address on their previous verified email address
1903 $change = $str != '' ? 'changed' : 'removed';
1904 $notificationResult = $this->sendMail(
1905 wfMessage( 'notificationemail_subject_' . $change )->text(),
1906 wfMessage( 'notificationemail_body_' . $change,
1907 $this->getRequest()->getIP(),
1908 $this->getName(),
1909 $str )->text()
1910 );
1911 }
1912
1913 $this->setEmail( $str );
1914
1915 if ( $str !== '' && $emailAuthentication ) {
1916 // Send a confirmation request to the new address if needed
1917 $result = $this->sendConfirmationMail( $type );
1918
1919 if ( $notificationResult !== null ) {
1920 $result->merge( $notificationResult );
1921 }
1922
1923 if ( $result->isGood() ) {
1924 // Say to the caller that a confirmation and notification mail has been sent
1925 $result->value = 'eauth';
1926 }
1927 } else {
1928 $result = Status::newGood( true );
1929 }
1930
1931 return $result;
1932 }
1933
1938 public function getRealName(): string {
1939 if ( !$this->isItemLoaded( 'realname' ) ) {
1940 $this->load();
1941 }
1942
1943 return $this->mRealName;
1944 }
1945
1950 public function setRealName( string $str ) {
1951 $this->load();
1952 $this->mRealName = $str;
1953 }
1954
1965 public function getTokenFromOption( $oname ) {
1966 $hiddenPrefs =
1967 MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::HiddenPrefs );
1968
1969 $id = $this->getId();
1970 if ( !$id || in_array( $oname, $hiddenPrefs ) ) {
1971 return false;
1972 }
1973
1974 $userOptionsLookup = MediaWikiServices::getInstance()
1975 ->getUserOptionsLookup();
1976 $token = $userOptionsLookup->getOption( $this, (string)$oname );
1977 if ( !$token ) {
1978 // Default to a value based on the user token to avoid space
1979 // wasted on storing tokens for all users. When this option
1980 // is set manually by the user, only then is it stored.
1981 $token = hash_hmac( 'sha1', "$oname:$id", $this->getToken() );
1982 }
1983
1984 return $token;
1985 }
1986
1996 public function resetTokenFromOption( $oname ) {
1997 $hiddenPrefs =
1998 MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::HiddenPrefs );
1999 if ( in_array( $oname, $hiddenPrefs ) ) {
2000 return false;
2001 }
2002
2003 $token = MWCryptRand::generateHex( 40 );
2004 MediaWikiServices::getInstance()
2005 ->getUserOptionsManager()
2006 ->setOption( $this, $oname, $token );
2007 return $token;
2008 }
2009
2014 public function getDatePreference() {
2015 // Important migration for old data rows
2016 if ( $this->mDatePreference === null ) {
2017 global $wgLang;
2018 $userOptionsLookup = MediaWikiServices::getInstance()
2019 ->getUserOptionsLookup();
2020 $value = $userOptionsLookup->getOption( $this, 'date' );
2021 $map = $wgLang->getDatePreferenceMigrationMap();
2022 if ( isset( $map[$value] ) ) {
2023 $value = $map[$value];
2024 }
2025 $this->mDatePreference = $value;
2026 }
2027 return $this->mDatePreference;
2028 }
2029
2036 public function requiresHTTPS() {
2037 if ( !$this->isRegistered() ) {
2038 return false;
2039 }
2040
2041 $services = MediaWikiServices::getInstance();
2042 $config = $services->getMainConfig();
2043 if ( $config->get( MainConfigNames::ForceHTTPS ) ) {
2044 return true;
2045 }
2046 if ( !$config->get( MainConfigNames::SecureLogin ) ) {
2047 return false;
2048 }
2049 return $services->getUserOptionsLookup()
2050 ->getBoolOption( $this, 'prefershttps' );
2051 }
2052
2057 public function getEditCount() {
2058 return MediaWikiServices::getInstance()
2059 ->getUserEditTracker()
2060 ->getUserEditCount( $this );
2061 }
2062
2071 public function isRegistered(): bool {
2072 return $this->getId() != 0;
2073 }
2074
2079 public function isAnon() {
2080 return !$this->isRegistered();
2081 }
2082
2087 public function isBot() {
2088 $userGroupManager = MediaWikiServices::getInstance()->getUserGroupManager();
2089 if ( in_array( 'bot', $userGroupManager->getUserGroups( $this ) )
2090 && $this->isAllowed( 'bot' )
2091 ) {
2092 return true;
2093 }
2094
2095 $isBot = false;
2096 $this->getHookRunner()->onUserIsBot( $this, $isBot );
2097
2098 return $isBot;
2099 }
2100
2110 public function isSystemUser() {
2111 $this->load();
2112 if ( $this->getEmail() || $this->mToken !== self::INVALID_TOKEN ||
2113 MediaWikiServices::getInstance()->getAuthManager()->userCanAuthenticate( $this->mName )
2114 ) {
2115 return false;
2116 }
2117 return true;
2118 }
2119
2121 public function isAllowedAny( ...$permissions ): bool {
2122 return $this->getThisAsAuthority()->isAllowedAny( ...$permissions );
2123 }
2124
2126 public function isAllowedAll( ...$permissions ): bool {
2127 return $this->getThisAsAuthority()->isAllowedAll( ...$permissions );
2128 }
2129
2130 public function isAllowed( string $permission, ?PermissionStatus $status = null ): bool {
2131 return $this->getThisAsAuthority()->isAllowed( $permission, $status );
2132 }
2133
2138 public function useRCPatrol() {
2139 $useRCPatrol =
2140 MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::UseRCPatrol );
2141 return $useRCPatrol && $this->isAllowedAny( 'patrol', 'patrolmarks' );
2142 }
2143
2148 public function useNPPatrol() {
2149 $useRCPatrol =
2150 MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::UseRCPatrol );
2151 $useNPPatrol =
2152 MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::UseNPPatrol );
2153 return (
2154 ( $useRCPatrol || $useNPPatrol )
2155 && ( $this->isAllowedAny( 'patrol', 'patrolmarks' ) )
2156 );
2157 }
2158
2163 public function useFilePatrol() {
2164 $useRCPatrol =
2165 MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::UseRCPatrol );
2166 $useFilePatrol = MediaWikiServices::getInstance()->getMainConfig()
2167 ->get( MainConfigNames::UseFilePatrol );
2168 return (
2169 ( $useRCPatrol || $useFilePatrol )
2170 && ( $this->isAllowedAny( 'patrol', 'patrolmarks' ) )
2171 );
2172 }
2173
2177 public function getRequest(): WebRequest {
2178 return $this->mRequest ?? RequestContext::getMain()->getRequest();
2179 }
2180
2186 public function getExperienceLevel() {
2187 $mainConfig = MediaWikiServices::getInstance()->getMainConfig();
2188 $learnerEdits = $mainConfig->get( MainConfigNames::LearnerEdits );
2189 $experiencedUserEdits = $mainConfig->get( MainConfigNames::ExperiencedUserEdits );
2190 $learnerMemberSince = $mainConfig->get( MainConfigNames::LearnerMemberSince );
2191 $experiencedUserMemberSince =
2192 $mainConfig->get( MainConfigNames::ExperiencedUserMemberSince );
2193 if ( $this->isAnon() ) {
2194 return false;
2195 }
2196
2197 $editCount = $this->getEditCount();
2198 $registration = $this->getRegistration();
2199 $now = time();
2200 $learnerRegistration = wfTimestamp( TS::MW, $now - $learnerMemberSince * 86400 );
2201 $experiencedRegistration = wfTimestamp( TS::MW, $now - $experiencedUserMemberSince * 86400 );
2202 if ( $registration === null ) {
2203 // for some very old accounts, this information is missing in the database
2204 // treat them as old enough to be 'experienced'
2205 $registration = $experiencedRegistration;
2206 }
2207
2208 if ( $editCount < $learnerEdits ||
2209 $registration > $learnerRegistration ) {
2210 return 'newcomer';
2211 }
2212
2213 if ( $editCount > $experiencedUserEdits &&
2214 $registration <= $experiencedRegistration
2215 ) {
2216 return 'experienced';
2217 }
2218
2219 return 'learner';
2220 }
2221
2230 public function setCookies( $request = null, $secure = null, $rememberMe = false ) {
2231 $this->load();
2232 if ( $this->mId == 0 ) {
2233 return;
2234 }
2235
2236 $session = $this->getRequest()->getSession();
2237 if ( $request && $session->getRequest() !== $request ) {
2238 $session = $session->sessionWithRequest( $request );
2239 }
2240 $delay = $session->delaySave();
2241
2242 if ( !$session->getUser()->equals( $this ) ) {
2243 if ( !$session->canSetUser() ) {
2244 LoggerFactory::getInstance( 'session' )
2245 ->warning( __METHOD__ .
2246 ": Cannot save user \"$this\" to a user " .
2247 "\"{$session->getUser()}\"'s immutable session"
2248 );
2249 return;
2250 }
2251 $session->setUser( $this );
2252 }
2253
2254 $session->setRememberUser( $rememberMe );
2255 if ( $secure !== null ) {
2256 $session->setForceHTTPS( $secure );
2257 }
2258
2259 $session->persist();
2260
2261 ScopedCallback::consume( $delay );
2262 }
2263
2267 public function logout() {
2268 if ( $this->getHookRunner()->onUserLogout( $this ) ) {
2269 $this->doLogout();
2270 }
2271 }
2272
2277 public function doLogout() {
2278 $session = $this->getRequest()->getSession();
2279 $accountType = MediaWikiServices::getInstance()->getUserIdentityUtils()->getShortUserTypeInternal( $this );
2280 if ( !$session->canSetUser() ) {
2281 LoggerFactory::getInstance( 'session' )
2282 ->warning( __METHOD__ . ": Cannot log out of an immutable session" );
2283 $error = 'immutable';
2284 } elseif ( !$session->getUser()->equals( $this ) ) {
2285 LoggerFactory::getInstance( 'session' )
2286 ->warning( __METHOD__ .
2287 ": Cannot log user \"$this\" out of a user \"{$session->getUser()}\"'s session"
2288 );
2289 // But we still may as well make this user object anon
2290 $this->clearInstanceCache( 'defaults' );
2291 $error = 'wronguser';
2292 } else {
2293 $this->clearInstanceCache( 'defaults' );
2294 $delay = $session->delaySave();
2295 $session->unpersist(); // Clear cookies (T127436)
2296 $session->setLoggedOutTimestamp( time() );
2297 $session->setUser( new User );
2298 $session->set( 'wsUserID', 0 ); // Other code expects this
2299 $session->resetAllTokens();
2300 ScopedCallback::consume( $delay );
2301 $error = false;
2302 }
2303 LoggerFactory::getInstance( 'authevents' )->info( 'Logout', [
2304 'event' => 'logout',
2305 'successful' => $error === false,
2306 'status' => $error ?: 'success',
2307 'accountType' => $accountType,
2308 ] );
2309 }
2310
2315 public function saveSettings() {
2316 if ( MediaWikiServices::getInstance()->getReadOnlyMode()->isReadOnly() ) {
2317 // @TODO: caller should deal with this instead!
2318 // This should really just be an exception.
2319 MWExceptionHandler::logException( new DBExpectedError(
2320 null,
2321 "Could not update user with ID '{$this->mId}'; DB is read-only."
2322 ) );
2323 return;
2324 }
2325
2326 $this->load();
2327 if ( $this->mId == 0 ) {
2328 return; // anon
2329 }
2330
2331 // Get a new user_touched that is higher than the old one.
2332 // This will be used for a CAS check as a last-resort safety
2333 // check against race conditions and replica DB lag.
2334 $newTouched = $this->newTouchedTimestamp();
2335
2336 $dbw = MediaWikiServices::getInstance()->getConnectionProvider()->getPrimaryDatabase();
2337 $dbw->doAtomicSection( __METHOD__, function ( IDatabase $dbw, $fname ) use ( $newTouched ) {
2338 $dbw->newUpdateQueryBuilder()
2339 ->update( 'user' )
2340 ->set( [
2341 'user_name' => $this->mName,
2342 'user_real_name' => $this->mRealName,
2343 'user_email' => $this->mEmail,
2344 'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ),
2345 'user_touched' => $dbw->timestamp( $newTouched ),
2346 'user_token' => strval( $this->mToken ),
2347 'user_email_token' => $this->mEmailToken,
2348 'user_email_token_expires' => $dbw->timestampOrNull( $this->mEmailTokenExpires ),
2349 ] )
2350 ->where( [ 'user_id' => $this->mId ] )
2351 ->andWhere( $this->makeUpdateConditions( $dbw ) )
2352 ->caller( $fname )->execute();
2353
2354 if ( !$dbw->affectedRows() ) {
2355 // Maybe the problem was a missed cache update; clear it to be safe
2356 $this->clearSharedCache( 'refresh' );
2357 // User was changed in the meantime or loaded with stale data
2358 $from = ( $this->queryFlagsUsed & IDBAccessObject::READ_LATEST ) ? 'primary' : 'replica';
2359 LoggerFactory::getInstance( 'preferences' )->warning(
2360 "CAS update failed on user_touched for user ID '{user_id}' ({db_flag} read)",
2361 [ 'user_id' => $this->mId, 'db_flag' => $from ]
2362 );
2363 throw new RuntimeException( "CAS update failed on user_touched. " .
2364 "The version of the user to be saved is older than the current version."
2365 );
2366 }
2367
2368 $dbw->newUpdateQueryBuilder()
2369 ->update( 'actor' )
2370 ->set( [ 'actor_name' => $this->mName ] )
2371 ->where( [ 'actor_user' => $this->mId ] )
2372 ->caller( $fname )->execute();
2373 MediaWikiServices::getInstance()->getActorStore()->deleteUserIdentityFromCache( $this );
2374 } );
2375
2376 $this->mTouched = $newTouched;
2377 if ( $this->isNamed() ) {
2378 MediaWikiServices::getInstance()->getUserOptionsManager()->saveOptionsInternal( $this );
2379 }
2380
2381 $this->getHookRunner()->onUserSaveSettings( $this );
2382 $this->clearSharedCache( 'changed' );
2383 $hcu = MediaWikiServices::getInstance()->getHtmlCacheUpdater();
2384 $hcu->purgeTitleUrls( $this->getUserPage(), $hcu::PURGE_INTENT_TXROUND_REFLECTED );
2385 }
2386
2393 public function idForName( $flags = IDBAccessObject::READ_NORMAL ) {
2394 $s = trim( $this->getName() );
2395 if ( $s === '' ) {
2396 return 0;
2397 }
2398
2399 $db = DBAccessObjectUtils::getDBFromRecency(
2400 MediaWikiServices::getInstance()->getDBLoadBalancerFactory(),
2401 $flags
2402 );
2403 $id = $db->newSelectQueryBuilder()
2404 ->select( 'user_id' )
2405 ->from( 'user' )
2406 ->where( [ 'user_name' => $s ] )
2407 ->recency( $flags )
2408 ->caller( __METHOD__ )->fetchField();
2409
2410 return (int)$id;
2411 }
2412
2426 public static function createNew( $name, $params = [] ) {
2427 return self::insertNewUser( static function ( UserIdentity $actor, IDatabase $dbw ) {
2428 return MediaWikiServices::getInstance()->getActorStore()->createNewActor( $actor, $dbw );
2429 }, $name, $params );
2430 }
2431
2439 private static function insertNewUser( callable $insertActor, $name, $params = [] ) {
2440 foreach ( [ 'password', 'newpassword', 'newpass_time', 'password_expires' ] as $field ) {
2441 if ( isset( $params[$field] ) ) {
2442 wfDeprecated( __METHOD__ . " with param '$field'", '1.27' );
2443 unset( $params[$field] );
2444 }
2445 }
2446
2447 $user = new User;
2448 $user->load();
2449 $user->setToken(); // init token
2450 $dbw = MediaWikiServices::getInstance()->getConnectionProvider()->getPrimaryDatabase();
2451
2452 $noPass = PasswordFactory::newInvalidPassword()->toString();
2453
2454 $fields = [
2455 'user_name' => $name,
2456 'user_password' => $noPass,
2457 'user_newpassword' => $noPass,
2458 'user_email' => $user->mEmail,
2459 'user_email_authenticated' => $dbw->timestampOrNull( $user->mEmailAuthenticated ),
2460 'user_real_name' => $user->mRealName,
2461 'user_token' => strval( $user->mToken ),
2462 'user_registration' => $dbw->timestamp(),
2463 'user_editcount' => 0,
2464 'user_touched' => $dbw->timestamp( $user->newTouchedTimestamp() ),
2465 ];
2466 foreach ( $params as $name => $value ) {
2467 $fields["user_$name"] = $value;
2468 }
2469
2470 return $dbw->doAtomicSection( __METHOD__, static function ( IDatabase $dbw, $fname )
2471 use ( $fields, $insertActor )
2472 {
2473 $dbw->newInsertQueryBuilder()
2474 ->insertInto( 'user' )
2475 ->ignore()
2476 ->row( $fields )
2477 ->caller( $fname )->execute();
2478 if ( $dbw->affectedRows() ) {
2479 $newUser = self::newFromId( $dbw->insertId() );
2480 $newUser->mName = $fields['user_name'];
2481 // Don't pass $this, since calling ::getId, ::getName might force ::load
2482 // and this user might not be ready for the yet.
2483 $newUser->mActorId = $insertActor(
2484 new UserIdentityValue( $newUser->mId, $newUser->mName ),
2485 $dbw
2486 );
2487 // Load the user from primary DB to avoid replica lag
2488 $newUser->load( IDBAccessObject::READ_LATEST );
2489 } else {
2490 $newUser = null;
2491 }
2492 return $newUser;
2493 } );
2494 }
2495
2521 public function addToDatabase() {
2522 $this->load();
2523 if ( !$this->mToken ) {
2524 $this->setToken(); // init token
2525 }
2526
2527 if ( !is_string( $this->mName ) ) {
2528 throw new RuntimeException( "User name field is not set." );
2529 }
2530
2531 $this->mTouched = $this->newTouchedTimestamp();
2532
2533 $dbw = MediaWikiServices::getInstance()->getConnectionProvider()->getPrimaryDatabase();
2534 $status = $dbw->doAtomicSection( __METHOD__, function ( IDatabase $dbw, $fname ) {
2535 $noPass = PasswordFactory::newInvalidPassword()->toString();
2536 $dbw->newInsertQueryBuilder()
2537 ->insertInto( 'user' )
2538 ->ignore()
2539 ->row( [
2540 'user_name' => $this->mName,
2541 'user_password' => $noPass,
2542 'user_newpassword' => $noPass,
2543 'user_email' => $this->mEmail,
2544 'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ),
2545 'user_real_name' => $this->mRealName,
2546 'user_token' => strval( $this->mToken ),
2547 'user_registration' => $dbw->timestamp(),
2548 'user_editcount' => 0,
2549 'user_touched' => $dbw->timestamp( $this->mTouched ),
2550 'user_is_temp' => $this->isTemp(),
2551 ] )
2552 ->caller( $fname )->execute();
2553 if ( !$dbw->affectedRows() ) {
2554 // Use locking reads to bypass any REPEATABLE-READ snapshot.
2555 $this->mId = $dbw->newSelectQueryBuilder()
2556 ->select( 'user_id' )
2557 ->lockInShareMode()
2558 ->from( 'user' )
2559 ->where( [ 'user_name' => $this->mName ] )
2560 ->caller( $fname )->fetchField();
2561 $loaded = false;
2562 if ( $this->mId && $this->loadFromDatabase( IDBAccessObject::READ_LOCKING ) ) {
2563 $loaded = true;
2564 }
2565 if ( !$loaded ) {
2566 throw new RuntimeException( $fname . ": hit a key conflict attempting " .
2567 "to insert user '{$this->mName}' row, but it was not present in select!" );
2568 }
2569 return Status::newFatal( 'userexists' );
2570 }
2571 $this->mId = $dbw->insertId();
2572 $this->queryFlagsUsed = IDBAccessObject::READ_LATEST;
2573
2574 // Don't pass $this, since calling ::getId, ::getName might force ::load
2575 // and this user might not be ready for that yet.
2576 $this->mActorId = MediaWikiServices::getInstance()
2577 ->getActorNormalization()
2578 ->acquireActorId( new UserIdentityValue( $this->mId, $this->mName ), $dbw );
2579 return Status::newGood();
2580 } );
2581 if ( !$status->isGood() ) {
2582 return $status;
2583 }
2584
2585 // Clear instance cache other than user table data and actor, which is already accurate
2586 $this->clearInstanceCache();
2587
2588 if ( $this->isNamed() ) {
2589 MediaWikiServices::getInstance()->getUserOptionsManager()->saveOptions( $this );
2590 }
2591 return Status::newGood();
2592 }
2593
2600 public function scheduleSpreadBlock() {
2601 DeferredUpdates::addCallableUpdate( function () {
2602 // Permit master queries in a GET request
2603 $scope = Profiler::instance()->getTransactionProfiler()->silenceForScope();
2604 $this->spreadAnyEditBlock();
2605 ScopedCallback::consume( $scope );
2606 } );
2607 }
2608
2615 public function spreadAnyEditBlock() {
2616 if ( !$this->isRegistered() ) {
2617 return false;
2618 }
2619
2620 $blockWasSpread = false;
2621 $this->getHookRunner()->onSpreadAnyEditBlock( $this, $blockWasSpread );
2622
2623 $block = $this->getBlock();
2624 if ( $block ) {
2625 $blockWasSpread = $blockWasSpread || $this->spreadBlock( $block );
2626 }
2627
2628 return $blockWasSpread;
2629 }
2630
2637 protected function spreadBlock( Block $block ): bool {
2638 wfDebug( __METHOD__ . "()" );
2639 $this->load();
2640 if ( $this->mId == 0 ) {
2641 return false;
2642 }
2643
2644 $blockStore = MediaWikiServices::getInstance()->getDatabaseBlockStore();
2645 foreach ( $block->toArray() as $singleBlock ) {
2646 if ( $singleBlock instanceof DatabaseBlock && $singleBlock->isAutoblocking() ) {
2647 return (bool)$blockStore->doAutoblock( $singleBlock, $this->getRequest()->getIP() );
2648 }
2649 }
2650 return false;
2651 }
2652
2660 public function isBlockedFromEmailuser() {
2661 wfDeprecated( __METHOD__, '1.41' );
2662 $block = $this->getBlock();
2663 return $block && $block->appliesToRight( 'sendemail' );
2664 }
2665
2672 public function isBlockedFromUpload() {
2673 $block = $this->getBlock();
2674 return $block && $block->appliesToRight( 'upload' );
2675 }
2676
2681 public function isAllowedToCreateAccount() {
2682 return $this->getThisAsAuthority()->isDefinitelyAllowed( 'createaccount' );
2683 }
2684
2690 public function getUserPage() {
2691 return Title::makeTitle( NS_USER, $this->getName() );
2692 }
2693
2699 public function getTalkPage() {
2700 $title = $this->getUserPage();
2701 return $title->getTalkPage();
2702 }
2703
2711 public function isNewbie() {
2712 // IP users and temp account users are excluded from the autoconfirmed group.
2713 return !$this->isAllowed( 'autoconfirmed' );
2714 }
2715
2728 public function getEditTokenObject( $salt = '', $request = null ) {
2729 if ( $this->isAnon() ) {
2730 return new LoggedOutEditToken();
2731 }
2732
2733 if ( !$request ) {
2734 $request = $this->getRequest();
2735 }
2736 return $request->getSession()->getToken( $salt );
2737 }
2738
2753 public function getEditToken( $salt = '', $request = null ) {
2754 return $this->getEditTokenObject( $salt, $request )->toString();
2755 }
2756
2770 public function matchEditToken( $val, $salt = '', $request = null, $maxage = null ) {
2771 return $this->getEditTokenObject( $salt, $request )->match( $val, $maxage );
2772 }
2773
2782 public function sendConfirmationMail( $type = 'created' ) {
2783 $emailer = MediaWikiServices::getInstance()->getConfirmEmailSender();
2784 $expiration = null; // gets passed-by-ref and defined in next line
2785 $token = $this->getConfirmationToken( $expiration );
2786 $confirmationUrl = $this->getConfirmationTokenUrl( $token );
2787 $invalidateUrl = $this->getInvalidationTokenUrl( $token );
2788
2789 return Status::wrap( $emailer->sendConfirmationMail(
2790 RequestContext::getMain(),
2791 $type,
2792 new ConfirmEmailData(
2793 $this->getUser(),
2794 $confirmationUrl,
2795 $invalidateUrl,
2796 $expiration
2797 )
2798 ) );
2799 }
2800
2812 public function sendMail( $subject, $body, $from = null, $replyto = null ) {
2813 $passwordSender = MediaWikiServices::getInstance()->getMainConfig()
2814 ->get( MainConfigNames::PasswordSender );
2815
2816 if ( $from instanceof User ) {
2817 $sender = MailAddress::newFromUser( $from );
2818 } else {
2819 $sender = new MailAddress( $passwordSender,
2820 wfMessage( 'emailsender' )->inContentLanguage()->text() );
2821 }
2822 $to = MailAddress::newFromUser( $this );
2823
2824 if ( is_array( $body ) ) {
2825 $bodyText = $body['text'] ?? '';
2826 $bodyHtml = $body['html'] ?? null;
2827 } else {
2828 $bodyText = $body;
2829 $bodyHtml = null;
2830 }
2831
2832 return Status::wrap( MediaWikiServices::getInstance()->getEmailer()
2833 ->send(
2834 [ $to ],
2835 $sender,
2836 $subject,
2837 $bodyText,
2838 $bodyHtml,
2839 [ 'replyTo' => $replyto ]
2840 ) );
2841 }
2842
2858 public function getConfirmationToken(
2859 ?string &$expiration,
2860 ?int $tokenLifeTimeSeconds = null
2861 ): string {
2862 $tokenLifeTimeSeconds ??= MediaWikiServices::getInstance()
2863 ->getMainConfig()->get( MainConfigNames::UserEmailConfirmationTokenExpiry );
2864 $now = ConvertibleTimestamp::time();
2865
2866 $expires = $now + $tokenLifeTimeSeconds;
2867 $expiration = wfTimestamp( TS::MW, $expires );
2868 $this->load();
2869 $token = MWCryptRand::generateHex( 32 );
2870 $hash = md5( $token );
2871 $this->mEmailToken = $hash;
2872 $this->mEmailTokenExpires = $expiration;
2873 return $token;
2874 }
2875
2882 protected function confirmationToken( &$expiration ) {
2883 return $this->getConfirmationToken( $expiration );
2884 }
2885
2892 public static function isWellFormedConfirmationToken( string $token ): bool {
2893 return preg_match( '/^[a-f0-9]{32}$/', $token );
2894 }
2895
2903 public function getConfirmationTokenUrl( string $token ): string {
2904 return $this->getTokenUrl( 'ConfirmEmail', $token );
2905 }
2906
2914 public function getInvalidationTokenUrl( string $token ): string {
2915 return $this->getTokenUrl( 'InvalidateEmail', $token );
2916 }
2917
2925 protected function invalidationTokenUrl( $token ) {
2926 return $this->getTokenUrl( 'InvalidateEmail', $token );
2927 }
2928
2944 public function getTokenUrl( string $page, string $token ): string {
2945 // Hack to bypass localization of 'Special:'
2946 $title = Title::makeTitle( NS_MAIN, "Special:$page/$token" );
2947 return $title->getCanonicalURL();
2948 }
2949
2957 public function confirmEmail() {
2958 // Check if it's already confirmed, so we don't touch the database
2959 // and fire the ConfirmEmailComplete hook on redundant confirmations.
2960 if ( !$this->isEmailConfirmed() ) {
2961 $this->setEmailAuthenticationTimestamp( wfTimestampNow() );
2962 $this->getHookRunner()->onConfirmEmailComplete( $this );
2963 }
2964 return true;
2965 }
2966
2974 public function invalidateEmail() {
2975 $this->load();
2976 $this->mEmailToken = null;
2977 $this->mEmailTokenExpires = null;
2978 $this->setEmailAuthenticationTimestamp( null );
2979 $this->mEmail = '';
2980 $this->getHookRunner()->onInvalidateEmailComplete( $this );
2981 return true;
2982 }
2983
2988 public function setEmailAuthenticationTimestamp( $timestamp ) {
2989 $this->load();
2990 $this->mEmailAuthenticated = $timestamp;
2991 $this->getHookRunner()->onUserSetEmailAuthenticationTimestamp(
2992 $this, $this->mEmailAuthenticated );
2993 }
2994
3002 public function canSendEmail() {
3003 wfDeprecated( __METHOD__, '1.41' );
3004 $permError = MediaWikiServices::getInstance()->getEmailUserFactory()
3005 ->newEmailUser( $this->getThisAsAuthority() )
3006 ->canSend();
3007 return $permError->isGood();
3008 }
3009
3015 public function canReceiveEmail() {
3016 $userOptionsLookup = MediaWikiServices::getInstance()
3017 ->getUserOptionsLookup();
3018 return $this->isEmailConfirmed() && !$userOptionsLookup->getOption( $this, 'disablemail' );
3019 }
3020
3031 public function isEmailConfirmed(): bool {
3032 $emailAuthentication = MediaWikiServices::getInstance()->getMainConfig()
3033 ->get( MainConfigNames::EmailAuthentication );
3034 $this->load();
3035 $confirmed = true;
3036 if ( $this->getHookRunner()->onEmailConfirmed( $this, $confirmed ) ) {
3037 return !$this->isAnon() &&
3038 Sanitizer::validateEmail( $this->getEmail() ) &&
3039 ( !$emailAuthentication || $this->getEmailAuthenticationTimestamp() );
3040 }
3041
3042 return $confirmed;
3043 }
3044
3049 public function isEmailConfirmationPending() {
3050 $emailAuthentication = MediaWikiServices::getInstance()->getMainConfig()
3051 ->get( MainConfigNames::EmailAuthentication );
3052 return $emailAuthentication &&
3053 !$this->isEmailConfirmed() &&
3054 $this->mEmailToken &&
3055 $this->mEmailTokenExpires > wfTimestamp();
3056 }
3057
3066 public function getRegistration() {
3067 return MediaWikiServices::getInstance()
3068 ->getUserRegistrationLookup()
3069 ->getRegistration( $this );
3070 }
3071
3079 public static function getRightDescription( $right ) {
3080 $key = "right-$right";
3081 $msg = wfMessage( $key );
3082 return $msg->isDisabled() ? $right : $msg->text();
3083 }
3084
3092 public static function getRightDescriptionHtml( $right ) {
3093 return wfMessage( "right-$right" )->parse();
3094 }
3095
3109 public static function getQueryInfo() {
3110 $ret = [
3111 'tables' => [ 'user', 'user_actor' => 'actor' ],
3112 'fields' => [
3113 'user_id',
3114 'user_name',
3115 'user_real_name',
3116 'user_email',
3117 'user_touched',
3118 'user_token',
3119 'user_email_authenticated',
3120 'user_email_token',
3121 'user_email_token_expires',
3122 'user_registration',
3123 'user_editcount',
3124 'user_actor.actor_id',
3125 ],
3126 'joins' => [
3127 'user_actor' => [ 'JOIN', 'user_actor.actor_user = user_id' ],
3128 ],
3129 ];
3130
3131 return $ret;
3132 }
3133
3143 public static function newQueryBuilder( IReadableDatabase $db ) {
3144 return $db->newSelectQueryBuilder()
3145 ->select( [
3146 'user_id',
3147 'user_name',
3148 'user_real_name',
3149 'user_email',
3150 'user_touched',
3151 'user_token',
3152 'user_email_authenticated',
3153 'user_email_token',
3154 'user_email_token_expires',
3155 'user_registration',
3156 'user_editcount',
3157 'user_actor.actor_id',
3158 ] )
3159 ->from( 'user' )
3160 ->join( 'actor', 'user_actor', 'user_actor.actor_user = user_id' );
3161 }
3162
3173 public static function newFatalPermissionDeniedStatus( $permission ) {
3174 return Status::wrap( MediaWikiServices::getInstance()->getPermissionManager()
3175 ->newFatalPermissionDeniedStatus(
3176 $permission,
3177 RequestContext::getMain()
3178 ) );
3179 }
3180
3196 public function getInstanceForUpdate() {
3197 wfDeprecated( __METHOD__, '1.45' );
3198 return $this->getInstanceFromPrimary( IDBAccessObject::READ_EXCLUSIVE );
3199 }
3200
3218 public function getInstanceFromPrimary( int $loadFlags = IDBAccessObject::READ_LATEST ): ?User {
3219 if ( $this->isAnon() ) {
3220 return null;
3221 } elseif ( ( $loadFlags & $this->queryFlagsUsed ) === $loadFlags ) {
3222 return $this;
3223 }
3224
3225 $user = self::newFromId( $this->getId() );
3226 if ( !$user->loadFromId( $loadFlags ) ) {
3227 return null;
3228 }
3229
3230 return $user;
3231 }
3232
3240 public function equals( ?UserIdentity $user ): bool {
3241 if ( !$user ) {
3242 return false;
3243 }
3244 // XXX it's not clear whether central ID providers are supposed to obey this
3245 return $this->getName() === $user->getName();
3246 }
3247
3253 public function getUser(): UserIdentity {
3254 return $this;
3255 }
3256
3264 public function probablyCan(
3265 string $action,
3266 PageIdentity $target,
3267 ?PermissionStatus $status = null
3268 ): bool {
3269 return $this->getThisAsAuthority()->probablyCan( $action, $target, $status );
3270 }
3271
3279 public function definitelyCan(
3280 string $action,
3281 PageIdentity $target,
3282 ?PermissionStatus $status = null
3283 ): bool {
3284 return $this->getThisAsAuthority()->definitelyCan( $action, $target, $status );
3285 }
3286
3295 public function isDefinitelyAllowed( string $action, ?PermissionStatus $status = null ): bool {
3296 return $this->getThisAsAuthority()->isDefinitelyAllowed( $action, $status );
3297 }
3298
3307 public function authorizeAction( string $action, ?PermissionStatus $status = null ): bool {
3308 return $this->getThisAsAuthority()->authorizeAction( $action, $status );
3309 }
3310
3318 public function authorizeRead(
3319 string $action,
3320 PageIdentity $target,
3321 ?PermissionStatus $status = null
3322 ): bool {
3323 return $this->getThisAsAuthority()->authorizeRead( $action, $target, $status );
3324 }
3325
3333 public function authorizeWrite(
3334 string $action, PageIdentity $target,
3335 ?PermissionStatus $status = null
3336 ): bool {
3337 return $this->getThisAsAuthority()->authorizeWrite( $action, $target, $status );
3338 }
3339
3344 private function getThisAsAuthority(): UserAuthority {
3345 if ( !$this->mThisAsAuthority ) {
3346 // TODO: For users that are not User::isGlobalSessionUser,
3347 // creating a UserAuthority here is incorrect, since it depends
3348 // on global WebRequest, but that is what we've used to do before Authority.
3349 // When PermissionManager is refactored into Authority, we need
3350 // to provide base implementation, based on just user groups/rights,
3351 // and use it here.
3352 $request = $this->getRequest();
3353 $uiContext = RequestContext::getMain();
3354
3355 $services = MediaWikiServices::getInstance();
3356 $this->mThisAsAuthority = new UserAuthority(
3357 $this,
3358 $request,
3359 $uiContext,
3360 $services->getPermissionManager(),
3361 $services->getRateLimiter(),
3362 $services->getFormatterFactory()->getBlockErrorFormatter( $uiContext )
3363 );
3364 }
3365
3366 return $this->mThisAsAuthority;
3367 }
3368
3372 private function isGlobalSessionUser(): bool {
3373 // The session user is set up towards the end of Setup.php. Until then,
3374 // assume it's a logged-out user.
3375 $sessionUser = RequestContext::getMain()->getUser();
3376 $globalUserName = $sessionUser->isSafeToLoad()
3377 ? $sessionUser->getName()
3378 : IPUtils::sanitizeIP( $sessionUser->getRequest()->getIP() );
3379
3380 return $this->getName() === $globalUserName;
3381 }
3382
3388 public function isTemp(): bool {
3389 if ( $this->isTemp === null ) {
3390 $this->isTemp = MediaWikiServices::getInstance()->getUserIdentityUtils()
3391 ->isTemp( $this );
3392 }
3393 return $this->isTemp;
3394 }
3395
3402 public function isNamed(): bool {
3403 return $this->isRegistered() && !$this->isTemp();
3404 }
3405}
3406
3408class_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.
if(MW_ENTRY_POINT==='index') if(!defined( 'MW_NO_SESSION') &&MW_ENTRY_POINT !=='cli' $wgLang
Definition Setup.php:551
global $wgFullyInitialised
Definition Setup.php:579
if(!defined('MW_SETUP_CALLBACK'))
Definition WebStart.php:68
const MW_ENTRY_POINT
Definition api.php:21
Utility functions for generating hashes.
A cryptographic random generator class used for generating secret keys.
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.
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:32
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.
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:70
getCanonicalURL( $query='')
Get the URL for a canonical link, for use in things like IRC and e-mail notifications.
Definition Title.php:2360
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:110
authorizeAction(string $action, ?PermissionStatus $status=null)
Authorize an action.This should be used immediately before performing the action.Calling this method ...
Definition User.php:3307
isAllowedToCreateAccount()
Get whether the user is allowed to create an account.
Definition User.php:2681
string null $mDatePreference
Lazy-initialized variables, invalidated with clearInstanceCache.
Definition User.php:225
touch()
Update the "touched" timestamp for the user.
Definition User.php:1668
isAllowed(string $permission, ?PermissionStatus $status=null)
Checks whether this authority has the given permission in general.
Definition User.php:2130
static newFromConfirmationCode( $code, $flags=IDBAccessObject::READ_NORMAL)
Factory method to fetch whichever user has a given email confirmation code.
Definition User.php:715
& __get( $name)
Definition User.php:280
logout()
Log this user out.
Definition User.php:2267
getRegistration()
Get the timestamp of account creation.
Definition User.php:3066
getWikiId()
Returns self::LOCAL to indicate the user is associated with the local wiki.
Definition User.php:269
int $queryFlagsUsed
IDBAccessObject::READ_* constant bitfield used to load data.
Definition User.php:235
getInstanceFromPrimary(int $loadFlags=IDBAccessObject::READ_LATEST)
Get an instance of this user that was loaded from the primary DB.
Definition User.php:3218
useRCPatrol()
Check whether to enable recent changes patrol features for this user.
Definition User.php:2138
isAllowedAny(... $permissions)
Checks whether this authority has any of the given permissions in general.Implementations must ensure...
Definition User.php:2121
const READ_EXCLUSIVE
Definition User.php:118
requiresHTTPS()
Determine based on the wiki configuration and the user's options, whether this user must be over HTTP...
Definition User.php:2036
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:2728
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:2753
confirmationToken(&$expiration)
Deprecated alias for getConfirmationToken() for CentralAuth.
Definition User.php:2882
static findUsersByGroup( $groups, $limit=5000, $after=null)
Return the users who are members of the given group(s).
Definition User.php:899
setToken( $token=false)
Set the random token (used for persistent authentication) Called from loadDefaults() among other plac...
Definition User.php:1825
canReceiveEmail()
Is this user allowed to receive e-mails within limits of current site configuration?
Definition User.php:3015
getId( $wikiId=self::LOCAL)
Get the user's ID.
Definition User.php:1471
getCacheKey(WANObjectCache $cache)
Definition User.php:505
loadFromCache()
Load user data from shared cache, given mId has already been set.
Definition User.php:518
getDBTouched()
Get the user_touched timestamp field (time of last DB updates)
Definition User.php:1736
clearInstanceCache( $reloadFrom=false)
Clear various cached data stored in this object.
Definition User.php:1306
checkPasswordValidity( $password)
Check if this is a valid password for this user.
Definition User.php:952
static string[] $mCacheVars
List of member variables which are saved to the shared cache (memcached).
Definition User.php:154
authorizeRead(string $action, PageIdentity $target, ?PermissionStatus $status=null)
Definition User.php:3318
getExperienceLevel()
Compute experienced level based on edit count and registration date.
Definition User.php:2186
isNamed()
Is the user a normal non-temporary registered user?
Definition User.php:3402
setEmailWithConfirmation(string $str)
Set the user's e-mail address and send a confirmation mail if needed.
Definition User.php:1882
loadFromUserObject( $user)
Load the data for this user object from another user object.
Definition User.php:1237
getUserPage()
Get this user's personal page title.
Definition User.php:2690
isSafeToLoad()
Test if it's safe to load this User object.
Definition User.php:342
isEmailConfirmed()
Is this user's e-mail address valid-looking and confirmed within limits of the current site configura...
Definition User.php:3031
invalidationTokenUrl( $token)
Deprecated alias for getInvalidationTokenUrl() for CentralAuth.
Definition User.php:2925
AbstractBlock false null $mGlobalBlock
Null when uninitialized, false when there is no block.
Definition User.php:227
isPingLimitable()
Is this user subject to rate limiting?
Definition User.php:1355
confirmEmail()
Mark the e-mail address confirmed.
Definition User.php:2957
getConfirmationToken(?string &$expiration, ?int $tokenLifeTimeSeconds=null)
Generate, store, and return a new e-mail confirmation code.
Definition User.php:2858
isNewbie()
Determine whether the user is a newbie.
Definition User.php:2711
resetTokenFromOption( $oname)
Reset a token stored in the preferences (like the watchlist one).
Definition User.php:1996
isBlockedFromEmailuser()
Get whether the user is blocked from using Special:Emailuser.
Definition User.php:2660
isLocked()
Check if user account is locked.
Definition User.php:1446
isBlockedFromUpload()
Get whether the user is blocked from using Special:Upload.
Definition User.php:2672
static newQueryBuilder(IReadableDatabase $db)
Get a SelectQueryBuilder with the tables, fields and join conditions needed to create a new User obje...
Definition User.php:3143
setCookies( $request=null, $secure=null, $rememberMe=false)
Persist this user's session (e.g.
Definition User.php:2230
int $mId
Cache variables.
Definition User.php:174
addToDatabase()
Add this existing user object to the database.
Definition User.php:2521
getBlock( $freshness=IDBAccessObject::READ_NORMAL, $disableIpBlockExemptChecking=false)
Get the block affecting the user, or null if the user is not blocked.
Definition User.php:1405
getTokenUrl(string $page, string $token)
Function to create a special page URL with a token path parameter.
Definition User.php:2944
setEmail(string $str)
Set the user's e-mail address.
Definition User.php:1865
static newFatalPermissionDeniedStatus( $permission)
Factory function for fatal permission-denied errors.
Definition User.php:3173
static purge( $dbDomain, $userId)
Definition User.php:494
string $mFrom
Initialization data source if mLoadedItems!==true.
Definition User.php:219
probablyCan(string $action, PageIdentity $target, ?PermissionStatus $status=null)
Definition User.php:3264
static newFromActorId( $id)
Static factory method for creation from a given actor ID.
Definition User.php:645
loadFromRow( $row, $data=null)
Initialize this object from a row from the user table.
Definition User.php:1127
checkAndSetTouched()
Bump user_touched if it didn't change since this object was loaded.
Definition User.php:1269
load( $flags=IDBAccessObject::READ_NORMAL)
Load the user table data for this object from the source given by mFrom.
Definition User.php:359
loadFromDatabase( $flags=IDBAccessObject::READ_LATEST)
Load user data from the database.
Definition User.php:1081
getEditCount()
Get the user's edit count.
Definition User.php:2057
doLogout()
Clear the user's session, and reset the instance cache.
Definition User.php:2277
getDatePreference()
Get the user's preferred date format.
Definition User.php:2014
clearSharedCache( $mode='refresh')
Clear user data from memcached.
Definition User.php:1625
static newFromId( $id)
Static factory method for creation from a given user ID.
Definition User.php:628
setActorId(int $actorId)
Sets the actor id.
Definition User.php:1587
setItemLoaded( $item)
Set that an item has been loaded.
Definition User.php:1044
static newSystemUser( $name, $options=[])
Static factory method for creation of a "system" user from username.
Definition User.php:800
loadFromId( $flags=IDBAccessObject::READ_NORMAL)
Load user table data, given mId has already been set.
Definition User.php:464
static newFromRow( $row, $data=null)
Create a new user object from a user row.
Definition User.php:750
isAnon()
Get whether the user is anonymous.
Definition User.php:2079
sendMail( $subject, $body, $from=null, $replyto=null)
Send an e-mail to this user's account.
Definition User.php:2812
changeAuthenticationData(array $data)
Changes credentials of the user.
Definition User.php:1754
static newFromAnyId( $userId, $userName, $actorId, $dbDomain=false)
Static factory method for creation from an ID, name, and/or actor ID.
Definition User.php:694
definitelyCan(string $action, PageIdentity $target, ?PermissionStatus $status=null)
Definition User.php:3279
string $mRealName
Definition User.php:184
isSystemUser()
Get whether the user is a system user.
Definition User.php:2110
getInvalidationTokenUrl(string $token)
Return a URL the user can use to invalidate their email address.
Definition User.php:2914
isItemLoaded( $item, $all='all')
Return whether an item has been loaded.
Definition User.php:1032
const TOKEN_LENGTH
Number of characters required for the user_token field.
Definition User.php:128
setId( $v)
Set the user and reload all fields according to a given ID.
Definition User.php:1495
string null $mToken
Definition User.php:193
isAllowedAll(... $permissions)
Checks whether this authority has any of the given permissions in general.Implementations must ensure...
Definition User.php:2126
getInstanceForUpdate()
Get a new instance of this user that was loaded from the primary DB via a locking read.
Definition User.php:3196
getTokenFromOption( $oname)
Get a token stored in the preferences (like the watchlist one), resetting it if it's empty (and savin...
Definition User.php:1965
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:3295
static getQueryInfo()
Return the tables, fields, and join conditions to be selected to create a new user object.
Definition User.php:3109
equals(?UserIdentity $user)
Checks if two user objects point to the same user.
Definition User.php:3240
isTemp()
Is the user an autocreated temporary user?
Definition User.php:3388
const MAINTENANCE_SCRIPT_USER
Username used for various maintenance scripts.
Definition User.php:145
isEmailConfirmationPending()
Check whether there is an outstanding request for e-mail confirmation.
Definition User.php:3049
getRealName()
Get the user's real name.
Definition User.php:1938
string $mTouched
TS::MW timestamp from the DB.
Definition User.php:189
getTitleKey()
Get the user's name escaped by underscores.
Definition User.php:1596
string null $mEmailTokenExpires
Definition User.php:199
bool null $mLocked
Definition User.php:229
getActorId( $dbwOrWikiId=self::LOCAL)
Get the user's actor ID.
Definition User.php:1550
string null $mQuickTouched
TS::MW timestamp from cache.
Definition User.php:191
validateCache( $timestamp)
Validate the cache for this account.
Definition User.php:1702
getEmailAuthenticationTimestamp()
Get the timestamp of the user's e-mail authentication.
Definition User.php:1854
authorizeWrite(string $action, PageIdentity $target, ?PermissionStatus $status=null)
Definition User.php:3333
__set( $name, $value)
Definition User.php:301
useFilePatrol()
Check whether to enable new files patrol features for this user.
Definition User.php:2163
static newFromSession(?WebRequest $request=null)
Create a new user object using data from session.
Definition User.php:728
canSendEmail()
Is this user allowed to send e-mails within limits of current site configuration?
Definition User.php:3002
static isWellFormedConfirmationToken(string $token)
Check if the given email confirmation token is well-formed (to detect mangling by email clients).
Definition User.php:2892
static createNew( $name, $params=[])
Add a user to the database, return the user object.
Definition User.php:2426
getRequest()
Get the WebRequest object to use with this object.
Definition User.php:2177
saveSettings()
Save this user's settings into the database.
Definition User.php:2315
loadDefaults( $name=false, $actorId=null)
Set cached properties to default.
Definition User.php:996
useNPPatrol()
Check whether to enable new pages patrol features for this user.
Definition User.php:2148
const INVALID_TOKEN
An invalid string value for the user_token field.
Definition User.php:133
pingLimiter( $action='edit', $incrBy=1)
Primitive rate limits: enforce maximum actions per time period to put a brake on flooding.
Definition User.php:1376
matchEditToken( $val, $salt='', $request=null, $maxage=null)
Check given value against the token value stored in the session.
Definition User.php:2770
invalidateEmail()
Invalidate the user's e-mail confirmation, and unauthenticate the e-mail address if it was already co...
Definition User.php:2974
isRegistered()
Get whether the user is registered.
Definition User.php:2071
getTalkPage()
Get this user's talk page title.
Definition User.php:2699
static newFromIdentity(UserIdentity $identity)
Returns a User object corresponding to the given UserIdentity.
Definition User.php:664
makeUpdateConditions(IReadableDatabase $db)
Build additional update conditions to protect against race conditions using a compare-and-set (CAS) m...
Definition User.php:1251
string null $mEmailToken
Definition User.php:197
int null $mActorId
Switched from protected to public for use in UserFactory.
Definition User.php:182
getToken( $forceCreation=true)
Get the user's current token.
Definition User.php:1781
setEmailAuthenticationTimestamp( $timestamp)
Set the e-mail authentication timestamp.
Definition User.php:2988
isHidden()
Check if user account is hidden.
Definition User.php:1461
static getRightDescription( $right)
Get the description of a given right as wikitext.
Definition User.php:3079
setName( $str)
Set the user name.
Definition User.php:1532
static getRightDescriptionHtml( $right)
Get the description of a given right as rendered HTML.
Definition User.php:3092
string null $mEmailAuthenticated
Definition User.php:195
debouncedDBTouch()
Update the db touched timestamp for the user if it hasn't been updated recently.
Definition User.php:1683
isValidPassword( $password)
Is the input a valid password for this user?
Definition User.php:926
getEmail()
Get the user's e-mail address.
Definition User.php:1841
getName()
Get the user name, or the IP of an anonymous user.
Definition User.php:1504
setRealName(string $str)
Set the user's real name.
Definition User.php:1950
spreadAnyEditBlock()
If this user is logged-in and blocked, block any IP address they've successfully logged in from.
Definition User.php:2615
getTouched()
Get the user touched timestamp.
Definition User.php:1714
static newFromName( $name, $validate='valid')
Definition User.php:602
invalidateCache()
Immediately touch the user data cache for this account.
Definition User.php:1651
scheduleSpreadBlock()
Schedule a deferred update which will block the IP address of the current user, if they are blocked w...
Definition User.php:2600
spreadBlock(Block $block)
If this (non-anonymous) user is blocked, block the IP address they've successfully logged in from.
Definition User.php:2637
getConfirmationTokenUrl(string $token)
Return a URL the user can use to confirm their email address.
Definition User.php:2903
idForName( $flags=IDBAccessObject::READ_NORMAL)
If only this user's username is known, and it exists, return the user ID.
Definition User.php:2393
sendConfirmationMail( $type='created')
Generate a new e-mail confirmation token and send a confirmation/invalidation mail to the user's give...
Definition User.php:2782
array bool $mLoadedItems
Array with already loaded items or true if all items have been loaded.
Definition User.php:206
Profiler base class that defines the interface and some shared functionality.
Definition Profiler.php:22
static instance()
Definition Profiler.php:90
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, 'UploadMaintenance'=> false, 'IllegalFileChars'=> ':\\/\\\\', 'DeletedDirectory'=> false, 'ImgAuthDetails'=> false, 'ImgAuthUrlPathMap'=>[], 'LocalFileRepo'=>['class'=> 'MediaWiki\\FileRepo\\LocalRepo', 'name'=> 'local', 'directory'=> null, 'scriptDirUrl'=> null, 'favicon'=> null, 'url'=> null, 'hashLevels'=> null, 'thumbScriptUrl'=> null, 'transformVia404'=> null, 'deletedDir'=> null, 'deletedHashLevels'=> null, 'updateCompatibleMetadata'=> null, 'reserializeMetadata'=> null,], 'ForeignFileRepos'=>[], 'UseInstantCommons'=> false, 'UseSharedUploads'=> false, 'SharedUploadDirectory'=> null, 'SharedUploadPath'=> null, 'HashedSharedUploadDirectory'=> true, 'RepositoryBaseUrl'=> 'https:'FetchCommonsDescriptions'=> false, 'SharedUploadDBname'=> false, 'SharedUploadDBprefix'=> '', 'CacheSharedUploads'=> true, 'ForeignUploadTargets'=>['local',], 'UploadDialog'=>['fields'=>['description'=> true, 'date'=> false, 'categories'=> false,], 'licensemessages'=>['local'=> 'generic-local', 'foreign'=> 'generic-foreign',], 'comment'=>['local'=> '', 'foreign'=> '',], 'format'=>['filepage'=> ' $DESCRIPTION', 'description'=> ' $TEXT', 'ownwork'=> '', 'license'=> '', 'uncategorized'=> '',],], 'FileBackends'=>[], 'LockManagers'=>[], 'ShowEXIF'=> null, 'UpdateCompatibleMetadata'=> false, 'AllowCopyUploads'=> false, 'CopyUploadsDomains'=>[], 'CopyUploadsFromSpecialUpload'=> false, 'CopyUploadProxy'=> false, 'CopyUploadTimeout'=> false, 'CopyUploadAllowOnWikiDomainConfig'=> false, 'MaxUploadSize'=> 104857600, 'MinUploadChunkSize'=> 1024, 'UploadNavigationUrl'=> false, 'UploadMissingFileUrl'=> false, 'ThumbnailScriptPath'=> false, 'SharedThumbnailScriptPath'=> false, 'HashedUploadDirectory'=> true, 'CSPUploadEntryPoint'=> true, 'FileExtensions'=>['png', 'gif', 'jpg', 'jpeg', 'webp',], 'ProhibitedFileExtensions'=>['html', 'htm', 'js', 'jsb', 'mhtml', 'mht', 'xhtml', 'xht', 'php', 'phtml', 'php3', 'php4', 'php5', 'phps', 'phar', 'shtml', 'jhtml', 'pl', 'py', 'cgi', 'exe', 'scr', 'dll', 'msi', 'vbs', 'bat', 'com', 'pif', 'cmd', 'vxd', 'cpl', 'xml',], 'MimeTypeExclusions'=>['text/html', 'application/javascript', 'text/javascript', 'text/x-javascript', 'application/x-shellscript', 'application/x-php', 'text/x-php', 'text/x-python', 'text/x-perl', 'text/x-bash', 'text/x-sh', 'text/x-csh', 'text/scriptlet', 'application/x-msdownload', 'application/x-msmetafile', 'application/java', 'application/xml', 'text/xml',], 'CheckFileExtensions'=> true, 'StrictFileExtensions'=> true, 'DisableUploadScriptChecks'=> false, 'UploadSizeWarning'=> false, 'TrustedMediaFormats'=>['BITMAP', 'AUDIO', 'VIDEO', 'image/svg+xml', 'application/pdf',], 'MediaHandlers'=>[], 'NativeImageLazyLoading'=> false, 'ParserTestMediaHandlers'=>['image/jpeg'=> 'MockBitmapHandler', 'image/png'=> 'MockBitmapHandler', 'image/gif'=> 'MockBitmapHandler', 'image/tiff'=> 'MockBitmapHandler', 'image/webp'=> 'MockBitmapHandler', 'image/x-ms-bmp'=> 'MockBitmapHandler', 'image/x-bmp'=> 'MockBitmapHandler', 'image/x-xcf'=> 'MockBitmapHandler', 'image/svg+xml'=> 'MockSvgHandler', 'image/vnd.djvu'=> 'MockDjVuHandler',], 'UseImageResize'=> true, 'UseImageMagick'=> false, 'ImageMagickConvertCommand'=> '/usr/bin/convert', 'MaxInterlacingAreas'=>[], 'SharpenParameter'=> '0x0.4', 'SharpenReductionThreshold'=> 0.85, 'ImageMagickTempDir'=> false, 'CustomConvertCommand'=> false, 'JpegTran'=> '/usr/bin/jpegtran', 'JpegPixelFormat'=> 'yuv420', 'JpegQuality'=> 80, 'Exiv2Command'=> '/usr/bin/exiv2', 'Exiftool'=> '/usr/bin/exiftool', 'SVGConverters'=>['ImageMagick'=> ' $path/convert -background "#ffffff00" -thumbnail $widthx$height\\! $input PNG:$output', 'sodipodi'=> ' $path/sodipodi -z -w $width -f $input -e $output', 'inkscape'=> ' $path/inkscape -z -w $width -f $input -e $output', 'batik'=> 'java -Djava.awt.headless=true -jar $path/batik-rasterizer.jar -w $width -d $output $input', 'rsvg'=> ' $path/rsvg-convert -w $width -h $height -o $output $input', 'imgserv'=> ' $path/imgserv-wrapper -i svg -o png -w$width $input $output', 'ImagickExt'=>['SvgHandler::rasterizeImagickExt',],], 'SVGConverter'=> 'ImageMagick', 'SVGConverterPath'=> '', 'SVGMaxSize'=> 5120, 'SVGMetadataCutoff'=> 5242880, 'SVGNativeRendering'=> false, 'SVGNativeRenderingSizeLimit'=> 51200, 'MediaInTargetLanguage'=> true, 'MaxImageArea'=> 12500000, 'MaxAnimatedGifArea'=> 12500000, 'TiffThumbnailType'=>[], 'ThumbnailEpoch'=> '20030516000000', 'AttemptFailureEpoch'=> 1, 'IgnoreImageErrors'=> false, 'GenerateThumbnailOnParse'=> true, 'ShowArchiveThumbnails'=> true, 'EnableAutoRotation'=> null, 'Antivirus'=> null, 'AntivirusSetup'=>['clamav'=>['command'=> 'clamscan --no-summary ', 'codemap'=>[0=> 0, 1=> 1, 52=> -1, ' *'=> false,], 'messagepattern'=> '/.*?:(.*)/sim',],], 'AntivirusRequired'=> true, 'VerifyMimeType'=> true, 'MimeTypeFile'=> 'internal', 'MimeInfoFile'=> 'internal', 'MimeDetectorCommand'=> null, 'TrivialMimeDetection'=> false, 'XMLMimeTypes'=>['http:'svg'=> 'image/svg+xml', 'http:'http:'html'=> 'text/html',], 'ImageLimits'=>[[320, 240,], [640, 480,], [800, 600,], [1024, 768,], [1280, 1024,], [2560, 2048,],], 'ThumbLimits'=>[120, 150, 180, 200, 250, 300,], 'ThumbnailNamespaces'=>[6,], 'ThumbnailSteps'=> null, 'ThumbnailStepsRatio'=> null, 'ThumbnailBuckets'=> null, 'ThumbnailMinimumBucketDistance'=> 50, 'UploadThumbnailRenderMap'=>[], 'UploadThumbnailRenderMethod'=> 'jobqueue', 'UploadThumbnailRenderHttpCustomHost'=> false, 'UploadThumbnailRenderHttpCustomDomain'=> false, 'UseTinyRGBForJPGThumbnails'=> false, 'GalleryOptions'=>[], 'ThumbUpright'=> 0.75, 'DirectoryMode'=> 511, 'ResponsiveImages'=> true, 'ImagePreconnect'=> false, 'DjvuUseBoxedCommand'=> false, 'DjvuDump'=> null, 'DjvuRenderer'=> null, 'DjvuTxt'=> null, 'DjvuPostProcessor'=> 'pnmtojpeg', 'DjvuOutputExtension'=> 'jpg', 'EmergencyContact'=> false, 'PasswordSender'=> false, 'NoReplyAddress'=> false, 'EnableEmail'=> true, 'EnableUserEmail'=> true, 'EnableSpecialMute'=> false, 'EnableUserEmailMuteList'=> false, 'UserEmailUseReplyTo'=> true, 'PasswordReminderResendTime'=> 24, 'NewPasswordExpiry'=> 604800, 'UserEmailConfirmationTokenExpiry'=> 604800, 'UserEmailConfirmationUseHTML'=> false, 'PasswordExpirationDays'=> false, 'PasswordExpireGrace'=> 604800, 'SMTP'=> false, 'AdditionalMailParams'=> null, 'AllowHTMLEmail'=> false, 'EnotifFromEditor'=> false, 'EmailAuthentication'=> true, 'EnotifWatchlist'=> false, 'EnotifUserTalk'=> false, 'EnotifRevealEditorAddress'=> false, 'EnotifMinorEdits'=> true, 'EnotifUseRealName'=> false, 'UsersNotifiedOnAllChanges'=>[], 'DBname'=> 'my_wiki', 'DBmwschema'=> null, 'DBprefix'=> '', 'DBserver'=> 'localhost', 'DBport'=> 5432, 'DBuser'=> 'wikiuser', 'DBpassword'=> '', 'DBtype'=> 'mysql', 'DBssl'=> false, 'DBcompress'=> false, 'DBStrictWarnings'=> false, 'DBadminuser'=> null, 'DBadminpassword'=> null, 'SearchType'=> null, 'SearchTypeAlternatives'=> null, 'DBTableOptions'=> 'ENGINE=InnoDB, DEFAULT CHARSET=binary', 'SQLMode'=> '', 'SQLiteDataDir'=> '', 'SharedDB'=> null, 'SharedPrefix'=> false, 'SharedTables'=>['user', 'user_properties', 'user_autocreate_serial',], 'SharedSchema'=> false, 'DBservers'=> false, 'LBFactoryConf'=>['class'=> 'Wikimedia\\Rdbms\\LBFactorySimple',], 'DataCenterUpdateStickTTL'=> 10, 'DBerrorLog'=> false, 'DBerrorLogTZ'=> false, 'LocalDatabases'=>[], 'DatabaseReplicaLagWarning'=> 10, 'DatabaseReplicaLagCritical'=> 30, 'MaxExecutionTimeForExpensiveQueries'=> 0, 'VirtualDomainsMapping'=>[], 'FileSchemaMigrationStage'=> 3, 'ExternalLinksDomainGaps'=>[], 'ContentHandlers'=>['wikitext'=>['class'=> 'MediaWiki\\Content\\WikitextContentHandler', 'services'=>['TitleFactory', 'ParserFactory', 'GlobalIdGenerator', 'LanguageNameUtils', 'LinkRenderer', 'MagicWordFactory', 'ParsoidParserFactory',],], 'javascript'=>['class'=> 'MediaWiki\\Content\\JavaScriptContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'UserOptionsLookup',],], 'json'=>['class'=> 'MediaWiki\\Content\\JsonContentHandler', 'services'=>['ParsoidParserFactory', 'TitleFactory',],], 'css'=>['class'=> 'MediaWiki\\Content\\CssContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'UserOptionsLookup',],], 'vue'=>['class'=> 'MediaWiki\\Content\\VueContentHandler', 'services'=>['MainConfig', 'ParserFactory',],], 'text'=> 'MediaWiki\\Content\\TextContentHandler', 'unknown'=> 'MediaWiki\\Content\\FallbackContentHandler',], 'NamespaceContentModels'=>[], 'TextModelsToParse'=>['wikitext', 'javascript', 'css',], 'CompressRevisions'=> false, 'ExternalStores'=>[], 'ExternalServers'=>[], 'DefaultExternalStore'=> false, 'RevisionCacheExpiry'=> 604800, 'PageLanguageUseDB'=> false, 'DiffEngine'=> null, 'ExternalDiffEngine'=> false, 'Wikidiff2Options'=>[], 'RequestTimeLimit'=> null, 'TransactionalTimeLimit'=> 120, 'CriticalSectionTimeLimit'=> 180.0, 'MiserMode'=> false, 'DisableQueryPages'=> false, 'QueryCacheLimit'=> 1000, 'WantedPagesThreshold'=> 1, 'AllowSlowParserFunctions'=> false, 'AllowSchemaUpdates'=> true, 'MaxArticleSize'=> 2048, 'MemoryLimit'=> '50M', 'PoolCounterConf'=> null, 'PoolCountClientConf'=>['servers'=>['127.0.0.1',], 'timeout'=> 0.1,], 'MaxUserDBWriteDuration'=> false, 'MaxJobDBWriteDuration'=> false, 'LinkHolderBatchSize'=> 1000, 'MaximumMovedPages'=> 100, 'ForceDeferredUpdatesPreSend'=> false, 'MultiShardSiteStats'=> false, 'CacheDirectory'=> false, 'MainCacheType'=> 0, 'MessageCacheType'=> -1, 'ParserCacheType'=> -1, 'SessionCacheType'=> -1, 'AnonSessionCacheType'=> false, 'LanguageConverterCacheType'=> -1, 'ObjectCaches'=>[0=>['class'=> 'Wikimedia\\ObjectCache\\EmptyBagOStuff', 'reportDupes'=> false,], 1=>['class'=> 'SqlBagOStuff', 'loggroup'=> 'SQLBagOStuff',], 'memcached-php'=>['class'=> 'Wikimedia\\ObjectCache\\MemcachedPhpBagOStuff', 'loggroup'=> 'memcached',], 'memcached-pecl'=>['class'=> 'Wikimedia\\ObjectCache\\MemcachedPeclBagOStuff', 'loggroup'=> 'memcached',], 'hash'=>['class'=> 'Wikimedia\\ObjectCache\\HashBagOStuff', 'reportDupes'=> false,], 'apc'=>['class'=> 'Wikimedia\\ObjectCache\\APCUBagOStuff', 'reportDupes'=> false,], 'apcu'=>['class'=> 'Wikimedia\\ObjectCache\\APCUBagOStuff', 'reportDupes'=> false,],], 'WANObjectCache'=>[], 'MicroStashType'=> -1, 'MainStash'=> 1, 'ParsoidCacheConfig'=>['StashType'=> null, 'StashDuration'=> 86400, 'WarmParsoidParserCache'=> false,], 'ParsoidSelectiveUpdateSampleRate'=> 0, 'ParserCacheFilterConfig'=>['pcache'=>['default'=>['minCpuTime'=> 0,],], 'parsoid-pcache'=>['default'=>['minCpuTime'=> 0,],], 'postproc-pcache'=>['default'=>['minCpuTime'=> 9223372036854775807,],], 'postproc-parsoid-pcache'=>['default'=>['minCpuTime'=> 9223372036854775807,],],], 'ChronologyProtectorSecret'=> '', 'ParserCacheExpireTime'=> 86400, 'ParserCacheAsyncExpireTime'=> 60, 'ParserCacheAsyncRefreshJobs'=> true, 'OldRevisionParserCacheExpireTime'=> 3600, 'ObjectCacheSessionExpiry'=> 3600, 'PHPSessionHandling'=> 'warn', 'SuspiciousIpExpiry'=> false, 'SessionPbkdf2Iterations'=> 10001, 'UseSessionCookieJwt'=> false, 'MemCachedServers'=>['127.0.0.1:11211',], 'MemCachedPersistent'=> false, 'MemCachedTimeout'=> 500000, 'UseLocalMessageCache'=> false, 'AdaptiveMessageCache'=> false, 'LocalisationCacheConf'=>['class'=> 'LocalisationCache', 'store'=> 'detect', 'storeClass'=> false, 'storeDirectory'=> false, 'storeServer'=>[], 'forceRecache'=> false, 'manualRecache'=> false,], 'CachePages'=> true, 'CacheEpoch'=> '20030516000000', 'GitInfoCacheDirectory'=> false, 'UseFileCache'=> false, 'FileCacheDepth'=> 2, 'RenderHashAppend'=> '', 'EnableSidebarCache'=> false, 'SidebarCacheExpiry'=> 86400, 'UseGzip'=> false, 'InvalidateCacheOnLocalSettingsChange'=> true, 'ExtensionInfoMTime'=> false, 'EnableRemoteBagOStuffTests'=> false, 'UseCdn'=> false, 'VaryOnXFP'=> false, 'InternalServer'=> false, 'CdnMaxAge'=> 18000, 'CdnMaxageLagged'=> 30, 'CdnMaxageStale'=> 10, 'CdnReboundPurgeDelay'=> 0, 'CdnMaxageSubstitute'=> 60, 'ForcedRawSMaxage'=> 300, 'CdnServers'=>[], 'CdnServersNoPurge'=>[], 'HTCPRouting'=>[], 'HTCPMulticastTTL'=> 1, 'UsePrivateIPs'=> false, 'CdnMatchParameterOrder'=> true, 'LanguageCode'=> 'en', 'GrammarForms'=>[], 'InterwikiMagic'=> true, 'HideInterlanguageLinks'=> false, 'ExtraInterlanguageLinkPrefixes'=>[], 'InterlanguageLinkCodeMap'=>[], 'ExtraLanguageNames'=>[], 'ExtraLanguageCodes'=>['bh'=> 'bho', 'no'=> 'nb', 'simple'=> 'en',], 'DummyLanguageCodes'=>[], 'AllUnicodeFixes'=> false, 'LegacyEncoding'=> false, 'AmericanDates'=> false, 'TranslateNumerals'=> true, 'UseDatabaseMessages'=> true, 'MaxMsgCacheEntrySize'=> 10000, 'DisableLangConversion'=> false, 'DisableTitleConversion'=> false, 'DefaultLanguageVariant'=> false, 'UsePigLatinVariant'=> false, 'DisabledVariants'=>[], 'VariantArticlePath'=> false, 'UseXssLanguage'=> false, 'LoginLanguageSelector'=> false, 'ForceUIMsgAsContentMsg'=>[], 'RawHtmlMessages'=>[], 'Localtimezone'=> null, 'LocalTZoffset'=> null, 'OverrideUcfirstCharacters'=>[], 'MimeType'=> 'text/html', 'Html5Version'=> null, 'EditSubmitButtonLabelPublish'=> false, 'XhtmlNamespaces'=>[], 'SiteNotice'=> '', 'BrowserFormatDetection'=> 'telephone=no', 'SkinMetaTags'=>[], 'DefaultSkin'=> 'vector-2022', 'FallbackSkin'=> 'fallback', 'SkipSkins'=>[], 'DisableOutputCompression'=> false, 'FragmentMode'=>['html5', 'legacy',], 'ExternalInterwikiFragmentMode'=> 'legacy', 'FooterIcons'=>['copyright'=>['copyright'=>[],], 'poweredby'=>['mediawiki'=>['src'=> null, 'url'=> 'https:'alt'=> 'Powered by MediaWiki', 'lang'=> 'en',],],], 'UseCombinedLoginLink'=> false, 'Edititis'=> false, 'Send404Code'=> true, 'ShowRollbackEditCount'=> 10, 'EnableCanonicalServerLink'=> false, 'InterwikiLogoOverride'=>[], 'ResourceModules'=>[], 'ResourceModuleSkinStyles'=>[], 'ResourceLoaderSources'=>[], 'ResourceBasePath'=> null, 'ResourceLoaderMaxage'=>[], 'ResourceLoaderDebug'=> false, 'ResourceLoaderMaxQueryLength'=> false, 'ResourceLoaderValidateJS'=> true, 'ResourceLoaderEnableJSProfiler'=> false, 'ResourceLoaderStorageEnabled'=> true, 'ResourceLoaderStorageVersion'=> 1, 'ResourceLoaderEnableSourceMapLinks'=> true, 'AllowSiteCSSOnRestrictedPages'=> false, 'VueDevelopmentMode'=> false, 'CodexDevelopmentDir'=> null, 'MetaNamespace'=> false, 'MetaNamespaceTalk'=> false, 'CanonicalNamespaceNames'=>[-2=> 'Media', -1=> 'Special', 0=> '', 1=> 'Talk', 2=> 'User', 3=> 'User_talk', 4=> 'Project', 5=> 'Project_talk', 6=> 'File', 7=> 'File_talk', 8=> 'MediaWiki', 9=> 'MediaWiki_talk', 10=> 'Template', 11=> 'Template_talk', 12=> 'Help', 13=> 'Help_talk', 14=> 'Category', 15=> 'Category_talk',], 'ExtraNamespaces'=>[], 'ExtraGenderNamespaces'=>[], 'NamespaceAliases'=>[], 'LegalTitleChars'=> ' %!"$&\'()*,\\-.\\/0-9:;=?@A-Z\\\\^_`a-z~\\x80-\\xFF+', 'CapitalLinks' => true, 'CapitalLinkOverrides' => [ ], 'NamespacesWithSubpages' => [ 1 => true, 2 => true, 3 => true, 4 => true, 5 => true, 7 => true, 8 => true, 9 => true, 10 => true, 11 => true, 12 => true, 13 => true, 15 => true, ], 'ContentNamespaces' => [ 0, ], 'ShortPagesNamespaceExclusions' => [ ], 'ExtraSignatureNamespaces' => [ ], 'InvalidRedirectTargets' => [ 'Filepath', 'Mypage', 'Mytalk', 'Redirect', 'Mylog', ], 'DisableHardRedirects' => false, 'FixDoubleRedirects' => false, 'LocalInterwikis' => [ ], 'InterwikiExpiry' => 10800, 'InterwikiCache' => false, 'InterwikiScopes' => 3, 'InterwikiFallbackSite' => 'wiki', 'RedirectSources' => false, 'SiteTypes' => [ 'mediawiki' => 'MediaWiki\\Site\\MediaWikiSite', ], 'MaxTocLevel' => 999, 'MaxPPNodeCount' => 1000000, 'MaxTemplateDepth' => 100, 'MaxPPExpandDepth' => 100, 'UrlProtocols' => [ 'bitcoin:', 'ftp: 'ftps: 'geo:', 'git: 'gopher: 'http: 'https: 'irc: 'ircs: 'magnet:', 'mailto:', 'matrix:', 'mms: 'news:', 'nntp: 'redis: 'sftp: 'sip:', 'sips:', 'sms:', 'ssh: 'svn: 'tel:', 'telnet: 'urn:', 'wikipedia: 'worldwind: 'xmpp:', ' ], 'CleanSignatures' => true, 'AllowExternalImages' => false, 'AllowExternalImagesFrom' => '', 'EnableImageWhitelist' => false, 'TidyConfig' => [ ], 'ParsoidSettings' => [ 'useSelser' => true, ], 'ParsoidExperimentalParserFunctionOutput' => false, 'UseLegacyMediaStyles' => false, 'RawHtml' => false, 'ExternalLinkTarget' => false, 'NoFollowLinks' => true, 'NoFollowNsExceptions' => [ ], 'NoFollowDomainExceptions' => [ 'mediawiki.org', ], 'RegisterInternalExternals' => false, 'ExternalLinksIgnoreDomains' => [ ], 'AllowDisplayTitle' => true, 'RestrictDisplayTitle' => true, 'ExpensiveParserFunctionLimit' => 100, 'PreprocessorCacheThreshold' => 1000, 'EnableScaryTranscluding' => false, 'TranscludeCacheExpiry' => 3600, 'EnableMagicLinks' => [ 'ISBN' => false, 'PMID' => false, 'RFC' => false, ], 'ParserEnableUserLanguage' => false, 'ArticleCountMethod' => 'link', 'ActiveUserDays' => 30, 'LearnerEdits' => 10, 'LearnerMemberSince' => 4, 'ExperiencedUserEdits' => 500, 'ExperiencedUserMemberSince' => 30, 'ManualRevertSearchRadius' => 15, 'RevertedTagMaxDepth' => 15, 'CentralIdLookupProviders' => [ 'local' => [ 'class' => 'MediaWiki\\User\\CentralId\\LocalIdLookup', 'services' => [ 'MainConfig', 'DBLoadBalancerFactory', 'HideUserUtils', ], ], ], 'CentralIdLookupProvider' => 'local', 'UserRegistrationProviders' => [ 'local' => [ 'class' => 'MediaWiki\\User\\Registration\\LocalUserRegistrationProvider', 'services' => [ 'ConnectionProvider', ], ], ], 'PasswordPolicy' => [ 'policies' => [ 'bureaucrat' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'sysop' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'interface-admin' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'bot' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'default' => [ 'MinimalPasswordLength' => [ 'value' => 8, 'suggestChangeOnLogin' => true, ], 'PasswordCannotBeSubstringInUsername' => [ 'value' => true, 'suggestChangeOnLogin' => true, ], 'PasswordCannotMatchDefaults' => [ 'value' => true, 'suggestChangeOnLogin' => true, ], 'MaximalPasswordLength' => [ 'value' => 4096, 'suggestChangeOnLogin' => true, ], 'PasswordNotInCommonList' => [ 'value' => true, 'suggestChangeOnLogin' => true, ], ], ], 'checks' => [ 'MinimalPasswordLength' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkMinimalPasswordLength', ], 'MinimumPasswordLengthToLogin' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkMinimumPasswordLengthToLogin', ], 'PasswordCannotBeSubstringInUsername' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkPasswordCannotBeSubstringInUsername', ], 'PasswordCannotMatchDefaults' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkPasswordCannotMatchDefaults', ], 'MaximalPasswordLength' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkMaximalPasswordLength', ], 'PasswordNotInCommonList' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkPasswordNotInCommonList', ], ], ], 'AuthManagerConfig' => null, 'AuthManagerAutoConfig' => [ 'preauth' => [ 'MediaWiki\\Auth\\ThrottlePreAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\ThrottlePreAuthenticationProvider', 'sort' => 0, ], ], 'primaryauth' => [ 'MediaWiki\\Auth\\TemporaryPasswordPrimaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\TemporaryPasswordPrimaryAuthenticationProvider', 'services' => [ 'DBLoadBalancerFactory', 'UserOptionsLookup', ], 'args' => [ [ 'authoritative' => false, ], ], 'sort' => 0, ], 'MediaWiki\\Auth\\LocalPasswordPrimaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\LocalPasswordPrimaryAuthenticationProvider', 'services' => [ 'DBLoadBalancerFactory', ], 'args' => [ [ 'authoritative' => true, ], ], 'sort' => 100, ], ], 'secondaryauth' => [ 'MediaWiki\\Auth\\CheckBlocksSecondaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\CheckBlocksSecondaryAuthenticationProvider', 'sort' => 0, ], 'MediaWiki\\Auth\\ResetPasswordSecondaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\ResetPasswordSecondaryAuthenticationProvider', 'sort' => 100, ], 'MediaWiki\\Auth\\EmailNotificationSecondaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\EmailNotificationSecondaryAuthenticationProvider', 'services' => [ 'DBLoadBalancerFactory', ], 'sort' => 200, ], ], ], 'RememberMe' => 'choose', 'ReauthenticateTime' => [ 'default' => 3600, ], 'AllowSecuritySensitiveOperationIfCannotReauthenticate' => [ 'default' => true, ], 'ChangeCredentialsBlacklist' => [ 'MediaWiki\\Auth\\TemporaryPasswordAuthenticationRequest', ], 'RemoveCredentialsBlacklist' => [ 'MediaWiki\\Auth\\PasswordAuthenticationRequest', ], 'InvalidPasswordReset' => true, 'PasswordDefault' => 'pbkdf2', 'PasswordConfig' => [ 'A' => [ 'class' => 'MediaWiki\\Password\\MWOldPassword', ], 'B' => [ 'class' => 'MediaWiki\\Password\\MWSaltedPassword', ], 'pbkdf2-legacyA' => [ 'class' => 'MediaWiki\\Password\\LayeredParameterizedPassword', 'types' => [ 'A', 'pbkdf2', ], ], 'pbkdf2-legacyB' => [ 'class' => 'MediaWiki\\Password\\LayeredParameterizedPassword', 'types' => [ 'B', 'pbkdf2', ], ], 'bcrypt' => [ 'class' => 'MediaWiki\\Password\\BcryptPassword', 'cost' => 9, ], 'pbkdf2' => [ 'class' => 'MediaWiki\\Password\\Pbkdf2PasswordUsingOpenSSL', 'algo' => 'sha512', 'cost' => '30000', 'length' => '64', ], 'argon2' => [ 'class' => 'MediaWiki\\Password\\Argon2Password', 'algo' => 'auto', ], ], 'PasswordResetRoutes' => [ 'username' => true, 'email' => true, ], 'MaxSigChars' => 255, 'SignatureValidation' => 'warning', 'SignatureAllowedLintErrors' => [ 'obsolete-tag', ], 'MaxNameChars' => 255, 'ReservedUsernames' => [ 'MediaWiki default', 'Conversion script', 'Maintenance script', 'Template namespace initialisation script', 'ScriptImporter', 'Delete page script', 'Move page script', 'Command line script', 'Unknown user', 'msg:double-redirect-fixer', 'msg:usermessage-editor', 'msg:proxyblocker', 'msg:sorbs', 'msg:spambot_username', 'msg:autochange-username', ], 'DefaultUserOptions' => [ 'ccmeonemails' => 0, 'date' => 'default', 'diffonly' => 0, 'diff-type' => 'table', 'disablemail' => 0, 'editfont' => 'monospace', 'editondblclick' => 0, 'editrecovery' => 0, 'editsectiononrightclick' => 0, 'email-allow-new-users' => 1, 'enotifminoredits' => 0, 'enotifrevealaddr' => 0, 'enotifusertalkpages' => 1, 'enotifwatchlistpages' => 1, 'extendwatchlist' => 1, 'fancysig' => 0, 'forceeditsummary' => 0, 'forcesafemode' => 0, 'gender' => 'unknown', 'hidecategorization' => 1, 'hideminor' => 0, 'hidepatrolled' => 0, 'imagesize' => 2, 'minordefault' => 0, 'newpageshidepatrolled' => 0, 'nickname' => '', 'norollbackdiff' => 0, 'prefershttps' => 1, 'previewonfirst' => 0, 'previewontop' => 1, 'pst-cssjs' => 1, 'rcdays' => 7, 'rcenhancedfilters-disable' => 0, 'rclimit' => 50, 'requireemail' => 0, 'search-match-redirect' => true, 'search-special-page' => 'Search', 'search-thumbnail-extra-namespaces' => true, 'searchlimit' => 20, 'showhiddencats' => 0, 'shownumberswatching' => 1, 'showrollbackconfirmation' => 0, 'skin' => false, 'skin-responsive' => 1, 'thumbsize' => 5, 'underline' => 2, 'useeditwarning' => 1, 'uselivepreview' => 0, 'usenewrc' => 1, 'watchcreations' => 1, 'watchcreations-expiry' => 'infinite', 'watchdefault' => 1, 'watchdefault-expiry' => 'infinite', 'watchdeletion' => 0, 'watchlistdays' => 7, 'watchlisthideanons' => 0, 'watchlisthidebots' => 0, 'watchlisthidecategorization' => 1, 'watchlisthideliu' => 0, 'watchlisthideminor' => 0, 'watchlisthideown' => 0, 'watchlisthidepatrolled' => 0, 'watchlistreloadautomatically' => 0, 'watchlistunwatchlinks' => 0, 'watchmoves' => 0, 'watchrollback' => 0, 'watchuploads' => 1, 'watchrollback-expiry' => 'infinite', 'watchstar-expiry' => 'infinite', 'wlenhancedfilters-disable' => 0, 'wllimit' => 250, ], 'ConditionalUserOptions' => [ ], 'HiddenPrefs' => [ ], 'UserJsPrefLimit' => 100, 'InvalidUsernameCharacters' => '@:>=', 'UserrightsInterwikiDelimiter' => '@', 'SecureLogin' => false, 'AuthenticationTokenVersion' => null, 'SessionProviders' => [ 'MediaWiki\\Session\\CookieSessionProvider' => [ 'class' => 'MediaWiki\\Session\\CookieSessionProvider', 'args' => [ [ 'priority' => 30, ], ], 'services' => [ 'JwtCodec', 'UrlUtils', ], ], 'MediaWiki\\Session\\BotPasswordSessionProvider' => [ 'class' => 'MediaWiki\\Session\\BotPasswordSessionProvider', 'args' => [ [ 'priority' => 75, ], ], 'services' => [ 'GrantsInfo', ], ], ], 'AutoCreateTempUser' => [ 'known' => false, 'enabled' => false, 'actions' => [ 'edit', ], 'genPattern' => '~$1', 'matchPattern' => null, 'reservedPattern' => '~$1', 'serialProvider' => [ 'type' => 'local', 'useYear' => true, ], 'serialMapping' => [ 'type' => 'readable-numeric', ], 'expireAfterDays' => 90, 'notifyBeforeExpirationDays' => 10, ], 'AutoblockExemptions' => [ ], 'AutoblockExpiry' => 86400, 'BlockAllowsUTEdit' => true, 'BlockCIDRLimit' => [ 'IPv4' => 16, 'IPv6' => 19, ], 'BlockDisablesLogin' => false, 'EnableMultiBlocks' => false, 'BlockTargetMigrationStage' => 768, 'WhitelistRead' => false, 'WhitelistReadRegexp' => false, 'EmailConfirmToEdit' => false, 'HideIdentifiableRedirects' => true, 'GroupPermissions' => [ '*' => [ 'createaccount' => true, 'read' => true, 'edit' => true, 'createpage' => true, 'createtalk' => true, 'viewmyprivateinfo' => true, 'editmyprivateinfo' => true, 'editmyoptions' => true, ], 'user' => [ 'move' => true, 'move-subpages' => true, 'move-rootuserpages' => true, 'move-categorypages' => true, 'movefile' => true, 'read' => true, 'edit' => true, 'createpage' => true, 'createtalk' => true, 'upload' => true, 'reupload' => true, 'reupload-shared' => true, 'minoredit' => true, 'editmyusercss' => true, 'editmyuserjson' => true, 'editmyuserjs' => true, 'editmyuserjsredirect' => true, 'sendemail' => true, 'applychangetags' => true, 'changetags' => true, 'viewmywatchlist' => true, 'editmywatchlist' => true, ], 'autoconfirmed' => [ 'autoconfirmed' => true, 'editsemiprotected' => true, ], 'bot' => [ 'bot' => true, 'autoconfirmed' => true, 'editsemiprotected' => true, 'nominornewtalk' => true, 'autopatrol' => true, 'suppressredirect' => true, 'apihighlimits' => true, ], 'sysop' => [ 'block' => true, 'createaccount' => true, 'delete' => true, 'bigdelete' => true, 'deletedhistory' => true, 'deletedtext' => true, 'undelete' => true, 'editcontentmodel' => true, 'editinterface' => true, 'editsitejson' => true, 'edituserjson' => true, 'import' => true, 'importupload' => true, 'move' => true, 'move-subpages' => true, 'move-rootuserpages' => true, 'move-categorypages' => true, 'patrol' => true, 'autopatrol' => true, 'protect' => true, 'editprotected' => true, 'rollback' => true, 'upload' => true, 'reupload' => true, 'reupload-shared' => true, 'unwatchedpages' => true, 'autoconfirmed' => true, 'editsemiprotected' => true, 'ipblock-exempt' => true, 'blockemail' => true, 'markbotedits' => true, 'apihighlimits' => true, 'browsearchive' => true, 'noratelimit' => true, 'movefile' => true, 'unblockself' => true, 'suppressredirect' => true, 'mergehistory' => true, 'managechangetags' => true, 'deletechangetags' => true, ], 'interface-admin' => [ 'editinterface' => true, 'editsitecss' => true, 'editsitejson' => true, 'editsitejs' => true, 'editusercss' => true, 'edituserjson' => true, 'edituserjs' => true, ], 'bureaucrat' => [ 'userrights' => true, 'noratelimit' => true, 'renameuser' => true, ], 'suppress' => [ 'hideuser' => true, 'suppressrevision' => true, 'viewsuppressed' => true, 'suppressionlog' => true, 'deleterevision' => true, 'deletelogentry' => true, ], ], 'PrivilegedGroups' => [ 'bureaucrat', 'interface-admin', 'suppress', 'sysop', ], 'RevokePermissions' => [ ], 'GroupInheritsPermissions' => [ ], 'ImplicitGroups' => [ '*', 'user', 'autoconfirmed', ], 'GroupsAddToSelf' => [ ], 'GroupsRemoveFromSelf' => [ ], 'RestrictedGroups' => [ ], 'RestrictionTypes' => [ 'create', 'edit', 'move', 'upload', ], 'RestrictionLevels' => [ '', 'autoconfirmed', 'sysop', ], 'CascadingRestrictionLevels' => [ 'sysop', ], 'SemiprotectedRestrictionLevels' => [ 'autoconfirmed', ], 'NamespaceProtection' => [ ], 'NonincludableNamespaces' => [ ], 'AutoConfirmAge' => 0, 'AutoConfirmCount' => 0, 'Autopromote' => [ 'autoconfirmed' => [ '&', [ 1, null, ], [ 2, null, ], ], ], 'AutopromoteOnce' => [ 'onEdit' => [ ], ], 'AutopromoteOnceLogInRC' => true, 'AutopromoteOnceRCExcludedGroups' => [ ], 'AddGroups' => [ ], 'RemoveGroups' => [ ], 'AvailableRights' => [ ], 'ImplicitRights' => [ ], 'DeleteRevisionsLimit' => 0, 'DeleteRevisionsBatchSize' => 1000, 'HideUserContribLimit' => 1000, 'AccountCreationThrottle' => [ [ 'count' => 0, 'seconds' => 86400, ], ], 'TempAccountCreationThrottle' => [ [ 'count' => 1, 'seconds' => 600, ], [ 'count' => 6, 'seconds' => 86400, ], ], 'TempAccountNameAcquisitionThrottle' => [ [ 'count' => 60, 'seconds' => 86400, ], ], 'SpamRegex' => [ ], 'SummarySpamRegex' => [ ], 'EnableDnsBlacklist' => false, 'DnsBlacklistUrls' => [ ], 'ProxyList' => [ ], 'ProxyWhitelist' => [ ], 'SoftBlockRanges' => [ ], 'ApplyIpBlocksToXff' => false, 'RateLimits' => [ 'edit' => [ 'ip' => [ 8, 60, ], 'newbie' => [ 8, 60, ], 'user' => [ 90, 60, ], ], 'move' => [ 'newbie' => [ 2, 120, ], 'user' => [ 8, 60, ], ], 'upload' => [ 'ip' => [ 8, 60, ], 'newbie' => [ 8, 60, ], ], 'rollback' => [ 'user' => [ 10, 60, ], 'newbie' => [ 5, 120, ], ], 'mailpassword' => [ 'ip' => [ 5, 3600, ], ], 'sendemail' => [ 'ip' => [ 5, 86400, ], 'newbie' => [ 5, 86400, ], 'user' => [ 20, 86400, ], ], 'changeemail' => [ 'ip-all' => [ 10, 3600, ], 'user' => [ 4, 86400, ], ], 'confirmemail' => [ 'ip-all' => [ 10, 3600, ], 'user' => [ 4, 86400, ], ], 'purge' => [ 'ip' => [ 30, 60, ], 'user' => [ 30, 60, ], ], 'linkpurge' => [ 'ip' => [ 30, 60, ], 'user' => [ 30, 60, ], ], 'renderfile' => [ 'ip' => [ 700, 30, ], 'user' => [ 700, 30, ], ], 'renderfile-nonstandard' => [ 'ip' => [ 70, 30, ], 'user' => [ 70, 30, ], ], 'stashedit' => [ 'ip' => [ 30, 60, ], 'newbie' => [ 30, 60, ], ], 'stashbasehtml' => [ 'ip' => [ 5, 60, ], 'newbie' => [ 5, 60, ], ], 'changetags' => [ 'ip' => [ 8, 60, ], 'newbie' => [ 8, 60, ], ], 'editcontentmodel' => [ 'newbie' => [ 2, 120, ], 'user' => [ 8, 60, ], ], ], 'RateLimitsExcludedIPs' => [ ], 'PutIPinRC' => true, 'QueryPageDefaultLimit' => 50, 'ExternalQuerySources' => [ ], 'PasswordAttemptThrottle' => [ [ 'count' => 5, 'seconds' => 300, ], [ 'count' => 150, 'seconds' => 172800, ], ], 'GrantPermissions' => [ 'basic' => [ 'autocreateaccount' => true, 'autoconfirmed' => true, 'autopatrol' => true, 'editsemiprotected' => true, 'ipblock-exempt' => true, 'nominornewtalk' => true, 'patrolmarks' => true, 'read' => true, 'unwatchedpages' => true, ], 'highvolume' => [ 'bot' => true, 'apihighlimits' => true, 'noratelimit' => true, 'markbotedits' => true, ], 'import' => [ 'import' => true, 'importupload' => true, ], 'editpage' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'pagelang' => true, ], 'editprotected' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'editprotected' => true, ], 'editmycssjs' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'editmyusercss' => true, 'editmyuserjson' => true, 'editmyuserjs' => true, ], 'editmyoptions' => [ 'editmyoptions' => true, 'editmyuserjson' => true, ], 'editinterface' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'editinterface' => true, 'edituserjson' => true, 'editsitejson' => true, ], 'editsiteconfig' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'editinterface' => true, 'edituserjson' => true, 'editsitejson' => true, 'editusercss' => true, 'edituserjs' => true, 'editsitecss' => true, 'editsitejs' => true, ], 'createeditmovepage' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createpage' => true, 'createtalk' => true, 'delete-redirect' => true, 'move' => true, 'move-rootuserpages' => true, 'move-subpages' => true, 'move-categorypages' => true, 'suppressredirect' => true, ], 'uploadfile' => [ 'upload' => true, 'reupload-own' => true, ], 'uploadeditmovefile' => [ 'upload' => true, 'reupload-own' => true, 'reupload' => true, 'reupload-shared' => true, 'upload_by_url' => true, 'movefile' => true, 'suppressredirect' => true, ], 'patrol' => [ 'patrol' => true, ], 'rollback' => [ 'rollback' => true, ], 'blockusers' => [ 'block' => true, 'blockemail' => true, ], 'viewdeleted' => [ 'browsearchive' => true, 'deletedhistory' => true, 'deletedtext' => true, ], 'viewrestrictedlogs' => [ 'suppressionlog' => true, ], 'delete' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'browsearchive' => true, 'deletedhistory' => true, 'deletedtext' => true, 'delete' => true, 'bigdelete' => true, 'deletelogentry' => true, 'deleterevision' => true, 'undelete' => true, ], 'oversight' => [ 'suppressrevision' => true, 'viewsuppressed' => true, ], 'protect' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'editprotected' => true, 'protect' => true, ], 'viewmywatchlist' => [ 'viewmywatchlist' => true, ], 'editmywatchlist' => [ 'editmywatchlist' => true, ], 'sendemail' => [ 'sendemail' => true, ], 'createaccount' => [ 'createaccount' => true, ], 'privateinfo' => [ 'viewmyprivateinfo' => true, ], 'mergehistory' => [ 'mergehistory' => true, ], ], 'GrantPermissionGroups' => [ 'basic' => 'hidden', 'editpage' => 'page-interaction', 'createeditmovepage' => 'page-interaction', 'editprotected' => 'page-interaction', 'patrol' => 'page-interaction', 'uploadfile' => 'file-interaction', 'uploadeditmovefile' => 'file-interaction', 'sendemail' => 'email', 'viewmywatchlist' => 'watchlist-interaction', 'editviewmywatchlist' => 'watchlist-interaction', 'editmycssjs' => 'customization', 'editmyoptions' => 'customization', 'editinterface' => 'administration', 'editsiteconfig' => 'administration', 'rollback' => 'administration', 'blockusers' => 'administration', 'delete' => 'administration', 'viewdeleted' => 'administration', 'viewrestrictedlogs' => 'administration', 'protect' => 'administration', 'oversight' => 'administration', 'createaccount' => 'administration', 'mergehistory' => 'administration', 'import' => 'administration', 'highvolume' => 'high-volume', 'privateinfo' => 'private-information', ], 'GrantRiskGroups' => [ 'basic' => 'low', 'editpage' => 'low', 'createeditmovepage' => 'low', 'editprotected' => 'vandalism', 'patrol' => 'low', 'uploadfile' => 'low', 'uploadeditmovefile' => 'low', 'sendemail' => 'security', 'viewmywatchlist' => 'low', 'editviewmywatchlist' => 'low', 'editmycssjs' => 'security', 'editmyoptions' => 'security', 'editinterface' => 'vandalism', 'editsiteconfig' => 'security', 'rollback' => 'low', 'blockusers' => 'vandalism', 'delete' => 'vandalism', 'viewdeleted' => 'vandalism', 'viewrestrictedlogs' => 'security', 'protect' => 'vandalism', 'oversight' => 'security', 'createaccount' => 'low', 'mergehistory' => 'vandalism', 'import' => 'security', 'highvolume' => 'low', 'privateinfo' => 'low', ], 'EnableBotPasswords' => true, 'BotPasswordsCluster' => false, 'BotPasswordsDatabase' => false, 'SecretKey' => false, 'JwtPrivateKey' => false, 'JwtPublicKey' => false, 'AllowUserJs' => false, 'AllowUserCss' => false, 'AllowUserCssPrefs' => true, 'UseSiteJs' => true, 'UseSiteCss' => true, 'BreakFrames' => false, 'EditPageFrameOptions' => 'DENY', 'ApiFrameOptions' => 'DENY', 'CSPHeader' => false, 'CSPReportOnlyHeader' => false, 'CSPFalsePositiveUrls' => [ 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'chrome-extension' => true, ], 'AllowCrossOrigin' => false, 'RestAllowCrossOriginCookieAuth' => false, 'SessionSecret' => false, 'CookieExpiration' => 2592000, 'ExtendedLoginCookieExpiration' => 15552000, 'SessionCookieJwtExpiration' => 14400, 'CookieDomain' => '', 'CookiePath' => '/', 'CookieSecure' => 'detect', 'CookiePrefix' => false, 'CookieHttpOnly' => true, 'CookieSameSite' => null, 'CacheVaryCookies' => [ ], 'SessionName' => false, 'CookieSetOnAutoblock' => true, 'CookieSetOnIpBlock' => true, 'DebugLogFile' => '', 'DebugLogPrefix' => '', 'DebugRedirects' => false, 'DebugRawPage' => false, 'DebugComments' => false, 'DebugDumpSql' => false, 'TrxProfilerLimits' => [ 'GET' => [ 'masterConns' => 0, 'writes' => 0, 'readQueryTime' => 5, 'readQueryRows' => 10000, ], 'POST' => [ 'readQueryTime' => 5, 'writeQueryTime' => 1, 'readQueryRows' => 100000, 'maxAffected' => 1000, ], 'POST-nonwrite' => [ 'writes' => 0, 'readQueryTime' => 5, 'readQueryRows' => 10000, ], 'PostSend-GET' => [ 'readQueryTime' => 5, 'writeQueryTime' => 1, 'readQueryRows' => 10000, 'maxAffected' => 1000, 'masterConns' => 0, 'writes' => 0, ], 'PostSend-POST' => [ 'readQueryTime' => 5, 'writeQueryTime' => 1, 'readQueryRows' => 100000, 'maxAffected' => 1000, ], 'JobRunner' => [ 'readQueryTime' => 30, 'writeQueryTime' => 5, 'readQueryRows' => 100000, 'maxAffected' => 500, ], 'Maintenance' => [ 'writeQueryTime' => 5, 'maxAffected' => 1000, ], ], 'DebugLogGroups' => [ ], 'MWLoggerDefaultSpi' => [ 'class' => 'MediaWiki\\Logger\\LegacySpi', ], 'ShowDebug' => false, 'SpecialVersionShowHooks' => false, 'ShowExceptionDetails' => false, 'LogExceptionBacktrace' => true, 'PropagateErrors' => true, 'ShowHostnames' => false, 'OverrideHostname' => false, 'DevelopmentWarnings' => false, 'DeprecationReleaseLimit' => false, 'Profiler' => [ ], 'StatsdServer' => false, 'StatsdMetricPrefix' => 'MediaWiki', 'StatsTarget' => null, 'StatsFormat' => null, 'StatsPrefix' => 'mediawiki', 'OpenTelemetryConfig' => null, 'PageInfoTransclusionLimit' => 50, 'EnableJavaScriptTest' => false, 'CachePrefix' => false, 'DebugToolbar' => false, 'DisableTextSearch' => false, 'AdvancedSearchHighlighting' => false, 'SearchHighlightBoundaries' => '[\\p{Z}\\p{P}\\p{C}]', 'OpenSearchTemplates' => [ 'application/x-suggestions+json' => false, 'application/x-suggestions+xml' => false, ], 'OpenSearchDefaultLimit' => 10, 'OpenSearchDescriptionLength' => 100, 'SearchSuggestCacheExpiry' => 1200, 'DisableSearchUpdate' => false, 'NamespacesToBeSearchedDefault' => [ true, ], 'DisableInternalSearch' => false, 'SearchForwardUrl' => null, 'SitemapNamespaces' => false, 'SitemapNamespacesPriorities' => false, 'SitemapApiConfig' => [ ], 'SpecialSearchFormOptions' => [ ], 'SearchMatchRedirectPreference' => false, 'SearchRunSuggestedQuery' => true, 'Diff3' => '/usr/bin/diff3', 'Diff' => '/usr/bin/diff', 'PreviewOnOpenNamespaces' => [ 14 => true, ], 'UniversalEditButton' => true, 'UseAutomaticEditSummaries' => true, 'CommandLineDarkBg' => false, 'ReadOnly' => null, 'ReadOnlyWatchedItemStore' => false, 'ReadOnlyFile' => false, 'UpgradeKey' => false, 'GitBin' => '/usr/bin/git', 'GitRepositoryViewers' => [ 'https: 'ssh: ], 'InstallerInitialPages' => [ [ 'titlemsg' => 'mainpage', 'text' => '{{subst:int:mainpagetext}}{{subst:int:mainpagedocfooter}}', ], ], 'RCMaxAge' => 7776000, 'WatchersMaxAge' => 15552000, 'UnwatchedPageSecret' => 1, 'RCFilterByAge' => false, 'RCLinkLimits' => [ 50, 100, 250, 500, ], 'RCLinkDays' => [ 1, 3, 7, 14, 30, ], 'RCFeeds' => [ ], 'RCEngines' => [ 'redis' => 'MediaWiki\\RCFeed\\RedisPubSubFeedEngine', 'udp' => 'MediaWiki\\RCFeed\\UDPRCFeedEngine', ], 'RCWatchCategoryMembership' => false, 'UseRCPatrol' => true, 'StructuredChangeFiltersLiveUpdatePollingRate' => 3, 'UseNPPatrol' => true, 'UseFilePatrol' => true, 'Feed' => true, 'FeedLimit' => 50, 'FeedCacheTimeout' => 60, 'FeedDiffCutoff' => 32768, 'OverrideSiteFeed' => [ ], 'FeedClasses' => [ 'rss' => 'MediaWiki\\Feed\\RSSFeed', 'atom' => 'MediaWiki\\Feed\\AtomFeed', ], 'AdvertisedFeedTypes' => [ 'atom', ], 'RCShowWatchingUsers' => false, 'RCShowChangedSize' => true, 'RCChangedSizeThreshold' => 500, 'ShowUpdatedMarker' => true, 'DisableAnonTalk' => false, 'UseTagFilter' => true, 'SoftwareTags' => [ 'mw-contentmodelchange' => true, 'mw-new-redirect' => true, 'mw-removed-redirect' => true, 'mw-changed-redirect-target' => true, 'mw-blank' => true, 'mw-replace' => true, 'mw-recreated' => true, 'mw-rollback' => true, 'mw-undo' => true, 'mw-manual-revert' => true, 'mw-reverted' => true, 'mw-server-side-upload' => true, 'mw-ipblock-appeal' => true, ], 'UnwatchedPageThreshold' => false, 'RecentChangesFlags' => [ 'newpage' => [ 'letter' => 'newpageletter', 'title' => 'recentchanges-label-newpage', 'legend' => 'recentchanges-legend-newpage', 'grouping' => 'any', ], 'minor' => [ 'letter' => 'minoreditletter', 'title' => 'recentchanges-label-minor', 'legend' => 'recentchanges-legend-minor', 'class' => 'minoredit', 'grouping' => 'all', ], 'bot' => [ 'letter' => 'boteditletter', 'title' => 'recentchanges-label-bot', 'legend' => 'recentchanges-legend-bot', 'class' => 'botedit', 'grouping' => 'all', ], 'unpatrolled' => [ 'letter' => 'unpatrolledletter', 'title' => 'recentchanges-label-unpatrolled', 'legend' => 'recentchanges-legend-unpatrolled', 'grouping' => 'any', ], ], 'WatchlistExpiry' => false, 'EnableWatchlistLabels' => false, 'WatchlistLabelsMaxPerUser' => 100, 'WatchlistPurgeRate' => 0.1, 'WatchlistExpiryMaxDuration' => '1 year', 'EnableChangesListQueryPartitioning' => false, 'RightsPage' => null, 'RightsUrl' => null, 'RightsText' => null, 'RightsIcon' => null, 'UseCopyrightUpload' => false, 'MaxCredits' => 0, 'ShowCreditsIfMax' => true, 'ImportSources' => [ ], 'ImportTargetNamespace' => null, 'ExportAllowHistory' => true, 'ExportMaxHistory' => 0, 'ExportAllowListContributors' => false, 'ExportMaxLinkDepth' => 0, 'ExportFromNamespaces' => false, 'ExportAllowAll' => false, 'ExportPagelistLimit' => 5000, 'XmlDumpSchemaVersion' => '0.11', 'WikiFarmSettingsDirectory' => null, 'WikiFarmSettingsExtension' => 'yaml', 'ExtensionFunctions' => [ ], 'ExtensionMessagesFiles' => [ ], 'MessagesDirs' => [ ], 'TranslationAliasesDirs' => [ ], 'ExtensionEntryPointListFiles' => [ ], 'EnableParserLimitReporting' => true, 'ValidSkinNames' => [ ], 'SpecialPages' => [ ], 'ExtensionCredits' => [ ], 'Hooks' => [ ], 'ServiceWiringFiles' => [ ], 'JobClasses' => [ 'deletePage' => 'MediaWiki\\Page\\DeletePageJob', 'refreshLinks' => 'MediaWiki\\JobQueue\\Jobs\\RefreshLinksJob', 'deleteLinks' => 'MediaWiki\\Page\\DeleteLinksJob', 'htmlCacheUpdate' => 'MediaWiki\\JobQueue\\Jobs\\HTMLCacheUpdateJob', 'sendMail' => [ 'class' => 'MediaWiki\\Mail\\EmaillingJob', 'services' => [ 'Emailer', ], ], 'enotifNotify' => [ 'class' => 'MediaWiki\\RecentChanges\\RecentChangeNotifyJob', 'services' => [ 'RecentChangeLookup', ], ], 'fixDoubleRedirect' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\DoubleRedirectJob', 'services' => [ 'RevisionLookup', 'MagicWordFactory', 'WikiPageFactory', ], 'needsPage' => true, ], 'AssembleUploadChunks' => 'MediaWiki\\JobQueue\\Jobs\\AssembleUploadChunksJob', 'PublishStashedFile' => 'MediaWiki\\JobQueue\\Jobs\\PublishStashedFileJob', 'ThumbnailRender' => 'MediaWiki\\JobQueue\\Jobs\\ThumbnailRenderJob', 'UploadFromUrl' => 'MediaWiki\\JobQueue\\Jobs\\UploadFromUrlJob', 'recentChangesUpdate' => 'MediaWiki\\RecentChanges\\RecentChangesUpdateJob', 'refreshLinksPrioritized' => 'MediaWiki\\JobQueue\\Jobs\\RefreshLinksJob', 'refreshLinksDynamic' => 'MediaWiki\\JobQueue\\Jobs\\RefreshLinksJob', 'activityUpdateJob' => 'MediaWiki\\Watchlist\\ActivityUpdateJob', 'categoryMembershipChange' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\CategoryMembershipChangeJob', 'services' => [ 'RecentChangeFactory', ], ], 'CategoryCountUpdateJob' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\CategoryCountUpdateJob', 'services' => [ 'ConnectionProvider', 'NamespaceInfo', ], ], 'clearUserWatchlist' => 'MediaWiki\\Watchlist\\ClearUserWatchlistJob', 'watchlistExpiry' => 'MediaWiki\\Watchlist\\WatchlistExpiryJob', 'cdnPurge' => 'MediaWiki\\JobQueue\\Jobs\\CdnPurgeJob', 'userGroupExpiry' => 'MediaWiki\\User\\UserGroupExpiryJob', 'clearWatchlistNotifications' => 'MediaWiki\\Watchlist\\ClearWatchlistNotificationsJob', 'userOptionsUpdate' => 'MediaWiki\\User\\Options\\UserOptionsUpdateJob', 'revertedTagUpdate' => 'MediaWiki\\JobQueue\\Jobs\\RevertedTagUpdateJob', 'null' => 'MediaWiki\\JobQueue\\Jobs\\NullJob', 'userEditCountInit' => 'MediaWiki\\User\\UserEditCountInitJob', 'parsoidCachePrewarm' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\ParsoidCachePrewarmJob', 'services' => [ 'ParserOutputAccess', 'PageStore', 'RevisionLookup', 'ParsoidSiteConfig', ], 'needsPage' => false, ], 'renameUserTable' => [ 'class' => 'MediaWiki\\RenameUser\\Job\\RenameUserTableJob', 'services' => [ 'MainConfig', 'DBLoadBalancerFactory', ], ], 'renameUserDerived' => [ 'class' => 'MediaWiki\\RenameUser\\Job\\RenameUserDerivedJob', 'services' => [ 'RenameUserFactory', 'UserFactory', ], ], 'renameUser' => [ 'class' => 'MediaWiki\\RenameUser\\Job\\RenameUserTableJob', 'services' => [ 'MainConfig', 'DBLoadBalancerFactory', ], ], ], 'JobTypesExcludedFromDefaultQueue' => [ 'AssembleUploadChunks', 'PublishStashedFile', 'UploadFromUrl', ], 'JobBackoffThrottling' => [ ], 'JobTypeConf' => [ 'default' => [ 'class' => 'MediaWiki\\JobQueue\\JobQueueDB', 'order' => 'random', 'claimTTL' => 3600, ], ], 'JobQueueIncludeInMaxLagFactor' => false, 'SpecialPageCacheUpdates' => [ 'Statistics' => [ 'MediaWiki\\Deferred\\SiteStatsUpdate', 'cacheUpdate', ], ], 'PagePropLinkInvalidations' => [ 'hiddencat' => 'categorylinks', ], 'CategoryMagicGallery' => true, 'CategoryPagingLimit' => 200, 'CategoryCollation' => 'uppercase', 'TempCategoryCollations' => [ ], 'SortedCategories' => false, 'TrackingCategories' => [ ], 'LogTypes' => [ '', 'block', 'protect', 'rights', 'delete', 'upload', 'move', 'import', 'interwiki', 'patrol', 'merge', 'suppress', 'tag', 'managetags', 'contentmodel', 'renameuser', ], 'LogRestrictions' => [ 'suppress' => 'suppressionlog', ], 'FilterLogTypes' => [ 'patrol' => true, 'tag' => true, 'newusers' => false, ], 'LogNames' => [ '' => 'all-logs-page', 'block' => 'blocklogpage', 'protect' => 'protectlogpage', 'rights' => 'rightslog', 'delete' => 'dellogpage', 'upload' => 'uploadlogpage', 'move' => 'movelogpage', 'import' => 'importlogpage', 'patrol' => 'patrol-log-page', 'merge' => 'mergelog', 'suppress' => 'suppressionlog', ], 'LogHeaders' => [ '' => 'alllogstext', 'block' => 'blocklogtext', 'delete' => 'dellogpagetext', 'import' => 'importlogpagetext', 'merge' => 'mergelogpagetext', 'move' => 'movelogpagetext', 'patrol' => 'patrol-log-header', 'protect' => 'protectlogtext', 'rights' => 'rightslogtext', 'suppress' => 'suppressionlogtext', 'upload' => 'uploadlogpagetext', ], 'LogActions' => [ ], 'LogActionsHandlers' => [ 'block/block' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'block/reblock' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'block/unblock' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'contentmodel/change' => 'MediaWiki\\Logging\\ContentModelLogFormatter', 'contentmodel/new' => 'MediaWiki\\Logging\\ContentModelLogFormatter', 'delete/delete' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/delete_redir' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/delete_redir2' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/event' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/restore' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/revision' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'import/interwiki' => 'MediaWiki\\Logging\\ImportLogFormatter', 'import/upload' => 'MediaWiki\\Logging\\ImportLogFormatter', 'interwiki/iw_add' => 'MediaWiki\\Logging\\InterwikiLogFormatter', 'interwiki/iw_delete' => 'MediaWiki\\Logging\\InterwikiLogFormatter', 'interwiki/iw_edit' => 'MediaWiki\\Logging\\InterwikiLogFormatter', 'managetags/activate' => 'MediaWiki\\Logging\\LogFormatter', 'managetags/create' => 'MediaWiki\\Logging\\LogFormatter', 'managetags/deactivate' => 'MediaWiki\\Logging\\LogFormatter', 'managetags/delete' => 'MediaWiki\\Logging\\LogFormatter', 'merge/merge' => [ 'class' => 'MediaWiki\\Logging\\MergeLogFormatter', 'services' => [ 'TitleParser', ], ], 'merge/merge-into' => [ 'class' => 'MediaWiki\\Logging\\MergeLogFormatter', 'services' => [ 'TitleParser', ], ], 'move/move' => [ 'class' => 'MediaWiki\\Logging\\MoveLogFormatter', 'services' => [ 'TitleParser', ], ], 'move/move_redir' => [ 'class' => 'MediaWiki\\Logging\\MoveLogFormatter', 'services' => [ 'TitleParser', ], ], 'patrol/patrol' => 'MediaWiki\\Logging\\PatrolLogFormatter', 'patrol/autopatrol' => 'MediaWiki\\Logging\\PatrolLogFormatter', 'protect/modify' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'protect/move_prot' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'protect/protect' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'protect/unprotect' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'renameuser/renameuser' => [ 'class' => 'MediaWiki\\Logging\\RenameuserLogFormatter', 'services' => [ 'TitleParser', ], ], 'rights/autopromote' => 'MediaWiki\\Logging\\RightsLogFormatter', 'rights/rights' => 'MediaWiki\\Logging\\RightsLogFormatter', 'suppress/block' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'suppress/delete' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'suppress/event' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'suppress/reblock' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'suppress/revision' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'tag/update' => 'MediaWiki\\Logging\\TagLogFormatter', 'upload/overwrite' => 'MediaWiki\\Logging\\UploadLogFormatter', 'upload/revert' => 'MediaWiki\\Logging\\UploadLogFormatter', 'upload/upload' => 'MediaWiki\\Logging\\UploadLogFormatter', ], 'ActionFilteredLogs' => [ 'block' => [ 'block' => [ 'block', ], 'reblock' => [ 'reblock', ], 'unblock' => [ 'unblock', ], ], 'contentmodel' => [ 'change' => [ 'change', ], 'new' => [ 'new', ], ], 'delete' => [ 'delete' => [ 'delete', ], 'delete_redir' => [ 'delete_redir', 'delete_redir2', ], 'restore' => [ 'restore', ], 'event' => [ 'event', ], 'revision' => [ 'revision', ], ], 'import' => [ 'interwiki' => [ 'interwiki', ], 'upload' => [ 'upload', ], ], 'managetags' => [ 'create' => [ 'create', ], 'delete' => [ 'delete', ], 'activate' => [ 'activate', ], 'deactivate' => [ 'deactivate', ], ], 'move' => [ 'move' => [ 'move', ], 'move_redir' => [ 'move_redir', ], ], 'newusers' => [ 'create' => [ 'create', 'newusers', ], 'create2' => [ 'create2', ], 'autocreate' => [ 'autocreate', ], 'byemail' => [ 'byemail', ], ], 'protect' => [ 'protect' => [ 'protect', ], 'modify' => [ 'modify', ], 'unprotect' => [ 'unprotect', ], 'move_prot' => [ 'move_prot', ], ], 'rights' => [ 'rights' => [ 'rights', ], 'autopromote' => [ 'autopromote', ], ], 'suppress' => [ 'event' => [ 'event', ], 'revision' => [ 'revision', ], 'delete' => [ 'delete', ], 'block' => [ 'block', ], 'reblock' => [ 'reblock', ], ], 'upload' => [ 'upload' => [ 'upload', ], 'overwrite' => [ 'overwrite', ], 'revert' => [ 'revert', ], ], ], 'NewUserLog' => true, 'PageCreationLog' => true, 'AllowSpecialInclusion' => true, 'DisableQueryPageUpdate' => false, 'CountCategorizedImagesAsUsed' => false, 'MaxRedirectLinksRetrieved' => 500, 'RangeContributionsCIDRLimit' => [ 'IPv4' => 16, 'IPv6' => 32, ], 'Actions' => [ ], 'DefaultRobotPolicy' => 'index,follow', 'NamespaceRobotPolicies' => [ ], 'ArticleRobotPolicies' => [ ], 'ExemptFromUserRobotsControl' => null, 'DebugAPI' => false, 'APIModules' => [ ], 'APIFormatModules' => [ ], 'APIMetaModules' => [ ], 'APIPropModules' => [ ], 'APIListModules' => [ ], 'APIMaxDBRows' => 5000, 'APIMaxResultSize' => 8388608, 'APIMaxUncachedDiffs' => 1, 'APIMaxLagThreshold' => 7, 'APICacheHelpTimeout' => 3600, 'APIUselessQueryPages' => [ 'MIMEsearch', 'LinkSearch', ], 'AjaxLicensePreview' => true, 'CrossSiteAJAXdomains' => [ ], 'CrossSiteAJAXdomainExceptions' => [ ], 'AllowedCorsHeaders' => [ 'Accept', 'Accept-Language', 'Content-Language', 'Content-Type', 'Accept-Encoding', 'DNT', 'Origin', 'User-Agent', 'Api-User-Agent', 'Access-Control-Max-Age', 'Authorization', ], 'RestAPIAdditionalRouteFiles' => [ ], 'RestSandboxSpecs' => [ ], 'MaxShellMemory' => 307200, 'MaxShellFileSize' => 102400, 'MaxShellTime' => 180, 'MaxShellWallClockTime' => 180, 'ShellCgroup' => false, 'PhpCli' => '/usr/bin/php', 'ShellRestrictionMethod' => 'autodetect', 'ShellboxUrls' => [ 'default' => null, ], 'ShellboxSecretKey' => null, 'ShellboxShell' => '/bin/sh', 'HTTPTimeout' => 25, 'HTTPConnectTimeout' => 5.0, 'HTTPMaxTimeout' => 0, 'HTTPMaxConnectTimeout' => 0, 'HTTPImportTimeout' => 25, 'AsyncHTTPTimeout' => 25, 'HTTPProxy' => '', 'LocalVirtualHosts' => [ ], 'LocalHTTPProxy' => false, 'AllowExternalReqID' => false, 'JobRunRate' => 1, 'RunJobsAsync' => false, 'UpdateRowsPerJob' => 300, 'UpdateRowsPerQuery' => 100, 'RedirectOnLogin' => null, 'VirtualRestConfig' => [ 'paths' => [ ], 'modules' => [ ], 'global' => [ 'timeout' => 360, 'forwardCookies' => false, 'HTTPProxy' => null, ], ], 'EventRelayerConfig' => [ 'default' => [ 'class' => 'Wikimedia\\EventRelayer\\EventRelayerNull', ], ], 'Pingback' => false, 'OriginTrials' => [ ], 'ReportToExpiry' => 86400, 'ReportToEndpoints' => [ ], 'FeaturePolicyReportOnly' => [ ], 'SkinsPreferred' => [ 'vector-2022', 'vector', ], 'SpecialContributeSkinsEnabled' => [ ], 'SpecialContributeNewPageTarget' => null, 'EnableEditRecovery' => false, 'EditRecoveryExpiry' => 2592000, 'UseCodexSpecialBlock' => false, 'ShowLogoutConfirmation' => false, 'EnableProtectionIndicators' => true, 'OutputPipelineStages' => [ ], 'FeatureShutdown' => [ ], 'CloneArticleParserOutput' => true, 'UseLeximorph' => false, 'UsePostprocCache' => false, ], 'type' => [ 'ConfigRegistry' => 'object', 'AssumeProxiesUseDefaultProtocolPorts' => 'boolean', 'ForceHTTPS' => 'boolean', 'ExtensionDirectory' => [ 'string', 'null', ], 'StyleDirectory' => [ 'string', 'null', ], 'UploadDirectory' => [ 'string', 'boolean', 'null', ], 'Logos' => [ 'object', 'boolean', ], 'ReferrerPolicy' => [ 'array', 'string', 'boolean', ], 'ActionPaths' => 'object', 'MainPageIsDomainRoot' => 'boolean', 'ImgAuthUrlPathMap' => 'object', 'LocalFileRepo' => 'object', 'ForeignFileRepos' => 'array', 'UseSharedUploads' => 'boolean', 'SharedUploadDirectory' => [ 'string', 'null', ], 'SharedUploadPath' => [ 'string', 'null', ], 'HashedSharedUploadDirectory' => 'boolean', 'FetchCommonsDescriptions' => 'boolean', 'SharedUploadDBname' => [ 'boolean', 'string', ], 'SharedUploadDBprefix' => 'string', 'CacheSharedUploads' => 'boolean', 'ForeignUploadTargets' => 'array', 'UploadDialog' => 'object', 'FileBackends' => 'object', 'LockManagers' => 'array', 'CopyUploadsDomains' => 'array', 'CopyUploadTimeout' => [ 'boolean', 'integer', ], 'SharedThumbnailScriptPath' => [ 'string', 'boolean', ], 'HashedUploadDirectory' => 'boolean', 'CSPUploadEntryPoint' => 'boolean', 'FileExtensions' => 'array', 'ProhibitedFileExtensions' => 'array', 'MimeTypeExclusions' => 'array', 'TrustedMediaFormats' => 'array', 'MediaHandlers' => 'object', 'NativeImageLazyLoading' => 'boolean', 'ParserTestMediaHandlers' => 'object', 'MaxInterlacingAreas' => 'object', 'SVGConverters' => 'object', 'SVGNativeRendering' => [ 'string', 'boolean', ], 'MaxImageArea' => [ 'string', 'integer', 'boolean', ], 'TiffThumbnailType' => 'array', 'GenerateThumbnailOnParse' => 'boolean', 'EnableAutoRotation' => [ 'boolean', 'null', ], 'Antivirus' => [ 'string', 'null', ], 'AntivirusSetup' => 'object', 'MimeDetectorCommand' => [ 'string', 'null', ], 'XMLMimeTypes' => 'object', 'ImageLimits' => 'array', 'ThumbLimits' => 'array', 'ThumbnailNamespaces' => 'array', 'ThumbnailSteps' => [ 'array', 'null', ], 'ThumbnailStepsRatio' => [ 'number', 'null', ], 'ThumbnailBuckets' => [ 'array', 'null', ], 'UploadThumbnailRenderMap' => 'object', 'GalleryOptions' => 'object', 'DjvuDump' => [ 'string', 'null', ], 'DjvuRenderer' => [ 'string', 'null', ], 'DjvuTxt' => [ 'string', 'null', ], 'DjvuPostProcessor' => [ 'string', 'null', ], 'UserEmailConfirmationUseHTML' => 'boolean', 'SMTP' => [ 'boolean', 'object', ], 'EnotifFromEditor' => 'boolean', 'EnotifRevealEditorAddress' => 'boolean', 'UsersNotifiedOnAllChanges' => 'object', 'DBmwschema' => [ 'string', 'null', ], 'SharedTables' => 'array', 'DBservers' => [ 'boolean', 'array', ], 'LBFactoryConf' => 'object', 'LocalDatabases' => 'array', 'VirtualDomainsMapping' => 'object', 'FileSchemaMigrationStage' => 'integer', 'ExternalLinksDomainGaps' => 'object', 'ContentHandlers' => 'object', 'NamespaceContentModels' => 'object', 'TextModelsToParse' => 'array', 'ExternalStores' => 'array', 'ExternalServers' => 'object', 'DefaultExternalStore' => [ 'array', 'boolean', ], 'RevisionCacheExpiry' => 'integer', 'PageLanguageUseDB' => 'boolean', 'DiffEngine' => [ 'string', 'null', ], 'ExternalDiffEngine' => [ 'string', 'boolean', ], 'Wikidiff2Options' => 'object', 'RequestTimeLimit' => [ 'integer', 'null', ], 'CriticalSectionTimeLimit' => 'number', 'PoolCounterConf' => [ 'object', 'null', ], 'PoolCountClientConf' => 'object', 'MaxUserDBWriteDuration' => [ 'integer', 'boolean', ], 'MaxJobDBWriteDuration' => [ 'integer', 'boolean', ], 'MultiShardSiteStats' => 'boolean', 'ObjectCaches' => 'object', 'WANObjectCache' => 'object', 'MicroStashType' => [ 'string', 'integer', ], 'ParsoidCacheConfig' => 'object', 'ParsoidSelectiveUpdateSampleRate' => 'integer', 'ParserCacheFilterConfig' => 'object', 'ChronologyProtectorSecret' => 'string', 'PHPSessionHandling' => 'string', 'SuspiciousIpExpiry' => [ 'integer', 'boolean', ], 'MemCachedServers' => 'array', 'LocalisationCacheConf' => 'object', 'ExtensionInfoMTime' => [ 'integer', 'boolean', ], 'CdnServers' => 'object', 'CdnServersNoPurge' => 'object', 'HTCPRouting' => 'object', 'GrammarForms' => 'object', 'ExtraInterlanguageLinkPrefixes' => 'array', 'InterlanguageLinkCodeMap' => 'object', 'ExtraLanguageNames' => 'object', 'ExtraLanguageCodes' => 'object', 'DummyLanguageCodes' => 'object', 'DisabledVariants' => 'object', 'ForceUIMsgAsContentMsg' => 'object', 'RawHtmlMessages' => 'array', 'OverrideUcfirstCharacters' => 'object', 'XhtmlNamespaces' => 'object', 'BrowserFormatDetection' => 'string', 'SkinMetaTags' => 'object', 'SkipSkins' => 'object', 'FragmentMode' => 'array', 'FooterIcons' => 'object', 'InterwikiLogoOverride' => 'array', 'ResourceModules' => 'object', 'ResourceModuleSkinStyles' => 'object', 'ResourceLoaderSources' => 'object', 'ResourceLoaderMaxage' => 'object', 'ResourceLoaderMaxQueryLength' => [ 'integer', 'boolean', ], 'CanonicalNamespaceNames' => 'object', 'ExtraNamespaces' => 'object', 'ExtraGenderNamespaces' => 'object', 'NamespaceAliases' => 'object', 'CapitalLinkOverrides' => 'object', 'NamespacesWithSubpages' => 'object', 'ContentNamespaces' => 'array', 'ShortPagesNamespaceExclusions' => 'array', 'ExtraSignatureNamespaces' => 'array', 'InvalidRedirectTargets' => 'array', 'LocalInterwikis' => 'array', 'InterwikiCache' => [ 'boolean', 'object', ], 'SiteTypes' => 'object', 'UrlProtocols' => 'array', 'TidyConfig' => 'object', 'ParsoidSettings' => 'object', 'ParsoidExperimentalParserFunctionOutput' => 'boolean', 'NoFollowNsExceptions' => 'array', 'NoFollowDomainExceptions' => 'array', 'ExternalLinksIgnoreDomains' => 'array', 'EnableMagicLinks' => 'object', 'ManualRevertSearchRadius' => 'integer', 'RevertedTagMaxDepth' => 'integer', 'CentralIdLookupProviders' => 'object', 'CentralIdLookupProvider' => 'string', 'UserRegistrationProviders' => 'object', 'PasswordPolicy' => 'object', 'AuthManagerConfig' => [ 'object', 'null', ], 'AuthManagerAutoConfig' => 'object', 'RememberMe' => 'string', 'ReauthenticateTime' => 'object', 'AllowSecuritySensitiveOperationIfCannotReauthenticate' => 'object', 'ChangeCredentialsBlacklist' => 'array', 'RemoveCredentialsBlacklist' => 'array', 'PasswordConfig' => 'object', 'PasswordResetRoutes' => 'object', 'SignatureAllowedLintErrors' => 'array', 'ReservedUsernames' => 'array', 'DefaultUserOptions' => 'object', 'ConditionalUserOptions' => 'object', 'HiddenPrefs' => 'array', 'UserJsPrefLimit' => 'integer', 'AuthenticationTokenVersion' => [ 'string', 'null', ], 'SessionProviders' => 'object', 'AutoCreateTempUser' => 'object', 'AutoblockExemptions' => 'array', 'BlockCIDRLimit' => 'object', 'EnableMultiBlocks' => 'boolean', 'BlockTargetMigrationStage' => 'integer', 'GroupPermissions' => 'object', 'PrivilegedGroups' => 'array', 'RevokePermissions' => 'object', 'GroupInheritsPermissions' => 'object', 'ImplicitGroups' => 'array', 'GroupsAddToSelf' => 'object', 'GroupsRemoveFromSelf' => 'object', 'RestrictedGroups' => 'object', 'RestrictionTypes' => 'array', 'RestrictionLevels' => 'array', 'CascadingRestrictionLevels' => 'array', 'SemiprotectedRestrictionLevels' => 'array', 'NamespaceProtection' => 'object', 'NonincludableNamespaces' => 'object', 'Autopromote' => 'object', 'AutopromoteOnce' => 'object', 'AutopromoteOnceRCExcludedGroups' => 'array', 'AddGroups' => 'object', 'RemoveGroups' => 'object', 'AvailableRights' => 'array', 'ImplicitRights' => 'array', 'AccountCreationThrottle' => [ 'integer', 'array', ], 'TempAccountCreationThrottle' => 'array', 'TempAccountNameAcquisitionThrottle' => 'array', 'SpamRegex' => 'array', 'SummarySpamRegex' => 'array', 'DnsBlacklistUrls' => 'array', 'ProxyList' => [ 'string', 'array', ], 'ProxyWhitelist' => 'array', 'SoftBlockRanges' => 'array', 'RateLimits' => 'object', 'RateLimitsExcludedIPs' => 'array', 'ExternalQuerySources' => 'object', 'PasswordAttemptThrottle' => 'array', 'GrantPermissions' => 'object', 'GrantPermissionGroups' => 'object', 'GrantRiskGroups' => 'object', 'EnableBotPasswords' => 'boolean', 'BotPasswordsCluster' => [ 'string', 'boolean', ], 'BotPasswordsDatabase' => [ 'string', 'boolean', ], 'CSPHeader' => [ 'boolean', 'object', ], 'CSPReportOnlyHeader' => [ 'boolean', 'object', ], 'CSPFalsePositiveUrls' => 'object', 'AllowCrossOrigin' => 'boolean', 'RestAllowCrossOriginCookieAuth' => 'boolean', 'CookieSameSite' => [ 'string', 'null', ], 'CacheVaryCookies' => 'array', 'TrxProfilerLimits' => 'object', 'DebugLogGroups' => 'object', 'MWLoggerDefaultSpi' => 'object', 'Profiler' => 'object', 'StatsTarget' => [ 'string', 'null', ], 'StatsFormat' => [ 'string', 'null', ], 'StatsPrefix' => 'string', 'OpenTelemetryConfig' => [ 'object', 'null', ], 'OpenSearchTemplates' => 'object', 'NamespacesToBeSearchedDefault' => 'object', 'SitemapNamespaces' => [ 'boolean', 'array', ], 'SitemapNamespacesPriorities' => [ 'boolean', 'object', ], 'SitemapApiConfig' => 'object', 'SpecialSearchFormOptions' => 'object', 'SearchMatchRedirectPreference' => 'boolean', 'SearchRunSuggestedQuery' => 'boolean', 'PreviewOnOpenNamespaces' => 'object', 'ReadOnlyWatchedItemStore' => 'boolean', 'GitRepositoryViewers' => 'object', 'InstallerInitialPages' => 'array', 'RCLinkLimits' => 'array', 'RCLinkDays' => 'array', 'RCFeeds' => 'object', 'RCEngines' => 'object', 'OverrideSiteFeed' => 'object', 'FeedClasses' => 'object', 'AdvertisedFeedTypes' => 'array', 'SoftwareTags' => 'object', 'RecentChangesFlags' => 'object', 'WatchlistExpiry' => 'boolean', 'EnableWatchlistLabels' => 'boolean', 'WatchlistLabelsMaxPerUser' => 'integer', 'WatchlistPurgeRate' => 'number', 'WatchlistExpiryMaxDuration' => [ 'string', 'null', ], 'EnableChangesListQueryPartitioning' => 'boolean', 'ImportSources' => 'object', 'ExtensionFunctions' => 'array', 'ExtensionMessagesFiles' => 'object', 'MessagesDirs' => 'object', 'TranslationAliasesDirs' => 'object', 'ExtensionEntryPointListFiles' => 'object', 'ValidSkinNames' => 'object', 'SpecialPages' => 'object', 'ExtensionCredits' => 'object', 'Hooks' => 'object', 'ServiceWiringFiles' => 'array', 'JobClasses' => 'object', 'JobTypesExcludedFromDefaultQueue' => 'array', 'JobBackoffThrottling' => 'object', 'JobTypeConf' => 'object', 'SpecialPageCacheUpdates' => 'object', 'PagePropLinkInvalidations' => 'object', 'TempCategoryCollations' => 'array', 'SortedCategories' => 'boolean', 'TrackingCategories' => 'array', 'LogTypes' => 'array', 'LogRestrictions' => 'object', 'FilterLogTypes' => 'object', 'LogNames' => 'object', 'LogHeaders' => 'object', 'LogActions' => 'object', 'LogActionsHandlers' => 'object', 'ActionFilteredLogs' => 'object', 'RangeContributionsCIDRLimit' => 'object', 'Actions' => 'object', 'NamespaceRobotPolicies' => 'object', 'ArticleRobotPolicies' => 'object', 'ExemptFromUserRobotsControl' => [ 'array', 'null', ], 'APIModules' => 'object', 'APIFormatModules' => 'object', 'APIMetaModules' => 'object', 'APIPropModules' => 'object', 'APIListModules' => 'object', 'APIUselessQueryPages' => 'array', 'CrossSiteAJAXdomains' => 'object', 'CrossSiteAJAXdomainExceptions' => 'object', 'AllowedCorsHeaders' => 'array', 'RestAPIAdditionalRouteFiles' => 'array', 'RestSandboxSpecs' => 'object', 'ShellRestrictionMethod' => [ 'string', 'boolean', ], 'ShellboxUrls' => 'object', 'ShellboxSecretKey' => [ 'string', 'null', ], 'ShellboxShell' => [ 'string', 'null', ], 'HTTPTimeout' => 'number', 'HTTPConnectTimeout' => 'number', 'HTTPMaxTimeout' => 'number', 'HTTPMaxConnectTimeout' => 'number', 'LocalVirtualHosts' => 'object', 'LocalHTTPProxy' => [ 'string', 'boolean', ], 'VirtualRestConfig' => 'object', 'EventRelayerConfig' => 'object', 'Pingback' => 'boolean', 'OriginTrials' => 'array', 'ReportToExpiry' => 'integer', 'ReportToEndpoints' => 'array', 'FeaturePolicyReportOnly' => 'array', 'SkinsPreferred' => 'array', 'SpecialContributeSkinsEnabled' => 'array', 'SpecialContributeNewPageTarget' => [ 'string', 'null', ], 'EnableEditRecovery' => 'boolean', 'EditRecoveryExpiry' => 'integer', 'UseCodexSpecialBlock' => 'boolean', 'ShowLogoutConfirmation' => 'boolean', 'EnableProtectionIndicators' => 'boolean', 'OutputPipelineStages' => 'object', 'FeatureShutdown' => 'array', 'CloneArticleParserOutput' => 'boolean', 'UseLeximorph' => 'boolean', 'UsePostprocCache' => 'boolean', ], 'mergeStrategy' => [ 'TiffThumbnailType' => 'replace', 'LBFactoryConf' => 'replace', 'InterwikiCache' => 'replace', 'PasswordPolicy' => 'array_replace_recursive', 'AuthManagerAutoConfig' => 'array_plus_2d', 'GroupPermissions' => 'array_plus_2d', 'RevokePermissions' => 'array_plus_2d', 'AddGroups' => 'array_merge_recursive', 'RemoveGroups' => 'array_merge_recursive', 'RateLimits' => 'array_plus_2d', 'GrantPermissions' => 'array_plus_2d', 'MWLoggerDefaultSpi' => 'replace', 'Profiler' => 'replace', 'Hooks' => 'array_merge_recursive', 'VirtualRestConfig' => 'array_plus_2d', ], 'dynamicDefault' => [ 'UsePathInfo' => [ 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultUsePathInfo', ], ], 'Script' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultScript', ], ], 'LoadScript' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLoadScript', ], ], 'RestPath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultRestPath', ], ], 'StylePath' => [ 'use' => [ 'ResourceBasePath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultStylePath', ], ], 'LocalStylePath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLocalStylePath', ], ], 'ExtensionAssetsPath' => [ 'use' => [ 'ResourceBasePath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultExtensionAssetsPath', ], ], 'ArticlePath' => [ 'use' => [ 'Script', 'UsePathInfo', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultArticlePath', ], ], 'UploadPath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultUploadPath', ], ], 'FileCacheDirectory' => [ 'use' => [ 'UploadDirectory', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultFileCacheDirectory', ], ], 'Logo' => [ 'use' => [ 'ResourceBasePath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLogo', ], ], 'DeletedDirectory' => [ 'use' => [ 'UploadDirectory', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultDeletedDirectory', ], ], 'ShowEXIF' => [ 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultShowEXIF', ], ], 'SharedPrefix' => [ 'use' => [ 'DBprefix', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultSharedPrefix', ], ], 'SharedSchema' => [ 'use' => [ 'DBmwschema', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultSharedSchema', ], ], 'DBerrorLogTZ' => [ 'use' => [ 'Localtimezone', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultDBerrorLogTZ', ], ], 'Localtimezone' => [ 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLocaltimezone', ], ], 'LocalTZoffset' => [ 'use' => [ 'Localtimezone', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLocalTZoffset', ], ], 'ResourceBasePath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultResourceBasePath', ], ], 'MetaNamespace' => [ 'use' => [ 'Sitename', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultMetaNamespace', ], ], 'CookieSecure' => [ 'use' => [ 'ForceHTTPS', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultCookieSecure', ], ], 'CookiePrefix' => [ 'use' => [ 'SharedDB', 'SharedPrefix', 'SharedTables', 'DBname', 'DBprefix', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultCookiePrefix', ], ], 'ReadOnlyFile' => [ 'use' => [ 'UploadDirectory', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultReadOnlyFile', ], ], ], ], 'config-schema' => [ 'UploadStashScalerBaseUrl' => [ 'deprecated' => 'since 1.36 Use thumbProxyUrl in $wgLocalFileRepo', ], 'IllegalFileChars' => [ 'deprecated' => 'since 1.41; no longer customizable', ], 'ThumbnailNamespaces' => [ 'items' => [ 'type' => 'integer', ], ], 'LocalDatabases' => [ 'items' => [ 'type' => 'string', ], ], 'ParserCacheFilterConfig' => [ 'additionalProperties' => [ 'type' => 'object', 'description' => 'A map of namespace IDs to filter definitions.', 'additionalProperties' => [ 'type' => 'object', 'description' => 'A map of filter names to values.', 'properties' => [ 'minCpuTime' => [ 'type' => 'number', ], ], ], ], ], 'PHPSessionHandling' => [ 'deprecated' => 'since 1.45 Integration with PHP session handling will be removed in the future', ], 'RawHtmlMessages' => [ 'items' => [ 'type' => 'string', ], ], 'InterwikiLogoOverride' => [ 'items' => [ 'type' => 'string', ], ], 'LegalTitleChars' => [ 'deprecated' => 'since 1.41; use Extension:TitleBlacklist to customize', ], 'ReauthenticateTime' => [ 'additionalProperties' => [ 'type' => 'integer', ], ], 'AllowSecuritySensitiveOperationIfCannotReauthenticate' => [ 'additionalProperties' => [ 'type' => 'boolean', ], ], 'ChangeCredentialsBlacklist' => [ 'items' => [ 'type' => 'string', ], ], 'RemoveCredentialsBlacklist' => [ 'items' => [ 'type' => 'string', ], ], 'GroupPermissions' => [ 'additionalProperties' => [ 'type' => 'object', 'additionalProperties' => [ 'type' => 'boolean', ], ], ], 'GroupInheritsPermissions' => [ 'additionalProperties' => [ 'type' => 'string', ], ], 'AvailableRights' => [ 'items' => [ 'type' => 'string', ], ], 'ImplicitRights' => [ 'items' => [ 'type' => 'string', ], ], 'SoftBlockRanges' => [ 'items' => [ 'type' => 'string', ], ], 'ExternalQuerySources' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'enabled' => [ 'type' => 'boolean', 'default' => false, ], 'url' => [ 'type' => 'string', 'format' => 'uri', ], 'timeout' => [ 'type' => 'integer', 'default' => 10, ], ], 'required' => [ 'enabled', 'url', ], 'additionalProperties' => false, ], ], 'GrantPermissions' => [ 'additionalProperties' => [ 'type' => 'object', 'additionalProperties' => [ 'type' => 'boolean', ], ], ], 'GrantPermissionGroups' => [ 'additionalProperties' => [ 'type' => 'string', ], ], 'SitemapNamespacesPriorities' => [ 'deprecated' => 'since 1.45 and ignored', ], 'SitemapApiConfig' => [ 'additionalProperties' => [ 'enabled' => [ 'type' => 'bool', ], 'sitemapsPerIndex' => [ 'type' => 'int', ], 'pagesPerSitemap' => [ 'type' => 'int', ], 'expiry' => [ 'type' => 'int', ], ], ], 'SoftwareTags' => [ 'additionalProperties' => [ 'type' => 'boolean', ], ], 'JobBackoffThrottling' => [ 'additionalProperties' => [ 'type' => 'number', ], ], 'JobTypeConf' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'class' => [ 'type' => 'string', ], 'order' => [ 'type' => 'string', ], 'claimTTL' => [ 'type' => 'integer', ], ], ], ], 'TrackingCategories' => [ 'deprecated' => 'since 1.25 Extensions should now register tracking categories using the new extension registration system.', ], 'RangeContributionsCIDRLimit' => [ 'additionalProperties' => [ 'type' => 'integer', ], ], 'RestSandboxSpecs' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'url' => [ 'type' => 'string', 'format' => 'url', ], 'name' => [ 'type' => 'string', ], 'msg' => [ 'type' => 'string', 'description' => 'a message key', ], ], 'required' => [ 'url', ], ], ], 'ShellboxUrls' => [ 'additionalProperties' => [ 'type' => [ 'string', 'boolean', 'null', ], ], ], ], 'obsolete-config' => [ 'MangleFlashPolicy' => 'Since 1.39; no longer has any effect.', 'EnableOpenSearchSuggest' => 'Since 1.35, no longer used', 'AutoloadAttemptLowercase' => 'Since 1.40; no longer has any effect.', ],]
Represents a block that may prevent users from performing specific operations.
Definition Block.php:31
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