34use Wikimedia\Assert\Assert;
36use Wikimedia\ScopedCallback;
100 'mEmailAuthenticated',
102 'mEmailTokenExpires',
240 return (
string)$this->
getName();
245 if ( $name ===
'mRights' ) {
248 } elseif ( !property_exists( $this, $name ) ) {
260 public function __set( $name, $value ) {
263 if ( $name ===
'mRights' ) {
264 MediaWikiServices::getInstance()->getPermissionManager()->overrideUserRightsForTesting(
266 is_null( $value ) ? [] : $value
268 } elseif ( !property_exists( $this, $name ) ) {
269 $this->$name = $value;
298 $this->mLoadedItems ===
true || $this->mFrom !==
'session';
306 public function load( $flags = self::READ_NORMAL ) {
309 if ( $this->mLoadedItems ===
true ) {
314 $oldLoadedItems = $this->mLoadedItems;
315 $this->mLoadedItems =
true;
316 $this->queryFlagsUsed = $flags;
320 \MediaWiki\Logger\LoggerFactory::getInstance(
'session' )
321 ->warning(
'User::loadFromSession called before the end of Setup.php', [
322 'exception' =>
new Exception(
'User::loadFromSession called before the end of Setup.php' ),
325 $this->mLoadedItems = $oldLoadedItems;
329 switch ( $this->mFrom ) {
335 if ( $this->mId != 0 ) {
336 $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
337 if ( $lb->hasOrMadeRecentMasterChanges() ) {
338 $flags |= self::READ_LATEST;
339 $this->queryFlagsUsed = $flags;
348 $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
349 if ( $lb->hasOrMadeRecentMasterChanges() ) {
350 $flags |= self::READ_LATEST;
351 $this->queryFlagsUsed = $flags;
354 list( $index, $options ) = DBAccessObjectUtils::getDBOptions( $flags );
355 $row =
wfGetDB( $index )->selectRow(
357 [
'actor_id',
'actor_user',
'actor_name' ],
358 $this->mFrom ===
'name' ? [
'actor_name' => $this->mName ] : [
'actor_id' => $this->mActorId ],
365 $this->
loadDefaults( $this->mFrom ===
'name' ? $this->mName : false );
366 } elseif ( $row->actor_user ) {
367 $this->mId = $row->actor_user;
370 $this->
loadDefaults( $row->actor_name, $row->actor_id );
378 Hooks::run(
'UserLoadAfterLoadFromSession', [ $this ] );
381 throw new UnexpectedValueException(
382 "Unrecognised value for User->mFrom: \"{$this->mFrom}\"" );
392 if ( $this->mId == 0 ) {
400 $latest = DBAccessObjectUtils::hasFlags( $flags, self::READ_LATEST );
410 $this->mLoadedItems =
true;
411 $this->queryFlagsUsed = $flags;
421 public static function purge( $dbDomain, $userId ) {
422 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
423 $key =
$cache->makeGlobalKey(
'user',
'id', $dbDomain, $userId );
433 $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
435 return $cache->makeGlobalKey(
'user',
'id', $lbFactory->getLocalDomainID(), $this->mId );
444 $id = $this->
getId();
456 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
457 $data =
$cache->getWithSetCallback(
460 function ( $oldValue, &$ttl, array &$setOpts ) use (
$cache ) {
462 wfDebug(
"User: cache miss for user {$this->mId}\n" );
469 foreach ( self::$mCacheVars as $name ) {
470 $data[$name] = $this->$name;
477 foreach ( $this->mGroupMemberships as $ugm ) {
478 if ( $ugm->getExpiry() ) {
479 $secondsUntilExpiry =
wfTimestamp( TS_UNIX, $ugm->getExpiry() ) - time();
480 if ( $secondsUntilExpiry > 0 && $secondsUntilExpiry < $ttl ) {
481 $ttl = $secondsUntilExpiry;
488 [
'pcTTL' => $cache::TTL_PROC_LONG,
'version' => self::VERSION ]
492 foreach ( self::$mCacheVars as $name ) {
493 $this->$name = $data[$name];
518 public static function newFromName( $name, $validate =
'valid' ) {
519 if ( $validate ===
true ) {
522 $name = self::getCanonicalName( $name, $validate );
523 if ( $name ===
false ) {
575 if ( $identity instanceof
User ) {
579 return self::newFromAnyId(
580 $identity->
getId() === 0 ?
null : $identity->
getId(),
599 public static function newFromAnyId( $userId, $userName, $actorId, $dbDomain =
false ) {
603 if ( $dbDomain !==
false ) {
609 $user->mFrom =
'defaults';
611 if ( $actorId !==
null ) {
612 $user->mActorId = (int)$actorId;
613 if ( $user->mActorId !== 0 ) {
614 $user->mFrom =
'actor';
619 if ( $userName !==
null && $userName !==
'' ) {
620 $user->mName = $userName;
621 $user->mFrom =
'name';
622 $user->setItemLoaded(
'name' );
625 if ( $userId !==
null ) {
626 $user->mId = (int)$userId;
627 if ( $user->mId !== 0 ) {
630 $user->setItemLoaded(
'id' );
633 if ( $user->mFrom ===
'defaults' ) {
634 throw new InvalidArgumentException(
635 'Cannot create a user with no name, no ID, and no actor ID'
654 $db = ( $flags & self::READ_LATEST ) == self::READ_LATEST
658 $id = $db->selectField(
662 'user_email_token' => md5( $code ),
663 'user_email_token_expires > ' . $db->addQuotes( $db->timestamp() ),
667 return $id ? self::newFromId( $id ) :
null;
679 $user->mFrom =
'session';
680 $user->mRequest = $request;
742 'validate' =>
'valid',
747 $name = self::getCanonicalName( $name, $options[
'validate'] );
748 if ( $name ===
false ) {
753 $userQuery = self::getQueryInfo();
754 $row =
$dbr->selectRow(
755 $userQuery[
'tables'],
756 $userQuery[
'fields'],
757 [
'user_name' => $name ],
765 $row = $dbw->selectRow(
766 $userQuery[
'tables'],
767 $userQuery[
'fields'],
768 [
'user_name' => $name ],
777 if ( !$options[
'create'] ) {
784 if ( !self::isValidUserName( $name ) || self::isUsableName( $name ) ) {
786 return self::createNew( $name, [
'token' => self::INVALID_TOKEN ] );
791 return $dbw->doAtomicSection( __METHOD__,
function (
IDatabase $dbw, $fname ) use ( $name ) {
795 [
'actor_name' => $name,
'actor_user' =>
null ],
801 return self::createNew( $name, [
'token' => self::INVALID_TOKEN ] );
807 $dbw->
delete(
'actor', [
'actor_id' => $row->actor_id ], $fname );
808 $user = self::createNew( $name, [
'token' => self::INVALID_TOKEN ] );
811 [
'actor_id' => $row->actor_id ],
812 [
'actor_id' => $user->getActorId() ],
815 $user->clearInstanceCache(
'id' );
816 $user->invalidateCache();
821 $user = self::newFromRow( $row );
825 if ( $user->mEmail || $user->mToken !== self::INVALID_TOKEN ||
826 AuthManager::singleton()->userCanAuthenticate( $name )
829 if ( !$options[
'steal'] ) {
833 AuthManager::singleton()->revokeAccessForUser( $name );
835 $user->invalidateEmail();
836 $user->mToken = self::INVALID_TOKEN;
837 $user->saveSettings();
838 SessionManager::singleton()->preventSessionsForUser( $user->getName() );
851 public static function whoIs( $id ) {
871 public static function idFromName( $name, $flags = self::READ_NORMAL ) {
873 $name = (string)$name;
874 $nt = Title::makeTitleSafe(
NS_USER, $name );
875 if ( is_null( $nt ) ) {
880 if ( !( $flags & self::READ_LATEST ) && array_key_exists( $name, self::$idCacheByName ) ) {
881 return is_null( self::$idCacheByName[$name] ) ? null : (int)self::$idCacheByName[$name];
884 list( $index, $options ) = DBAccessObjectUtils::getDBOptions( $flags );
890 [
'user_name' => $nt->getText() ],
895 if (
$s ===
false ) {
898 $result = (int)
$s->user_id;
901 if ( count( self::$idCacheByName ) >= 1000 ) {
902 self::$idCacheByName = [];
905 self::$idCacheByName[$name] = $result;
914 self::$idCacheByName = [];
933 public static function isIP( $name ) {
934 return preg_match(
'/^\d{1,3}\.\d{1,3}\.\d{1,3}\.(?:xxx|\d{1,3})$/', $name )
935 || IP::isIPv6( $name );
945 return IP::isValidRange( $this->mName );
963 || self::isIP( $name )
964 || strpos( $name,
'/' ) !==
false
966 || $name != MediaWikiServices::getInstance()->getContentLanguage()->ucfirst( $name )
973 $parsed = Title::newFromText( $name );
974 if ( is_null( $parsed )
975 || $parsed->getNamespace()
976 || strcmp( $name, $parsed->getPrefixedText() ) ) {
982 $unicodeBlacklist =
'/[' .
983 '\x{0080}-\x{009f}' . # iso-8859-1 control chars
984 '\x{00a0}' . # non-breaking space
985 '\x{2000}-\x{200f}' . # various whitespace
986 '\x{2028}-\x{202f}' . # breaks and control chars
987 '\x{3000}' . # ideographic space
988 '\x{e000}-\x{f8ff}' . #
private use
990 if ( preg_match( $unicodeBlacklist, $name ) ) {
1011 if ( !self::isValidUserName( $name ) ) {
1015 if ( !self::$reservedUsernames ) {
1017 Hooks::run(
'UserGetReservedNames', [ &self::$reservedUsernames ] );
1021 foreach ( self::$reservedUsernames as $reserved ) {
1022 if ( substr( $reserved, 0, 4 ) ==
'msg:' ) {
1023 $reserved =
wfMessage( substr( $reserved, 4 ) )->inContentLanguage()->plain();
1025 if ( $reserved == $name ) {
1043 if ( $groups === [] ) {
1047 $groups = array_unique( (array)$groups );
1048 $limit = min( 5000, $limit );
1050 $conds = [
'ug_group' => $groups ];
1051 if ( $after !==
null ) {
1052 $conds[] =
'ug_user > ' . (int)$after;
1056 $ids =
$dbr->selectFieldValues(
1063 'ORDER BY' =>
'ug_user',
1088 if ( strlen( $name ) > 235 ) {
1090 ": '$name' invalid due to length" );
1099 ": '$name' invalid due to wgInvalidUsernameCharacters" );
1103 return self::isUsableName( $name );
1146 $status = Status::newGood( [] );
1149 if ( !Hooks::run(
'isValidPassword', [ $password, &$result, $this ] ) ) {
1150 $status->error( $result );
1154 if ( $result ===
false ) {
1155 $status->merge( $upp->checkUserPassword( $this, $password ),
true );
1159 if ( $result ===
true ) {
1163 $status->error( $result );
1182 $name = MediaWikiServices::getInstance()->getContentLanguage()->ucfirst( $name );
1184 # Reject names containing '#'; these will be cleaned up
1185 # with title normalisation, but then it's too late to
1187 if ( strpos( $name,
'#' ) !==
false ) {
1193 $t = ( $validate !== false ) ?
1194 Title::newFromText( $name,
NS_USER ) : Title::makeTitle(
NS_USER, $name );
1196 if ( is_null(
$t ) ||
$t->getNamespace() !==
NS_USER ||
$t->isExternal() ) {
1200 $name =
$t->getText();
1202 switch ( $validate ) {
1206 if ( !self::isValidUserName( $name ) ) {
1211 if ( !self::isUsableName( $name ) ) {
1216 if ( !self::isCreatableName( $name ) ) {
1221 throw new InvalidArgumentException(
1222 'Invalid parameter value for $validate in ' . __METHOD__ );
1238 $this->mName = $name;
1239 $this->mActorId = $actorId;
1240 $this->mRealName =
'';
1242 $this->mOptionOverrides =
null;
1243 $this->mOptionsLoaded =
false;
1245 $loggedOut = $this->mRequest && !defined(
'MW_NO_SESSION' )
1246 ? $this->mRequest->getSession()->getLoggedOutTimestamp() : 0;
1247 if ( $loggedOut !== 0 ) {
1248 $this->mTouched =
wfTimestamp( TS_MW, $loggedOut );
1250 $this->mTouched =
'1'; # Allow any pages to be cached
1253 $this->mToken =
null;
1254 $this->mEmailAuthenticated =
null;
1255 $this->mEmailToken =
'';
1256 $this->mEmailTokenExpires =
null;
1258 $this->mGroupMemberships = [];
1260 Hooks::run(
'UserLoadDefaults', [ $this, $name ] );
1276 return ( $this->mLoadedItems ===
true && $all ===
'all' ) ||
1277 ( isset( $this->mLoadedItems[$item] ) && $this->mLoadedItems[$item] ===
true );
1286 if ( is_array( $this->mLoadedItems ) ) {
1287 $this->mLoadedItems[$item] =
true;
1299 $session = $this->
getRequest()->getSession();
1300 $user = $session->getUser();
1301 if ( $user->isLoggedIn() ) {
1307 MediaWikiServices::getInstance()->getBlockManager()->trackBlockWithCookie( $this );
1310 $session->set(
'wsUserID', $this->
getId() );
1311 $session->set(
'wsUserName', $this->
getName() );
1312 $session->set(
'wsToken', $this->
getToken() );
1326 MediaWikiServices::getInstance()->getBlockManager()->trackBlockWithCookie( $this );
1338 $this->mId = intval( $this->mId );
1340 if ( !$this->mId ) {
1346 list( $index, $options ) = DBAccessObjectUtils::getDBOptions( $flags );
1349 $userQuery = self::getQueryInfo();
1350 $s = $db->selectRow(
1351 $userQuery[
'tables'],
1352 $userQuery[
'fields'],
1353 [
'user_id' => $this->mId ],
1359 $this->queryFlagsUsed = $flags;
1360 Hooks::run(
'UserLoadFromDatabase', [ $this, &
$s ] );
1362 if (
$s !==
false ) {
1365 $this->mGroupMemberships =
null;
1390 if ( !is_object( $row ) ) {
1391 throw new InvalidArgumentException(
'$row must be an object' );
1396 $this->mGroupMemberships =
null;
1398 if ( isset( $row->actor_id ) ) {
1399 $this->mActorId = (int)$row->actor_id;
1400 if ( $this->mActorId !== 0 ) {
1401 $this->mFrom =
'actor';
1408 if ( isset( $row->user_name ) && $row->user_name !==
'' ) {
1409 $this->mName = $row->user_name;
1410 $this->mFrom =
'name';
1416 if ( isset( $row->user_real_name ) ) {
1417 $this->mRealName = $row->user_real_name;
1423 if ( isset( $row->user_id ) ) {
1424 $this->mId = intval( $row->user_id );
1425 if ( $this->mId !== 0 ) {
1426 $this->mFrom =
'id';
1433 if ( isset( $row->user_id ) && isset( $row->user_name ) && $row->user_name !==
'' ) {
1434 self::$idCacheByName[$row->user_name] = $row->user_id;
1437 if ( isset( $row->user_editcount ) ) {
1438 $this->mEditCount = $row->user_editcount;
1443 if ( isset( $row->user_touched ) ) {
1444 $this->mTouched =
wfTimestamp( TS_MW, $row->user_touched );
1449 if ( isset( $row->user_token ) ) {
1453 $this->mToken = rtrim( $row->user_token,
" \0" );
1454 if ( $this->mToken ===
'' ) {
1455 $this->mToken =
null;
1461 if ( isset( $row->user_email ) ) {
1462 $this->mEmail = $row->user_email;
1463 $this->mEmailAuthenticated =
wfTimestampOrNull( TS_MW, $row->user_email_authenticated );
1464 $this->mEmailToken = $row->user_email_token;
1465 $this->mEmailTokenExpires =
wfTimestampOrNull( TS_MW, $row->user_email_token_expires );
1472 $this->mLoadedItems =
true;
1475 if ( is_array( $data ) ) {
1476 if ( isset( $data[
'user_groups'] ) && is_array( $data[
'user_groups'] ) ) {
1477 if ( $data[
'user_groups'] === [] ) {
1478 $this->mGroupMemberships = [];
1480 $firstGroup = reset( $data[
'user_groups'] );
1481 if ( is_array( $firstGroup ) || is_object( $firstGroup ) ) {
1482 $this->mGroupMemberships = [];
1483 foreach ( $data[
'user_groups'] as $row ) {
1484 $ugm = UserGroupMembership::newFromRow( (
object)$row );
1485 $this->mGroupMemberships[$ugm->getGroup()] = $ugm;
1490 if ( isset( $data[
'user_properties'] ) && is_array( $data[
'user_properties'] ) ) {
1503 foreach ( self::$mCacheVars as $var ) {
1504 $this->$var = $user->$var;
1512 if ( is_null( $this->mGroupMemberships ) ) {
1513 $db = ( $this->queryFlagsUsed & self::READ_LATEST )
1516 $this->mGroupMemberships = UserGroupMembership::getMembershipsForUser(
1543 if ( $toPromote === [] ) {
1553 foreach ( $toPromote as $group ) {
1556 $newGroups = array_merge( $oldGroups, $toPromote );
1560 Hooks::run(
'UserGroupsChanged', [ $this, $toPromote, [],
false,
false, $oldUGMs, $newUGMs ] );
1563 $logEntry->setPerformer( $this );
1565 $logEntry->setParameters( [
1566 '4::oldgroups' => $oldGroups,
1567 '5::newgroups' => $newGroups,
1569 $logid = $logEntry->insert();
1571 $logEntry->publish( $logid );
1587 if ( $this->mTouched ) {
1589 $conditions[
'user_touched'] = $db->
timestamp( $this->mTouched );
1607 if ( !$this->mId ) {
1615 $dbw->update(
'user',
1616 [
'user_touched' => $dbw->timestamp( $newTouched ) ],
1617 $this->makeUpdateConditions( $dbw, [
1618 'user_id' => $this->mId,
1622 $success = ( $dbw->affectedRows() > 0 );
1625 $this->mTouched = $newTouched;
1645 $this->mNewtalk = -1;
1646 $this->mDatePreference =
null;
1647 $this->mBlockedby = -1; # Unset
1648 $this->mHash =
false;
1649 $this->mEffectiveGroups =
null;
1650 $this->mImplicitGroups =
null;
1651 $this->mGroupMemberships =
null;
1652 $this->mOptions =
null;
1653 $this->mOptionsLoaded =
false;
1654 $this->mEditCount =
null;
1658 MediaWikiServices::getInstance()->getPermissionManager()->invalidateUsersRightsCache(
1663 if ( $reloadFrom ) {
1664 $this->mLoadedItems = [];
1665 $this->mFrom = $reloadFrom;
1681 Assert::invariant( defined(
'MW_PHPUNIT_TEST' ),
'Unit tests only' );
1682 self::$defOpt =
null;
1683 self::$defOptLang =
null;
1695 $contLang = MediaWikiServices::getInstance()->getContentLanguage();
1696 if ( self::$defOpt !==
null && self::$defOptLang === $contLang->getCode() ) {
1700 return self::$defOpt;
1705 self::$defOptLang = $contLang->getCode();
1706 self::$defOpt[
'language'] = self::$defOptLang;
1707 foreach ( LanguageConverter::$languagesWithVariants as $langCode ) {
1708 if ( $langCode === $contLang->getCode() ) {
1709 self::$defOpt[
'variant'] = $langCode;
1711 self::$defOpt[
"variant-$langCode"] = $langCode;
1719 self::$defOpt[
'searchNs' . $nsnum] = (bool)$val;
1723 Hooks::run(
'UserGetDefaultOptions', [ &self::$defOpt ] );
1725 return self::$defOpt;
1735 $defOpts = self::getDefaultOptions();
1736 return $defOpts[$opt] ??
null;
1749 if ( $this->mBlockedby != -1 ) {
1753 wfDebug( __METHOD__ .
": checking...\n" );
1773 $sessionUser = RequestContext::getMain()->getUser();
1774 $globalUserName = $sessionUser->isSafeToLoad()
1775 ? $sessionUser->getName()
1776 : IP::sanitizeIP( $sessionUser->getRequest()->getIP() );
1778 if ( $this->
getName() === $globalUserName ) {
1784 $block = MediaWikiServices::getInstance()->getBlockManager()->getUserBlock(
1791 $this->mBlock = $block;
1792 $this->mBlockedby = $block->getByName();
1793 $this->mBlockreason = $block->getReason();
1794 $this->mHideName = $block->getHideName();
1795 $this->mAllowUsertalk = $block->isUsertalkEditAllowed();
1797 $this->mBlock =
null;
1798 $this->mBlockedby =
'';
1799 $this->mBlockreason =
'';
1800 $this->mHideName = 0;
1801 $this->mAllowUsertalk =
false;
1807 Hooks::run(
'GetBlockedStatus', [ &$thisUser ],
'1.34' );
1819 return MediaWikiServices::getInstance()->getBlockManager()
1820 ->isDnsBlacklisted( $ip, $checkWhitelist );
1836 if ( IP::isIPv4( $ip ) ) {
1838 $ipReversed = implode(
'.', array_reverse( explode(
'.', $ip ) ) );
1840 foreach ( (array)$bases as
$base ) {
1844 if ( is_array(
$base ) ) {
1845 if ( count(
$base ) >= 2 ) {
1847 $host =
"{$base[1]}.$ipReversed.{$base[0]}";
1849 $host =
"$ipReversed.{$base[0]}";
1851 $basename =
$base[0];
1853 $host =
"$ipReversed.$base";
1857 $ipList = gethostbynamel( $host );
1860 wfDebugLog(
'dnsblacklist',
"Hostname $host is {$ipList[0]}, it's a proxy says $basename!" );
1865 wfDebugLog(
'dnsblacklist',
"Requested $host, not found in $basename." );
1893 $resultProxyList = [];
1894 $deprecatedIPEntries = [];
1898 $keyIsIP = IP::isIPAddress( $key );
1899 $valueIsIP = IP::isIPAddress( $value );
1900 if ( $keyIsIP && !$valueIsIP ) {
1901 $deprecatedIPEntries[] = $key;
1902 $resultProxyList[] = $key;
1903 } elseif ( $keyIsIP && $valueIsIP ) {
1904 $deprecatedIPEntries[] = $key;
1905 $resultProxyList[] = $key;
1906 $resultProxyList[] = $value;
1908 $resultProxyList[] = $value;
1912 if ( $deprecatedIPEntries ) {
1914 'IP addresses in the keys of $wgProxyList (found the following IP addresses in keys: ' .
1915 implode(
', ', $deprecatedIPEntries ) .
', please move them to values)',
'1.30' );
1918 $proxyListIPSet =
new IPSet( $resultProxyList );
1919 return $proxyListIPSet->match( $ip );
1935 return !$this->
isAllowed(
'noratelimit' );
1958 $logger = \MediaWiki\Logger\LoggerFactory::getInstance(
'ratelimit' );
1962 if ( !Hooks::run(
'PingLimiter', [ &$user, $action, &$result, $incrBy ] ) ) {
1971 $limits = array_merge(
1972 [
'&can-bypass' =>
true ],
1981 $logger->debug( __METHOD__ .
": limiting $action rate for {$this->getName()}" );
1984 $id = $this->
getId();
1986 $cache = ObjectCache::getLocalClusterInstance();
1990 if ( isset( $limits[
'anon'] ) ) {
1991 $keys[
$cache->makeKey(
'limiter', $action,
'anon' )] = $limits[
'anon'];
1995 if ( isset( $limits[
'user-global'] ) ) {
1996 $lookup = CentralIdLookup::factoryNonLocal();
1998 $centralId = $lookup
1999 ? $lookup->centralIdFromLocalUser( $this, CentralIdLookup::AUDIENCE_RAW )
2004 $realm = $lookup->getProviderId();
2006 $globalKey =
$cache->makeGlobalKey(
'limiter', $action,
'user-global',
2007 $realm, $centralId );
2010 $globalKey =
$cache->makeKey(
'limiter', $action,
'user-global',
2013 $keys[$globalKey] = $limits[
'user-global'];
2019 if ( isset( $limits[
'ip'] ) ) {
2021 $keys[
$cache->makeGlobalKey(
'limiter', $action,
'ip', $ip )] = $limits[
'ip'];
2024 if ( isset( $limits[
'subnet'] ) ) {
2026 $subnet = IP::getSubnet( $ip );
2027 if ( $subnet !==
false ) {
2028 $keys[
$cache->makeGlobalKey(
'limiter', $action,
'subnet', $subnet )] = $limits[
'subnet'];
2035 if ( $id !== 0 && isset( $limits[
'user'] ) ) {
2037 $userLimit = $limits[
'user'];
2040 if ( $id !== 0 && $isNewbie && isset( $limits[
'newbie'] ) ) {
2041 $userLimit = $limits[
'newbie'];
2045 foreach ( $this->
getGroups() as $group ) {
2046 if ( isset( $limits[$group] ) ) {
2047 if ( $userLimit ===
false
2048 || $limits[$group][0] / $limits[$group][1] > $userLimit[0] / $userLimit[1]
2050 $userLimit = $limits[$group];
2057 if ( $userLimit !==
false ) {
2061 list( $max, $period ) = $userLimit;
2062 $logger->debug( __METHOD__ .
": effective user limit: $max in {$period}s" );
2063 $keys[
$cache->makeKey(
'limiter', $action,
'user', $id )] = $userLimit;
2067 if ( isset( $limits[
'ip-all'] ) ) {
2070 if ( $isNewbie || $userLimit ===
false
2071 || $limits[
'ip-all'][0] / $limits[
'ip-all'][1] > $userLimit[0] / $userLimit[1] ) {
2072 $keys[
$cache->makeGlobalKey(
'limiter', $action,
'ip-all', $ip )] = $limits[
'ip-all'];
2077 if ( isset( $limits[
'subnet-all'] ) ) {
2079 $subnet = IP::getSubnet( $ip );
2080 if ( $subnet !==
false ) {
2082 if ( $isNewbie || $userLimit ===
false
2083 || $limits[
'ip-all'][0] / $limits[
'ip-all'][1]
2084 > $userLimit[0] / $userLimit[1] ) {
2085 $keys[
$cache->makeGlobalKey(
'limiter', $action,
'subnet-all', $subnet )]
2086 = $limits[
'subnet-all'];
2094 $now = MWTimestamp::time();
2098 foreach (
$keys as $key => $limit ) {
2106 function (
$cache, $key, $data, &$expiry )
2107 use ( $action, $logger, &$triggered, $now, $clockFudge, $limit, $incrBy )
2112 list( $max, $period ) = $limit;
2114 $expiry = $now + (int)$period;
2121 $fields = explode(
'|', $data );
2122 $storedCount = (int)( $fields[0] ?? 0 );
2123 $storedExpiry = (int)( $fields[1] ?? PHP_INT_MAX );
2126 if ( $storedExpiry < ( $now + $clockFudge ) ) {
2128 'User::pingLimiter: '
2129 .
'Stale rate limit entry, cache key failed to expire (T246991)',
2131 'action' => $action,
2134 'period' => $period,
2135 'count' => $storedCount,
2137 'expiry' => MWTimestamp::convert( TS_DB, $storedExpiry ),
2143 $expiry = min( $storedExpiry, $now + (
int)$period );
2144 $count = $storedCount;
2149 if ( $count >= $max ) {
2150 if ( !$triggered ) {
2152 'User::pingLimiter: User tripped rate limit',
2154 'action' => $action,
2158 'period' => $period,
2169 $data =
"$count|$expiry";
2191 $this->
getBlock()->appliesToRight(
'edit' );
2202 return $this->mBlock instanceof
AbstractBlock ? $this->mBlock :
null;
2217 return MediaWikiServices::getInstance()->getPermissionManager()
2218 ->isBlockedFrom( $this,
$title, $fromReplica );
2227 return $this->mBlockedby;
2236 return $this->mBlockreason;
2245 return ( $this->mBlock ? $this->mBlock->getId() : false );
2271 if ( $this->mGlobalBlock !==
null ) {
2272 return $this->mGlobalBlock ?:
null;
2275 if ( IP::isIPAddress( $this->
getName() ) ) {
2284 Hooks::run(
'UserIsBlockedGlobally', [ &$user, $ip, &$blocked, &$block ] );
2286 if ( $blocked && $block ===
null ) {
2290 'systemBlock' =>
'global-block'
2294 $this->mGlobalBlock = $blocked ? $block :
false;
2295 return $this->mGlobalBlock ?:
null;
2304 if ( $this->mLocked !==
null ) {
2305 return $this->mLocked;
2308 $this->mLocked =
false;
2309 Hooks::run(
'UserIsLocked', [ $this, &$this->mLocked ] );
2310 return $this->mLocked;
2319 if ( $this->mHideName !==
null ) {
2320 return (
bool)$this->mHideName;
2323 if ( !$this->mHideName ) {
2325 $this->mHideName =
false;
2326 Hooks::run(
'UserIsHidden', [ $this, &$this->mHideName ],
'1.34' );
2328 return (
bool)$this->mHideName;
2336 if ( $this->mId ===
null && $this->mName !==
null &&
2348 return (
int)$this->mId;
2367 return $this->mName;
2371 if ( $this->mName ===
false ) {
2373 $this->mName = IP::sanitizeIP( $this->
getRequest()->getIP() );
2376 return $this->mName;
2394 $this->mName = $str;
2408 if ( !$this->mActorId && $dbw ) {
2409 $migration = MediaWikiServices::getInstance()->getActorMigration();
2410 $this->mActorId = $migration->getNewActorId( $dbw, $this );
2416 return (
int)$this->mActorId;
2424 return str_replace(
' ',
'_', $this->
getName() );
2435 if ( $this->mNewtalk === -1 ) {
2436 $this->mNewtalk =
false; # reset talk page status
2440 if ( !$this->mId ) {
2444 $this->mNewtalk =
false;
2449 $this->mNewtalk = $this->
checkNewtalk(
'user_id', $this->mId );
2453 return (
bool)$this->mNewtalk;
2473 if ( !Hooks::run(
'UserRetrieveNewTalks', [ &$user, &$talks ] ) ) {
2483 $timestamp =
$dbr->selectField(
'user_newtalk',
2484 'MIN(user_last_timestamp)',
2485 $this->
isAnon() ? [
'user_ip' => $this->
getName() ] : [
'user_id' => $this->
getId() ],
2487 $rev = $timestamp ? Revision::loadFromTimestamp(
$dbr, $utp, $timestamp ) :
null;
2490 'wiki' => WikiMap::getWikiIdFromDbDomain( WikiMap::getCurrentWikiDbDomain() ),
2491 'link' => $utp->getLocalURL(),
2503 $newMessageRevisionId =
null;
2509 if ( $newMessageLinks && count( $newMessageLinks ) === 1
2510 && WikiMap::isCurrentWikiId( $newMessageLinks[0][
'wiki'] )
2511 && $newMessageLinks[0][
'rev']
2514 $newMessageRevision = $newMessageLinks[0][
'rev'];
2515 $newMessageRevisionId = $newMessageRevision->getId();
2518 return $newMessageRevisionId;
2532 $ok =
$dbr->selectField(
'user_newtalk', $field, [ $field => $id ], __METHOD__ );
2534 return $ok !==
false;
2546 $prevRev = $curRev ? $curRev->getPrevious() :
false;
2547 $ts = $prevRev ? $prevRev->getTimestamp() :
null;
2550 $dbw->insert(
'user_newtalk',
2551 [ $field => $id,
'user_last_timestamp' => $dbw->timestampOrNull( $ts ) ],
2554 if ( $dbw->affectedRows() ) {
2555 wfDebug( __METHOD__ .
": set on ($field, $id)\n" );
2559 wfDebug( __METHOD__ .
" already set ($field, $id)\n" );
2571 $dbw->delete(
'user_newtalk',
2574 if ( $dbw->affectedRows() ) {
2575 wfDebug( __METHOD__ .
": killed on ($field, $id)\n" );
2579 wfDebug( __METHOD__ .
": already gone ($field, $id)\n" );
2595 $this->mNewtalk = $val;
2602 $id = $this->
getId();
2624 if ( $this->mTouched ) {
2625 $time = max( $time,
wfTimestamp( TS_UNIX, $this->mTouched ) + 1 );
2642 if ( !$this->
getId() ) {
2646 $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
2647 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
2650 if ( $mode ===
'refresh' ) {
2651 $cache->delete( $key, 1 );
2653 $lb->getConnectionRef(
DB_MASTER )->onTransactionPreCommitOrIdle(
2654 function () use (
$cache, $key ) {
2685 $id = $this->
getId();
2687 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
2688 $key =
$cache->makeKey(
'user-quicktouched',
'id', $id );
2689 $cache->touchCheckKey( $key );
2690 $this->mQuickTouched =
null;
2700 return ( $timestamp >= $this->
getTouched() );
2715 if ( $this->mQuickTouched ===
null ) {
2716 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
2717 $key =
$cache->makeKey(
'user-quicktouched',
'id', $this->mId );
2722 return max( $this->mTouched, $this->mQuickTouched );
2725 return $this->mTouched;
2736 return $this->mTouched;
2782 $manager = AuthManager::singleton();
2785 if ( !$manager->userExists( $this->getName() ) ) {
2786 throw new LogicException(
'Cannot set a password for a user that is not in the database.' );
2790 'username' => $this->
getName(),
2794 if ( !$status->isGood() ) {
2795 \MediaWiki\Logger\LoggerFactory::getInstance(
'authentication' )
2796 ->info( __METHOD__ .
': Password change rejected: '
2797 . $status->getWikiText(
null,
null,
'en' ) );
2801 $this->
setOption(
'watchlisttoken',
false );
2802 SessionManager::singleton()->invalidateSessionsForUser( $this );
2820 $manager = AuthManager::singleton();
2821 $reqs = $manager->getAuthenticationRequests( AuthManager::ACTION_CHANGE, $this );
2822 $reqs = AuthenticationRequest::loadRequestsFromSubmission( $reqs, $data );
2824 $status = Status::newGood(
'ignored' );
2825 foreach ( $reqs as $req ) {
2826 $status->merge( $manager->allowsAuthenticationDataChange( $req ),
true );
2828 if ( $status->getValue() ===
'ignored' ) {
2829 $status->warning(
'authenticationdatachange-ignored' );
2832 if ( $status->isGood() ) {
2833 foreach ( $reqs as $req ) {
2834 $manager->changeAuthenticationData( $req );
2850 if ( !$this->mToken && $forceCreation ) {
2854 if ( !$this->mToken ) {
2859 if ( $this->mToken === self::INVALID_TOKEN ) {
2867 return $this->mToken;
2874 $len = max( 32, self::TOKEN_LENGTH );
2875 if ( strlen( $ret ) < $len ) {
2877 throw new \UnexpectedValueException(
'Hmac returned less than 128 bits' );
2880 return substr( $ret, -$len );
2891 if ( $this->mToken === self::INVALID_TOKEN ) {
2892 \MediaWiki\Logger\LoggerFactory::getInstance(
'session' )
2893 ->debug( __METHOD__ .
": Ignoring attempt to set token for system user \"$this\"" );
2894 } elseif ( !$token ) {
2897 $this->mToken = $token;
2907 Hooks::run(
'UserGetEmail', [ $this, &$this->mEmail ] );
2908 return $this->mEmail;
2917 Hooks::run(
'UserGetEmailAuthenticationTimestamp', [ $this, &$this->mEmailAuthenticated ] );
2918 return $this->mEmailAuthenticated;
2927 if ( $str == $this->mEmail ) {
2931 $this->mEmail = $str;
2932 Hooks::run(
'UserSetEmail', [ $this, &$this->mEmail ] );
2946 return Status::newFatal(
'emaildisabled' );
2950 if ( $str === $oldaddr ) {
2951 return Status::newGood(
true );
2954 $type = $oldaddr !=
'' ?
'changed' :
'set';
2955 $notificationResult =
null;
2960 $change = $str !=
'' ?
'changed' :
'removed';
2961 $notificationResult = $this->
sendMail(
2962 wfMessage(
'notificationemail_subject_' . $change )->text(),
2963 wfMessage(
'notificationemail_body_' . $change,
2976 if ( $notificationResult !==
null ) {
2977 $result->merge( $notificationResult );
2980 if ( $result->isGood() ) {
2982 $result->value =
'eauth';
2985 $result = Status::newGood(
true );
3000 return $this->mRealName;
3009 $this->mRealName = $str;
3022 public function getOption( $oname, $defaultOverride =
null, $ignoreHidden =
false ) {
3026 # We want 'disabled' preferences to always behave as the default value for
3027 # users, even if they have set the option explicitly in their settings (ie they
3028 # set it, and then it was disabled removing their ability to change it). But
3029 # we don't want to erase the preferences in the database in case the preference
3030 # is re-enabled again. So don't touch $mOptions, just override the returned value
3032 return self::getDefaultOption( $oname );
3035 if ( array_key_exists( $oname, $this->mOptions ) ) {
3036 return $this->mOptions[$oname];
3039 return $defaultOverride;
3053 $options = $this->mOptions;
3055 # We want 'disabled' preferences to always behave as the default value for
3056 # users, even if they have set the option explicitly in their settings (ie they
3057 # set it, and then it was disabled removing their ability to change it). But
3058 # we don't want to erase the preferences in the database in case the preference
3059 # is re-enabled again. So don't touch $mOptions, just override the returned value
3061 $default = self::getDefaultOption( $pref );
3062 if ( $default !==
null ) {
3063 $options[$pref] = $default;
3067 if ( $flags & self::GETOPTIONS_EXCLUDE_DEFAULTS ) {
3068 $options = array_diff_assoc( $options, self::getDefaultOptions() );
3082 return (
bool)$this->
getOption( $oname );
3096 $val = $defaultOverride;
3098 return intval( $val );
3113 if ( is_null( $val ) ) {
3114 $val = self::getDefaultOption( $oname );
3117 $this->mOptions[$oname] = $val;
3133 $id = $this->
getId();
3143 $token = hash_hmac(
'sha1',
"$oname:$id", $this->
getToken() );
3195 'registered-multiselect',
3196 'registered-checkmatrix',
3217 if ( $options ===
null ) {
3218 $options = $this->mOptions;
3221 $preferencesFactory = MediaWikiServices::getInstance()->getPreferencesFactory();
3222 $prefs = $preferencesFactory->getFormDescriptor( $this,
$context );
3227 $specialOptions = array_fill_keys( $preferencesFactory->getSaveBlacklist(),
true );
3228 foreach ( $specialOptions as $name => $value ) {
3229 unset( $prefs[$name] );
3234 $multiselectOptions = [];
3235 foreach ( $prefs as $name => $info ) {
3236 if ( ( isset( $info[
'type'] ) && $info[
'type'] ==
'multiselect' ) ||
3237 ( isset( $info[
'class'] ) && $info[
'class'] == HTMLMultiSelectField::class ) ) {
3238 $opts = HTMLFormField::flattenOptions( $info[
'options'] );
3239 $prefix = $info[
'prefix'] ?? $name;
3241 foreach ( $opts as $value ) {
3242 $multiselectOptions[
"$prefix$value"] =
true;
3245 unset( $prefs[$name] );
3248 $checkmatrixOptions = [];
3249 foreach ( $prefs as $name => $info ) {
3250 if ( ( isset( $info[
'type'] ) && $info[
'type'] ==
'checkmatrix' ) ||
3251 ( isset( $info[
'class'] ) && $info[
'class'] == HTMLCheckMatrix::class ) ) {
3252 $columns = HTMLFormField::flattenOptions( $info[
'columns'] );
3253 $rows = HTMLFormField::flattenOptions( $info[
'rows'] );
3254 $prefix = $info[
'prefix'] ?? $name;
3256 foreach ( $columns as $column ) {
3257 foreach ( $rows as $row ) {
3258 $checkmatrixOptions[
"$prefix$column-$row"] =
true;
3262 unset( $prefs[$name] );
3267 foreach ( $options as $key => $value ) {
3268 if ( isset( $prefs[$key] ) ) {
3269 $mapping[$key] =
'registered';
3270 } elseif ( isset( $multiselectOptions[$key] ) ) {
3271 $mapping[$key] =
'registered-multiselect';
3272 } elseif ( isset( $checkmatrixOptions[$key] ) ) {
3273 $mapping[$key] =
'registered-checkmatrix';
3274 } elseif ( isset( $specialOptions[$key] ) ) {
3275 $mapping[$key] =
'special';
3276 } elseif ( substr( $key, 0, 7 ) ===
'userjs-' ) {
3277 $mapping[$key] =
'userjs';
3279 $mapping[$key] =
'unused';
3301 $resetKinds = [
'registered',
'registered-multiselect',
'registered-checkmatrix',
'unused' ],
3305 $defaultOptions = self::getDefaultOptions();
3307 if ( !is_array( $resetKinds ) ) {
3308 $resetKinds = [ $resetKinds ];
3311 if ( in_array(
'all', $resetKinds ) ) {
3312 $newOptions = $defaultOptions;
3315 $context = RequestContext::getMain();
3319 $resetKinds = array_intersect( $resetKinds, self::listOptionKinds() );
3324 foreach ( $this->mOptions as $key => $value ) {
3325 if ( in_array( $optionKinds[$key], $resetKinds ) ) {
3326 if ( array_key_exists( $key, $defaultOptions ) ) {
3327 $newOptions[$key] = $defaultOptions[$key];
3330 $newOptions[$key] = $value;
3335 Hooks::run(
'UserResetAllOptions', [ $this, &$newOptions, $this->mOptions, $resetKinds ] );
3337 $this->mOptions = $newOptions;
3338 $this->mOptionsLoaded =
true;
3347 if ( is_null( $this->mDatePreference ) ) {
3350 $map =
$wgLang->getDatePreferenceMigrationMap();
3351 if ( isset( $map[$value] ) ) {
3352 $value = $map[$value];
3354 $this->mDatePreference = $value;
3356 return $this->mDatePreference;
3374 Hooks::run(
'UserRequiresHTTPS', [ $this, &$https ] );
3407 return MediaWikiServices::getInstance()->getPermissionManager()->getUserPermissions( $this );
3419 return array_keys( $this->mGroupMemberships );
3432 return $this->mGroupMemberships;
3443 if ( $recache || is_null( $this->mEffectiveGroups ) ) {
3444 $this->mEffectiveGroups = array_unique( array_merge(
3451 Hooks::run(
'UserEffectiveGroups', [ &$user, &$this->mEffectiveGroups ] );
3453 $this->mEffectiveGroups = array_values( array_unique( $this->mEffectiveGroups ) );
3455 return $this->mEffectiveGroups;
3466 if ( $recache || is_null( $this->mImplicitGroups ) ) {
3467 $this->mImplicitGroups = [
'*' ];
3468 if ( $this->
getId() ) {
3469 $this->mImplicitGroups[] =
'user';
3471 $this->mImplicitGroups = array_unique( array_merge(
3472 $this->mImplicitGroups,
3479 $this->mEffectiveGroups =
null;
3482 return $this->mImplicitGroups;
3497 if ( is_null( $this->mFormerGroups ) ) {
3498 $db = ( $this->queryFlagsUsed & self::READ_LATEST )
3501 $res = $db->select(
'user_former_groups',
3503 [
'ufg_user' => $this->mId ],
3505 $this->mFormerGroups = [];
3506 foreach (
$res as $row ) {
3507 $this->mFormerGroups[] = $row->ufg_group;
3511 return $this->mFormerGroups;
3519 if ( !$this->
getId() ) {
3523 if ( $this->mEditCount ===
null ) {
3527 $count =
$dbr->selectField(
3528 'user',
'user_editcount',
3529 [
'user_id' => $this->mId ],
3533 if ( $count ===
null ) {
3537 $this->mEditCount = $count;
3539 return (
int)$this->mEditCount;
3561 if ( !Hooks::run(
'UserAddGroup', [ $this, &$group, &$expiry ] ) ) {
3567 if ( !$ugm->insert(
true ) ) {
3571 $this->mGroupMemberships[$group] = $ugm;
3576 MediaWikiServices::getInstance()->getPermissionManager()->invalidateUsersRightsCache( $this );
3591 if ( !Hooks::run(
'UserRemoveGroup', [ $this, &$group ] ) ) {
3595 $ugm = UserGroupMembership::getMembership( $this->mId, $group );
3597 if ( !$ugm || !$ugm->delete() ) {
3602 unset( $this->mGroupMemberships[$group] );
3607 MediaWikiServices::getInstance()->getPermissionManager()->invalidateUsersRightsCache( $this );
3623 return $this->
getId() != 0;
3652 Hooks::run(
"UserIsBot", [ $this, &$isBot ] );
3667 return MediaWikiServices::getInstance()
3668 ->getPermissionManager()
3669 ->userHasAnyRight( $this, ...$permissions );
3679 return MediaWikiServices::getInstance()
3680 ->getPermissionManager()
3681 ->userHasAllRights( $this, ...$permissions );
3695 return MediaWikiServices::getInstance()->getPermissionManager()
3696 ->userHasRight( $this, $action );
3738 if ( $this->mRequest ) {
3739 return $this->mRequest;
3755 if (
$title->isWatchable() && ( !$checkRights || $this->isAllowed(
'viewmywatchlist' ) ) ) {
3756 return MediaWikiServices::getInstance()->getWatchedItemStore()->isWatched( $this,
$title );
3769 if ( !$checkRights || $this->
isAllowed(
'editmywatchlist' ) ) {
3770 MediaWikiServices::getInstance()->getWatchedItemStore()->addWatchBatchForUser(
3786 if ( !$checkRights || $this->
isAllowed(
'editmywatchlist' ) ) {
3787 $store = MediaWikiServices::getInstance()->getWatchedItemStore();
3788 $store->removeWatch( $this,
$title->getSubjectPage() );
3789 $store->removeWatch( $this,
$title->getTalkPage() );
3811 if ( !$this->
isAllowed(
'editmywatchlist' ) ) {
3819 if ( !Hooks::run(
'UserClearNewTalkNotification', [ &$user, $oldid ] ) ) {
3824 DeferredUpdates::addCallableUpdate(
function () use (
$title, $oldid ) {
3834 $rl = MediaWikiServices::getInstance()->getRevisionLookup();
3835 $oldRev = $rl->getRevisionById( $oldid, Title::READ_LATEST );
3837 $newRev = $rl->getNextRevision( $oldRev );
3866 MediaWikiServices::getInstance()->getWatchedItemStore()
3867 ->resetNotificationTimestamp( $this,
$title, $force, $oldid );
3888 $id = $this->
getId();
3893 $watchedItemStore = MediaWikiServices::getInstance()->getWatchedItemStore();
3894 $watchedItemStore->resetAllNotificationTimestampsForUser( $this );
3922 $registration > $learnerRegistration ) {
3927 $registration <= $experiencedRegistration
3929 return 'experienced';
3943 public function setCookies( $request =
null, $secure =
null, $rememberMe =
false ) {
3945 if ( $this->mId == 0 ) {
3949 $session = $this->
getRequest()->getSession();
3950 if ( $request && $session->getRequest() !== $request ) {
3951 $session = $session->sessionWithRequest( $request );
3953 $delay = $session->delaySave();
3955 if ( !$session->getUser()->equals( $this ) ) {
3956 if ( !$session->canSetUser() ) {
3957 \MediaWiki\Logger\LoggerFactory::getInstance(
'session' )
3958 ->warning( __METHOD__ .
3959 ": Cannot save user \"$this\" to a user \"{$session->getUser()}\"'s immutable session"
3963 $session->setUser( $this );
3966 $session->setRememberUser( $rememberMe );
3967 if ( $secure !==
null ) {
3968 $session->setForceHTTPS( $secure );
3971 $session->persist();
3973 ScopedCallback::consume( $delay );
3982 if ( Hooks::run(
'UserLogout', [ &$user ] ) ) {
3992 $session = $this->
getRequest()->getSession();
3993 if ( !$session->canSetUser() ) {
3994 \MediaWiki\Logger\LoggerFactory::getInstance(
'session' )
3995 ->warning( __METHOD__ .
": Cannot log out of an immutable session" );
3996 $error =
'immutable';
3997 } elseif ( !$session->getUser()->equals( $this ) ) {
3998 \MediaWiki\Logger\LoggerFactory::getInstance(
'session' )
3999 ->warning( __METHOD__ .
4000 ": Cannot log user \"$this\" out of a user \"{$session->getUser()}\"'s session"
4004 $error =
'wronguser';
4007 $delay = $session->delaySave();
4008 $session->unpersist();
4009 $session->setLoggedOutTimestamp( time() );
4010 $session->setUser(
new User );
4011 $session->set(
'wsUserID', 0 );
4012 $session->resetAllTokens();
4013 ScopedCallback::consume( $delay );
4016 \MediaWiki\Logger\LoggerFactory::getInstance(
'authevents' )->info(
'Logout', [
4017 'event' =>
'logout',
4018 'successful' => $error ===
false,
4019 'status' => $error ?:
'success',
4033 "Could not update user with ID '{$this->mId}'; DB is read-only."
4039 if ( $this->mId == 0 ) {
4049 $dbw->doAtomicSection( __METHOD__,
function (
IDatabase $dbw, $fname ) use ( $newTouched ) {
4052 'user_name' => $this->mName,
4053 'user_real_name' => $this->mRealName,
4054 'user_email' => $this->mEmail,
4055 'user_email_authenticated' => $dbw->
timestampOrNull( $this->mEmailAuthenticated ),
4056 'user_touched' => $dbw->
timestamp( $newTouched ),
4057 'user_token' => strval( $this->mToken ),
4058 'user_email_token' => $this->mEmailToken,
4059 'user_email_token_expires' => $dbw->
timestampOrNull( $this->mEmailTokenExpires ),
4060 ], $this->makeUpdateConditions( $dbw, [
4061 'user_id' => $this->mId,
4069 $from = ( $this->queryFlagsUsed & self::READ_LATEST ) ?
'master' :
'replica';
4070 LoggerFactory::getInstance(
'preferences' )->warning(
4071 "CAS update failed on user_touched for user ID '{user_id}' ({db_flag} read)",
4072 [
'user_id' => $this->mId,
'db_flag' => $from ]
4074 throw new MWException(
"CAS update failed on user_touched. " .
4075 "The version of the user to be saved is older than the current version."
4081 [
'actor_name' => $this->mName ],
4082 [
'actor_user' => $this->mId ],
4087 $this->mTouched = $newTouched;
4090 Hooks::run(
'UserSaveSettings', [ $this ] );
4107 $db = ( ( $flags & self::READ_LATEST ) == self::READ_LATEST )
4111 $options = ( ( $flags & self::READ_LOCKING ) == self::READ_LOCKING )
4112 ? [
'LOCK IN SHARE MODE' ]
4115 $id = $db->selectField(
'user',
4116 'user_id', [
'user_name' =>
$s ], __METHOD__, $options );
4137 foreach ( [
'password',
'newpassword',
'newpass_time',
'password_expires' ] as $field ) {
4138 if ( isset( $params[$field] ) ) {
4139 wfDeprecated( __METHOD__ .
" with param '$field'",
'1.27' );
4140 unset( $params[$field] );
4147 if ( isset( $params[
'options'] ) ) {
4148 $user->mOptions = $params[
'options'] + (array)$user->mOptions;
4149 unset( $params[
'options'] );
4153 $noPass = PasswordFactory::newInvalidPassword()->toString();
4156 'user_name' => $name,
4157 'user_password' => $noPass,
4158 'user_newpassword' => $noPass,
4159 'user_email' => $user->mEmail,
4160 'user_email_authenticated' => $dbw->timestampOrNull( $user->mEmailAuthenticated ),
4161 'user_real_name' => $user->mRealName,
4162 'user_token' => strval( $user->mToken ),
4163 'user_registration' => $dbw->timestamp( $user->mRegistration ),
4164 'user_editcount' => 0,
4165 'user_touched' => $dbw->timestamp( $user->newTouchedTimestamp() ),
4167 foreach ( $params as $name => $value ) {
4168 $fields[
"user_$name"] = $value;
4171 return $dbw->doAtomicSection( __METHOD__,
function (
IDatabase $dbw, $fname ) use ( $fields ) {
4172 $dbw->
insert(
'user', $fields, $fname, [
'IGNORE' ] );
4174 $newUser = self::newFromId( $dbw->insertId() );
4175 $newUser->mName = $fields[
'user_name'];
4176 $newUser->updateActorId( $dbw );
4178 $newUser->load( self::READ_LATEST );
4214 if ( !$this->mToken ) {
4218 if ( !is_string( $this->mName ) ) {
4219 throw new RuntimeException(
"User name field is not set." );
4225 $status = $dbw->doAtomicSection( __METHOD__,
function (
IDatabase $dbw, $fname ) {
4226 $noPass = PasswordFactory::newInvalidPassword()->toString();
4229 'user_name' => $this->mName,
4230 'user_password' => $noPass,
4231 'user_newpassword' => $noPass,
4232 'user_email' => $this->mEmail,
4233 'user_email_authenticated' => $dbw->
timestampOrNull( $this->mEmailAuthenticated ),
4234 'user_real_name' => $this->mRealName,
4235 'user_token' => strval( $this->mToken ),
4236 'user_registration' => $dbw->
timestamp( $this->mRegistration ),
4237 'user_editcount' => 0,
4238 'user_touched' => $dbw->
timestamp( $this->mTouched ),
4247 [
'user_name' => $this->mName ],
4249 [
'LOCK IN SHARE MODE' ]
4256 throw new MWException( $fname .
": hit a key conflict attempting " .
4257 "to insert user '{$this->mName}' row, but it was not present in select!" );
4259 return Status::newFatal(
'userexists' );
4262 self::$idCacheByName[$this->mName] = $this->mId;
4265 return Status::newGood();
4267 if ( !$status->isGood() ) {
4275 return Status::newGood();
4285 [
'actor_user' => $this->mId,
'actor_name' => $this->mName ],
4288 $this->mActorId = (int)$dbw->
insertId();
4310 wfDebug( __METHOD__ .
"()\n" );
4312 if ( $this->mId == 0 ) {
4316 $userblock = DatabaseBlock::newFromTarget( $this->
getName() );
4317 if ( !$userblock ) {
4321 return (
bool)$userblock->doAutoblock( $this->
getRequest()->getIP() );
4330 if ( $this->mBlock && $this->mBlock->appliesToRight(
'createaccount' ) ) {
4331 return $this->mBlock;
4334 # T15611: if the IP address the user is trying to create an account from is
4335 # blocked with createaccount disabled, prevent new account creation there even
4336 # when the user is logged in
4337 if ( $this->mBlockedFromCreateAccount ===
false && !$this->
isAllowed(
'ipblock-exempt' ) ) {
4338 $this->mBlockedFromCreateAccount = DatabaseBlock::newFromTarget(
4342 return $this->mBlockedFromCreateAccount instanceof
AbstractBlock
4343 && $this->mBlockedFromCreateAccount->
appliesToRight(
'createaccount' )
4344 ? $this->mBlockedFromCreateAccount
4354 return $this->mBlock && $this->mBlock->appliesToRight(
'sendemail' );
4365 return $this->mBlock && $this->mBlock->appliesToRight(
'upload' );
4392 return $title->getTalkPage();
4401 return !$this->
isAllowed(
'autoconfirmed' );
4413 $manager = AuthManager::singleton();
4414 $reqs = AuthenticationRequest::loadRequestsFromSubmission(
4415 $manager->getAuthenticationRequests( AuthManager::ACTION_LOGIN ),
4417 'username' => $this->getName(),
4418 'password' => $password,
4421 $res = $manager->beginAuthentication( $reqs,
'null:' );
4422 switch (
$res->status ) {
4423 case AuthenticationResponse::PASS:
4425 case AuthenticationResponse::FAIL:
4427 LoggerFactory::getInstance(
'authentication' )
4428 ->info( __METHOD__ .
': Authentication failed: ' .
$res->message->plain() );
4431 throw new BadMethodCallException(
4432 'AuthManager returned a response unsupported by ' . __METHOD__
4470 return $request->getSession()->getToken( $salt );
4502 public function matchEditToken( $val, $salt =
'', $request =
null, $maxage =
null ) {
4517 $val = substr( $val, 0, strspn( $val,
'0123456789abcdef' ) ) . Token::SUFFIX;
4536 if (
$type ==
'created' ||
$type ===
false ) {
4537 $message =
'confirmemail_body';
4539 } elseif (
$type ===
true ) {
4540 $message =
'confirmemail_body_changed';
4544 $message =
'confirmemail_body_' .
$type;
4548 'subject' =>
wfMessage(
'confirmemail_subject' )->text(),
4553 $wgLang->userTimeAndDate( $expiration, $this ),
4555 $wgLang->userDate( $expiration, $this ),
4556 $wgLang->userTime( $expiration, $this ) )->text(),
4563 'confirmURL' => $url,
4564 'invalidateURL' => $invalidateURL,
4565 'expiration' => $expiration
4568 Hooks::run(
'UserSendConfirmationMail', [ $this, &$mail, $info ] );
4569 return $this->
sendMail( $mail[
'subject'], $mail[
'body'], $mail[
'from'], $mail[
'replyTo'] );
4583 public function sendMail( $subject, $body, $from =
null, $replyto =
null ) {
4586 if ( $from instanceof
User ) {
4590 wfMessage(
'emailsender' )->inContentLanguage()->text() );
4595 'replyTo' => $replyto,
4616 $hash = md5( $token );
4617 $this->mEmailToken = $hash;
4618 $this->mEmailTokenExpires = $expiration;
4628 return $this->
getTokenUrl(
'ConfirmEmail', $token );
4637 return $this->
getTokenUrl(
'InvalidateEmail', $token );
4656 $title = Title::makeTitle(
NS_MAIN,
"Special:$page/$token" );
4657 return $title->getCanonicalURL();
4672 Hooks::run(
'ConfirmEmailComplete', [ $this ] );
4686 $this->mEmailToken =
null;
4687 $this->mEmailTokenExpires =
null;
4690 Hooks::run(
'InvalidateEmailComplete', [ $this ] );
4700 $this->mEmailAuthenticated = $timestamp;
4701 Hooks::run(
'UserSetEmailAuthenticationTimestamp', [ $this, &$this->mEmailAuthenticated ] );
4717 Hooks::run(
'UserCanSendEmail', [ &$user, &$canSend ] );
4746 if ( Hooks::run(
'EmailConfirmed', [ &$user, &$confirmed ] ) ) {
4750 if ( !Sanitizer::validateEmail( $this->mEmail ) ) {
4770 $this->mEmailToken &&
4786 return $this->mRegistration;
4818 if ( $this->
getId() == 0 ) {
4822 $actorWhere = ActorMigration::newMigration()->getWhere(
$dbr,
'rev_user', $this );
4823 $tsField = isset( $actorWhere[
'tables'][
'temp_rev_user'] )
4824 ?
'revactor_timestamp' :
'rev_timestamp';
4825 $sortOrder = $first ?
'ASC' :
'DESC';
4826 $time =
$dbr->selectField(
4827 [
'revision' ] + $actorWhere[
'tables'],
4829 [ $actorWhere[
'conds'] ],
4831 [
'ORDER BY' =>
"$tsField $sortOrder" ],
4832 $actorWhere[
'joins']
4850 return MediaWikiServices::getInstance()->getPermissionManager()->getGroupPermissions( $groups );
4863 return MediaWikiServices::getInstance()->getPermissionManager()->getGroupsWithPermission( $role );
4882 return MediaWikiServices::getInstance()->getPermissionManager()
4883 ->groupHasPermission( $group, $role );
4905 return MediaWikiServices::getInstance()->getPermissionManager()->isEveryoneAllowed( $right );
4916 return array_values( array_diff(
4918 self::getImplicitGroups()
4930 return MediaWikiServices::getInstance()->getPermissionManager()->getAllPermissions();
4967 $groups[
'add'] = self::getAllGroups();
4976 $groups[
'remove'] = self::getAllGroups();
4984 if ( is_int( $key ) ) {
4992 if ( is_int( $key ) ) {
5003 $groups[
'add-self'] = self::getAllGroups();
5011 $groups[
'remove-self'] = self::getAllGroups();
5027 if ( $this->
isAllowed(
'userrights' ) ) {
5032 $all = array_merge( self::getAllGroups() );
5050 foreach ( $addergroups as $addergroup ) {
5051 $groups = array_merge_recursive(
5054 $groups[
'add'] = array_unique( $groups[
'add'] );
5055 $groups[
'remove'] = array_unique( $groups[
'remove'] );
5056 $groups[
'add-self'] = array_unique( $groups[
'add-self'] );
5057 $groups[
'remove-self'] = array_unique( $groups[
'remove-self'] );
5070 DeferredUpdates::addUpdate(
5072 DeferredUpdates::POSTSEND
5082 $this->mEditCount = $count;
5095 $actorWhere = ActorMigration::newMigration()->getWhere(
$dbr,
'rev_user', $this );
5096 $count = (int)
$dbr->selectField(
5097 [
'revision' ] + $actorWhere[
'tables'],
5099 [ $actorWhere[
'conds'] ],
5102 $actorWhere[
'joins']
5108 [
'user_editcount' => $count ],
5110 'user_id' => $this->
getId(),
5111 'user_editcount IS NULL OR user_editcount < ' . (
int)$count
5127 $key =
"right-$right";
5129 return $msg->isDisabled() ? $right : $msg->text();
5140 $key =
"grant-$grant";
5142 return $msg->isDisabled() ? $grant : $msg->text();
5192 if ( $this->mOptionsLoaded ) {
5196 $this->mOptions = self::getDefaultOptions();
5198 if ( !$this->
getId() ) {
5203 $variant = MediaWikiServices::getInstance()->getContentLanguage()->getDefaultVariant();
5204 $this->mOptions[
'variant'] = $variant;
5205 $this->mOptions[
'language'] = $variant;
5206 $this->mOptionsLoaded =
true;
5211 if ( !is_null( $this->mOptionOverrides ) ) {
5212 wfDebug(
"User: loading options for user " . $this->
getId() .
" from override cache.\n" );
5213 foreach ( $this->mOptionOverrides as $key => $value ) {
5214 $this->mOptions[$key] = $value;
5217 if ( !is_array( $data ) ) {
5218 wfDebug(
"User: loading options for user " . $this->
getId() .
" from database.\n" );
5220 $dbr = ( $this->queryFlagsUsed & self::READ_LATEST )
5226 [
'up_property',
'up_value' ],
5227 [
'up_user' => $this->
getId() ],
5231 $this->mOptionOverrides = [];
5233 foreach (
$res as $row ) {
5238 if ( $row->up_value ===
'0' ) {
5241 $data[$row->up_property] = $row->up_value;
5245 foreach ( $data as $property => $value ) {
5246 $this->mOptionOverrides[$property] = $value;
5247 $this->mOptions[$property] = $value;
5252 $this->mOptions[
'language'] = LanguageCode::replaceDeprecatedCodes(
5253 $this->mOptions[
'language']
5256 $this->mOptionsLoaded =
true;
5258 Hooks::run(
'UserLoadOptions', [ $this, &$this->mOptions ] );
5270 $saveOptions = $this->mOptions;
5274 if ( !Hooks::run(
'UserSaveOptions', [ $this, &$saveOptions ] ) ) {
5278 $userId = $this->
getId();
5281 foreach ( $saveOptions as $key => $value ) {
5283 $defaultOption = self::getDefaultOption( $key );
5284 if ( ( $defaultOption ===
null && $value !==
false && $value !==
null )
5285 || $value != $defaultOption
5288 'up_user' => $userId,
5289 'up_property' => $key,
5290 'up_value' => $value,
5297 $res = $dbw->select(
'user_properties',
5298 [
'up_property',
'up_value' ], [
'up_user' => $userId ], __METHOD__ );
5303 foreach (
$res as $row ) {
5304 if ( !isset( $saveOptions[$row->up_property] )
5305 || strcmp( $saveOptions[$row->up_property], $row->up_value ) != 0
5307 $keysDelete[] = $row->up_property;
5311 if ( count( $keysDelete ) ) {
5319 $dbw->delete(
'user_properties',
5320 [
'up_user' => $userId,
'up_property' => $keysDelete ], __METHOD__ );
5323 $dbw->insert(
'user_properties', $insert_rows, __METHOD__, [
'IGNORE' ] );
5341 'user_email_authenticated',
5343 'user_email_token_expires',
5344 'user_registration',
5360 'tables' => [
'user',
'user_actor' =>
'actor' ],
5368 'user_email_authenticated',
5370 'user_email_token_expires',
5371 'user_registration',
5373 'user_actor.actor_id',
5376 'user_actor' => [
'JOIN',
'user_actor.actor_user = user_id' ],
5394 foreach ( MediaWikiServices::getInstance()
5397 $groups[] = UserGroupMembership::getLink( $group, RequestContext::getMain(),
'wiki' );
5401 return Status::newFatal(
'badaccess-groups',
$wgLang->commaList( $groups ), count( $groups ) );
5404 return Status::newFatal(
'badaccess-group0' );
5417 if ( !$this->
getId() ) {
5421 $user = self::newFromId( $this->
getId() );
5422 if ( !$user->loadFromId( self::READ_EXCLUSIVE ) ) {
5447 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.
$wgDefaultUserOptions
Settings added to this array will override the default globals for the user preferences used by anony...
$wgDisableAnonTalk
Disable links to talk pages of anonymous users (IPs) in listings on special pages like page history,...
$wgAutopromoteOnceLogInRC
Put user rights log entries for autopromotion in recent changes?
$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...
$wgShowUpdatedMarker
Show "Updated (since my last visit)" marker in RC view, watchlist and history view for watched pages ...
$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.
$wgInvalidUsernameCharacters
Characters to prevent during new account creations.
$wgEnableEmail
Set to true to enable the e-mail basic features: Password reminders, etc.
$wgMaxNameChars
Maximum number of bytes in username.
$wgSecureLogin
This is to let user authenticate using https when they come from http.
$wgImplicitGroups
Implicit groups, aren't shown on Special:Listusers or somewhere else.
$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.
$wgReservedUsernames
Array of usernames which may not be registered or logged in from Maintenance scripts can still use th...
$wgDefaultSkin
Default skin, for new users and anonymous visitors.
$wgRevokePermissions
Permission keys revoked from users in each group.
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.
$wgNamespacesToBeSearchedDefault
List of namespaces which are searched by default.
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)
Throws a warning that $function is deprecated.
$wgGroupPermissions['sysop']['replacetext']
foreach( $wgExtensionFunctions as $func) if(!defined('MW_NO_SESSION') &&! $wgCommandLineMode) if(! $wgCommandLineMode) $wgFullyInitialised
if(! $wgDBerrorLogTZ) $wgRequest
static isExternal( $username)
Tells whether the username is external or not.
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.
static newFromUser(User $user)
Create a new MailAddress object for the given user.
Class for creating new log entries and inserting them into the database.
Handles increment the edit count for a given set of users.
Represents a "user group membership" – a specific instance of a user belonging to a group.
static send( $to, $from, $subject, $body, $options=[])
This function will perform a direct (authenticated) login to a SMTP Server to use for mail relaying i...
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,...
getEditTimestamp( $first)
Get the timestamp of the first or latest edit.
loadFromSession()
Load user data from the session.
addWatch( $title, $checkRights=self::CHECK_USER_RIGHTS)
Watch an article.
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 string null $defOptLang
Is the user an IP range?
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.
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.
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.
loadOptions( $data=null)
Load the user options either from cache, the database or an array.
getBoolOption( $oname)
Get the user's current setting for a given option, as a boolean value.
getBlockedStatus( $fromReplica=true)
Get blocking information.
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().
deleteNewtalk( $field, $id)
Clear the new messages flag for the given user.
loadFromDatabase( $flags=self::READ_LATEST)
Load user and user_group 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.
checkTemporaryPassword( $plaintext)
Check if the given clear-text password matches the temporary password sent by e-mail for password res...
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.
setInternalPassword( $str)
Set the password and reset the random token unconditionally.
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).
getEffectiveGroups( $recache=false)
Get the list of implicit group memberships this user has.
static selectFields()
Return the list of user fields that should be selected to create a new user object.
isHidden()
Check if user account is hidden.
static array null $defOpt
Is the user an IP range?
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.
static string[] false $reservedUsernames
Cache for self::isUsableName()
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.
static resetGetDefaultOptionsForTestsOnly()
Reset the process cache of default user options.
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.
setPassword( $str)
Set the password and reset the random token.
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.
loadGroups()
Load the groups from the database if they aren't already loaded.
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.
bool $mOptionsLoaded
Whether the cache variables have been loaded.
saveOptions()
Saves the non-default options for this user, as previously set e.g.
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.
UserGroupMembership[] $mGroupMemberships
Associative array of (group name => UserGroupMembership object)
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.
setPasswordInternal( $str)
Actually set the password and such.
isEmailConfirmationPending()
Check whether there is an outstanding request for e-mail confirmation.
checkNewtalk( $field, $id)
Internal uncached check for new messages.
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.
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.
isLoggedIn()
Get whether the user is logged in.
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...
updateNewtalk( $field, $id, $curRev=null)
Add or update the new messages flag.
static createNew( $name, $params=[])
Add a user to the database, return the user object.
addNewUserLogEntryAutoCreate()
Add an autocreate newuser log entry for this user Used by things like CentralAuth and perhaps other a...
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().
getBlock( $fromReplica=true)
Get the block affecting the user, or null if the user is not blocked.
resetTokenFromOption( $oname)
Reset a token stored in the preferences (like the watchlist one).
static getImplicitGroups()
Get a list of implicit groups TODO: Should we deprecate this? It's trivial, but we don't want to enco...
changeableGroups()
Returns an array of groups that this user can add and remove.
int bool $mNewtalk
Lazy-initialized variables, invalidated with clearInstanceCache.
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.
checkPassword( $password)
Check to see if the given clear-text password is one of the accepted passwords.
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.