29use Wikimedia\ScopedCallback;
36define(
'EDIT_TOKEN_SUFFIX', Token::SUFFIX );
101 'mEmailAuthenticated',
103 'mEmailTokenExpires',
149 'editusercssjs', # deprecated
162 'move-categorypages',
163 'move-rootuserpages',
167 'override-export-depth',
190 'userrights-interwiki',
324 return (
string)$this->
getName();
350 $this->mLoadedItems ===
true || $this->mFrom !==
'session';
361 if ( $this->mLoadedItems ===
true ) {
366 $oldLoadedItems = $this->mLoadedItems;
367 $this->mLoadedItems =
true;
368 $this->queryFlagsUsed =
$flags;
372 \MediaWiki\Logger\LoggerFactory::getInstance(
'session' )
373 ->warning(
'User::loadFromSession called before the end of Setup.php', [
374 'exception' =>
new Exception(
'User::loadFromSession called before the end of Setup.php' ),
377 $this->mLoadedItems = $oldLoadedItems;
381 switch ( $this->mFrom ) {
387 if (
wfGetLB()->hasOrMadeRecentMasterChanges() ) {
388 $flags |= self::READ_LATEST;
389 $this->queryFlagsUsed =
$flags;
392 $this->mId = self::idFromName( $this->mName,
$flags );
408 Hooks::run(
'UserLoadAfterLoadFromSession', [ $this ] );
411 throw new UnexpectedValueException(
412 "Unrecognised value for User->mFrom: \"{$this->mFrom}\"" );
422 if ( $this->mId == 0 ) {
440 $this->mLoadedItems =
true;
441 $this->queryFlagsUsed =
$flags;
451 public static function purge( $wikiId, $userId ) {
452 $cache = ObjectCache::getMainWANInstance();
453 $key =
$cache->makeGlobalKey(
'user',
'id', $wikiId, $userId );
463 return $cache->makeGlobalKey(
'user',
'id',
wfWikiID(), $this->mId );
473 $cache = ObjectCache::getMainWANInstance();
474 $data =
$cache->getWithSetCallback(
479 wfDebug(
"User: cache miss for user {$this->mId}\n" );
486 foreach ( self::$mCacheVars
as $name ) {
487 $data[
$name] = $this->$name;
495 [
'pcTTL' => $cache::TTL_PROC_LONG,
'version' => self::VERSION ]
499 foreach ( self::$mCacheVars
as $name ) {
500 $this->$name = $data[
$name];
525 public static function newFromName( $name, $validate =
'valid' ) {
526 if ( $validate ===
true ) {
529 $name = self::getCanonicalName(
$name, $validate );
530 if (
$name ===
false ) {
537 $u->setItemLoaded(
'name' );
568 $db = (
$flags & self::READ_LATEST ) == self::READ_LATEST
572 $id = $db->selectField(
576 'user_email_token' => md5(
$code ),
577 'user_email_token_expires > ' . $db->addQuotes( $db->timestamp() ),
593 $user->mFrom =
'session';
614 $user->loadFromRow( $row, $data );
655 'validate' =>
'valid',
661 if (
$name ===
false ) {
665 $fields = self::selectFields();
668 $row = $dbw->selectRow(
671 [
'user_name' =>
$name ],
678 $user = self::newFromRow( $row );
682 if (
$user->mEmail ||
$user->mToken !== self::INVALID_TOKEN ||
683 AuthManager::singleton()->userCanAuthenticate(
$name )
690 AuthManager::singleton()->revokeAccessForUser(
$name );
692 $user->invalidateEmail();
693 $user->mToken = self::INVALID_TOKEN;
694 $user->saveSettings();
695 SessionManager::singleton()->preventSessionsForUser(
$user->getName() );
708 public static function whoIs( $id ) {
730 if ( is_null( $nt ) ) {
735 if ( !(
$flags & self::READ_LATEST ) && isset( self::$idCacheByName[
$name] ) ) {
736 return self::$idCacheByName[
$name];
745 [
'user_name' => $nt->getText() ],
750 if (
$s ===
false ) {
758 if ( count( self::$idCacheByName ) > 1000 ) {
759 self::$idCacheByName = [];
769 self::$idCacheByName = [];
788 public static function isIP( $name ) {
789 return preg_match(
'/^\d{1,3}\.\d{1,3}\.\d{1,3}\.(?:xxx|\d{1,3})$/',
$name )
809 || strpos(
$name,
'/' ) !==
false
818 $parsed = Title::newFromText(
$name );
819 if ( is_null( $parsed )
820 || $parsed->getNamespace()
821 || strcmp(
$name, $parsed->getPrefixedText() ) ) {
827 $unicodeBlacklist =
'/[' .
828 '\x{0080}-\x{009f}' . # iso-8859-1 control chars
829 '\x{00a0}' . # non-breaking space
830 '\x{2000}-\x{200f}' . # various whitespace
831 '\x{2028}-\x{202f}' . # breaks
and control chars
832 '\x{3000}' . # ideographic space
833 '\x{e000}-\x{f8ff}' . #
private use
835 if ( preg_match( $unicodeBlacklist,
$name ) ) {
856 if ( !self::isValidUserName(
$name ) ) {
860 static $reservedUsernames =
false;
861 if ( !$reservedUsernames ) {
863 Hooks::run(
'UserGetReservedNames', [ &$reservedUsernames ] );
867 foreach ( $reservedUsernames
as $reserved ) {
868 if ( substr( $reserved, 0, 4 ) ==
'msg:' ) {
869 $reserved =
wfMessage( substr( $reserved, 4 ) )->inContentLanguage()->text();
871 if ( $reserved ==
$name ) {
889 if ( $groups === [] ) {
893 $groups = array_unique( (
array)$groups );
896 $conds = [
'ug_group' => $groups ];
897 if ( $after !==
null ) {
898 $conds[] =
'ug_user > ' . (int)$after;
902 $ids =
$dbr->selectFieldValues(
909 'ORDER BY' =>
'ug_user',
934 if ( strlen(
$name ) > 235 ) {
936 ": '$name' invalid due to length" );
944 ": '$name' invalid due to wgInvalidUsernameCharacters" );
949 return self::isUsableName(
$name );
975 foreach (
$result->getErrorsByType(
'error' )
as $error ) {
978 foreach (
$result->getErrorsByType(
'warning' )
as $warning ) {
1017 if ( !Hooks::run(
'isValidPassword', [ $password, &
$result, $this ] ) ) {
1023 $status->merge( $upp->checkUserPassword( $this, $password, $purpose ) );
1025 } elseif (
$result ===
true ) {
1051 # Reject names containing '#'; these will be cleaned up
1052 # with title normalisation, but then it's too late to
1054 if ( strpos(
$name,
'#' ) !==
false ) {
1060 $t = ( $validate !==
false ) ?
1063 if ( is_null(
$t ) ||
$t->getNamespace() !==
NS_USER ||
$t->isExternal() ) {
1068 $name = AuthManager::callLegacyAuthPlugin(
1069 'getCanonicalName', [
$t->getText() ],
$t->getText()
1072 switch ( $validate ) {
1091 throw new InvalidArgumentException(
1092 'Invalid parameter value for $validate in ' . __METHOD__ );
1107 $user = self::newFromId( $uid );
1108 return $user->getEditCount();
1132 $this->mName =
$name;
1133 $this->mRealName =
'';
1135 $this->mOptionOverrides =
null;
1136 $this->mOptionsLoaded =
false;
1138 $loggedOut = $this->mRequest && !defined(
'MW_NO_SESSION' )
1139 ? $this->mRequest->getSession()->getLoggedOutTimestamp() : 0;
1140 if ( $loggedOut !== 0 ) {
1143 $this->mTouched =
'1'; # Allow any
pages to be cached
1146 $this->mToken =
null;
1147 $this->mEmailAuthenticated =
null;
1148 $this->mEmailToken =
'';
1149 $this->mEmailTokenExpires =
null;
1151 $this->mGroups = [];
1153 Hooks::run(
'UserLoadDefaults', [ $this,
$name ] );
1169 return ( $this->mLoadedItems ===
true && $all ===
'all' ) ||
1170 ( isset( $this->mLoadedItems[$item] ) && $this->mLoadedItems[$item] ===
true );
1179 if ( is_array( $this->mLoadedItems ) ) {
1180 $this->mLoadedItems[$item] =
true;
1192 Hooks::run(
'UserLoadFromSession', [ $this, &
$result ],
'1.27' );
1199 $session = $this->
getRequest()->getSession();
1200 $user = $session->getUser();
1201 if (
$user->isLoggedIn() ) {
1204 $session->set(
'wsUserID', $this->
getId() );
1205 $session->set(
'wsUserName', $this->
getName() );
1206 $session->set(
'wsToken', $this->
getToken() );
1222 $this->mId = intval( $this->mId );
1224 if ( !$this->mId ) {
1233 $s = $db->selectRow(
1235 self::selectFields(),
1236 [
'user_id' => $this->mId ],
1241 $this->queryFlagsUsed =
$flags;
1242 Hooks::run(
'UserLoadFromDatabase', [ $this, &
$s ] );
1244 if (
$s !==
false ) {
1247 $this->mGroups =
null;
1270 $this->mGroups =
null;
1272 if ( isset( $row->user_name ) ) {
1273 $this->mName = $row->user_name;
1274 $this->mFrom =
'name';
1280 if ( isset( $row->user_real_name ) ) {
1281 $this->mRealName = $row->user_real_name;
1287 if ( isset( $row->user_id ) ) {
1288 $this->mId = intval( $row->user_id );
1289 $this->mFrom =
'id';
1295 if ( isset( $row->user_id ) && isset( $row->user_name ) ) {
1296 self::$idCacheByName[$row->user_name] = $row->user_id;
1299 if ( isset( $row->user_editcount ) ) {
1300 $this->mEditCount = $row->user_editcount;
1305 if ( isset( $row->user_touched ) ) {
1311 if ( isset( $row->user_token ) ) {
1315 $this->mToken = rtrim( $row->user_token,
" \0" );
1316 if ( $this->mToken ===
'' ) {
1317 $this->mToken =
null;
1323 if ( isset( $row->user_email ) ) {
1324 $this->mEmail = $row->user_email;
1326 $this->mEmailToken = $row->user_email_token;
1334 $this->mLoadedItems =
true;
1337 if ( is_array( $data ) ) {
1338 if ( isset( $data[
'user_groups'] ) && is_array( $data[
'user_groups'] ) ) {
1339 $this->mGroups = $data[
'user_groups'];
1341 if ( isset( $data[
'user_properties'] ) && is_array( $data[
'user_properties'] ) ) {
1354 foreach ( self::$mCacheVars
as $var ) {
1355 $this->$var =
$user->$var;
1363 if ( is_null( $this->mGroups ) ) {
1364 $db = ( $this->queryFlagsUsed & self::READ_LATEST )
1367 $res = $db->select(
'user_groups',
1369 [
'ug_user' => $this->mId ],
1371 $this->mGroups = [];
1372 foreach (
$res as $row ) {
1373 $this->mGroups[] = $row->ug_group;
1400 if ( !count( $toPromote ) ) {
1409 foreach ( $toPromote
as $group ) {
1413 Hooks::run(
'UserGroupsChanged', [ $this, $toPromote, [],
false,
false ] );
1414 AuthManager::callLegacyAuthPlugin(
'updateExternalDBGroups', [ $this, $toPromote ] );
1416 $newGroups = array_merge( $oldGroups, $toPromote );
1419 $logEntry->setPerformer( $this );
1421 $logEntry->setParameters( [
1422 '4::oldgroups' => $oldGroups,
1423 '5::newgroups' => $newGroups,
1425 $logid = $logEntry->insert();
1427 $logEntry->publish( $logid );
1443 if ( $this->mTouched ) {
1445 $conditions[
'user_touched'] = $db->
timestamp( $this->mTouched );
1463 if ( !$this->mId ) {
1471 $dbw->update(
'user',
1472 [
'user_touched' => $dbw->timestamp( $newTouched ) ],
1473 $this->makeUpdateConditions( $dbw, [
1474 'user_id' => $this->mId,
1478 $success = ( $dbw->affectedRows() > 0 );
1481 $this->mTouched = $newTouched;
1499 $this->mNewtalk = -1;
1500 $this->mDatePreference =
null;
1501 $this->mBlockedby = -1; # Unset
1502 $this->mHash =
false;
1503 $this->mRights =
null;
1504 $this->mEffectiveGroups =
null;
1505 $this->mImplicitGroups =
null;
1506 $this->mGroups =
null;
1507 $this->mOptions =
null;
1508 $this->mOptionsLoaded =
false;
1509 $this->mEditCount =
null;
1511 if ( $reloadFrom ) {
1512 $this->mLoadedItems = [];
1513 $this->mFrom = $reloadFrom;
1526 static $defOpt =
null;
1527 static $defOptLang =
null;
1529 if ( $defOpt !==
null && $defOptLang ===
$wgContLang->getCode() ) {
1539 $defOpt[
'language'] = $defOptLang;
1541 $defOpt[$langCode ==
$wgContLang->getCode() ?
'variant' :
"variant-$langCode"] = $langCode;
1548 $defOpt[
'searchNs' . $nsnum] = (
boolean)$val;
1552 Hooks::run(
'UserGetDefaultOptions', [ &$defOpt ] );
1564 $defOpts = self::getDefaultOptions();
1565 if ( isset( $defOpts[$opt] ) ) {
1566 return $defOpts[$opt];
1581 if ( -1 != $this->mBlockedby ) {
1585 wfDebug( __METHOD__ .
": checking...\n" );
1594 # We only need to worry about passing the IP address to the Block generator if the
1595 # user is not immune to autoblocks/hardblocks, and they are the current user so we
1596 # know which IP address they're actually coming from
1598 if ( !$this->
isAllowed(
'ipblock-exempt' ) ) {
1601 $globalUserName =
$wgUser->isSafeToLoad()
1604 if ( $this->
getName() === $globalUserName ) {
1615 if ( self::isLocallyBlockedProxy( $ip ) ) {
1618 $block->mReason =
wfMessage(
'proxyblockreason' )->text();
1619 $block->setTarget( $ip );
1623 $block->mReason =
wfMessage(
'sorbsreason' )->text();
1624 $block->setTarget( $ip );
1629 if ( !$block instanceof
Block
1634 $xff = $this->
getRequest()->getHeader(
'X-Forwarded-For' );
1635 $xff = array_map(
'trim', explode(
',', $xff ) );
1636 $xff = array_diff( $xff, [ $ip ] );
1639 if ( $block instanceof
Block ) {
1640 # Mangle the reason to alert the user that the block
1641 # originated from matching the X-Forwarded-For header.
1642 $block->mReason =
wfMessage(
'xffblockreason', $block->mReason )->text();
1646 if ( $block instanceof
Block ) {
1647 wfDebug( __METHOD__ .
": Found block.\n" );
1648 $this->mBlock = $block;
1649 $this->mBlockedby = $block->getByName();
1650 $this->mBlockreason = $block->mReason;
1651 $this->mHideName = $block->mHideName;
1652 $this->mAllowUsertalk = !$block->prevents(
'editownusertalk' );
1654 $this->mBlockedby =
'';
1655 $this->mHideName = 0;
1656 $this->mAllowUsertalk =
false;
1660 Hooks::run(
'GetBlockedStatus', [ &$this ] );
1698 $ipReversed = implode(
'.', array_reverse( explode(
'.', $ip ) ) );
1704 if ( is_array(
$base ) ) {
1705 if ( count(
$base ) >= 2 ) {
1707 $host =
"{$base[1]}.$ipReversed.{$base[0]}";
1709 $host =
"$ipReversed.{$base[0]}";
1711 $basename =
$base[0];
1713 $host =
"$ipReversed.$base";
1717 $ipList = gethostbynamel( $host );
1720 wfDebugLog(
'dnsblacklist',
"Hostname $host is {$ipList[0]}, it's a proxy says $basename!" );
1724 wfDebugLog(
'dnsblacklist',
"Requested $host, not found in $basename." );
1753 } elseif ( array_search( $ip,
$wgProxyList ) !==
false ) {
1777 return !$this->
isAllowed(
'noratelimit' );
1797 if ( !Hooks::run(
'PingLimiter', [ &$this, $action, &
$result,
$incrBy ] ) ) {
1806 $limits = array_merge(
1807 [
'&can-bypass' =>
true ],
1817 $id = $this->
getId();
1823 if ( isset( $limits[
'anon'] ) ) {
1824 $keys[
wfMemcKey(
'limiter', $action,
'anon' )] = $limits[
'anon'];
1828 if ( isset( $limits[
'user'] ) ) {
1829 $userLimit = $limits[
'user'];
1832 if ( $isNewbie && isset( $limits[
'newbie'] ) ) {
1833 $keys[
wfMemcKey(
'limiter', $action,
'user', $id )] = $limits[
'newbie'];
1840 if ( isset( $limits[
'ip'] ) ) {
1842 $keys[
"mediawiki:limiter:$action:ip:$ip"] = $limits[
'ip'];
1845 if ( isset( $limits[
'subnet'] ) ) {
1848 if ( $subnet !==
false ) {
1849 $keys[
"mediawiki:limiter:$action:subnet:$subnet"] = $limits[
'subnet'];
1857 if ( isset( $limits[$group] ) ) {
1858 if ( $userLimit ===
false
1859 || $limits[$group][0] / $limits[$group][1] > $userLimit[0] / $userLimit[1]
1861 $userLimit = $limits[$group];
1867 if ( $userLimit !==
false ) {
1868 list( $max, $period ) = $userLimit;
1869 wfDebug( __METHOD__ .
": effective user limit: $max in {$period}s\n" );
1870 $keys[
wfMemcKey(
'limiter', $action,
'user', $id )] = $userLimit;
1874 if ( isset( $limits[
'ip-all'] ) ) {
1877 if ( $isNewbie || $userLimit ===
false
1878 || $limits[
'ip-all'][0] / $limits[
'ip-all'][1] > $userLimit[0] / $userLimit[1] ) {
1879 $keys[
"mediawiki:limiter:$action:ip-all:$ip"] = $limits[
'ip-all'];
1884 if ( isset( $limits[
'subnet-all'] ) ) {
1887 if ( $subnet !==
false ) {
1889 if ( $isNewbie || $userLimit ===
false
1890 || $limits[
'ip-all'][0] / $limits[
'ip-all'][1]
1891 > $userLimit[0] / $userLimit[1] ) {
1892 $keys[
"mediawiki:limiter:$action:subnet-all:$subnet"] = $limits[
'subnet-all'];
1897 $cache = ObjectCache::getLocalClusterInstance();
1902 $summary =
"(limit $max in {$period}s)";
1907 wfDebugLog(
'ratelimit',
"User '{$this->getName()}' " .
1908 "(IP {$this->getRequest()->getIP()}) tripped $key at $count $summary" );
1911 wfDebug( __METHOD__ .
": ok. $key at $count $summary\n" );
1914 wfDebug( __METHOD__ .
": adding record for $key $summary\n" );
1916 $cache->add( $key, 0, intval( $period ) );
1946 return $this->mBlock instanceof
Block ? $this->mBlock :
null;
1959 $blocked = $this->
isBlocked( $bFromSlave );
1962 if ( !$this->mHideName && $allowUsertalk &&
$title->getText() === $this->
getName()
1965 wfDebug( __METHOD__ .
": self-talk page, ignoring any blocks\n" );
1968 Hooks::run(
'UserIsBlockedFrom', [ $this,
$title, &$blocked, &$allowUsertalk ] );
1979 return $this->mBlockedby;
1988 return $this->mBlockreason;
1997 return ( $this->mBlock ? $this->mBlock->getId() :
false );
2023 if ( $this->mGlobalBlock !==
null ) {
2024 return $this->mGlobalBlock ?:
null;
2034 Hooks::run(
'UserIsBlockedGlobally', [ &$this, $ip, &$blocked, &$block ] );
2036 if ( $blocked && $block ===
null ) {
2042 $this->mGlobalBlock = $blocked ? $block :
false;
2043 return $this->mGlobalBlock ?:
null;
2052 if ( $this->mLocked !==
null ) {
2053 return $this->mLocked;
2055 $authUser = AuthManager::callLegacyAuthPlugin(
'getUserInstance', [ &$this ],
null );
2056 $this->mLocked = $authUser && $authUser->isLocked();
2057 Hooks::run(
'UserIsLocked', [ $this, &$this->mLocked ] );
2058 return $this->mLocked;
2067 if ( $this->mHideName !==
null ) {
2068 return $this->mHideName;
2071 if ( !$this->mHideName ) {
2072 $authUser = AuthManager::callLegacyAuthPlugin(
'getUserInstance', [ &$this ],
null );
2073 $this->mHideName = $authUser && $authUser->isHidden();
2074 Hooks::run(
'UserIsHidden', [ $this, &$this->mHideName ] );
2076 return $this->mHideName;
2084 if ( $this->mId ===
null && $this->mName !==
null &&
User::isIP( $this->mName ) ) {
2092 return (
int)$this->mId;
2111 return $this->mName;
2114 if ( $this->mName ===
false ) {
2118 return $this->mName;
2137 $this->mName = $str;
2145 return str_replace(
' ',
'_', $this->
getName() );
2156 if ( $this->mNewtalk === -1 ) {
2157 $this->mNewtalk =
false; # reset talk
page status
2161 if ( !$this->mId ) {
2165 $this->mNewtalk =
false;
2170 $this->mNewtalk = $this->
checkNewtalk(
'user_id', $this->mId );
2174 return (
bool)$this->mNewtalk;
2192 if ( !Hooks::run(
'UserRetrieveNewTalks', [ &$this, &$talks ] ) ) {
2201 'MIN(user_last_timestamp)',
2202 $this->
isAnon() ? [
'user_ip' => $this->
getName() ] : [
'user_id' => $this->
getId() ],
2205 return [ [
'wiki' =>
wfWikiID(),
'link' => $utp->getLocalURL(),
'rev' =>
$rev ] ];
2214 $newMessageRevisionId =
null;
2216 if ( $newMessageLinks ) {
2220 if ( count( $newMessageLinks ) === 1
2221 && $newMessageLinks[0][
'wiki'] ===
wfWikiID()
2222 && $newMessageLinks[0][
'rev']
2225 $newMessageRevision = $newMessageLinks[0][
'rev'];
2226 $newMessageRevisionId = $newMessageRevision->getId();
2229 return $newMessageRevisionId;
2243 $ok =
$dbr->selectField(
'user_newtalk', $field, [ $field => $id ], __METHOD__ );
2245 return $ok !==
false;
2257 $prevRev = $curRev ? $curRev->getPrevious() :
false;
2258 $ts = $prevRev ? $prevRev->getTimestamp() :
null;
2261 $dbw->insert(
'user_newtalk',
2262 [ $field => $id,
'user_last_timestamp' => $dbw->timestampOrNull( $ts ) ],
2265 if ( $dbw->affectedRows() ) {
2266 wfDebug( __METHOD__ .
": set on ($field, $id)\n" );
2269 wfDebug( __METHOD__ .
" already set ($field, $id)\n" );
2282 $dbw->delete(
'user_newtalk',
2285 if ( $dbw->affectedRows() ) {
2286 wfDebug( __METHOD__ .
": killed on ($field, $id)\n" );
2289 wfDebug( __METHOD__ .
": already gone ($field, $id)\n" );
2306 $this->mNewtalk = $val;
2313 $id = $this->
getId();
2336 if ( $this->mTouched && $time <= $this->mTouched ) {
2354 if ( !$this->
getId() ) {
2358 $cache = ObjectCache::getMainWANInstance();
2360 if ( $mode ===
'refresh' ) {
2361 $cache->delete( $key, 1 );
2395 $id = $this->
getId();
2397 $key =
wfMemcKey(
'user-quicktouched',
'id', $id );
2398 ObjectCache::getMainWANInstance()->touchCheckKey( $key );
2399 $this->mQuickTouched =
null;
2424 if ( $this->mQuickTouched ===
null ) {
2425 $key =
wfMemcKey(
'user-quicktouched',
'id', $this->mId );
2426 $cache = ObjectCache::getMainWANInstance();
2431 return max( $this->mTouched, $this->mQuickTouched );
2434 return $this->mTouched;
2445 return $this->mTouched;
2454 throw new BadMethodCallException( __METHOD__ .
' has been removed in 1.27' );
2463 throw new BadMethodCallException( __METHOD__ .
' has been removed in 1.27' );
2507 $manager = AuthManager::singleton();
2510 if ( !$manager->userExists( $this->getName() ) ) {
2511 throw new LogicException(
'Cannot set a password for a user that is not in the database.' );
2515 'username' => $this->
getName(),
2520 \MediaWiki\Logger\LoggerFactory::getInstance(
'authentication' )
2521 ->info( __METHOD__ .
': Password change rejected: '
2522 .
$status->getWikiText(
null,
null,
'en' ) );
2526 $this->
setOption(
'watchlisttoken',
false );
2527 SessionManager::singleton()->invalidateSessionsForUser( $this );
2545 $manager = AuthManager::singleton();
2546 $reqs = $manager->getAuthenticationRequests( AuthManager::ACTION_CHANGE, $this );
2547 $reqs = AuthenticationRequest::loadRequestsFromSubmission( $reqs, $data );
2549 $status = Status::newGood(
'ignored' );
2550 foreach ( $reqs
as $req ) {
2551 $status->merge( $manager->allowsAuthenticationDataChange(
$req ),
true );
2553 if (
$status->getValue() ===
'ignored' ) {
2554 $status->warning(
'authenticationdatachange-ignored' );
2558 foreach ( $reqs
as $req ) {
2559 $manager->changeAuthenticationData(
$req );
2575 if ( !$this->mToken && $forceCreation ) {
2579 if ( !$this->mToken ) {
2582 } elseif ( $this->mToken === self::INVALID_TOKEN ) {
2588 return $this->mToken;
2594 $len = max( 32, self::TOKEN_LENGTH );
2595 if ( strlen(
$ret ) < $len ) {
2597 throw new \UnexpectedValueException(
'Hmac returned less than 128 bits' );
2599 return substr(
$ret, -$len );
2611 if ( $this->mToken === self::INVALID_TOKEN ) {
2612 \MediaWiki\Logger\LoggerFactory::getInstance(
'session' )
2613 ->debug( __METHOD__ .
": Ignoring attempt to set token for system user \"$this\"" );
2614 } elseif ( !$token ) {
2617 $this->mToken = $token;
2630 throw new BadMethodCallException( __METHOD__ .
' has been removed in 1.27' );
2640 throw new BadMethodCallException( __METHOD__ .
' has been removed in 1.27' );
2649 Hooks::run(
'UserGetEmail', [ $this, &$this->mEmail ] );
2650 return $this->mEmail;
2659 Hooks::run(
'UserGetEmailAuthenticationTimestamp', [ $this, &$this->mEmailAuthenticated ] );
2660 return $this->mEmailAuthenticated;
2669 if ( $str == $this->mEmail ) {
2673 $this->mEmail = $str;
2674 Hooks::run(
'UserSetEmail', [ $this, &$this->mEmail ] );
2688 return Status::newFatal(
'emaildisabled' );
2692 if ( $str === $oldaddr ) {
2693 return Status::newGood(
true );
2696 $type = $oldaddr !=
'' ?
'changed' :
'set';
2697 $notificationResult =
null;
2702 if (
$type ==
'changed' ) {
2703 $change = $str !=
'' ?
'changed' :
'removed';
2704 $notificationResult = $this->
sendMail(
2705 wfMessage(
'notificationemail_subject_' . $change )->
text(),
2706 wfMessage(
'notificationemail_body_' . $change,
2720 if ( $notificationResult !==
null ) {
2721 $result->merge( $notificationResult );
2729 $result = Status::newGood(
true );
2744 return $this->mRealName;
2753 $this->mRealName = $str;
2766 public function getOption( $oname, $defaultOverride =
null, $ignoreHidden =
false ) {
2770 # We want 'disabled' preferences to always behave as the default value for
2771 # users, even if they have set the option explicitly in their settings (ie they
2772 # set it, and then it was disabled removing their ability to change it). But
2773 # we don't want to erase the preferences in the database in case the preference
2774 # is re-enabled again. So don't touch $mOptions, just override the returned value
2776 return self::getDefaultOption( $oname );
2779 if ( array_key_exists( $oname, $this->mOptions ) ) {
2780 return $this->mOptions[$oname];
2782 return $defaultOverride;
2799 # We want 'disabled' preferences to always behave as the default value for
2800 # users, even if they have set the option explicitly in their settings (ie they
2801 # set it, and then it was disabled removing their ability to change it). But
2802 # we don't want to erase the preferences in the database in case the preference
2803 # is re-enabled again. So don't touch $mOptions, just override the returned value
2805 $default = self::getDefaultOption( $pref );
2806 if ( $default !==
null ) {
2811 if (
$flags & self::GETOPTIONS_EXCLUDE_DEFAULTS ) {
2826 return (
bool)$this->
getOption( $oname );
2840 $val = $defaultOverride;
2842 return intval( $val );
2857 if ( is_null( $val ) ) {
2858 $val = self::getDefaultOption( $oname );
2861 $this->mOptions[$oname] = $val;
2877 $id = $this->
getId();
2887 $token = hash_hmac(
'sha1',
"$oname:$id", $this->
getToken() );
2939 'registered-multiselect',
2940 'registered-checkmatrix',
2972 unset( $prefs[
$name] );
2977 $multiselectOptions = [];
2978 foreach ( $prefs
as $name => $info ) {
2979 if ( ( isset( $info[
'type'] ) && $info[
'type'] ==
'multiselect' ) ||
2980 ( isset( $info[
'class'] ) && $info[
'class'] ==
'HTMLMultiSelectField' ) ) {
2982 $prefix = isset( $info[
'prefix'] ) ? $info[
'prefix'] :
$name;
2985 $multiselectOptions[
"$prefix$value"] =
true;
2988 unset( $prefs[
$name] );
2991 $checkmatrixOptions = [];
2992 foreach ( $prefs
as $name => $info ) {
2993 if ( ( isset( $info[
'type'] ) && $info[
'type'] ==
'checkmatrix' ) ||
2994 ( isset( $info[
'class'] ) && $info[
'class'] ==
'HTMLCheckMatrix' ) ) {
2997 $prefix = isset( $info[
'prefix'] ) ? $info[
'prefix'] :
$name;
2999 foreach ( $columns
as $column ) {
3000 foreach ( $rows
as $row ) {
3001 $checkmatrixOptions[
"$prefix$column-$row"] =
true;
3005 unset( $prefs[
$name] );
3011 if ( isset( $prefs[$key] ) ) {
3012 $mapping[$key] =
'registered';
3013 } elseif ( isset( $multiselectOptions[$key] ) ) {
3014 $mapping[$key] =
'registered-multiselect';
3015 } elseif ( isset( $checkmatrixOptions[$key] ) ) {
3016 $mapping[$key] =
'registered-checkmatrix';
3017 } elseif ( isset( $specialOptions[$key] ) ) {
3018 $mapping[$key] =
'special';
3019 } elseif ( substr( $key, 0, 7 ) ===
'userjs-' ) {
3020 $mapping[$key] =
'userjs';
3022 $mapping[$key] =
'unused';
3044 $resetKinds = [
'registered',
'registered-multiselect',
'registered-checkmatrix',
'unused' ],
3048 $defaultOptions = self::getDefaultOptions();
3050 if ( !is_array( $resetKinds ) ) {
3051 $resetKinds = [ $resetKinds ];
3054 if ( in_array(
'all', $resetKinds ) ) {
3055 $newOptions = $defaultOptions;
3062 $resetKinds = array_intersect( $resetKinds, self::listOptionKinds() );
3067 foreach ( $this->mOptions
as $key =>
$value ) {
3068 if ( in_array( $optionKinds[$key], $resetKinds ) ) {
3069 if ( array_key_exists( $key, $defaultOptions ) ) {
3070 $newOptions[$key] = $defaultOptions[$key];
3073 $newOptions[$key] =
$value;
3078 Hooks::run(
'UserResetAllOptions', [ $this, &$newOptions, $this->mOptions, $resetKinds ] );
3080 $this->mOptions = $newOptions;
3081 $this->mOptionsLoaded =
true;
3090 if ( is_null( $this->mDatePreference ) ) {
3093 $map =
$wgLang->getDatePreferenceMigrationMap();
3094 if ( isset( $map[
$value] ) ) {
3097 $this->mDatePreference =
$value;
3099 return $this->mDatePreference;
3114 Hooks::run(
'UserRequiresHTTPS', [ $this, &$https ] );
3143 if ( is_null( $this->mRights ) ) {
3145 Hooks::run(
'UserGetRights', [ $this, &$this->mRights ] );
3149 if ( !defined(
'MW_NO_SESSION' ) ) {
3150 $allowedRights = $this->
getRequest()->getSession()->getAllowedUserRights();
3151 if ( $allowedRights !==
null ) {
3152 $this->mRights = array_intersect( $this->mRights, $allowedRights );
3157 $this->mRights = array_values( array_unique( $this->mRights ) );
3168 $config->get(
'BlockDisablesLogin' ) &&
3172 $this->mRights = array_intersect( $this->mRights, $anon->getRights() );
3175 return $this->mRights;
3186 return $this->mGroups;
3197 if ( $recache || is_null( $this->mEffectiveGroups ) ) {
3198 $this->mEffectiveGroups = array_unique( array_merge(
3203 Hooks::run(
'UserEffectiveGroups', [ &$this, &$this->mEffectiveGroups ] );
3205 $this->mEffectiveGroups = array_values( array_unique( $this->mEffectiveGroups ) );
3207 return $this->mEffectiveGroups;
3218 if ( $recache || is_null( $this->mImplicitGroups ) ) {
3219 $this->mImplicitGroups = [
'*' ];
3220 if ( $this->
getId() ) {
3221 $this->mImplicitGroups[] =
'user';
3223 $this->mImplicitGroups = array_unique( array_merge(
3224 $this->mImplicitGroups,
3231 $this->mEffectiveGroups =
null;
3234 return $this->mImplicitGroups;
3249 if ( is_null( $this->mFormerGroups ) ) {
3250 $db = ( $this->queryFlagsUsed & self::READ_LATEST )
3253 $res = $db->select(
'user_former_groups',
3255 [
'ufg_user' => $this->mId ],
3257 $this->mFormerGroups = [];
3258 foreach (
$res as $row ) {
3259 $this->mFormerGroups[] = $row->ufg_group;
3263 return $this->mFormerGroups;
3271 if ( !$this->
getId() ) {
3275 if ( $this->mEditCount ===
null ) {
3280 'user',
'user_editcount',
3281 [
'user_id' => $this->mId ],
3289 $this->mEditCount =
$count;
3291 return (
int)$this->mEditCount;
3303 if ( !Hooks::run(
'UserAddGroup', [ $this, &$group ] ) ) {
3308 if ( $this->
getId() ) {
3309 $dbw->insert(
'user_groups',
3311 'ug_user' => $this->
getId(),
3312 'ug_group' => $group,
3319 $this->mGroups[] = $group;
3322 $this->mGroups = array_unique( $this->mGroups );
3327 $this->mRights =
null;
3342 if ( !Hooks::run(
'UserRemoveGroup', [ $this, &$group ] ) ) {
3347 $dbw->delete(
'user_groups',
3349 'ug_user' => $this->
getId(),
3350 'ug_group' => $group,
3354 $dbw->insert(
'user_former_groups',
3356 'ufg_user' => $this->
getId(),
3357 'ufg_group' => $group,
3364 $this->mGroups = array_diff( $this->mGroups, [ $group ] );
3369 $this->mRights =
null;
3381 return $this->
getId() != 0;
3402 Hooks::run(
"UserIsBot", [ $this, &$isBot ] );
3414 $permissions = func_get_args();
3415 foreach ( $permissions
as $permission ) {
3416 if ( $this->
isAllowed( $permission ) ) {
3429 $permissions = func_get_args();
3430 foreach ( $permissions
as $permission ) {
3431 if ( !$this->
isAllowed( $permission ) ) {
3444 if ( $action ===
'' ) {
3449 return in_array( $action, $this->
getRights(),
true );
3491 if ( $this->mRequest ) {
3492 return $this->mRequest;
3507 public function isWatched( $title, $checkRights = self::CHECK_USER_RIGHTS ) {
3508 if (
$title->isWatchable() && ( !$checkRights || $this->isAllowed(
'viewmywatchlist' ) ) ) {
3509 return MediaWikiServices::getInstance()->getWatchedItemStore()->isWatched( $this,
$title );
3521 public function addWatch( $title, $checkRights = self::CHECK_USER_RIGHTS ) {
3522 if ( !$checkRights || $this->
isAllowed(
'editmywatchlist' ) ) {
3523 MediaWikiServices::getInstance()->getWatchedItemStore()->addWatchBatchForUser(
3538 public function removeWatch( $title, $checkRights = self::CHECK_USER_RIGHTS ) {
3539 if ( !$checkRights || $this->
isAllowed(
'editmywatchlist' ) ) {
3540 $store = MediaWikiServices::getInstance()->getWatchedItemStore();
3541 $store->removeWatch( $this,
$title->getSubjectPage() );
3542 $store->removeWatch( $this,
$title->getTalkPage() );
3564 if ( !$this->
isAllowed(
'editmywatchlist' ) ) {
3570 if ( !Hooks::run(
'UserClearNewTalkNotification', [ &$this, $oldid ] ) ) {
3575 DeferredUpdates::addCallableUpdate(
function()
use (
$title, $oldid ) {
3585 ?
$title->getNextRevisionID( $oldid, Title::GAID_FOR_UPDATE )
3611 MediaWikiServices::getInstance()->getWatchedItemStore()
3612 ->resetNotificationTimestamp( $this,
$title, $force, $oldid );
3633 $id = $this->
getId();
3639 $asOfTimes = array_unique( $dbw->selectFieldValues(
3641 'wl_notificationtimestamp',
3642 [
'wl_user' => $id,
'wl_notificationtimestamp IS NOT NULL' ],
3644 [
'ORDER BY' =>
'wl_notificationtimestamp DESC',
'LIMIT' => 500 ]
3646 if ( !$asOfTimes ) {
3653 [
'wl_notificationtimestamp' =>
null ],
3654 [
'wl_user' => $id,
'wl_notificationtimestamp' => $asOfTimes ],
3661 function ()
use ( $dbw, $id ) {
3664 $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
3665 $ticket =
$lbFactory->getEmptyTransactionTicket( __METHOD__ );
3666 $asOfTimes = array_unique( $dbw->selectFieldValues(
3668 'wl_notificationtimestamp',
3669 [
'wl_user' => $id,
'wl_notificationtimestamp IS NOT NULL' ],
3675 [
'wl_notificationtimestamp' =>
null ],
3676 [
'wl_user' => $id,
'wl_notificationtimestamp' => $asOfTimeBatch ],
3679 $lbFactory->commitAndWaitForReplication( __METHOD__, $ticket );
3767 if ( 0 == $this->mId ) {
3771 $session = $this->
getRequest()->getSession();
3773 $session = $session->sessionWithRequest(
$request );
3775 $delay = $session->delaySave();
3777 if ( !$session->getUser()->equals( $this ) ) {
3778 if ( !$session->canSetUser() ) {
3779 \MediaWiki\Logger\LoggerFactory::getInstance(
'session' )
3780 ->warning( __METHOD__ .
3781 ": Cannot save user \"$this\" to a user \"{$session->getUser()}\"'s immutable session"
3785 $session->setUser( $this );
3788 $session->setRememberUser( $rememberMe );
3789 if ( $secure !==
null ) {
3790 $session->setForceHTTPS( $secure );
3793 $session->persist();
3795 ScopedCallback::consume( $delay );
3802 if ( Hooks::run(
'UserLogout', [ &$this ] ) ) {
3812 $session = $this->
getRequest()->getSession();
3813 if ( !$session->canSetUser() ) {
3814 \MediaWiki\Logger\LoggerFactory::getInstance(
'session' )
3815 ->warning( __METHOD__ .
": Cannot log out of an immutable session" );
3816 $error =
'immutable';
3817 } elseif ( !$session->getUser()->equals( $this ) ) {
3818 \MediaWiki\Logger\LoggerFactory::getInstance(
'session' )
3819 ->warning( __METHOD__ .
3820 ": Cannot log user \"$this\" out of a user \"{$session->getUser()}\"'s session"
3824 $error =
'wronguser';
3827 $delay = $session->delaySave();
3828 $session->unpersist();
3829 $session->setLoggedOutTimestamp( time() );
3830 $session->setUser(
new User );
3831 $session->set(
'wsUserID', 0 );
3832 $session->resetAllTokens();
3833 ScopedCallback::consume( $delay );
3836 \MediaWiki\Logger\LoggerFactory::getInstance(
'authevents' )->info(
'Logout', [
3837 'event' =>
'logout',
3838 'successful' => $error ===
false,
3839 'status' => $error ?:
'success',
3853 "Could not update user with ID '{$this->mId}'; DB is read-only."
3859 if ( 0 == $this->mId ) {
3869 $dbw->update(
'user',
3871 'user_name' => $this->mName,
3872 'user_real_name' => $this->mRealName,
3873 'user_email' => $this->mEmail,
3874 'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ),
3875 'user_touched' => $dbw->timestamp( $newTouched ),
3876 'user_token' => strval( $this->mToken ),
3877 'user_email_token' => $this->mEmailToken,
3878 'user_email_token_expires' => $dbw->timestampOrNull( $this->mEmailTokenExpires ),
3879 ], $this->makeUpdateConditions( $dbw, [
3880 'user_id' => $this->mId,
3884 if ( !$dbw->affectedRows() ) {
3888 $from = ( $this->queryFlagsUsed & self::READ_LATEST ) ?
'master' :
'replica';
3890 "CAS update failed on user_touched for user ID '{$this->mId}' (read from $from);" .
3891 " the version of the user to be saved is older than the current version."
3895 $this->mTouched = $newTouched;
3898 Hooks::run(
'UserSaveSettings', [ $this ] );
3915 $db = ( (
$flags & self::READ_LATEST ) == self::READ_LATEST )
3919 $options = ( (
$flags & self::READ_LOCKING ) == self::READ_LOCKING )
3920 ? [
'LOCK IN SHARE MODE' ]
3923 $id = $db->selectField(
'user',
3924 'user_id', [
'user_name' =>
$s ], __METHOD__,
$options );
3945 foreach ( [
'password',
'newpassword',
'newpass_time',
'password_expires' ]
as $field ) {
3946 if ( isset(
$params[$field] ) ) {
3947 wfDeprecated( __METHOD__ .
" with param '$field'",
'1.27' );
3955 if ( isset(
$params[
'options'] ) ) {
3960 $seqVal = $dbw->nextSequenceValue(
'user_user_id_seq' );
3962 $noPass = PasswordFactory::newInvalidPassword()->toString();
3965 'user_id' => $seqVal,
3966 'user_name' =>
$name,
3967 'user_password' => $noPass,
3968 'user_newpassword' => $noPass,
3969 'user_email' =>
$user->mEmail,
3970 'user_email_authenticated' => $dbw->timestampOrNull(
$user->mEmailAuthenticated ),
3971 'user_real_name' =>
$user->mRealName,
3972 'user_token' => strval(
$user->mToken ),
3973 'user_registration' => $dbw->timestamp(
$user->mRegistration ),
3974 'user_editcount' => 0,
3975 'user_touched' => $dbw->timestamp(
$user->newTouchedTimestamp() ),
3978 $fields[
"user_$name"] =
$value;
3980 $dbw->insert(
'user', $fields, __METHOD__, [
'IGNORE' ] );
3981 if ( $dbw->affectedRows() ) {
4017 if ( !$this->mToken ) {
4023 $noPass = PasswordFactory::newInvalidPassword()->toString();
4026 $seqVal = $dbw->nextSequenceValue(
'user_user_id_seq' );
4027 $dbw->insert(
'user',
4029 'user_id' => $seqVal,
4030 'user_name' => $this->mName,
4031 'user_password' => $noPass,
4032 'user_newpassword' => $noPass,
4033 'user_email' => $this->mEmail,
4034 'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ),
4035 'user_real_name' => $this->mRealName,
4036 'user_token' => strval( $this->mToken ),
4037 'user_registration' => $dbw->timestamp( $this->mRegistration ),
4038 'user_editcount' => 0,
4039 'user_touched' => $dbw->timestamp( $this->mTouched ),
4043 if ( !$dbw->affectedRows() ) {
4045 $this->mId = $dbw->selectField(
4048 [
'user_name' => $this->mName ],
4050 [
'LOCK IN SHARE MODE' ]
4059 throw new MWException( __METHOD__ .
": hit a key conflict attempting " .
4060 "to insert user '{$this->mName}' row, but it was not present in select!" );
4062 return Status::newFatal(
'userexists' );
4064 $this->mId = $dbw->insertId();
4065 self::$idCacheByName[$this->mName] = $this->mId;
4071 return Status::newGood();
4093 wfDebug( __METHOD__ .
"()\n" );
4095 if ( $this->mId == 0 ) {
4100 if ( !$userblock ) {
4104 return (
bool)$userblock->doAutoblock( $this->
getRequest()->getIP() );
4113 if ( $this->mBlock && $this->mBlock->prevents(
'createaccount' ) ) {
4114 return $this->mBlock;
4117 # bug 13611: if the IP address the user is trying to create an account from is
4118 # blocked with createaccount disabled, prevent new account creation there even
4119 # when the user is logged in
4120 if ( $this->mBlockedFromCreateAccount ===
false && !$this->
isAllowed(
'ipblock-exempt' ) ) {
4123 return $this->mBlockedFromCreateAccount instanceof
Block
4124 && $this->mBlockedFromCreateAccount->
prevents(
'createaccount' )
4125 ? $this->mBlockedFromCreateAccount
4135 return $this->mBlock && $this->mBlock->prevents(
'sendemail' );
4162 return $title->getTalkPage();
4171 return !$this->
isAllowed(
'autoconfirmed' );
4181 $manager = AuthManager::singleton();
4182 $reqs = AuthenticationRequest::loadRequestsFromSubmission(
4183 $manager->getAuthenticationRequests( AuthManager::ACTION_LOGIN ),
4185 'username' => $this->getName(),
4186 'password' => $password,
4189 $res = AuthManager::singleton()->beginAuthentication( $reqs,
'null:' );
4190 switch (
$res->status ) {
4191 case AuthenticationResponse::PASS:
4193 case AuthenticationResponse::FAIL:
4195 \MediaWiki\Logger\LoggerFactory::getInstance(
'authentication' )
4196 ->info( __METHOD__ .
': Authentication failed: ' .
$res->message->plain() );
4199 throw new BadMethodCallException(
4200 'AuthManager returned a response unsupported by ' . __METHOD__
4237 return $request->getSession()->getToken( $salt );
4265 return MediaWiki\Session\Token::getTimestamp( $val );
4295 $val = substr( $val, 0, strspn( $val,
'0123456789abcdef' ) ) . Token::SUFFIX;
4314 if (
$type ==
'created' ||
$type ===
false ) {
4315 $message =
'confirmemail_body';
4316 } elseif (
$type ===
true ) {
4317 $message =
'confirmemail_body_changed';
4320 $message =
'confirmemail_body_' .
$type;
4328 $wgLang->userTimeAndDate( $expiration, $this ),
4330 $wgLang->userDate( $expiration, $this ),
4331 $wgLang->userTime( $expiration, $this ) )->text() );
4357 'replyTo' => $replyto,
4378 $hash = md5( $token );
4379 $this->mEmailToken = $hash;
4380 $this->mEmailTokenExpires = $expiration;
4390 return $this->
getTokenUrl(
'ConfirmEmail', $token );
4399 return $this->
getTokenUrl(
'InvalidateEmail', $token );
4418 $title = Title::makeTitle(
NS_MAIN,
"Special:$page/$token" );
4419 return $title->getCanonicalURL();
4434 Hooks::run(
'ConfirmEmailComplete', [ $this ] );
4448 $this->mEmailToken =
null;
4449 $this->mEmailTokenExpires =
null;
4452 Hooks::run(
'InvalidateEmailComplete', [ $this ] );
4463 Hooks::run(
'UserSetEmailAuthenticationTimestamp', [ $this, &$this->mEmailAuthenticated ] );
4477 Hooks::run(
'UserCanSendEmail', [ &$this, &$canSend ] );
4504 if ( Hooks::run(
'EmailConfirmed', [ &$this, &$confirmed ] ) ) {
4508 if ( !Sanitizer::validateEmail( $this->mEmail ) ) {
4528 $this->mEmailToken &&
4544 return $this->mRegistration;
4554 if ( $this->
getId() == 0 ) {
4558 $time =
$dbr->selectField(
'revision',
'rev_timestamp',
4559 [
'rev_user' => $this->
getId() ],
4561 [
'ORDER BY' =>
'rev_timestamp ASC' ]
4579 foreach ( $groups
as $group ) {
4581 $rights = array_merge( $rights,
4587 foreach ( $groups
as $group ) {
4589 $rights = array_diff( $rights,
4593 return array_unique( $rights );
4604 $allowedGroups = [];
4606 if ( self::groupHasPermission( $group, $role ) ) {
4607 $allowedGroups[] = $group;
4610 return $allowedGroups;
4651 if ( isset(
$cache[$right] ) && !defined(
'MW_PHPUNIT_TEST' ) ) {
4662 if ( isset( $rights[$right] ) && $rights[$right] ) {
4670 if ( !defined(
'MW_NO_SESSION' ) ) {
4671 $allowedRights = SessionManager::getGlobalSession()->getAllowedUserRights();
4672 if ( $allowedRights !==
null && !in_array( $right, $allowedRights,
true ) ) {
4679 if ( !Hooks::run(
'UserIsEveryoneAllowed', [ $right ] ) ) {
4696 return $msg->isBlank() ? $group : $msg->text();
4708 return $msg->isBlank() ? $group : $msg->text();
4721 self::getImplicitGroups()
4730 if ( self::$mAllRights ===
false ) {
4733 self::$mAllRights = array_unique( array_merge( self::$mCoreRights,
$wgAvailableRights ) );
4735 self::$mAllRights = self::$mCoreRights;
4737 Hooks::run(
'UserGetAllRights', [ &self::$mAllRights ] );
4739 return self::$mAllRights;
4750 # Deprecated, use $wgImplicitGroups instead
4751 Hooks::run(
'UserGetImplicitGroups', [ &$groups ],
'1.25' );
4763 $msg =
wfMessage(
'grouppage-' . $group )->inContentLanguage();
4764 if ( $msg->exists() ) {
4765 $title = Title::newFromText( $msg->text() );
4766 if ( is_object(
$title ) ) {
4782 if ( $text ==
'' ) {
4783 $text = self::getGroupName( $group );
4785 $title = self::getGroupPage( $group );
4789 return htmlspecialchars( $text );
4802 if ( $text ==
'' ) {
4803 $text = self::getGroupName( $group );
4805 $title = self::getGroupPage( $group );
4808 return "[[$page|$text]]";
4837 $groups[
'add'] = self::getAllGroups();
4846 $groups[
'remove'] = self::getAllGroups();
4854 if ( is_int( $key ) ) {
4862 if ( is_int( $key ) ) {
4897 if ( $this->
isAllowed(
'userrights' ) ) {
4920 foreach ( $addergroups
as $addergroup ) {
4921 $groups = array_merge_recursive(
4924 $groups[
'add'] = array_unique( $groups[
'add'] );
4925 $groups[
'remove'] = array_unique( $groups[
'remove'] );
4926 $groups[
'add-self'] = array_unique( $groups[
'add-self'] );
4927 $groups[
'remove-self'] = array_unique( $groups[
'remove-self'] );
4958 [
'user_editcount=user_editcount+1' ],
4959 [
'user_id' => $this->
getId(),
'user_editcount IS NOT NULL' ],
4963 if ( $dbw->affectedRows() == 0 ) {
4966 if (
$dbr !== $dbw ) {
4978 if ( $this->mEditCount ===
null ) {
4981 $this->mEditCount += (
$dbr !== $dbw ) ? 1 : 0;
4983 $this->mEditCount++;
5003 [
'rev_user' => $this->getId() ],
5011 [
'user_editcount' =>
$count ],
5012 [
'user_id' => $this->
getId() ],
5026 $key =
"right-$right";
5028 return $msg->isBlank() ? $right : $msg->text();
5040 public static function crypt( $password, $salt =
false ) {
5044 $hash = $passwordFactory->newFromPlaintext( $password );
5045 return $hash->toString();
5064 if ( preg_match(
'/^[0-9a-f]{32}$/', $hash ) ) {
5067 $password =
":B:{$userId}:{$hash}";
5069 $password =
":A:{$hash}";
5075 $hash = $passwordFactory->newFromCiphertext( $hash );
5076 return $hash->equals( $password );
5127 if ( $this->mOptionsLoaded ) {
5131 $this->mOptions = self::getDefaultOptions();
5133 if ( !$this->
getId() ) {
5139 $this->mOptions[
'variant'] = $variant;
5140 $this->mOptions[
'language'] = $variant;
5141 $this->mOptionsLoaded =
true;
5146 if ( !is_null( $this->mOptionOverrides ) ) {
5147 wfDebug(
"User: loading options for user " . $this->
getId() .
" from override cache.\n" );
5148 foreach ( $this->mOptionOverrides
as $key =>
$value ) {
5149 $this->mOptions[$key] =
$value;
5152 if ( !is_array( $data ) ) {
5153 wfDebug(
"User: loading options for user " . $this->
getId() .
" from database.\n" );
5155 $dbr = ( $this->queryFlagsUsed & self::READ_LATEST )
5161 [
'up_property',
'up_value' ],
5162 [
'up_user' => $this->
getId() ],
5166 $this->mOptionOverrides = [];
5168 foreach (
$res as $row ) {
5169 $data[$row->up_property] = $row->up_value;
5178 $this->mOptionsLoaded =
true;
5180 Hooks::run(
'UserLoadOptions', [ $this, &$this->mOptions ] );
5192 $saveOptions = $this->mOptions;
5196 if ( !Hooks::run(
'UserSaveOptions', [ $this, &$saveOptions ] ) ) {
5200 $userId = $this->
getId();
5203 foreach ( $saveOptions
as $key =>
$value ) {
5205 $defaultOption = self::getDefaultOption( $key );
5206 if ( ( $defaultOption ===
null &&
$value !==
false &&
$value !==
null )
5207 ||
$value != $defaultOption
5210 'up_user' => $userId,
5211 'up_property' => $key,
5219 $res = $dbw->select(
'user_properties',
5220 [
'up_property',
'up_value' ], [
'up_user' => $userId ], __METHOD__ );
5225 foreach (
$res as $row ) {
5226 if ( !isset( $saveOptions[$row->up_property] )
5227 || strcmp( $saveOptions[$row->up_property], $row->up_value ) != 0
5229 $keysDelete[] = $row->up_property;
5233 if ( count( $keysDelete ) ) {
5241 $dbw->delete(
'user_properties',
5242 [
'up_user' => $userId,
'up_property' => $keysDelete ], __METHOD__ );
5245 $dbw->insert(
'user_properties', $insert_rows, __METHOD__, [
'IGNORE' ] );
5292 # Note that the pattern requirement will always be satisfied if the
5293 # input is empty, so we need required in all cases.
5295 # @todo FIXME: Bug 23769: This needs to not claim the password is required
5296 # if e-mail confirmation is being used. Since HTML5 input validation
5297 # is b0rked anyway in some browsers, just return nothing. When it's
5298 # re-enabled, fix this code to not output required for e-mail
5300 # $ret = array( 'required' );
5303 # We can't actually do this right now, because Opera 9.6 will print out
5304 # the entered password visibly in its error message! When other
5305 # browsers add support for this attribute, or Opera fixes its support,
5306 # we can add support with a version check to avoid doing this on Opera
5307 # versions where it will be a problem. Reported to Opera as
5308 # DSK-262266, but they don't have a public bug tracker for us to follow.
5333 'user_email_authenticated',
5335 'user_email_token_expires',
5336 'user_registration',
5351 $groups = array_map(
5352 [
'User',
'makeGroupLinkWiki' ],
5357 return Status::newFatal(
'badaccess-groups',
$wgLang->commaList( $groups ), count( $groups ) );
5359 return Status::newFatal(
'badaccess-group0' );
5373 if ( !$this->
getId() ) {
5378 if ( !
$user->loadFromId( self::READ_EXCLUSIVE ) ) {
and(b) You must cause any modified files to carry prominent notices stating that You changed the files
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
$wgRateLimitsExcludedIPs
Array of IPs which should be excluded from rate limits.
$wgApplyIpBlocksToXff
Whether to look at the X-Forwarded-For header's list of (potentially spoofed) IPs and apply IP blocks...
$wgUserEmailConfirmationTokenExpiry
The time, in seconds, when an email confirmation email expires.
$wgMaxArticleSize
Maximum article size in kilobytes.
$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.
$wgCookieExpiration
Default cookie lifetime, in seconds.
$wgPasswordPolicy
Password policy for local wiki users.
$wgUseFilePatrol
Use file patrolling to check new files on Special:Newfiles.
string null $wgAuthenticationTokenVersion
Versioning for authentication tokens.
$wgProxyWhitelist
Proxy whitelist, list of addresses that are assumed to be non-proxy despite what the other methods mi...
$wgClockSkewFudge
Clock skew or the one-second resolution of time() can occasionally cause cache problems when the user...
$wgGroupsAddToSelf
A map of group names that the user is in, to group names that those users are allowed to add or revok...
$wgPasswordSalt
For compatibility with old installations set to false.
$wgAvailableRights
A list of available rights, in addition to the ones defined by the core.
$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...
$wgUseNPPatrol
Use new page patrolling to check new pages on Special:Newpages.
$wgInvalidUsernameCharacters
Characters to prevent during new account creations.
$wgExtendedLoginCookieExpiration
Default login cookie lifetime, in seconds.
$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...
$wgBlockAllowsUTEdit
Set this to true to allow blocked users to edit their own user talk page.
$wgGroupPermissions
Permission keys given to users in each group.
$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.
$wgEnableDnsBlacklist
Whether to use DNS blacklists in $wgDnsBlacklistUrls to check for open proxies.
$wgDnsBlacklistUrls
List of DNS blacklists to use, if $wgEnableDnsBlacklist is true.
$wgMinimalPasswordLength
Specifies the minimal length of a user password.
$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.
$wgUpdateRowsPerQuery
Number of rows to update per query.
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.
wfGetLB( $wiki=false)
Get a load balancer object.
wfReadOnly()
Check whether the wiki is in read-only mode.
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
wfMemcKey()
Make a cache key for the local wiki.
wfCanIPUseHTTPS( $ip)
Determine whether the client at a given source IP is likely to be able to access the wiki via HTTPS.
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.
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
wfWikiID()
Get an ASCII string identifying this wiki This is used as a prefix in memcached keys.
if(! $wgDBerrorLogTZ) $wgRequest
Deferrable Update for closure/callback updates that should use auto-commit mode.
prevents( $action, $x=null)
Get/set whether the Block prevents a given action.
setBlocker( $user)
Set the user who implemented (or will implement) this block.
static getBlocksForIPList(array $ipChain, $isAnon, $fromMaster=false)
Get all blocks that match any IP from an array of IP addresses.
static chooseBlock(array $blocks, array $ipChain)
From a list of multiple blocks, find the most exact and strongest Block.
setTarget( $target)
Set the target for this block, and update $this->type accordingly.
static newFromTarget( $specificTarget, $vagueTarget=null, $fromMaster=false)
Given a target and the target's type, get an existing Block object if possible.
getRequest()
Get the WebRequest object.
static getDBOptions( $bitfield)
Get an appropriate DB index, options, and fallback DB index for a query.
static hasFlags( $bitfield, $flags)
Base class for the more common types of database errors.
Relational database abstraction object.
timestamp( $ts=0)
Convert a timestamp in one of the formats accepted by wfTimestamp() to the format used for inserting ...
static isIPv4( $ip)
Given a string, determine if it as valid IP in IPv4 only.
static sanitizeIP( $ip)
Convert an IP into a verbose, uppercase, normalized form.
static isIPAddress( $ip)
Determine if a string is as valid IP address or network (CIDR prefix).
static getSubnet( $ip)
Returns the subnet of a given IP.
static isIPv6( $ip)
Given a string, determine if it as valid IP in IPv6 only.
static array $languagesWithVariants
languages supporting variants
static link( $target, $html=null, $customAttribs=[], $query=[], $options=[])
This function returns an HTML link to the given target.
Value object representing a logged-out user's edit token.
static hmac( $data, $key, $raw=true)
Generate an acceptably unstable one-way-hmac of some text making use of the best hash algorithm that ...
static generateHex( $chars, $forceStrong=false)
Generate a run of (ideally) 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 log entries manually, to inject them into the database.
Factory class for creating and checking Password objects.
static getSaveBlacklist()
static getPreferences( $user, IContextSource $context)
static getMain()
Static methods.
static loadFromTimestamp( $db, $title, $timestamp)
Load the revision for the given title with the given timestamp.
static newFromId( $id, $flags=0)
Load a page revision from a given revision ID number.
static normalizeKey( $key)
Normalize a skin preference value to a form that can be loaded.
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,...
loadFromSession()
Load user data from the session.
addWatch( $title, $checkRights=self::CHECK_USER_RIGHTS)
Watch an article.
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.
getPasswordValidity( $password)
Given unvalidated password input, return error message on failure.
Block $mBlockedFromCreateAccount
getName()
Get the user name, or the IP of an anonymous user.
addToDatabase()
Add this existing user object to the database.
static passwordChangeInputAttribs()
Provide an array of HTML5 attributes to put on an input element intended for the user to enter a new ...
requiresHTTPS()
Determine based on the wiki configuration and the user's options, whether this user must be over HTTP...
isBlocked( $bFromSlave=true)
Check if user is blocked.
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.
static $mCacheVars
Array of Strings List of member variables which are saved to the shared cache (memcached).
getEmailAuthenticationTimestamp()
Get the timestamp of the user's e-mail authentication.
isBlockedFromEmailuser()
Get whether the user is blocked from using Special:Emailuser.
$mOptionsLoaded
Bool Whether the cache variables have been loaded.
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.
getBlockedStatus( $bFromSlave=true)
Get blocking information.
static changeableByGroup( $group)
Returns an array of the groups that a particular group can add/remove.
const VERSION
@const int Serialized record version.
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
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.
static $mAllRights
String Cached results of getAllRights()
getTokenUrl( $page, $token)
Internal function to format the e-mail validation/invalidation URLs.
isBlockedGlobally( $ip='')
Check if user is blocked on all wikis.
string $mQuickTouched
TS_MW timestamp from cache.
const INVALID_TOKEN
@const string An invalid value for user_token
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.
static crypt( $password, $salt=false)
Make a new-style password hash.
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.
static $mCoreRights
Array of Strings Core rights.
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.
clearCookie( $name, $secure=null, $params=[])
Clear a cookie on the user's client.
isPasswordReminderThrottled()
Has password reminder email been sent within the last $wgPasswordReminderResendTime hours?
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.
static edits( $uid)
Count the number of edits of a user.
isItemLoaded( $item, $all='all')
Return whether an item has been loaded.
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.
static randomPassword()
Return a random password.
static purge( $wikiId, $userId)
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?
loadDefaults( $name=false)
Set cached properties to default.
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?
clearSharedCache( $mode='changed')
Clear user data from 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.
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.
$mNewtalk
Lazy-initialized variables, invalidated with clearInstanceCache.
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 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.
checkPasswordValidity( $password, $purpose='login')
Check if this is a valid password for this user.
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.
static getGroupPage( $group)
Get the title of a page describing a particular group.
idForName( $flags=0)
If only this user's username is known, and it exists, return the user ID.
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.
addGroup( $group)
Add the user to the given group.
setPassword( $str)
Set the password and reset the random token.
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.
incEditCountImmediate()
Increment the user's edit-count field.
setExtendedLoginCookie( $name, $value, $secure)
Set an extended login cookie on the user's client.
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.
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...
integer $queryFlagsUsed
User::READ_* constant bitfield used to load data.
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.
saveOptions()
Saves the non-default options for this user, as previously set e.g.
static getRightDescription( $right)
Get the description of a given right.
getEditCount()
Get the user's edit count.
addAutopromoteOnceGroups( $event)
Add the user to the group if he/she meets given criteria.
isValidPassword( $password)
Is the input a valid password for this user?
getBlock( $bFromSlave=true)
Get the block affecting the user, or null if the user is not blocked.
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.
static getPasswordFactory()
Lazily instantiate and return a factory object for making passwords.
getTitleKey()
Get the user's name escaped by underscores.
static isIP( $name)
Does the string match an anonymous IP address?
static makeGroupLinkWiki( $group, $text='')
Create a link to the group in Wikitext, if available; else return the group name.
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.
isBlockedFrom( $title, $bFromSlave=false)
Check if user is blocked from editing a particular article.
getUserPage()
Get this user's personal page title.
$mFrom
String Initialization data source if mLoadedItems!==true.
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)
initEditCount( $add=0)
Initialize user_editcount from data out of the revision table.
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.
isAllowedAny()
Check if user is allowed to access a feature / make an action.
getEmail()
Get the user's e-mail address.
static newFatalPermissionDeniedStatus( $permission)
Factory function for fatal permission-denied errors.
setNewpassword( $str, $throttle=true)
Set the password for a password reminder or new account email.
getAutomaticGroups( $recache=false)
Get the list of implicit group memberships this user has.
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
const GETOPTIONS_EXCLUDE_DEFAULTS
Exclude user options that are set to their default value.
const TOKEN_LENGTH
@const int Number of characters in user_token field.
equals(User $user)
Checks if two user objects point to the same user.
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.
static getGroupMember( $group, $username='#')
Get the localized descriptive name for a member of a group, if it exists.
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.
setCookie( $name, $value, $exp=0, $secure=null, $params=[], $request=null)
Set a cookie on the user's client.
setItemLoaded( $item)
Set that an item has been loaded.
incEditCount()
Deferred version of incEditCountImmediate()
$mLoadedItems
Array with already loaded items or true if all items have been loaded.
blockedFor()
If user is blocked, return the specified reason for the block.
static getGroupName( $group)
Get the localized descriptive name for a group, if it exists.
static listOptionKinds()
Return a list of the types of user options currently returned by User::getOptionKinds().
static comparePasswords( $hash, $password, $userId=false)
Compare a password hash with a plain-text password.
resetTokenFromOption( $oname)
Reset a token stored in the preferences (like the watchlist one).
static makeGroupLinkHTML( $group, $text='')
Create a link to the group in HTML, if available; else return the group name.
static getImplicitGroups()
Get a list of implicit groups.
changeableGroups()
Returns an array of groups that this user can add and remove.
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.
static getEditTokenTimestamp( $val)
Get the embedded timestamp from a token.
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.
makeUpdateConditions(Database $db, array $conditions)
Builds update conditions.
const EDIT_TOKEN_SUFFIX
Global constant made accessible as class constants so that autoloader magic can be used.
clearAllNotifications()
Resets all of the given user's page-change notification timestamps.
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...
The ContentHandler facility adds support for arbitrary content types on wiki pages
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at etc Handles the details of getting and saving to the user table of the and dealing with sessions and cookies OutputPage Encapsulates the entire HTML page that will be sent in response to any server request It is used by calling its functions to add in any and then calling but I prefer the flexibility This should also do the output encoding The system allocates a global one in $wgOut Title Represents the title of an article
this class mediates it Skin Encapsulates a look and feel for the wiki All of the functions that render HTML and make choices about how to render it are here and are called from various other places when and is meant to be subclassed with other skins that may override some of its functions The User object contains a reference to a and so rather than having a global skin object we just rely on the global User and get the skin with $wgUser and also has some character encoding functions and other locale stuff The current user interface language is instantiated as and the local content language as $wgContLang
when a variable name is used in a it is silently declared as a new local masking the global
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at etc Handles the details of getting and saving to the user table of the and dealing with sessions and cookies OutputPage Encapsulates the entire HTML page that will be sent in response to any server request It is used by calling its functions to add text
this class mediates it Skin Encapsulates a look and feel for the wiki All of the functions that render HTML and make choices about how to render it are here and are called from various other places when and is meant to be subclassed with other skins that may override some of its functions The User object contains a reference to a and so rather than having a global skin object we just rely on the global User and get the skin with $wgUser and also has some character encoding functions and other locale stuff The current user interface language is instantiated as $wgLang
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
const EDIT_TOKEN_SUFFIX
String Some punctuation to prevent editing from broken text-mangling proxies.
this hook is for auditing only $req
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set $status
the array() calling protocol came about after MediaWiki 1.4rc1.
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a local account $user
namespace are movable Hooks may change this value to override the return value of MWNamespace::isMovable(). 'NewDifferenceEngine' do that in ParserLimitReportFormat instead use this to modify the parameters of the image and a DIV can begin in one section and end in another Make sure your code can handle that case gracefully See the EditSectionClearerLink extension for an example zero but section is usually empty its values are the globals values before the output is cached one of or reset my talk my contributions etc etc otherwise the built in rate limiting checks are if enabled allows for interception of redirect as a string mapping parameter names to values & $type
see documentation in includes Linker php for Linker::makeImageLink & $time
The index of the header message $result[1]=The index of the body text message $result[2 through n]=Parameters passed to body text message. Please note the header message cannot receive/use parameters. 'ImportHandleLogItemXMLTag':When parsing a XML tag in a log item. Return false to stop further processing of the tag $reader:XMLReader object $logInfo:Array of information 'ImportHandlePageXMLTag':When parsing a XML tag in a page. Return false to stop further processing of the tag $reader:XMLReader object & $pageInfo:Array of information 'ImportHandleRevisionXMLTag':When parsing a XML tag in a page revision. Return false to stop further processing of the tag $reader:XMLReader object $pageInfo:Array of page information $revisionInfo:Array of revision information 'ImportHandleToplevelXMLTag':When parsing a top level XML tag. Return false to stop further processing of the tag $reader:XMLReader object 'ImportHandleUploadXMLTag':When parsing a XML tag in a file upload. Return false to stop further processing of the tag $reader:XMLReader object $revisionInfo:Array of information 'ImportLogInterwikiLink':Hook to change the interwiki link used in log entries and edit summaries for transwiki imports. & $fullInterwikiPrefix:Interwiki prefix, may contain colons. & $pageTitle:String that contains page title. 'ImportSources':Called when reading from the $wgImportSources configuration variable. Can be used to lazy-load the import sources list. & $importSources:The value of $wgImportSources. Modify as necessary. See the comment in DefaultSettings.php for the detail of how to structure this array. 'InfoAction':When building information to display on the action=info page. $context:IContextSource object & $pageInfo:Array of information 'InitializeArticleMaybeRedirect':MediaWiki check to see if title is a redirect. & $title:Title object for the current page & $request:WebRequest & $ignoreRedirect:boolean to skip redirect check & $target:Title/string of redirect target & $article:Article object 'InternalParseBeforeLinks':during Parser 's internalParse method before links but after nowiki/noinclude/includeonly/onlyinclude and other processings. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InternalParseBeforeSanitize':during Parser 's internalParse method just before the parser removes unwanted/dangerous HTML tags and after nowiki/noinclude/includeonly/onlyinclude and other processings. Ideal for syntax-extensions after template/parser function execution which respect nowiki and HTML-comments. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InterwikiLoadPrefix':When resolving if a given prefix is an interwiki or not. Return true without providing an interwiki to continue interwiki search. $prefix:interwiki prefix we are looking for. & $iwData:output array describing the interwiki with keys iw_url, iw_local, iw_trans and optionally iw_api and iw_wikiid. 'InvalidateEmailComplete':Called after a user 's email has been invalidated successfully. $user:user(object) whose email is being invalidated 'IRCLineURL':When constructing the URL to use in an IRC notification. Callee may modify $url and $query, URL will be constructed as $url . $query & $url:URL to index.php & $query:Query string $rc:RecentChange object that triggered url generation 'IsFileCacheable':Override the result of Article::isFileCacheable()(if true) & $article:article(object) being checked 'IsTrustedProxy':Override the result of IP::isTrustedProxy() & $ip:IP being check & $result:Change this value to override the result of IP::isTrustedProxy() 'IsUploadAllowedFromUrl':Override the result of UploadFromUrl::isAllowedUrl() $url:URL used to upload from & $allowed:Boolean indicating if uploading is allowed for given URL 'isValidEmailAddr':Override the result of Sanitizer::validateEmail(), for instance to return false if the domain name doesn 't match your organization. $addr:The e-mail address entered by the user & $result:Set this and return false to override the internal checks 'isValidPassword':Override the result of User::isValidPassword() $password:The password entered by the user & $result:Set this and return false to override the internal checks $user:User the password is being validated for 'Language::getMessagesFileName':$code:The language code or the language we 're looking for a messages file for & $file:The messages file path, you can override this to change the location. 'LanguageGetMagic':DEPRECATED! Use $magicWords in a file listed in $wgExtensionMessagesFiles instead. Use this to define synonyms of magic words depending of the language & $magicExtensions:associative array of magic words synonyms $lang:language code(string) 'LanguageGetNamespaces':Provide custom ordering for namespaces or remove namespaces. Do not use this hook to add namespaces. Use CanonicalNamespaces for that. & $namespaces:Array of namespaces indexed by their numbers 'LanguageGetSpecialPageAliases':DEPRECATED! Use $specialPageAliases in a file listed in $wgExtensionMessagesFiles instead. Use to define aliases of special pages names depending of the language & $specialPageAliases:associative array of magic words synonyms $lang:language code(string) 'LanguageGetTranslatedLanguageNames':Provide translated language names. & $names:array of language code=> language name $code:language of the preferred translations 'LanguageLinks':Manipulate a page 's language links. This is called in various places to allow extensions to define the effective language links for a page. $title:The page 's Title. & $links:Associative array mapping language codes to prefixed links of the form "language:title". & $linkFlags:Associative array mapping prefixed links to arrays of flags. Currently unused, but planned to provide support for marking individual language links in the UI, e.g. for featured articles. 'LanguageSelector':Hook to change the language selector available on a page. $out:The output page. $cssClassName:CSS class name of the language selector. 'LinkBegin':DEPRECATED! Use HtmlPageLinkRendererBegin instead. Used when generating internal and interwiki links in Linker::link(), before processing starts. Return false to skip default processing and return $ret. See documentation for Linker::link() for details on the expected meanings of parameters. $skin:the Skin object $target:the Title that the link is pointing to & $html:the contents that the< a > tag should have(raw HTML) $result
namespace and then decline to actually register it file or subcat img or subcat $title
namespace are movable Hooks may change this value to override the return value of MWNamespace::isMovable(). 'NewDifferenceEngine' do that in ParserLimitReportFormat instead use this to modify the parameters of the image and a DIV can begin in one section and end in another Make sure your code can handle that case gracefully See the EditSectionClearerLink extension for an example zero but section is usually empty its values are the globals values before the output is cached one of or reset my talk my contributions etc etc otherwise the built in rate limiting checks are if enabled $incrBy
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context $options
either a unescaped string or a HtmlArmor object after in associative array form externallinks including delete and has completed for all link tables whether this was an auto creation default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a set this to the key of the message First element is the message additional optional elements are parameters for the key that are processed with wfMessage() -> params() ->parseAsBlock() - offset Set to overwrite offset parameter in $wgRequest set to '' to unset offset - wrap String Wrap the message in html(usually something like "<div ...>$1</div>"). - flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException':Called before an exception(or PHP error) is logged. This is meant for integration with external error aggregation services
null for the local wiki Added in
it s the revision text itself In either if gzip is the revision text is gzipped $flags
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return true
error also a ContextSource you ll probably need to make sure the header is varied on $request
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context the output can only depend on parameters provided to this hook not on global state indicating whether full HTML should be generated If generation of HTML may be but other information should still be present in the ParserOutput object to manipulate or replace but no entry for that model exists in $wgContentHandlers if desired whether it is OK to use $contentModel on $title Handler functions that modify $ok should generally return false to prevent further hooks from further modifying $ok inclusive $limit
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses & $ret
this hook is for auditing only or null if authentication failed before getting that far $username
namespace are movable Hooks may change this value to override the return value of MWNamespace::isMovable(). 'NewDifferenceEngine' do that in ParserLimitReportFormat instead use this to modify the parameters of the image and a DIV can begin in one section and end in another Make sure your code can handle that case gracefully See the EditSectionClearerLink extension for an example zero but section is usually empty its values are the globals values before the output is cached one of or reset my talk page
We ve cleaned up the code here by removing clumps of infrequently used code and moving them off somewhere else It s much easier for someone working with this code to see what s _really_ going and make changes or fix bugs In we can take all the code that deals with the little used title reversing we can concentrate it all in an extension file
Allows to change the fields on the form that will be generated $name
namespace are movable Hooks may change this value to override the return value of MWNamespace::isMovable(). 'NewDifferenceEngine' do that in ParserLimitReportFormat instead use this to modify the parameters of the image and a DIV can begin in one section and end in another Make sure your code can handle that case gracefully See the EditSectionClearerLink extension for an example zero but section is usually empty its values are the globals values before the output is cached $page
presenting them properly to the user as errors is done by the caller return true use this to change the list i e etc $rev
processing should stop and the error should be shown to the user * false
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output modifiable & $code
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Interface for objects which can provide a MediaWiki context on request.
Interface for database access objects.
MediaWiki has optional support for a high distributed memory object caching system For general information on but for a larger site with heavy load
This document describes the state of Postgres support in and is fairly well maintained The main code is very well while extensions are very hit and miss it is probably the most supported database after MySQL Much of the work in making MediaWiki database agnostic came about through the work of creating Postgres as and are nearing end of but without copying over all the usage comments General notes on the but these can almost always be programmed around *Although Postgres has a true BOOLEAN boolean columns are always mapped to as the code does not always treat the column as a boolean(which is limited to accepting true, false, 0, 1, t, or f) *The default data type for all VARCHAR
const TS_UNIX
Unix time - the number of seconds since 1970-01-01 00:00:00 UTC.
const TS_MW
MediaWiki concatenated string timestamp (YYYYMMDDHHMMSS)