28use MediaWiki\HookContainer\ProtectedHookAccessorTrait;
42use Wikimedia\ScopedCallback;
59#[AllowDynamicProperties]
61 use ProtectedHookAccessorTrait;
111 'mEmailAuthenticated',
113 'mEmailTokenExpires',
245 return (
string)$this->
getName();
250 if ( $name ===
'mRights' ) {
253 } elseif ( $name ===
'mOptions' ) {
257 } elseif ( !property_exists( $this, $name ) ) {
269 public function __set( $name, $value ) {
272 if ( $name ===
'mRights' ) {
273 MediaWikiServices::getInstance()->getPermissionManager()->overrideUserRightsForTesting(
275 $value ===
null ? [] : $value
277 } elseif ( $name ===
'mOptions' ) {
279 MediaWikiServices::getInstance()->getUserOptionsManager()->clearUserOptionsCache( $this );
280 foreach ( $value as $key => $val ) {
283 } elseif ( !property_exists( $this, $name ) ) {
284 $this->$name = $value;
313 $this->mLoadedItems ===
true || $this->mFrom !==
'session';
321 public function load( $flags = self::READ_NORMAL ) {
324 if ( $this->mLoadedItems ===
true ) {
330 $this->mLoadedItems =
true;
331 $this->queryFlagsUsed = $flags;
335 \MediaWiki\Logger\LoggerFactory::getInstance(
'session' )
336 ->warning(
'User::loadFromSession called before the end of Setup.php', [
337 'exception' =>
new Exception(
'User::loadFromSession called before the end of Setup.php' ),
340 $this->mLoadedItems = $oldLoadedItems;
344 switch ( $this->mFrom ) {
350 if ( $this->mId != 0 ) {
351 $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
352 if ( $lb->hasOrMadeRecentMasterChanges() ) {
353 $flags |= self::READ_LATEST;
354 $this->queryFlagsUsed = $flags;
363 $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
364 if ( $lb->hasOrMadeRecentMasterChanges() ) {
365 $flags |= self::READ_LATEST;
366 $this->queryFlagsUsed = $flags;
369 list( $index, $options ) = DBAccessObjectUtils::getDBOptions( $flags );
370 $row =
wfGetDB( $index )->selectRow(
372 [
'actor_id',
'actor_user',
'actor_name' ],
373 $this->mFrom ===
'name' ? [
'actor_name' => $this->mName ] : [
'actor_id' =>
$this->mActorId ],
380 $this->
loadDefaults( $this->mFrom ===
'name' ? $this->mName : false );
381 } elseif ( $row->actor_user ) {
382 $this->mId = $row->actor_user;
385 $this->
loadDefaults( $row->actor_name, $row->actor_id );
393 $this->getHookRunner()->onUserLoadAfterLoadFromSession( $this );
396 throw new UnexpectedValueException(
397 "Unrecognised value for User->mFrom: \"{$this->mFrom}\"" );
407 if ( $this->mId == 0 ) {
415 $latest = DBAccessObjectUtils::hasFlags( $flags, self::READ_LATEST );
425 $this->mLoadedItems =
true;
426 $this->queryFlagsUsed = $flags;
436 public static function purge( $dbDomain, $userId ) {
437 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
438 $key =
$cache->makeGlobalKey(
'user',
'id', $dbDomain, $userId );
448 $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
450 return $cache->makeGlobalKey(
'user',
'id', $lbFactory->getLocalDomainID(), $this->mId );
459 $id = $this->
getId();
461 return $id ? [ $this->
getCacheKey( $cache ) ] : [];
473 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
474 $data =
$cache->getWithSetCallback(
479 wfDebug(
"User: cache miss for user {$this->mId}" );
484 foreach ( self::$mCacheVars as $name ) {
485 $data[$name] = $this->$name;
491 $groupMemberships = MediaWikiServices::getInstance()
492 ->getUserGroupManager()
493 ->getUserGroupMemberships( $this, $this->queryFlagsUsed );
497 foreach ( $groupMemberships as $ugm ) {
498 if ( $ugm->getExpiry() ) {
499 $secondsUntilExpiry =
500 wfTimestamp( TS_UNIX, $ugm->getExpiry() ) - time();
502 if ( $secondsUntilExpiry > 0 && $secondsUntilExpiry < $ttl ) {
503 $ttl = $secondsUntilExpiry;
511 [
'pcTTL' => $cache::TTL_PROC_LONG,
'version' =>
self::VERSION ]
515 foreach ( self::$mCacheVars as $name ) {
516 $this->$name = $data[$name];
541 public static function newFromName( $name, $validate =
'valid' ) {
542 if ( $validate ===
true ) {
546 if ( $name ===
false ) {
598 return MediaWikiServices::getInstance()
600 ->newFromUserIdentity( $identity );
616 public static function newFromAnyId( $userId, $userName, $actorId, $dbDomain =
false ) {
620 if ( $dbDomain !==
false ) {
626 $user->mFrom =
'defaults';
628 if ( $actorId !==
null ) {
629 $user->mActorId = (int)$actorId;
630 if ( $user->mActorId !== 0 ) {
631 $user->mFrom =
'actor';
636 if ( $userName !==
null && $userName !==
'' ) {
637 $user->mName = $userName;
638 $user->mFrom =
'name';
639 $user->setItemLoaded(
'name' );
642 if ( $userId !==
null ) {
643 $user->mId = (int)$userId;
644 if ( $user->mId !== 0 ) {
647 $user->setItemLoaded(
'id' );
650 if ( $user->mFrom ===
'defaults' ) {
651 throw new InvalidArgumentException(
652 'Cannot create a user with no name, no ID, and no actor ID'
671 $db = ( $flags & self::READ_LATEST ) == self::READ_LATEST
675 $id = $db->selectField(
679 'user_email_token' => md5( $code ),
680 'user_email_token_expires > ' . $db->addQuotes( $db->timestamp() ),
697 $user->mFrom =
'session';
698 $user->mRequest = $request;
760 'validate' =>
'valid',
766 if ( $name ===
false ) {
772 $row =
$dbr->selectRow(
773 $userQuery[
'tables'],
774 $userQuery[
'fields'],
775 [
'user_name' => $name ],
783 $row = $dbw->selectRow(
784 $userQuery[
'tables'],
785 $userQuery[
'fields'],
786 [
'user_name' => $name ],
796 if ( !$options[
'create'] ) {
803 if ( !self::isValidUserName( $name ) || self::isUsableName( $name ) ) {
810 return $dbw->doAtomicSection( __METHOD__,
function (
IDatabase $dbw, $fname ) use ( $name ) {
814 [
'actor_name' => $name,
'actor_user' =>
null ],
826 $dbw->
delete(
'actor', [
'actor_id' => $row->actor_id ], $fname );
830 [
'actor_id' => $row->actor_id ],
831 [
'actor_id' => $user->getActorId() ],
834 $user->clearInstanceCache(
'id' );
835 $user->invalidateCache();
842 if ( !$user->isSystemUser() ) {
845 if ( !$options[
'steal'] ) {
849 MediaWikiServices::getInstance()->getAuthManager()->revokeAccessForUser( $name );
851 $user->invalidateEmail();
853 $user->saveSettings();
854 SessionManager::singleton()->preventSessionsForUser( $user->getName() );
867 public static function whoIs( $id ) {
887 public static function idFromName( $name, $flags = self::READ_NORMAL ) {
889 $name = (string)$name;
890 $nt = Title::makeTitleSafe(
NS_USER, $name );
891 if ( $nt ===
null ) {
896 if ( !( $flags & self::READ_LATEST ) && array_key_exists( $name, self::$idCacheByName ) ) {
897 return self::$idCacheByName[$name] ===
null ? null : (int)self::$idCacheByName[$name];
900 list( $index, $options ) = DBAccessObjectUtils::getDBOptions( $flags );
906 [
'user_name' => $nt->getText() ],
911 if (
$s ===
false ) {
914 $result = (int)
$s->user_id;
917 if ( count( self::$idCacheByName ) >= 1000 ) {
918 self::$idCacheByName = [];
921 self::$idCacheByName[$name] = $result;
930 self::$idCacheByName = [];
951 public static function isIP( $name ) {
952 return preg_match(
'/^\d{1,3}\.\d{1,3}\.\d{1,3}\.(?:xxx|\d{1,3})$/', $name )
953 || IPUtils::isIPv6( $name );
964 return IPUtils::isValidRange( $this->mName );
980 return MediaWikiServices::getInstance()->getUserNameUtils()->isValid( $name );
996 return MediaWikiServices::getInstance()->getUserNameUtils()->isUsable( $name );
1010 if ( $groups === [] ) {
1011 return UserArrayFromResult::newFromIDs( [] );
1014 $groups = array_unique( (array)$groups );
1015 $limit = min( 5000, $limit );
1017 $conds = [
'ug_group' => $groups ];
1018 if ( $after !==
null ) {
1019 $conds[] =
'ug_user > ' . (int)$after;
1023 $ids =
$dbr->selectFieldValues(
1030 'ORDER BY' =>
'ug_user',
1051 return MediaWikiServices::getInstance()->getUserNameUtils()->isCreatable( $name );
1094 $status = Status::newGood( [] );
1097 if ( !$this->getHookRunner()->onIsValidPassword( $password, $result, $this ) ) {
1098 $status->error( $result );
1102 if ( $result ===
false ) {
1103 $status->merge( $upp->checkUserPassword( $this, $password ),
true );
1107 if ( $result ===
true ) {
1111 $status->error( $result );
1132 $validationLevels = [
1133 'valid' => UserNameUtils::RIGOR_VALID,
1134 'usable' => UserNameUtils::RIGOR_USABLE,
1135 'creatable' => UserNameUtils::RIGOR_CREATABLE
1138 if ( $validate ===
false ) {
1139 $validation = UserNameUtils::RIGOR_NONE;
1140 } elseif ( array_key_exists( $validate, $validationLevels ) ) {
1141 $validation = $validationLevels[ $validate ];
1145 $validation = $validate;
1148 return MediaWikiServices::getInstance()
1149 ->getUserNameUtils()
1150 ->getCanonical( (
string)$name, $validation );
1164 $this->mName = $name;
1165 $this->mActorId = $actorId;
1166 $this->mRealName =
'';
1169 $loggedOut = $this->mRequest && !defined(
'MW_NO_SESSION' )
1170 ? $this->mRequest->getSession()->getLoggedOutTimestamp() : 0;
1171 if ( $loggedOut !== 0 ) {
1172 $this->mTouched =
wfTimestamp( TS_MW, $loggedOut );
1174 $this->mTouched =
'1'; # Allow any pages to be cached
1177 $this->mToken =
null;
1178 $this->mEmailAuthenticated =
null;
1179 $this->mEmailToken =
'';
1180 $this->mEmailTokenExpires =
null;
1183 $this->getHookRunner()->onUserLoadDefaults( $this, $name );
1199 return ( $this->mLoadedItems ===
true && $all ===
'all' ) ||
1200 ( isset( $this->mLoadedItems[$item] ) && $this->mLoadedItems[$item] ===
true );
1209 if ( is_array( $this->mLoadedItems ) ) {
1210 $this->mLoadedItems[$item] =
true;
1222 $session = $this->
getRequest()->getSession();
1223 $user = $session->getUser();
1224 if ( $user->isLoggedIn() ) {
1228 $session->set(
'wsUserID', $this->
getId() );
1229 $session->set(
'wsUserName', $this->
getName() );
1230 $session->set(
'wsToken', $this->
getToken() );
1258 $this->mId = intval( $this->mId );
1260 if ( !$this->mId ) {
1266 list( $index, $options ) = DBAccessObjectUtils::getDBOptions( $flags );
1270 $s = $db->selectRow(
1271 $userQuery[
'tables'],
1272 $userQuery[
'fields'],
1273 [
'user_id' => $this->mId ],
1279 $this->queryFlagsUsed = $flags;
1280 $this->getHookRunner()->onUserLoadFromDatabase( $this,
$s );
1282 if (
$s !==
false ) {
1309 if ( !is_object( $row ) ) {
1310 throw new InvalidArgumentException(
'$row must be an object' );
1315 if ( isset( $row->actor_id ) ) {
1316 $this->mActorId = (int)$row->actor_id;
1317 if ( $this->mActorId !== 0 ) {
1318 $this->mFrom =
'actor';
1325 if ( isset( $row->user_name ) && $row->user_name !==
'' ) {
1326 $this->mName = $row->user_name;
1327 $this->mFrom =
'name';
1333 if ( isset( $row->user_real_name ) ) {
1334 $this->mRealName = $row->user_real_name;
1340 if ( isset( $row->user_id ) ) {
1341 $this->mId = intval( $row->user_id );
1342 if ( $this->mId !== 0 ) {
1343 $this->mFrom =
'id';
1350 if ( isset( $row->user_id ) && isset( $row->user_name ) && $row->user_name !==
'' ) {
1351 self::$idCacheByName[$row->user_name] = $row->user_id;
1354 if ( isset( $row->user_editcount ) ) {
1355 $this->mEditCount = $row->user_editcount;
1360 if ( isset( $row->user_touched ) ) {
1361 $this->mTouched =
wfTimestamp( TS_MW, $row->user_touched );
1366 if ( isset( $row->user_token ) ) {
1370 $this->mToken = rtrim( $row->user_token,
" \0" );
1371 if ( $this->mToken ===
'' ) {
1372 $this->mToken =
null;
1378 if ( isset( $row->user_email ) ) {
1379 $this->mEmail = $row->user_email;
1380 $this->mEmailAuthenticated =
wfTimestampOrNull( TS_MW, $row->user_email_authenticated );
1381 $this->mEmailToken = $row->user_email_token;
1382 $this->mEmailTokenExpires =
wfTimestampOrNull( TS_MW, $row->user_email_token_expires );
1389 $this->mLoadedItems =
true;
1392 if ( is_array( $data ) ) {
1394 if ( isset( $data[
'user_groups'] ) && is_array( $data[
'user_groups'] ) ) {
1395 MediaWikiServices::getInstance()
1396 ->getUserGroupManager()
1397 ->loadGroupMembershipsFromArray(
1399 $data[
'user_groups'],
1400 $this->queryFlagsUsed
1403 if ( isset( $data[
'user_properties'] ) && is_array( $data[
'user_properties'] ) ) {
1404 MediaWikiServices::getInstance()
1405 ->getUserOptionsManager()
1406 ->loadUserOptions( $this, $this->queryFlagsUsed, $data[
'user_properties'] );
1418 foreach ( self::$mCacheVars as $var ) {
1419 $this->$var = $user->$var;
1439 return MediaWikiServices::getInstance()
1440 ->getUserGroupManager()
1441 ->addUserToAutopromoteOnceGroups( $this, $event );
1454 if ( $this->mTouched ) {
1456 $conditions[
'user_touched'] = $db->
timestamp( $this->mTouched );
1475 if ( !$this->mId ) {
1483 $dbw->update(
'user',
1484 [
'user_touched' => $dbw->timestamp( $newTouched ) ],
1485 $this->makeUpdateConditions( $dbw, [
1486 'user_id' => $this->mId,
1490 $success = ( $dbw->affectedRows() > 0 );
1493 $this->mTouched = $newTouched;
1513 $this->mDatePreference =
null;
1514 $this->mBlockedby = -1; # Unset
1515 $this->mHash =
false;
1516 $this->mEditCount =
null;
1519 $services = MediaWikiServices::getInstance();
1520 $services->getPermissionManager()->invalidateUsersRightsCache( $this );
1521 $services->getUserOptionsManager()->clearUserOptionsCache( $this );
1522 $services->getTalkPageNotificationManager()->clearInstanceCache( $this );
1523 $services->getUserGroupManager()->clearCache( $this );
1524 $services->getUserEditTracker()->clearUserEditCache( $this );
1527 if ( $reloadFrom ) {
1528 $this->mLoadedItems = [];
1529 $this->mFrom = $reloadFrom;
1541 return MediaWikiServices::getInstance()
1542 ->getUserOptionsLookup()
1543 ->getDefaultOptions();
1554 return MediaWikiServices::getInstance()
1555 ->getUserOptionsLookup()
1556 ->getDefaultOption( $opt );
1570 private function getBlockedStatus( $fromReplica =
true, $disableIpBlockExemptChecking =
false ) {
1571 if ( $this->mBlockedby != -1 ) {
1575 wfDebug( __METHOD__ .
": checking blocked status for " . $this->
getName() );
1595 $sessionUser = RequestContext::getMain()->getUser();
1596 $globalUserName = $sessionUser->isSafeToLoad()
1597 ? $sessionUser->getName()
1598 : IPUtils::sanitizeIP( $sessionUser->getRequest()->getIP() );
1600 if ( $this->
getName() === $globalUserName ) {
1605 $block = MediaWikiServices::getInstance()->getBlockManager()->getUserBlock(
1609 $disableIpBlockExemptChecking
1613 $this->mBlock = $block;
1614 $this->mBlockedby = $block->getByName();
1615 $this->mBlockreason = $block->getReason();
1616 $this->mHideName = $block->getHideName();
1617 $this->mAllowUsertalk = $block->isUsertalkEditAllowed();
1619 $this->mBlock =
null;
1620 $this->mBlockedby =
'';
1621 $this->mBlockreason =
'';
1622 $this->mHideName = 0;
1623 $this->mAllowUsertalk =
false;
1637 return MediaWikiServices::getInstance()->getBlockManager()
1638 ->isDnsBlacklisted( $ip, $checkWhitelist );
1654 if ( IPUtils::isIPv4( $ip ) ) {
1656 $ipReversed = implode(
'.', array_reverse( explode(
'.', $ip ) ) );
1658 foreach ( (array)$bases as
$base ) {
1662 if ( is_array(
$base ) ) {
1663 if ( count(
$base ) >= 2 ) {
1665 $host =
"{$base[1]}.$ipReversed.{$base[0]}";
1667 $host =
"$ipReversed.{$base[0]}";
1669 $basename =
$base[0];
1671 $host =
"$ipReversed.$base";
1675 $ipList = gethostbynamel( $host );
1678 wfDebugLog(
'dnsblacklist',
"Hostname $host is {$ipList[0]}, it's a proxy says $basename!" );
1683 wfDebugLog(
'dnsblacklist',
"Requested $host, not found in $basename." );
1712 return $proxyListIPSet->match( $ip );
1728 return !$this->
isAllowed(
'noratelimit' );
1748 $logger = \MediaWiki\Logger\LoggerFactory::getInstance(
'ratelimit' );
1752 if ( !$this->getHookRunner()->onPingLimiter( $this, $action, $result, $incrBy ) ) {
1761 $limits = array_merge(
1762 [
'&can-bypass' =>
true ],
1771 $logger->debug( __METHOD__ .
": limiting $action rate for {$this->getName()}" );
1774 $id = $this->
getId();
1776 $cache = ObjectCache::getLocalClusterInstance();
1780 if ( isset( $limits[
'anon'] ) ) {
1781 $keys[
$cache->makeKey(
'limiter', $action,
'anon' )] = $limits[
'anon'];
1785 if ( isset( $limits[
'user-global'] ) ) {
1786 $lookup = CentralIdLookup::factoryNonLocal();
1788 $centralId = $lookup
1789 ? $lookup->centralIdFromLocalUser( $this, CentralIdLookup::AUDIENCE_RAW )
1794 $realm = $lookup->getProviderId();
1796 $globalKey =
$cache->makeGlobalKey(
'limiter', $action,
'user-global',
1797 $realm, $centralId );
1800 $globalKey =
$cache->makeKey(
'limiter', $action,
'user-global',
1803 $keys[$globalKey] = $limits[
'user-global'];
1809 if ( isset( $limits[
'ip'] ) ) {
1811 $keys[
$cache->makeGlobalKey(
'limiter', $action,
'ip', $ip )] = $limits[
'ip'];
1814 if ( isset( $limits[
'subnet'] ) ) {
1816 $subnet = IPUtils::getSubnet( $ip );
1817 if ( $subnet !==
false ) {
1818 $keys[
$cache->makeGlobalKey(
'limiter', $action,
'subnet', $subnet )] = $limits[
'subnet'];
1825 if ( $id !== 0 && isset( $limits[
'user'] ) ) {
1827 $userLimit = $limits[
'user'];
1830 if ( $id !== 0 && $isNewbie && isset( $limits[
'newbie'] ) ) {
1831 $userLimit = $limits[
'newbie'];
1835 foreach ( $this->
getGroups() as $group ) {
1836 if ( isset( $limits[$group] ) ) {
1837 if ( $userLimit ===
false
1838 || $limits[$group][0] / $limits[$group][1] > $userLimit[0] / $userLimit[1]
1840 $userLimit = $limits[$group];
1847 if ( $userLimit !==
false ) {
1851 list( $max, $period ) = $userLimit;
1852 $logger->debug( __METHOD__ .
": effective user limit: $max in {$period}s" );
1853 $keys[
$cache->makeKey(
'limiter', $action,
'user', $id )] = $userLimit;
1857 if ( isset( $limits[
'ip-all'] ) ) {
1860 if ( $isNewbie || $userLimit ===
false
1861 || $limits[
'ip-all'][0] / $limits[
'ip-all'][1] > $userLimit[0] / $userLimit[1] ) {
1862 $keys[
$cache->makeGlobalKey(
'limiter', $action,
'ip-all', $ip )] = $limits[
'ip-all'];
1867 if ( isset( $limits[
'subnet-all'] ) ) {
1869 $subnet = IPUtils::getSubnet( $ip );
1870 if ( $subnet !==
false ) {
1872 if ( $isNewbie || $userLimit ===
false
1873 || $limits[
'ip-all'][0] / $limits[
'ip-all'][1]
1874 > $userLimit[0] / $userLimit[1] ) {
1875 $keys[
$cache->makeGlobalKey(
'limiter', $action,
'subnet-all', $subnet )] = $limits[
'subnet-all'];
1883 $now = MWTimestamp::time();
1887 foreach (
$keys as $key => $limit ) {
1896 function (
$cache, $key, $data, &$expiry )
1897 use ( $action, $logger, &$triggered, $now, $clockFudge, $limit, $incrBy )
1902 list( $max, $period ) = $limit;
1904 $expiry = $now + (int)$period;
1911 $fields = explode(
'|', $data );
1912 $storedCount = (int)( $fields[0] ?? 0 );
1913 $storedExpiry = (int)( $fields[1] ?? PHP_INT_MAX );
1916 if ( $storedExpiry < ( $now + $clockFudge ) ) {
1918 'User::pingLimiter: '
1919 .
'Stale rate limit entry, cache key failed to expire (T246991)',
1921 'action' => $action,
1924 'period' => $period,
1925 'count' => $storedCount,
1927 'expiry' => MWTimestamp::convert( TS_DB, $storedExpiry ),
1933 $expiry = min( $storedExpiry, $now + (
int)$period );
1934 $count = $storedCount;
1939 if ( $count >= $max ) {
1940 if ( !$triggered ) {
1942 'User::pingLimiter: User tripped rate limit',
1944 'action' => $action,
1948 'period' => $period,
1959 $data =
"$count|$expiry";
1991 public function getBlock( $fromReplica =
true, $disableIpBlockExemptChecking =
false ) {
1993 return $this->mBlock instanceof
AbstractBlock ? $this->mBlock :
null;
2008 return MediaWikiServices::getInstance()->getPermissionManager()
2009 ->isBlockedFrom( $this,
$title, $fromReplica );
2038 return ( $this->mBlock ? $this->mBlock->getId() : false );
2064 if ( $this->mGlobalBlock !==
null ) {
2065 return $this->mGlobalBlock ?:
null;
2068 if ( IPUtils::isIPAddress( $this->
getName() ) ) {
2075 $this->getHookRunner()->onUserIsBlockedGlobally( $this, $ip, $blocked, $block );
2077 if ( $blocked && $block ===
null ) {
2081 'systemBlock' =>
'global-block'
2085 $this->mGlobalBlock = $blocked ? $block :
false;
2086 return $this->mGlobalBlock ?:
null;
2095 if ( $this->mLocked !==
null ) {
2099 $this->mLocked =
false;
2100 $this->getHookRunner()->onUserIsLocked( $this, $this->mLocked );
2110 if ( $this->mHideName !==
null ) {
2122 if ( $this->mId ===
null && $this->mName !==
null &&
2123 ( self::isIP( $this->mName ) || ExternalUserNames::isExternal( $this->mName ) )
2157 if ( $this->mName ===
false ) {
2159 $this->mName = IPUtils::sanitizeIP( $this->
getRequest()->getIP() );
2180 $this->mName = $str;
2194 if ( !$this->mActorId && $dbw ) {
2195 $migration = MediaWikiServices::getInstance()->getActorMigration();
2196 $this->mActorId = $migration->getNewActorId( $dbw, $this );
2210 return str_replace(
' ',
'_', $this->
getName() );
2220 return MediaWikiServices::getInstance()
2221 ->getTalkPageNotificationManager()
2222 ->userHasNewMessages( $this );
2244 if ( !$this->getHookRunner()->onUserRetrieveNewTalks( $this, $talks ) ) {
2248 $services = MediaWikiServices::getInstance();
2249 $userHasNewMessages = $services->getTalkPageNotificationManager()
2250 ->userHasNewMessages( $this );
2251 if ( !$userHasNewMessages ) {
2255 $timestamp = $services->getTalkPageNotificationManager()
2256 ->getLatestSeenMessageTimestamp( $this );
2259 $revRecord = $services->getRevisionLookup()
2260 ->getRevisionByTimestamp( $utp, $timestamp );
2267 'wiki' => WikiMap::getCurrentWikiId(),
2268 'link' => $utp->getLocalURL(),
2282 $newMessageRevisionId =
null;
2288 if ( $newMessageLinks && count( $newMessageLinks ) === 1
2289 && WikiMap::isCurrentWikiId( $newMessageLinks[0][
'wiki'] )
2290 && $newMessageLinks[0][
'rev']
2293 $newMessageRevision = $newMessageLinks[0][
'rev'];
2294 $newMessageRevisionId = $newMessageRevision->getId();
2297 return $newMessageRevisionId;
2310 if ( $curRev && $curRev instanceof
Revision ) {
2311 $curRev = $curRev->getRevisionRecord();
2314 MediaWikiServices::getInstance()
2315 ->getTalkPageNotificationManager()
2316 ->setUserHasNewMessages( $this, $curRev );
2318 MediaWikiServices::getInstance()
2319 ->getTalkPageNotificationManager()
2320 ->removeUserHasNewMessages( $this );
2332 if ( $this->mTouched ) {
2333 $time = max( $time,
wfTimestamp( TS_UNIX, $this->mTouched ) + 1 );
2350 if ( !$this->
getId() ) {
2354 $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
2355 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
2358 if ( $mode ===
'refresh' ) {
2359 $cache->delete( $key, 1 );
2361 $lb->getConnectionRef(
DB_MASTER )->onTransactionPreCommitOrIdle(
2362 function () use (
$cache, $key ) {
2393 $id = $this->
getId();
2395 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
2396 $key =
$cache->makeKey(
'user-quicktouched',
'id', $id );
2397 $cache->touchCheckKey( $key );
2398 $this->mQuickTouched =
null;
2408 return ( $timestamp >= $this->
getTouched() );
2423 if ( $this->mQuickTouched ===
null ) {
2424 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
2425 $key =
$cache->makeKey(
'user-quicktouched',
'id', $this->mId );
2430 return max( $this->mTouched, $this->mQuickTouched );
2460 $manager = MediaWikiServices::getInstance()->getAuthManager();
2461 $reqs = $manager->getAuthenticationRequests( AuthManager::ACTION_CHANGE, $this );
2462 $reqs = AuthenticationRequest::loadRequestsFromSubmission( $reqs, $data );
2464 $status = Status::newGood(
'ignored' );
2465 foreach ( $reqs as $req ) {
2466 $status->merge( $manager->allowsAuthenticationDataChange( $req ),
true );
2468 if ( $status->getValue() ===
'ignored' ) {
2469 $status->warning(
'authenticationdatachange-ignored' );
2472 if ( $status->isGood() ) {
2473 foreach ( $reqs as $req ) {
2474 $manager->changeAuthenticationData( $req );
2490 if ( !$this->mToken && $forceCreation ) {
2494 if ( !$this->mToken ) {
2499 if ( $this->mToken === self::INVALID_TOKEN ) {
2514 $len = max( 32, self::TOKEN_LENGTH );
2515 if ( strlen( $ret ) < $len ) {
2517 throw new \UnexpectedValueException(
'Hmac returned less than 128 bits' );
2520 return substr( $ret, -$len );
2531 if ( $this->mToken === self::INVALID_TOKEN ) {
2532 \MediaWiki\Logger\LoggerFactory::getInstance(
'session' )
2533 ->debug( __METHOD__ .
": Ignoring attempt to set token for system user \"$this\"" );
2534 } elseif ( !$token ) {
2537 $this->mToken = $token;
2547 $this->getHookRunner()->onUserGetEmail( $this, $this->mEmail );
2557 $this->getHookRunner()->onUserGetEmailAuthenticationTimestamp(
2558 $this, $this->mEmailAuthenticated );
2568 if ( $str == $this->mEmail ) {
2572 $this->mEmail = $str;
2573 $this->getHookRunner()->onUserSetEmail( $this, $this->mEmail );
2587 return Status::newFatal(
'emaildisabled' );
2591 if ( $str === $oldaddr ) {
2592 return Status::newGood(
true );
2595 $type = $oldaddr !=
'' ?
'changed' :
'set';
2596 $notificationResult =
null;
2601 $change = $str !=
'' ?
'changed' :
'removed';
2602 $notificationResult = $this->
sendMail(
2603 wfMessage(
'notificationemail_subject_' . $change )->text(),
2604 wfMessage(
'notificationemail_body_' . $change,
2617 if ( $notificationResult !==
null ) {
2618 $result->merge( $notificationResult );
2621 if ( $result->isGood() ) {
2623 $result->value =
'eauth';
2626 $result = Status::newGood(
true );
2650 $this->mRealName = $str;
2665 public function getOption( $oname, $defaultOverride =
null, $ignoreHidden =
false ) {
2666 if ( $oname ===
null ) {
2669 return MediaWikiServices::getInstance()
2670 ->getUserOptionsLookup()
2671 ->getOption( $this, $oname, $defaultOverride, $ignoreHidden );
2684 return MediaWikiServices::getInstance()
2685 ->getUserOptionsLookup()
2686 ->getOptions( $this, $flags );
2698 return MediaWikiServices::getInstance()
2699 ->getUserOptionsLookup()
2700 ->getBoolOption( $this, $oname );
2713 if ( $oname ===
null ) {
2716 return MediaWikiServices::getInstance()
2717 ->getUserOptionsLookup()
2718 ->getIntOption( $this, $oname, $defaultOverride );
2731 MediaWikiServices::getInstance()
2732 ->getUserOptionsManager()
2733 ->setOption( $this, $oname, $val );
2749 $id = $this->
getId();
2759 $token = hash_hmac(
'sha1',
"$oname:$id", $this->
getToken() );
2810 return MediaWikiServices::getInstance()
2811 ->getUserOptionsManager()
2812 ->listOptionKinds();
2829 return MediaWikiServices::getInstance()
2830 ->getUserOptionsManager()
2831 ->getOptionKinds( $this, $context, $options );
2850 $resetKinds = [
'registered',
'registered-multiselect',
'registered-checkmatrix',
'unused' ],
2853 MediaWikiServices::getInstance()
2854 ->getUserOptionsManager()
2857 $context ?? RequestContext::getMain(),
2868 if ( $this->mDatePreference ===
null ) {
2871 $map =
$wgLang->getDatePreferenceMigrationMap();
2872 if ( isset( $map[$value] ) ) {
2873 $value = $map[$value];
2875 $this->mDatePreference = $value;
2895 $this->getHookRunner()->onUserRequiresHTTPS( $this, $https );
2928 return MediaWikiServices::getInstance()->getPermissionManager()->getUserPermissions( $this );
2940 return MediaWikiServices::getInstance()
2941 ->getUserGroupManager()
2942 ->getUserGroups( $this, $this->queryFlagsUsed );
2955 return MediaWikiServices::getInstance()
2956 ->getUserGroupManager()
2957 ->getUserGroupMemberships( $this, $this->queryFlagsUsed );
2971 return MediaWikiServices::getInstance()
2972 ->getUserGroupManager()
2973 ->getUserEffectiveGroups( $this, $this->queryFlagsUsed, $recache );
2987 return MediaWikiServices::getInstance()
2988 ->getUserGroupManager()
2989 ->getUserImplicitGroups( $this, $this->queryFlagsUsed, $recache );
3004 return MediaWikiServices::getInstance()
3005 ->getUserGroupManager()
3006 ->getUserFormerGroups( $this, $this->queryFlagsUsed );
3014 if ( !$this->
getId() ) {
3018 if ( $this->mEditCount ===
null ) {
3019 $this->mEditCount = MediaWikiServices::getInstance()
3020 ->getUserEditTracker()
3021 ->getUserEditCount( $this );
3040 return MediaWikiServices::getInstance()
3041 ->getUserGroupManager()
3042 ->addUserToGroup( $this, $group, $expiry,
true );
3055 return MediaWikiServices::getInstance()
3056 ->getUserGroupManager()
3057 ->removeUserFromGroup( $this, $group );
3070 return $this->
getId() != 0;
3101 $this->getHookRunner()->onUserIsBot( $this, $isBot );
3117 if ( $this->mEmail || $this->mToken !== self::INVALID_TOKEN ||
3118 MediaWikiServices::getInstance()->getAuthManager()->userCanAuthenticate( $this->mName )
3135 return MediaWikiServices::getInstance()
3136 ->getPermissionManager()
3137 ->userHasAnyRight( $this, ...$permissions );
3147 return MediaWikiServices::getInstance()
3148 ->getPermissionManager()
3149 ->userHasAllRights( $this, ...$permissions );
3163 return MediaWikiServices::getInstance()->getPermissionManager()
3164 ->userHasRight( $this, $action );
3206 if ( $this->mRequest ) {
3223 if (
$title->isWatchable() && ( !$checkRights || $this->isAllowed(
'viewmywatchlist' ) ) ) {
3224 return MediaWikiServices::getInstance()->getWatchedItemStore()->isWatched( $this,
$title );
3240 if (
$title->isWatchable() && ( !$checkRights || $this->
isAllowed(
'viewmywatchlist' ) ) ) {
3241 return MediaWikiServices::getInstance()->getWatchedItemStore()
3242 ->isTempWatched( $this,
$title );
3258 $checkRights = self::CHECK_USER_RIGHTS,
3259 ?
string $expiry =
null
3261 if ( !$checkRights || $this->isAllowed(
'editmywatchlist' ) ) {
3262 $store = MediaWikiServices::getInstance()->getWatchedItemStore();
3263 $store->addWatch( $this,
$title->getSubjectPage(), $expiry );
3264 $store->addWatch( $this,
$title->getTalkPage(), $expiry );
3266 $this->invalidateCache();
3277 if ( !$checkRights || $this->isAllowed(
'editmywatchlist' ) ) {
3278 $store = MediaWikiServices::getInstance()->getWatchedItemStore();
3279 $store->removeWatch( $this,
$title->getSubjectPage() );
3280 $store->removeWatch( $this,
$title->getTalkPage() );
3282 $this->invalidateCache();
3297 MediaWikiServices::getInstance()
3298 ->getWatchlistNotificationManager()
3299 ->clearTitleUserNotifications( $this,
$title, $oldid );
3313 MediaWikiServices::getInstance()
3314 ->getWatchlistNotificationManager()
3315 ->clearAllUserNotifications( $this );
3329 if ( $this->isAnon() ) {
3333 $editCount = $this->getEditCount();
3334 $registration = $this->getRegistration();
3340 $registration > $learnerRegistration ) {
3345 $registration <= $experiencedRegistration
3347 return 'experienced';
3361 public function setCookies( $request =
null, $secure =
null, $rememberMe =
false ) {
3363 if ( $this->mId == 0 ) {
3367 $session = $this->getRequest()->getSession();
3368 if ( $request && $session->getRequest() !== $request ) {
3369 $session = $session->sessionWithRequest( $request );
3371 $delay = $session->delaySave();
3373 if ( !$session->getUser()->equals( $this ) ) {
3374 if ( !$session->canSetUser() ) {
3375 \MediaWiki\Logger\LoggerFactory::getInstance(
'session' )
3376 ->warning( __METHOD__ .
3377 ": Cannot save user \"$this\" to a user \"{$session->getUser()}\"'s immutable session"
3381 $session->setUser( $this );
3384 $session->setRememberUser( $rememberMe );
3385 if ( $secure !==
null ) {
3386 $session->setForceHTTPS( $secure );
3389 $session->persist();
3391 ScopedCallback::consume( $delay );
3400 if ( $this->getHookRunner()->onUserLogout( $user ) ) {
3410 $session = $this->getRequest()->getSession();
3411 if ( !$session->canSetUser() ) {
3412 \MediaWiki\Logger\LoggerFactory::getInstance(
'session' )
3413 ->warning( __METHOD__ .
": Cannot log out of an immutable session" );
3414 $error =
'immutable';
3415 } elseif ( !$session->getUser()->equals( $this ) ) {
3416 \MediaWiki\Logger\LoggerFactory::getInstance(
'session' )
3417 ->warning( __METHOD__ .
3418 ": Cannot log user \"$this\" out of a user \"{$session->getUser()}\"'s session"
3421 $this->clearInstanceCache(
'defaults' );
3422 $error =
'wronguser';
3424 $this->clearInstanceCache(
'defaults' );
3425 $delay = $session->delaySave();
3426 $session->unpersist();
3427 $session->setLoggedOutTimestamp( time() );
3428 $session->setUser(
new User );
3429 $session->set(
'wsUserID', 0 );
3430 $session->resetAllTokens();
3431 ScopedCallback::consume( $delay );
3434 \MediaWiki\Logger\LoggerFactory::getInstance(
'authevents' )->info(
'Logout', [
3435 'event' =>
'logout',
3436 'successful' => $error ===
false,
3437 'status' => $error ?:
'success',
3451 "Could not update user with ID '{$this->mId}'; DB is read-only."
3457 if ( $this->mId == 0 ) {
3464 $newTouched = $this->newTouchedTimestamp();
3467 $dbw->doAtomicSection( __METHOD__,
function (
IDatabase $dbw, $fname ) use ( $newTouched ) {
3470 'user_name' => $this->mName,
3471 'user_real_name' => $this->mRealName,
3472 'user_email' => $this->mEmail,
3473 'user_email_authenticated' => $dbw->
timestampOrNull( $this->mEmailAuthenticated ),
3474 'user_touched' => $dbw->
timestamp( $newTouched ),
3475 'user_token' => strval( $this->mToken ),
3476 'user_email_token' => $this->mEmailToken,
3477 'user_email_token_expires' => $dbw->
timestampOrNull( $this->mEmailTokenExpires ),
3478 ], $this->makeUpdateConditions( $dbw, [
3479 'user_id' => $this->mId,
3485 $this->clearSharedCache(
'refresh' );
3487 $from = ( $this->queryFlagsUsed & self::READ_LATEST ) ?
'master' :
'replica';
3488 LoggerFactory::getInstance(
'preferences' )->warning(
3489 "CAS update failed on user_touched for user ID '{user_id}' ({db_flag} read)",
3490 [
'user_id' => $this->mId,
'db_flag' => $from ]
3492 throw new MWException(
"CAS update failed on user_touched. " .
3493 "The version of the user to be saved is older than the current version."
3499 [
'actor_name' => $this->mName ],
3500 [
'actor_user' => $this->mId ],
3505 $this->mTouched = $newTouched;
3506 MediaWikiServices::getInstance()->getUserOptionsManager()->saveOptions( $this );
3508 $this->getHookRunner()->onUserSaveSettings( $this );
3509 $this->clearSharedCache(
'changed' );
3510 $hcu = MediaWikiServices::getInstance()->getHtmlCacheUpdater();
3511 $hcu->purgeTitleUrls( $this->getUserPage(), $hcu::PURGE_INTENT_TXROUND_REFLECTED );
3521 $s = trim( $this->getName() );
3526 $db = ( ( $flags & self::READ_LATEST ) == self::READ_LATEST )
3530 $options = ( ( $flags & self::READ_LOCKING ) == self::READ_LOCKING )
3531 ? [
'LOCK IN SHARE MODE' ]
3534 $id = $db->selectField(
'user',
3535 'user_id', [
'user_name' =>
$s ], __METHOD__, $options );
3556 foreach ( [
'password',
'newpassword',
'newpass_time',
'password_expires' ] as $field ) {
3557 if ( isset( $params[$field] ) ) {
3558 wfDeprecated( __METHOD__ .
" with param '$field'",
'1.27' );
3559 unset( $params[$field] );
3566 if ( isset( $params[
'options'] ) ) {
3567 MediaWikiServices::getInstance()
3568 ->getUserOptionsManager()
3569 ->loadUserOptions( $user, $user->queryFlagsUsed, $params[
'options'] );
3570 unset( $params[
'options'] );
3574 $noPass = PasswordFactory::newInvalidPassword()->toString();
3577 'user_name' => $name,
3578 'user_password' => $noPass,
3579 'user_newpassword' => $noPass,
3580 'user_email' => $user->mEmail,
3581 'user_email_authenticated' => $dbw->timestampOrNull( $user->mEmailAuthenticated ),
3582 'user_real_name' => $user->mRealName,
3583 'user_token' => strval( $user->mToken ),
3584 'user_registration' => $dbw->timestamp( $user->mRegistration ),
3585 'user_editcount' => 0,
3586 'user_touched' => $dbw->timestamp( $user->newTouchedTimestamp() ),
3588 foreach ( $params as $name => $value ) {
3589 $fields[
"user_$name"] = $value;
3592 return $dbw->doAtomicSection( __METHOD__,
function (
IDatabase $dbw, $fname ) use ( $fields ) {
3593 $dbw->
insert(
'user', $fields, $fname, [
'IGNORE' ] );
3595 $newUser = self::newFromId( $dbw->insertId() );
3596 $newUser->mName = $fields[
'user_name'];
3597 $newUser->updateActorId( $dbw );
3599 $newUser->load( self::READ_LATEST );
3635 if ( !$this->mToken ) {
3639 if ( !is_string( $this->mName ) ) {
3640 throw new RuntimeException(
"User name field is not set." );
3643 $this->mTouched = $this->newTouchedTimestamp();
3646 $status = $dbw->doAtomicSection( __METHOD__,
function (
IDatabase $dbw, $fname ) {
3647 $noPass = PasswordFactory::newInvalidPassword()->toString();
3650 'user_name' => $this->mName,
3651 'user_password' => $noPass,
3652 'user_newpassword' => $noPass,
3653 'user_email' => $this->mEmail,
3654 'user_email_authenticated' => $dbw->
timestampOrNull( $this->mEmailAuthenticated ),
3655 'user_real_name' => $this->mRealName,
3656 'user_token' => strval( $this->mToken ),
3657 'user_registration' => $dbw->
timestamp( $this->mRegistration ),
3658 'user_editcount' => 0,
3659 'user_touched' => $dbw->
timestamp( $this->mTouched ),
3668 [
'user_name' => $this->mName ],
3670 [
'LOCK IN SHARE MODE' ]
3673 if ( $this->mId && $this->loadFromDatabase( self::READ_LOCKING ) ) {
3677 throw new MWException( $fname .
": hit a key conflict attempting " .
3678 "to insert user '{$this->mName}' row, but it was not present in select!" );
3680 return Status::newFatal(
'userexists' );
3683 self::$idCacheByName[$this->mName] = $this->mId;
3684 $this->updateActorId( $dbw );
3686 return Status::newGood();
3688 if ( !$status->isGood() ) {
3693 $this->clearInstanceCache();
3695 MediaWikiServices::getInstance()->getUserOptionsManager()->saveOptions( $this );
3696 return Status::newGood();
3706 [
'actor_user' => $this->mId,
'actor_name' => $this->mName ],
3709 $this->mActorId = (int)$dbw->
insertId();
3718 if ( $this->isLoggedIn() && $this->getBlock() ) {
3719 return $this->spreadBlock();
3733 if ( $this->mId == 0 ) {
3737 $userblock = DatabaseBlock::newFromTarget( $this->getName() );
3738 if ( !$userblock ) {
3742 return (
bool)$userblock->doAutoblock( $this->getRequest()->getIP() );
3750 $this->getBlockedStatus();
3751 if ( $this->mBlock && $this->mBlock->appliesToRight(
'createaccount' ) ) {
3752 return $this->mBlock;
3755 # T15611: if the IP address the user is trying to create an account from is
3756 # blocked with createaccount disabled, prevent new account creation there even
3757 # when the user is logged in
3758 if ( $this->mBlockedFromCreateAccount ===
false && !$this->isAllowed(
'ipblock-exempt' ) ) {
3759 $this->mBlockedFromCreateAccount = DatabaseBlock::newFromTarget(
3760 null, $this->getRequest()->getIP()
3763 return $this->mBlockedFromCreateAccount instanceof
AbstractBlock
3764 && $this->mBlockedFromCreateAccount->
appliesToRight(
'createaccount' )
3765 ? $this->mBlockedFromCreateAccount
3774 $this->getBlockedStatus();
3775 return $this->mBlock && $this->mBlock->appliesToRight(
'sendemail' );
3785 $this->getBlockedStatus();
3786 return $this->mBlock && $this->mBlock->appliesToRight(
'upload' );
3794 return $this->isAllowed(
'createaccount' ) && !$this->isBlockedFromCreateAccount();
3803 return Title::makeTitle(
NS_USER, $this->getName() );
3812 $title = $this->getUserPage();
3813 return $title->getTalkPage();
3822 return !$this->isAllowed(
'autoconfirmed' );
3837 if ( $this->isAnon() ) {
3842 $request = $this->getRequest();
3844 return $request->getSession()->getToken( $salt );
3861 return $this->getEditTokenObject( $salt, $request )->toString();
3876 public function matchEditToken( $val, $salt =
'', $request =
null, $maxage =
null ) {
3877 return $this->getEditTokenObject( $salt, $request )->match( $val, $maxage );
3891 $val = substr( $val, 0, strspn( $val,
'0123456789abcdef' ) ) . Token::SUFFIX;
3892 return $this->matchEditToken( $val, $salt, $request, $maxage );
3905 $token = $this->confirmationToken( $expiration );
3906 $url = $this->confirmationTokenUrl( $token );
3907 $invalidateURL = $this->invalidationTokenUrl( $token );
3908 $this->saveSettings();
3910 if (
$type ==
'created' ||
$type ===
false ) {
3911 $message =
'confirmemail_body';
3913 } elseif (
$type ===
true ) {
3914 $message =
'confirmemail_body_changed';
3918 $message =
'confirmemail_body_' .
$type;
3922 'subject' =>
wfMessage(
'confirmemail_subject' )->text(),
3924 $this->getRequest()->getIP(),
3927 $wgLang->userTimeAndDate( $expiration, $this ),
3929 $wgLang->userDate( $expiration, $this ),
3930 $wgLang->userTime( $expiration, $this ) )->text(),
3936 'ip' => $this->getRequest()->getIP(),
3937 'confirmURL' => $url,
3938 'invalidateURL' => $invalidateURL,
3939 'expiration' => $expiration
3942 $this->getHookRunner()->onUserSendConfirmationMail( $this, $mail, $info );
3943 return $this->sendMail( $mail[
'subject'], $mail[
'body'], $mail[
'from'], $mail[
'replyTo'] );
3957 public function sendMail( $subject, $body, $from =
null, $replyto =
null ) {
3960 if ( $from instanceof
User ) {
3961 $sender = MailAddress::newFromUser( $from );
3964 wfMessage(
'emailsender' )->inContentLanguage()->text() );
3966 $to = MailAddress::newFromUser( $this );
3968 return UserMailer::send( $to, $sender, $subject, $body, [
3969 'replyTo' => $replyto,
3990 $hash = md5( $token );
3991 $this->mEmailToken = $hash;
3992 $this->mEmailTokenExpires = $expiration;
4002 return $this->getTokenUrl(
'ConfirmEmail', $token );
4011 return $this->getTokenUrl(
'InvalidateEmail', $token );
4030 $title = Title::makeTitle(
NS_MAIN,
"Special:$page/$token" );
4031 return $title->getCanonicalURL();
4044 if ( !$this->isEmailConfirmed() ) {
4046 $this->getHookRunner()->onConfirmEmailComplete( $this );
4060 $this->mEmailToken =
null;
4061 $this->mEmailTokenExpires =
null;
4062 $this->setEmailAuthenticationTimestamp(
null );
4064 $this->getHookRunner()->onInvalidateEmailComplete( $this );
4074 $this->mEmailAuthenticated = $timestamp;
4075 $this->getHookRunner()->onUserSetEmailAuthenticationTimestamp(
4076 $this, $this->mEmailAuthenticated );
4089 $canSend = $this->isEmailConfirmed();
4090 $this->getHookRunner()->onUserCanSendEmail( $this, $canSend );
4100 return $this->isEmailConfirmed() && !$this->getOption(
'disablemail' );
4119 if ( $this->getHookRunner()->onEmailConfirmed( $user, $confirmed ) ) {
4120 if ( $this->isAnon() ) {
4123 if ( !Sanitizer::validateEmail( $this->mEmail ) ) {
4142 !$this->isEmailConfirmed() &&
4143 $this->mEmailToken &&
4155 if ( $this->isAnon() ) {
4159 return $this->mRegistration;
4169 return MediaWikiServices::getInstance()
4170 ->getUserEditTracker()
4171 ->getFirstEditTimestamp( $this );
4182 return MediaWikiServices::getInstance()
4183 ->getUserEditTracker()
4184 ->getLatestEditTimestamp( $this );
4197 return MediaWikiServices::getInstance()->getPermissionManager()->getGroupPermissions( $groups );
4210 return MediaWikiServices::getInstance()->getPermissionManager()->getGroupsWithPermission( $role );
4229 return MediaWikiServices::getInstance()->getPermissionManager()
4230 ->groupHasPermission( $group, $role );
4252 return MediaWikiServices::getInstance()->getPermissionManager()->isEveryoneAllowed( $right );
4263 return MediaWikiServices::getInstance()
4264 ->getUserGroupManager()
4277 return MediaWikiServices::getInstance()->getPermissionManager()->getAllPermissions();
4286 return MediaWikiServices::getInstance()
4287 ->getUserGroupManager()
4288 ->listAllImplicitGroups();
4315 $groups[
'add'] = self::getAllGroups();
4324 $groups[
'remove'] = self::getAllGroups();
4332 if ( is_int( $key ) ) {
4340 if ( is_int( $key ) ) {
4351 $groups[
'add-self'] = self::getAllGroups();
4359 $groups[
'remove-self'] = self::getAllGroups();
4375 if ( $this->isAllowed(
'userrights' ) ) {
4380 $all = array_merge( self::getAllGroups() );
4396 $addergroups = $this->getEffectiveGroups();
4398 foreach ( $addergroups as $addergroup ) {
4399 $groups = array_merge_recursive(
4400 $groups, $this->changeableByGroup( $addergroup )
4402 $groups[
'add'] = array_unique( $groups[
'add'] );
4403 $groups[
'remove'] = array_unique( $groups[
'remove'] );
4404 $groups[
'add-self'] = array_unique( $groups[
'add-self'] );
4405 $groups[
'remove-self'] = array_unique( $groups[
'remove-self'] );
4414 if ( $this->isAnon() ) {
4418 DeferredUpdates::addUpdate(
4420 DeferredUpdates::POSTSEND
4430 $this->mEditCount = $count;
4441 return MediaWikiServices::getInstance()
4442 ->getUserEditTracker()
4443 ->initializeUserEditCount( $this );
4454 $key =
"right-$right";
4456 return $msg->isDisabled() ? $right : $msg->text();
4467 $key =
"grant-$grant";
4469 return $msg->isDisabled() ? $grant : $msg->text();
4507 'tables' => [
'user',
'user_actor' =>
'actor' ],
4515 'user_email_authenticated',
4517 'user_email_token_expires',
4518 'user_registration',
4520 'user_actor.actor_id',
4523 'user_actor' => [
'JOIN',
'user_actor.actor_user = user_id' ],
4541 foreach ( MediaWikiServices::getInstance()
4543 ->getGroupsWithPermission( $permission ) as $group ) {
4544 $groups[] = UserGroupMembership::getLink( $group, RequestContext::getMain(),
'wiki' );
4548 return Status::newFatal(
'badaccess-groups',
$wgLang->commaList( $groups ), count( $groups ) );
4551 return Status::newFatal(
'badaccess-group0' );
4564 if ( !$this->getId() ) {
4568 $user = self::newFromId( $this->getId() );
4569 if ( !$user->loadFromId( self::READ_EXCLUSIVE ) ) {
4585 return $this->getName() === $user->
getName();
4594 return $this->mAllowUsertalk;
$wgRateLimitsExcludedIPs
Array of IPs / CIDR ranges which should be excluded from rate limits.
$wgUserEmailConfirmationTokenExpiry
The time, in seconds, when an email confirmation email expires.
$wgMaxArticleSize
Maximum article size in kilobytes.
$wgLearnerMemberSince
Specify the difference engine to use.
$wgProxyList
Big list of banned IP addresses.
$wgHiddenPrefs
An array of preferences to not show for the user.
$wgEnableUserEmail
Set to true to enable user-to-user e-mail.
$wgPasswordPolicy
Password policy for the wiki.
$wgExperiencedUserMemberSince
Specify the difference engine to use.
$wgUseFilePatrol
Use file patrolling to check new files on Special:Newfiles.
string null $wgAuthenticationTokenVersion
Versioning for authentication tokens.
$wgGroupsAddToSelf
A map of group names that the user is in, to group names that those users are allowed to add or revok...
$wgUseRCPatrol
Use RC Patrolling to check for vandalism (from recent changes and watchlists) New pages and new files...
$wgLearnerEdits
The following variables define 3 user experience levels:
$wgUseNPPatrol
Use new page patrolling to check new pages on Special:Newpages.
$wgEnableEmail
Set to true to enable the e-mail basic features: Password reminders, etc.
$wgSecureLogin
This is to let user authenticate using https when they come from http.
$wgAddGroups
$wgAddGroups and $wgRemoveGroups can be used to give finer control over who can assign which groups a...
$wgExperiencedUserEdits
Specify the difference engine to use.
bool $wgForceHTTPS
If this is true, when an insecure HTTP request is received, always redirect to HTTPS.
$wgEmailAuthentication
Require email authentication before sending mail to an email address.
$wgPasswordSender
Sender email address for e-mail notifications.
$wgRateLimits
Simple rate limiter options to brake edit floods.
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfTimestampOrNull( $outputtype=TS_UNIX, $ts=null)
Return a formatted timestamp, or null if input is null.
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
wfReadOnly()
Check whether the wiki is in read-only mode.
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
wfCanIPUseHTTPS( $ip)
Determine whether the client at a given source IP is likely to be able to access the wiki via HTTPS.
wfLogWarning( $msg, $callerOffset=1, $level=E_USER_WARNING)
Send a warning as a PHP error and the debug log.
wfDebugLog( $logGroup, $text, $dest='all', array $context=[])
Send a line to a supplementary debug log file, if configured, or main debug log if not.
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 $function is deprecated.
foreach( $wgExtensionFunctions as $func) if(!defined('MW_NO_SESSION') &&! $wgCommandLineMode) if(! $wgCommandLineMode) $wgFullyInitialised
if(! $wgDBerrorLogTZ) $wgRequest
if(ini_get('mbstring.func_overload')) if(!defined('MW_ENTRY_POINT'))
Pre-config setup: Before loading LocalSettings.php.
Value object representing a logged-out user's edit token.
static generateHex( $chars)
Generate a run of cryptographically random data and return it in hexadecimal string format.
Stores a single person's name and email address.
Handles increment the edit count for a given set of users.
Check if a user's password complies with any password policies that apply to that user,...
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
loadFromSession()
Load user data from the session.
setEditCountInternal( $count)
This method should not be called outside User/UserEditCountUpdate.
string $mTouched
TS_MW timestamp from the DB.
logout()
Log this user out.
getOptions( $flags=0)
Get all user's options.
getRequest()
Get the WebRequest object to use with this object.
getName()
Get the user name, or the IP of an anonymous user.
addToDatabase()
Add this existing user object to the database.
requiresHTTPS()
Determine based on the wiki configuration and the user's options, whether this user must be over HTTP...
updateActorId(IDatabase $dbw)
Update the actor ID after an insert.
getExperienceLevel()
Compute experienced level based on edit count and registration date.
static isEveryoneAllowed( $right)
Check if all users may be assumed to have the given permission.
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
resetOptions( $resetKinds=[ 'registered', 'registered-multiselect', 'registered-checkmatrix', 'unused'], IContextSource $context=null)
Reset certain (or all) options to the site defaults.
static whoIsReal( $id)
Get the real name of a user given their user ID.
invalidateCache()
Immediately touch the user data cache for this account.
getEmailAuthenticationTimestamp()
Get the timestamp of the user's e-mail authentication.
isBlockedFromEmailuser()
Get whether the user is blocked from using Special:Emailuser.
static getQueryInfo()
Return the tables, fields, and join conditions to be selected to create a new user object.
static isCreatableName( $name)
Usernames which fail to pass this function will be blocked from new account registrations,...
getFirstEditTimestamp()
Get the timestamp of the first edit.
getOptionKinds(IContextSource $context, $options=null)
Return an associative array mapping preferences keys to the kind of a preference they're used for.
AbstractBlock null $mBlock
static purge( $dbDomain, $userId)
static changeableByGroup( $group)
Returns an array of the groups that a particular group can add/remove.
const VERSION
Version number to tag cached versions of serialized User objects.
getEditTokenObject( $salt='', $request=null)
Initialize (if necessary) and return a session token value which can be used in edit forms to show th...
static getAllGroups()
Return the set of defined explicit groups.
string $mEmailTokenExpires
isAllowUsertalk()
Checks if usertalk is allowed.
string $mFrom
Initialization data source if mLoadedItems!==true.
setCookies( $request=null, $secure=null, $rememberMe=false)
Persist this user's session (e.g.
getEditToken( $salt='', $request=null)
Initialize (if necessary) and return a session token value which can be used in edit forms to show th...
isDnsBlacklisted( $ip, $checkWhitelist=false)
Whether the given IP is in a DNS blacklist.
int $queryFlagsUsed
User::READ_* constant bitfield used to load data.
equals(UserIdentity $user)
Checks if two user objects point to the same user.
getTokenUrl( $page, $token)
Internal function to format the e-mail validation/invalidation URLs.
addGroup( $group, $expiry=null)
Add the user to the given group.
getBlockedStatus( $fromReplica=true, $disableIpBlockExemptChecking=false)
Get blocking information.
isRegistered()
Alias of isLoggedIn() with a name that describes its actual functionality.
isBlockedGlobally( $ip='')
Check if user is blocked on all wikis.
string $mQuickTouched
TS_MW timestamp from cache.
const INVALID_TOKEN
An invalid string value for the user_token field.
isSafeToLoad()
Test if it's safe to load this User object.
static groupHasPermission( $group, $role)
Check, if the given group has the given permission.
isEmailConfirmed()
Is this user's e-mail address valid-looking and confirmed within limits of the current site configura...
spreadAnyEditBlock()
If this user is logged-in and blocked, block any IP address they've successfully logged in from.
useFilePatrol()
Check whether to enable new files patrol features for this user.
loadFromId( $flags=self::READ_NORMAL)
Load user table data, given mId has already been set.
isAllowed( $action='')
Internal mechanics of testing a permission.
getDBTouched()
Get the user_touched timestamp field (time of last DB updates)
setName( $str)
Set the user name.
changeAuthenticationData(array $data)
Changes credentials of the user.
getId()
Get the user's ID.
getRealName()
Get the user's real name.
getBoolOption( $oname)
Get the user's current setting for a given option, as a boolean value.
getRegistration()
Get the timestamp of account creation.
static isLocallyBlockedProxy( $ip)
Check if an IP address is in the local proxy list.
getGlobalBlock( $ip='')
Check if user is blocked on all wikis.
isAllowedAny(... $permissions)
Check if user is allowed to access a feature / make an action.
isItemLoaded( $item, $all='all')
Return whether an item has been loaded.
getMutableCacheKeys(WANObjectCache $cache)
getTokenFromOption( $oname)
Get a token stored in the preferences (like the watchlist one), resetting it if it's empty (and savin...
isNewbie()
Determine whether the user is a newbie.
clearNotification(&$title, $oldid=0)
Clear the user's notification timestamp for the given title.
static resetIdByNameCache()
Reset the cache used in idFromName().
loadFromDatabase( $flags=self::READ_LATEST)
Load user data from the database.
invalidationTokenUrl( $token)
Return a URL the user can use to invalidate their email address.
useNPPatrol()
Check whether to enable new pages patrol features for this user.
getIntOption( $oname, $defaultOverride=0)
Get the user's current setting for a given option, as an integer value.
clearInstanceCache( $reloadFrom=false)
Clear various cached data stored in this object.
canReceiveEmail()
Is this user allowed to receive e-mails within limits of current site configuration?
trackBlockWithCookie()
Set the 'BlockID' cookie depending on block type and user authentication status.
getOption( $oname, $defaultOverride=null, $ignoreHidden=false)
Get the user's current setting for a given option.
touch()
Update the "touched" timestamp for the user.
isPingLimitable()
Is this user subject to rate limiting?
static string[] $mCacheVars
Array of Strings List of member variables which are saved to the shared cache (memcached).
static getCanonicalName( $name, $validate='valid')
Given unvalidated user input, return a canonical username, or false if the username is invalid.
static newFromRow( $row, $data=null)
Create a new user object from a user row.
getToken( $forceCreation=true)
Get the user's current token.
static newFromId( $id)
Static factory method for creation from a given user ID.
array bool $mLoadedItems
Array with already loaded items or true if all items have been loaded.
confirmEmail()
Mark the e-mail address confirmed.
static getGroupsWithPermission( $role)
Get all the groups who have a given permission.
setId( $v)
Set the user and reload all fields according to a given ID.
static getGroupPermissions( $groups)
Get the permissions associated with a given list of groups.
static getDefaultOptions()
Combine the language default options with any site-specific options and add the default language vari...
static findUsersByGroup( $groups, $limit=5000, $after=null)
Return the users who are members of the given group(s).
isSystemUser()
Get whether the user is a system user.
getEffectiveGroups( $recache=false)
Get the list of implicit group memberships this user has.
isHidden()
Check if user account is hidden.
static newFromConfirmationCode( $code, $flags=0)
Factory method to fetch whichever user has a given email confirmation code.
setEmailWithConfirmation( $str)
Set the user's e-mail address and a confirmation mail if needed.
newTouchedTimestamp()
Generate a current or new-future timestamp to be stored in the user_touched field when we update thin...
inDnsBlacklist( $ip, $bases)
Whether the given IP is in a given DNS blacklist.
loadFromUserObject( $user)
Load the data for this user object from another user object.
static isUsableName( $name)
Usernames which fail to pass this function will be blocked from user login and new account registrati...
static getDefaultOption( $opt)
Get a given default option value.
getDatePreference()
Get the user's preferred date format.
checkPasswordValidity( $password)
Check if this is a valid password for this user.
idForName( $flags=0)
If only this user's username is known, and it exists, return the user ID.
getGroupMemberships()
Get the list of explicit group memberships this user has, stored as UserGroupMembership objects.
matchEditTokenNoSuffix( $val, $salt='', $request=null, $maxage=null)
Check given value against the token value stored in the session, ignoring the suffix.
isWatched( $title, $checkRights=self::CHECK_USER_RIGHTS)
Check the watched status of an article.
loadFromRow( $row, $data=null)
Initialize this object from a row from the user table.
static newFromAnyId( $userId, $userName, $actorId, $dbDomain=false)
Static factory method for creation from an ID, name, and/or actor ID.
confirmationTokenUrl( $token)
Return a URL the user can use to confirm their email address.
isAllowedToCreateAccount()
Get whether the user is allowed to create an account.
static newFromSession(WebRequest $request=null)
Create a new user object using data from session.
static getAllRights()
Get a list of all available permissions.
getNewtalk()
Check if the user has new messages.
getGroups()
Get the list of explicit group memberships this user has.
addNewUserLogEntry( $action=false, $reason='')
Add a newuser log entry for this user.
validateCache( $timestamp)
Validate the cache for this account.
useRCPatrol()
Check whether to enable recent changes patrol features for this user.
makeUpdateConditions(IDatabase $db, array $conditions)
Builds update conditions.
static whoIs( $id)
Get the username corresponding to a given user ID.
invalidateEmail()
Invalidate the user's e-mail confirmation, and unauthenticate the e-mail address if it was already co...
setEmailAuthenticationTimestamp( $timestamp)
Set the e-mail authentication timestamp.
static newSystemUser( $name, $options=[])
Static factory method for creation of a "system" user from username.
setOption( $oname, $val)
Set the given option for a user.
static getRightDescription( $right)
Get the description of a given right.
isAllowedAll(... $permissions)
getEditCount()
Get the user's edit count.
getActorId(IDatabase $dbw=null)
Get the user's actor ID.
addAutopromoteOnceGroups( $event)
Add the user to the group if he/she meets given criteria.
clearSharedCache( $mode='refresh')
Clear user data from memcached.
isValidPassword( $password)
Is the input a valid password for this user?
isBlockedFromCreateAccount()
Get whether the user is explicitly blocked from account creation.
spreadBlock()
If this (non-anonymous) user is blocked, block the IP address they've successfully logged in from.
getFormerGroups()
Returns the groups the user has belonged to.
setRealName( $str)
Set the user's real name.
getLatestEditTimestamp()
Get the timestamp of the latest edit.
static newFromIdentity(UserIdentity $identity)
Returns a User object corresponding to the given UserIdentity.
AbstractBlock $mGlobalBlock
getTitleKey()
Get the user's name escaped by underscores.
static isIP( $name)
Does the string match an anonymous IP address?
getTouched()
Get the user touched timestamp.
sendMail( $subject, $body, $from=null, $replyto=null)
Send an e-mail to this user's account.
isLocked()
Check if user account is locked.
checkAndSetTouched()
Bump user_touched if it didn't change since this object was loaded.
removeWatch( $title, $checkRights=self::CHECK_USER_RIGHTS)
Stop watching an article.
isEmailConfirmationPending()
Check whether there is an outstanding request for e-mail confirmation.
getUserPage()
Get this user's personal page title.
isIPRange()
Is the user an IP range?
confirmationToken(&$expiration)
Generate, store, and return a new e-mail confirmation code.
canSendEmail()
Is this user allowed to send e-mails within limits of current site configuration?
getBlockId()
If user is blocked, return the ID for the block.
__construct()
Lightweight constructor for an anonymous user.
setNewtalk( $val, $curRev=null)
Update the 'You have new messages!' status.
getStubThreshold()
Get the user preferred stub threshold.
pingLimiter( $action='edit', $incrBy=1)
Primitive rate limits: enforce maximum actions per time period to put a brake on flooding.
string $mDatePreference
Lazy-initialized variables, invalidated with clearInstanceCache.
static isValidUserName( $name)
Is the input a valid username?
sendConfirmationMail( $type='created')
Generate a new e-mail confirmation token and send a confirmation/invalidation mail to the user's give...
getTalkPage()
Get this user's talk page title.
isTempWatched( $title, $checkRights=self::CHECK_USER_RIGHTS)
Check if the article is temporarily watched.
addWatch( $title, $checkRights=self::CHECK_USER_RIGHTS, ?string $expiry=null)
Watch an article.
isLoggedIn()
Get whether the user is registered.
static idFromName( $name, $flags=self::READ_NORMAL)
Get database id given a user name.
loadFromCache()
Load user data from shared cache, given mId has already been set.
getCacheKey(WANObjectCache $cache)
saveSettings()
Save this user's settings into the database.
getNewMessageRevisionId()
Get the revision ID for the last talk page revision viewed by the talk page owner.
static getGrantName( $grant)
Get the name of a given grant.
getEmail()
Get the user's e-mail address.
static newFatalPermissionDeniedStatus( $permission)
Factory function for fatal permission-denied errors.
static int[] $idCacheByName
getAutomaticGroups( $recache=false)
Get the list of implicit group memberships this user has.
isBlockedFromUpload()
Get whether the user is blocked from using Special:Upload.
load( $flags=self::READ_NORMAL)
Load the user table data for this object from the source given by mFrom.
blockedBy()
If user is blocked, return the name of the user who placed the block.
getRights()
Get the permissions this user has.
matchEditToken( $val, $salt='', $request=null, $maxage=null)
Check given value against the token value stored in the session.
string $mEmailAuthenticated
loadDefaults( $name=false, $actorId=null)
Set cached properties to default.
const GETOPTIONS_EXCLUDE_DEFAULTS
Exclude user options that are set to their default value.
const TOKEN_LENGTH
Number of characters required for the user_token field.
setToken( $token=false)
Set the random token (used for persistent authentication) Called from loadDefaults() among other plac...
static createNew( $name, $params=[])
Add a user to the database, return the user object.
getBlock( $fromReplica=true, $disableIpBlockExemptChecking=false)
Get the block affecting the user, or null if the user is not blocked.
doLogout()
Clear the user's session, and reset the instance cache.
setItemLoaded( $item)
Set that an item has been loaded.
AbstractBlock bool $mBlockedFromCreateAccount
incEditCount()
Schedule a deferred update to update the user's edit count.
blockedFor()
If user is blocked, return the specified reason for the block.
static listOptionKinds()
Return a list of the types of user options currently returned by User::getOptionKinds().
resetTokenFromOption( $oname)
Reset a token stored in the preferences (like the watchlist one).
static getImplicitGroups()
Get a list of implicit groups.
changeableGroups()
Returns an array of groups that this user can add and remove.
isBlocked( $fromReplica=true)
Check if user is blocked.
getNewMessageLinks()
Return the data needed to construct links for new talk page message alerts.
isAnon()
Get whether the user is anonymous.
setEmail( $str)
Set the user's e-mail address.
string $mBlockreason
TODO: This should be removed when User::BlockedFor and AbstractBlock::getReason are hard deprecated.
getInstanceForUpdate()
Get a new instance of this user that was loaded from the master via a locking read.
isBlockedFrom( $title, $fromReplica=false)
Check if user is blocked from editing a particular article.
static newFromActorId( $id)
Static factory method for creation from a given actor ID.
clearAllNotifications()
Resets all of the given user's page-change notification timestamps.
initEditCountInternal(IDatabase $dbr)
Initialize user_editcount from data out of the revision table.
removeGroup( $group)
Remove the user from the given group.
Multi-datacenter aware caching interface.
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form stripping il...
Interface for objects which can provide a MediaWiki context on request.
Interface for database access objects.